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.
- package/dist/DboBuilder/ViewHandler/subscribe.d.ts.map +1 -1
- package/dist/DboBuilder/ViewHandler/subscribe.js +4 -2
- package/dist/DboBuilder/ViewHandler/subscribe.js.map +1 -1
- package/dist/DboBuilder/ViewRelatedTables/getViewRelatedTableJoinCondition.d.ts +1 -1
- package/dist/DboBuilder/ViewRelatedTables/getViewRelatedTableJoinCondition.js +1 -1
- package/dist/DboBuilder/ViewRelatedTables/getViewRelatedTableJoinCondition.js.map +1 -1
- package/dist/DboBuilder/ViewRelatedTables/getViewRelatedTables.d.ts +2 -4
- package/dist/DboBuilder/ViewRelatedTables/getViewRelatedTables.d.ts.map +1 -1
- package/dist/DboBuilder/ViewRelatedTables/getViewRelatedTables.js +1 -1
- package/dist/DboBuilder/ViewRelatedTables/getViewRelatedTables.js.map +1 -1
- package/dist/DboBuilder/getSubscribeRelatedTables.js +2 -2
- package/dist/DboBuilder/getSubscribeRelatedTables.js.map +1 -1
- package/dist/PubSubManager/PubSubManager.d.ts +9 -11
- package/dist/PubSubManager/PubSubManager.d.ts.map +1 -1
- package/dist/PubSubManager/PubSubManager.js +5 -2
- package/dist/PubSubManager/PubSubManager.js.map +1 -1
- package/dist/PubSubManager/addSub.d.ts +2 -3
- package/dist/PubSubManager/addSub.d.ts.map +1 -1
- package/dist/PubSubManager/addSub.js +4 -5
- package/dist/PubSubManager/addSub.js.map +1 -1
- package/dist/PubSubManager/addTrigger.d.ts +1 -1
- package/dist/PubSubManager/addTrigger.d.ts.map +1 -1
- package/dist/PubSubManager/addTrigger.js +1 -4
- package/dist/PubSubManager/addTrigger.js.map +1 -1
- package/dist/PubSubManager/init/getDataWatchFunctionQuery.d.ts.map +1 -1
- package/dist/PubSubManager/init/getDataWatchFunctionQuery.js +62 -72
- package/dist/PubSubManager/init/getDataWatchFunctionQuery.js.map +1 -1
- package/dist/PubSubManager/init/getPubSubManagerInitQuery.js +1 -1
- package/dist/PubSubManager/notifListener.d.ts.map +1 -1
- package/dist/PubSubManager/notifListener.js +44 -35
- package/dist/PubSubManager/notifListener.js.map +1 -1
- package/dist/PubSubManager/refreshTriggers.d.ts.map +1 -1
- package/dist/PubSubManager/refreshTriggers.js +9 -10
- package/dist/PubSubManager/refreshTriggers.js.map +1 -1
- package/lib/DboBuilder/ViewHandler/subscribe.ts +5 -2
- package/lib/DboBuilder/ViewRelatedTables/getViewRelatedTableJoinCondition.ts +1 -1
- package/lib/DboBuilder/ViewRelatedTables/getViewRelatedTables.ts +1 -1
- package/lib/DboBuilder/getSubscribeRelatedTables.ts +2 -2
- package/lib/PubSubManager/PubSubManager.ts +9 -10
- package/lib/PubSubManager/addSub.ts +6 -8
- package/lib/PubSubManager/addTrigger.ts +3 -5
- package/lib/PubSubManager/init/getDataWatchFunctionQuery.ts +62 -72
- package/lib/PubSubManager/init/getPubSubManagerInitQuery.ts +1 -1
- package/lib/PubSubManager/notifListener.ts +53 -48
- package/lib/PubSubManager/refreshTriggers.ts +13 -10
- 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
|
|
46
|
+
SELECT app_id, string_agg(DISTINCT table_condition_id::text, ',') as cids
|
|
47
47
|
FROM prostgles.v_triggers
|
|
48
|
-
WHERE
|
|
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
|
-
|
|
117
|
-
SELECT
|
|
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
|
-
|
|
121
|
-
|
|
122
|
-
|
|
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
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
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
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
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
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
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
|
-
|
|
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
|
|
191
|
+
) THEN %s::text END AS table_condition_id
|
|
202
192
|
$c$,
|
|
203
193
|
table_name,
|
|
204
194
|
condition,
|
|
205
|
-
|
|
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.
|
|
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
|
|
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(({
|
|
104
|
-
conditionIds.includes(
|
|
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
|
-
|
|
115
|
-
|
|
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
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
const
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
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
|
-
|
|
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
|
-
|
|
23
|
-
|
|
24
|
-
|
|
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
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
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({
|