prostgles-server 4.2.490 → 4.2.492

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 (46) hide show
  1. package/dist/DboBuilder/ViewHandler/subscribe.d.ts.map +1 -1
  2. package/dist/DboBuilder/ViewHandler/subscribe.js +4 -2
  3. package/dist/DboBuilder/ViewHandler/subscribe.js.map +1 -1
  4. package/dist/DboBuilder/ViewRelatedTables/getViewRelatedTableJoinCondition.d.ts +1 -1
  5. package/dist/DboBuilder/ViewRelatedTables/getViewRelatedTableJoinCondition.js +1 -1
  6. package/dist/DboBuilder/ViewRelatedTables/getViewRelatedTableJoinCondition.js.map +1 -1
  7. package/dist/DboBuilder/ViewRelatedTables/getViewRelatedTables.d.ts +2 -4
  8. package/dist/DboBuilder/ViewRelatedTables/getViewRelatedTables.d.ts.map +1 -1
  9. package/dist/DboBuilder/ViewRelatedTables/getViewRelatedTables.js +1 -1
  10. package/dist/DboBuilder/ViewRelatedTables/getViewRelatedTables.js.map +1 -1
  11. package/dist/DboBuilder/getSubscribeRelatedTables.js +2 -2
  12. package/dist/DboBuilder/getSubscribeRelatedTables.js.map +1 -1
  13. package/dist/PubSubManager/PubSubManager.d.ts +9 -11
  14. package/dist/PubSubManager/PubSubManager.d.ts.map +1 -1
  15. package/dist/PubSubManager/PubSubManager.js +5 -2
  16. package/dist/PubSubManager/PubSubManager.js.map +1 -1
  17. package/dist/PubSubManager/addSub.d.ts +2 -3
  18. package/dist/PubSubManager/addSub.d.ts.map +1 -1
  19. package/dist/PubSubManager/addSub.js +4 -5
  20. package/dist/PubSubManager/addSub.js.map +1 -1
  21. package/dist/PubSubManager/addTrigger.d.ts +1 -1
  22. package/dist/PubSubManager/addTrigger.d.ts.map +1 -1
  23. package/dist/PubSubManager/addTrigger.js +1 -4
  24. package/dist/PubSubManager/addTrigger.js.map +1 -1
  25. package/dist/PubSubManager/init/getDataWatchFunctionQuery.d.ts.map +1 -1
  26. package/dist/PubSubManager/init/getDataWatchFunctionQuery.js +62 -72
  27. package/dist/PubSubManager/init/getDataWatchFunctionQuery.js.map +1 -1
  28. package/dist/PubSubManager/init/getPubSubManagerInitQuery.js +1 -1
  29. package/dist/PubSubManager/notifListener.d.ts.map +1 -1
  30. package/dist/PubSubManager/notifListener.js +44 -35
  31. package/dist/PubSubManager/notifListener.js.map +1 -1
  32. package/dist/PubSubManager/refreshTriggers.d.ts.map +1 -1
  33. package/dist/PubSubManager/refreshTriggers.js +9 -10
  34. package/dist/PubSubManager/refreshTriggers.js.map +1 -1
  35. package/lib/DboBuilder/ViewHandler/subscribe.ts +5 -2
  36. package/lib/DboBuilder/ViewRelatedTables/getViewRelatedTableJoinCondition.ts +1 -1
  37. package/lib/DboBuilder/ViewRelatedTables/getViewRelatedTables.ts +1 -1
  38. package/lib/DboBuilder/getSubscribeRelatedTables.ts +2 -2
  39. package/lib/PubSubManager/PubSubManager.ts +9 -10
  40. package/lib/PubSubManager/addSub.ts +6 -8
  41. package/lib/PubSubManager/addTrigger.ts +3 -5
  42. package/lib/PubSubManager/init/getDataWatchFunctionQuery.ts +62 -72
  43. package/lib/PubSubManager/init/getPubSubManagerInitQuery.ts +1 -1
  44. package/lib/PubSubManager/notifListener.ts +53 -48
  45. package/lib/PubSubManager/refreshTriggers.ts +13 -10
  46. package/package.json +1 -1
@@ -43,9 +43,9 @@ export const getDataWatchFunctionQuery = (debugMode: boolean | undefined) => {
43
43
  IF (c_ids IS NOT NULL OR has_errors) THEN
44
44
 
45
45
  FOR v_trigger IN
46
- SELECT app_id, string_agg(DISTINCT c_id::text, ',') as cids
46
+ SELECT app_id, string_agg(DISTINCT table_condition_id::text, ',') as cids
47
47
  FROM prostgles.v_triggers
48
- WHERE c_id = ANY(c_ids)
48
+ WHERE table_condition_id = ANY(c_ids)
49
49
  OR has_errors
50
50
  GROUP BY app_id
51
51
  LOOP
@@ -112,79 +112,69 @@ export const getDataWatchFunctionQuery = (debugMode: boolean | undefined) => {
112
112
  const CHANGED_COLUMNS_FOR_EACH_TRIGGER_CHECK = `
113
113
  -- Determine changed columns for UPDATE operations
114
114
  IF TG_OP = 'UPDATE' THEN
115
-
116
- IF NOT EXISTS (
117
- SELECT 1
118
- FROM prostgles.v_triggers
115
+
116
+ FOR v_trigger IN
117
+ SELECT *
118
+ FROM prostgles.v_triggers
119
119
  WHERE table_name = escaped_table
120
- /* If any value is null it means some condition is tracking all columns so we need to check them all */
121
- AND columns_info IS NULL
122
- ) THEN
120
+ AND columns_info IS NOT NULL
121
+ /* These require the views to be added before as CTEs to ensure the condition works */
122
+ AND related_view_name IS NULL
123
+ AND related_view_def IS NULL
124
+ LOOP
123
125
 
124
126
  changed_columns_by_trigger_id := COALESCE(changed_columns_by_trigger_id, '{}');
125
-
126
- FOR v_trigger IN
127
- SELECT *
128
- FROM prostgles.v_triggers
129
- WHERE table_name = escaped_table
130
- AND columns_info IS NOT NULL
131
- /* These require the views to be added before as CTEs to ensure the condition works */
132
- AND related_view_name IS NULL
133
- AND related_view_def IS NULL
134
- LOOP
135
-
136
- query := format(
137
- $c$
138
- WITH changed AS (
139
- SELECT column_name
140
- FROM jsonb_object_keys(%1$L) as column_name
141
- WHERE EXISTS (
142
- SELECT 1
143
- FROM (SELECT * FROM old_table as %5$I WHERE %4$s ) o
144
- FULL OUTER JOIN (SELECT * FROM new_table as %5$I WHERE %4$s ) n
145
- ON %2$s
146
- WHERE %3$s
147
- )
127
+
128
+ query := format(
129
+ $c$
130
+ WITH changed AS (
131
+ SELECT column_name
132
+ FROM jsonb_object_keys(%1$L) as column_name
133
+ WHERE EXISTS (
134
+ SELECT 1
135
+ FROM (SELECT * FROM old_table as %5$I WHERE %4$s ) o
136
+ FULL OUTER JOIN (SELECT * FROM new_table as %5$I WHERE %4$s ) n
137
+ ON %2$s
138
+ WHERE %3$s
148
139
  )
149
- SELECT array_agg(column_name)
150
- FROM changed;
151
- $c$,
152
- v_trigger.columns_info->'tracked_columns',
153
- v_trigger.columns_info->>'join_condition',
154
- v_trigger.columns_info->>'where_statement',
155
- v_trigger.condition,
156
- TG_TABLE_NAME
157
- );
140
+ )
141
+ SELECT array_agg(column_name)
142
+ FROM changed;
143
+ $c$,
144
+ v_trigger.columns_info->'tracked_columns',
145
+ v_trigger.columns_info->>'join_condition',
146
+ v_trigger.columns_info->>'where_statement',
147
+ v_trigger.condition,
148
+ TG_TABLE_NAME
149
+ );
150
+
151
+ BEGIN
152
+ EXECUTE query INTO changed_columns;
153
+ EXCEPTION WHEN OTHERS THEN
154
+
155
+ has_errors := TRUE;
158
156
 
159
- BEGIN
160
- EXECUTE query INTO changed_columns;
161
- EXCEPTION WHEN OTHERS THEN
162
-
163
- has_errors := TRUE;
164
-
165
- GET STACKED DIAGNOSTICS
166
- err_text = MESSAGE_TEXT,
167
- err_detail = PG_EXCEPTION_DETAIL,
168
- err_hint = PG_EXCEPTION_HINT;
169
- END;
170
-
171
- /* It is possible to get no changes */
172
- IF changed_columns IS NOT NULL THEN
173
-
174
- changed_columns := COALESCE(changed_columns, '{}');
175
- changed_columns_by_trigger_id := jsonb_set(
176
- changed_columns_by_trigger_id,
177
- ARRAY[v_trigger.c_id::TEXT],
178
- to_jsonb(changed_columns)
179
- );
180
- END IF;
181
-
182
- --PERFORM pg_notify('debug', changed_columns::TEXT || v_trigger.c_id::TEXT || changed_columns_by_trigger_id::TEXT );
183
-
184
- END LOOP;
185
-
157
+ GET STACKED DIAGNOSTICS
158
+ err_text = MESSAGE_TEXT,
159
+ err_detail = PG_EXCEPTION_DETAIL,
160
+ err_hint = PG_EXCEPTION_HINT;
161
+ END;
186
162
 
187
- END IF;
163
+ /* It is possible to get no changes */
164
+ IF changed_columns IS NOT NULL THEN
165
+
166
+ changed_columns := COALESCE(changed_columns, '{}');
167
+ changed_columns_by_trigger_id := jsonb_set(
168
+ changed_columns_by_trigger_id,
169
+ ARRAY[v_trigger.table_condition_id::TEXT],
170
+ to_jsonb(changed_columns)
171
+ );
172
+ END IF;
173
+
174
+ --PERFORM pg_notify('debug', changed_columns::TEXT || v_trigger.table_condition_id::TEXT || changed_columns_by_trigger_id::TEXT );
175
+
176
+ END LOOP;
177
+
188
178
  END IF;
189
179
 
190
180
  `;
@@ -198,11 +188,11 @@ const EACH_TRIGGER_CHECK_ALL_COLUMNS = `
198
188
  SELECT 1
199
189
  FROM %s
200
190
  WHERE %s
201
- ) THEN %s::text END AS c_id
191
+ ) THEN %s::text END AS table_condition_id
202
192
  $c$,
203
193
  table_name,
204
194
  condition,
205
- c_id
195
+ table_condition_id
206
196
  ),
207
197
  E' UNION \n '
208
198
  )
@@ -238,7 +228,7 @@ const EACH_TRIGGER_CHECK_ALL_COLUMNS = `
238
228
  ||
239
229
  format(
240
230
  $c$
241
- SELECT ARRAY_AGG(DISTINCT t.c_id)
231
+ SELECT ARRAY_AGG(DISTINCT t.table_condition_id)
242
232
  FROM (
243
233
  %s
244
234
  ) t
@@ -219,7 +219,7 @@ BEGIN
219
219
  CREATE OR REPLACE VIEW prostgles.v_triggers AS
220
220
  SELECT *
221
221
  , (ROW_NUMBER() OVER( ORDER BY table_name, condition ))::text AS id
222
- , ROW_NUMBER() OVER(PARTITION BY app_id, table_name ORDER BY table_name, condition ) - 1 AS c_id
222
+ , ROW_NUMBER() OVER(PARTITION BY app_id, table_name ORDER BY table_name, condition ) - 1 AS table_condition_id
223
223
  FROM prostgles.app_triggers;
224
224
  COMMENT ON VIEW prostgles.v_triggers IS 'Augment trigger table with natural IDs and per app IDs';
225
225
 
@@ -1,6 +1,6 @@
1
1
  import { includes, pickKeys } from "prostgles-types";
2
2
  import { parseFieldFilter } from "../DboBuilder/ViewHandler/parseFieldFilter";
3
- import type { PubSubManager } from "./PubSubManager";
3
+ import type { PubSubManager, Subscription } from "./PubSubManager";
4
4
  import { DELIMITER, log, NOTIF_TYPE, type NotifTypeName } from "./PubSubManagerUtils";
5
5
 
6
6
  /* Relay relevant data to relevant subscriptions */
@@ -100,8 +100,8 @@ export async function notifListener(this: PubSubManager, data: { payload: string
100
100
  /* Trigger ok */
101
101
  } else if (conditionIds?.every((id) => Number.isInteger(id))) {
102
102
  state = "ok";
103
- const firedTableConditions = tableTriggerConditions.filter(({ idx }) =>
104
- conditionIds.includes(idx),
103
+ const firedTableConditions = tableTriggerConditions.filter(({ table_condition_id }) =>
104
+ conditionIds.includes(table_condition_id),
105
105
  );
106
106
  const orphanedTableConditions = conditionIds.filter((condId) => {
107
107
  const tc = tableTriggerConditions.at(condId);
@@ -111,8 +111,10 @@ export async function notifListener(this: PubSubManager, data: { payload: string
111
111
  void this.deleteOrphanedTriggers(new Set(table_name));
112
112
  }
113
113
 
114
- firedTableConditions.map(({ idx, subs, syncs }) => {
115
- const changedColumns = changedColumnsByTriggerId && (changedColumnsByTriggerId[idx] ?? []);
114
+ const triggeredSubs = new Set<Subscription>();
115
+ firedTableConditions.map(({ table_condition_id, condition, subs, syncs }) => {
116
+ const changedColumns =
117
+ !changedColumnsByTriggerId ? "*" : (changedColumnsByTriggerId[table_condition_id] ?? []);
116
118
  log(
117
119
  "notifListener",
118
120
  subs.map((s) => s.channel_name),
@@ -123,54 +125,57 @@ export async function notifListener(this: PubSubManager, data: { payload: string
123
125
  void this.syncData(s, undefined, "trigger");
124
126
  });
125
127
 
126
- /* Throttle the subscriptions */
127
- const activeAndReadySubs = subs.filter((sub) =>
128
- sub.triggers.some(
129
- (trg) =>
130
- this.dbo[trg.table_name] &&
131
- sub.is_ready &&
132
- ((sub.socket_id && this.sockets[sub.socket_id]) || sub.onData),
133
- ),
134
- );
135
-
136
- activeAndReadySubs.forEach((sub) => {
137
- const operation = (op_name?.toLowerCase() || "insert") as keyof NonNullable<typeof actions>;
138
- const { tracked_columns, subscribeOptions } = sub;
139
- const { throttle = 0, throttleOpts, actions, skipChangedColumnsCheck } = subscribeOptions;
140
- if (
141
- !skipChangedColumnsCheck &&
142
- changedColumns &&
143
- operation === "update" &&
144
- tracked_columns
145
- ) {
146
- const subFieldsHaveChanged = changedColumns.some((changedColumn) =>
147
- tracked_columns.includes(changedColumn),
148
- );
149
- if (!subFieldsHaveChanged) return;
150
- }
151
-
152
- const actionIsIgnored =
153
- actions &&
154
- !includes(parseFieldFilter(actions, false, ["insert", "update", "delete"]), operation);
155
- if (actionIsIgnored) {
128
+ const operation = (op_name?.toLowerCase() || "insert") as "insert" | "delete" | "update";
129
+
130
+ subs.forEach((sub) => {
131
+ const { triggers, subscribeOptions } = sub;
132
+ const { actions, skipChangedColumnsCheck } = subscribeOptions;
133
+
134
+ const subIsActive =
135
+ sub.is_ready && ((sub.socket_id && this.sockets[sub.socket_id]) || sub.onData);
136
+ if (!subIsActive) return;
137
+
138
+ const didTrigger = triggers.find((subTrigger) => {
139
+ const matchesTableCondition =
140
+ subTrigger.table_name === table_name && subTrigger.condition === condition;
141
+ if (!matchesTableCondition) return false;
142
+ const matchesAction =
143
+ !actions ||
144
+ includes(parseFieldFilter(actions, false, ["insert", "update", "delete"]), operation);
145
+ if (!matchesAction) return false;
146
+
147
+ const subTrackedColumns = subTrigger.tracked_columns;
148
+ const matchesChangedColumns =
149
+ skipChangedColumnsCheck ||
150
+ operation !== "update" ||
151
+ changedColumns === "*" ||
152
+ !subTrackedColumns ||
153
+ changedColumns.some((changedColumn) => subTrackedColumns.includes(changedColumn));
154
+ return matchesChangedColumns;
155
+ });
156
+ if (!didTrigger) {
156
157
  return;
157
158
  }
158
-
159
- if (!throttleOpts?.skipFirst && sub.lastPushed <= Date.now() - throttle) {
160
- /* It is assumed the policy was checked before this point */
161
- void this.pushSubData(sub);
162
- } else if (!sub.is_throttling) {
163
- log("throttling sub for", throttle, "ms");
164
- sub.is_throttling = setTimeout(() => {
165
- log("throttling finished. pushSubData...");
166
- sub.is_throttling = null;
167
- void this.pushSubData(sub);
168
- }, throttle);
169
- }
159
+ triggeredSubs.add(sub);
170
160
  });
171
161
  });
172
162
 
173
- /* Trigger unknown issue */
163
+ triggeredSubs.forEach((sub) => {
164
+ const {
165
+ subscribeOptions: { throttle = 0, throttleOpts },
166
+ } = sub;
167
+ if (!throttleOpts?.skipFirst && sub.lastPushed <= Date.now() - throttle) {
168
+ /* It is assumed the policy was checked before this point */
169
+ void this.pushSubData(sub);
170
+ } else if (!sub.is_throttling) {
171
+ log("throttling sub for", throttle, "ms");
172
+ sub.is_throttling = setTimeout(() => {
173
+ log("throttling finished. pushSubData...");
174
+ sub.is_throttling = null;
175
+ void this.pushSubData(sub);
176
+ }, throttle);
177
+ }
178
+ });
174
179
  } else {
175
180
  state = "invalid_condition_ids";
176
181
  }
@@ -4,6 +4,7 @@ export async function refreshTriggers(this: PubSubManager) {
4
4
  const start = Date.now();
5
5
  const triggers = await this.db.any<{
6
6
  table_name: string;
7
+ table_condition_id: string;
7
8
  condition: string;
8
9
  condition_hash: string;
9
10
  columns_info: TableTriggerInfo["columnInfo"];
@@ -19,17 +20,19 @@ export async function refreshTriggers(this: PubSubManager) {
19
20
 
20
21
  const oldTriggers = new Map(this._triggers);
21
22
 
22
- this._triggers = new Map();
23
- triggers.map((t) => {
24
- this._triggers.set(t.table_name, this._triggers.get(t.table_name) ?? []);
23
+ triggers.forEach((t) => {
24
+ this._triggers.set(
25
+ t.table_name,
26
+ this._triggers.get(t.table_name) ?? new Map<number, TableTriggerInfo>(),
27
+ );
25
28
  const tableTriggers = this._triggers.get(t.table_name)!;
26
- if (!tableTriggers.map((t) => t.condition).includes(t.condition)) {
27
- tableTriggers.push({
28
- condition: t.condition,
29
- hash: t.condition_hash,
30
- columnInfo: t.columns_info,
31
- });
32
- }
29
+ const table_condition_id = Number(t.table_condition_id);
30
+ tableTriggers.set(table_condition_id, {
31
+ condition: t.condition,
32
+ hash: t.condition_hash,
33
+ columnInfo: t.columns_info,
34
+ table_condition_id,
35
+ });
33
36
  });
34
37
 
35
38
  await this._log({
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "prostgles-server",
3
- "version": "4.2.490",
3
+ "version": "4.2.492",
4
4
  "description": "",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",