prostgles-server 4.2.439 → 4.2.441
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/QueryStreamer.js +1 -1
- package/dist/DboBuilder/QueryStreamer.js.map +1 -1
- package/dist/DboBuilder/ViewHandler/ViewHandler.d.ts +6 -6
- package/dist/DboBuilder/ViewHandler/ViewHandler.d.ts.map +1 -1
- package/dist/DboBuilder/ViewHandler/ViewHandler.js +6 -8
- package/dist/DboBuilder/ViewHandler/ViewHandler.js.map +1 -1
- package/dist/DboBuilder/ViewHandler/subscribe.d.ts +4 -12
- package/dist/DboBuilder/ViewHandler/subscribe.d.ts.map +1 -1
- package/dist/DboBuilder/ViewHandler/subscribe.js +11 -25
- package/dist/DboBuilder/ViewHandler/subscribe.js.map +1 -1
- package/dist/Logging.d.ts +2 -1
- package/dist/Logging.d.ts.map +1 -1
- package/dist/PubSubManager/PubSubManager.d.ts +19 -11
- package/dist/PubSubManager/PubSubManager.d.ts.map +1 -1
- package/dist/PubSubManager/PubSubManager.js +35 -25
- package/dist/PubSubManager/PubSubManager.js.map +1 -1
- package/dist/PubSubManager/addSub.d.ts.map +1 -1
- package/dist/PubSubManager/addSub.js +6 -6
- package/dist/PubSubManager/addSub.js.map +1 -1
- package/dist/PubSubManager/addSync.js +1 -1
- package/dist/PubSubManager/addSync.js.map +1 -1
- package/dist/PubSubManager/init/getDataWatchFunctionQuery.d.ts.map +1 -1
- package/dist/PubSubManager/init/getDataWatchFunctionQuery.js +22 -1
- package/dist/PubSubManager/init/getDataWatchFunctionQuery.js.map +1 -1
- package/dist/PubSubManager/notifListener.js +3 -3
- package/dist/PubSubManager/notifListener.js.map +1 -1
- package/dist/PubSubManager/pushSubData.d.ts +1 -1
- package/dist/PubSubManager/pushSubData.d.ts.map +1 -1
- package/dist/PubSubManager/pushSubData.js +21 -42
- package/dist/PubSubManager/pushSubData.js.map +1 -1
- package/dist/PubSubManager/refreshTriggers.d.ts.map +1 -1
- package/dist/PubSubManager/refreshTriggers.js +6 -5
- package/dist/PubSubManager/refreshTriggers.js.map +1 -1
- package/dist/PublishParser/PublishParser.d.ts +3 -6
- package/dist/PublishParser/PublishParser.d.ts.map +1 -1
- package/dist/PublishParser/PublishParser.js +32 -42
- package/dist/PublishParser/PublishParser.js.map +1 -1
- package/dist/PublishParser/getSchemaFromPublish.js +11 -11
- package/dist/PublishParser/getSchemaFromPublish.js.map +1 -1
- package/dist/PublishParser/getTableRulesWithoutFileTable.d.ts.map +1 -1
- package/dist/PublishParser/getTableRulesWithoutFileTable.js +2 -2
- package/dist/PublishParser/getTableRulesWithoutFileTable.js.map +1 -1
- package/dist/PublishParser/publishTypesAndUtils.d.ts +1 -0
- package/dist/PublishParser/publishTypesAndUtils.d.ts.map +1 -1
- package/dist/SyncReplication.d.ts.map +1 -1
- package/dist/SyncReplication.js +1 -1
- package/dist/SyncReplication.js.map +1 -1
- package/dist/initProstgles.d.ts +2 -2
- package/dist/initProstgles.d.ts.map +1 -1
- package/dist/initProstgles.js +14 -1
- package/dist/initProstgles.js.map +1 -1
- package/dist/runClientRequest.d.ts.map +1 -1
- package/dist/runClientRequest.js +4 -3
- package/dist/runClientRequest.js.map +1 -1
- package/lib/DboBuilder/QueryStreamer.ts +1 -1
- package/lib/DboBuilder/ViewHandler/ViewHandler.ts +32 -30
- package/lib/DboBuilder/ViewHandler/subscribe.ts +22 -48
- package/lib/Logging.ts +3 -1
- package/lib/PubSubManager/PubSubManager.ts +50 -42
- package/lib/PubSubManager/addSub.ts +13 -8
- package/lib/PubSubManager/addSync.ts +1 -1
- package/lib/PubSubManager/init/getDataWatchFunctionQuery.ts +28 -1
- package/lib/PubSubManager/notifListener.ts +8 -8
- package/lib/PubSubManager/pushSubData.ts +20 -35
- package/lib/PubSubManager/refreshTriggers.ts +9 -8
- package/lib/PublishParser/PublishParser.ts +40 -52
- package/lib/PublishParser/getSchemaFromPublish.ts +21 -21
- package/lib/PublishParser/getTableRulesWithoutFileTable.ts +5 -6
- package/lib/PublishParser/publishTypesAndUtils.ts +1 -0
- package/lib/SyncReplication.ts +1 -0
- package/lib/initProstgles.ts +21 -3
- package/lib/runClientRequest.ts +6 -5
- package/package.json +3 -3
|
@@ -5,6 +5,7 @@ import type {
|
|
|
5
5
|
FieldFilter,
|
|
6
6
|
SelectParams,
|
|
7
7
|
SubscribeParams,
|
|
8
|
+
SubscriptionChannels,
|
|
8
9
|
} from "prostgles-types";
|
|
9
10
|
import { asName, isObject, postgresToTsType } from "prostgles-types";
|
|
10
11
|
import type { TableEvent } from "../../Logging";
|
|
@@ -23,7 +24,7 @@ import { getInfo } from "./getInfo";
|
|
|
23
24
|
import { parseFieldFilter } from "./parseFieldFilter";
|
|
24
25
|
import { prepareWhere } from "./prepareWhere";
|
|
25
26
|
import { size } from "./size";
|
|
26
|
-
import type {
|
|
27
|
+
import type { OnData } from "./subscribe";
|
|
27
28
|
import { subscribe } from "./subscribe";
|
|
28
29
|
import { validateViewRules } from "./validateViewRules";
|
|
29
30
|
import { escapeTSNames } from "../../utils/utils";
|
|
@@ -64,7 +65,7 @@ export class ViewHandler {
|
|
|
64
65
|
tableOrViewInfo: TableSchema,
|
|
65
66
|
dboBuilder: DboBuilder,
|
|
66
67
|
tx?: { t: pgPromise.ITask<{}>; dbTX: TableHandlers },
|
|
67
|
-
joinPaths?: JoinPaths
|
|
68
|
+
joinPaths?: JoinPaths,
|
|
68
69
|
) {
|
|
69
70
|
this.db = db;
|
|
70
71
|
this.tx = tx;
|
|
@@ -84,7 +85,7 @@ export class ViewHandler {
|
|
|
84
85
|
this.joins = this.dboBuilder.joins;
|
|
85
86
|
this.columnsForTypes.map(({ name, udt_name, is_nullable }) => {
|
|
86
87
|
this.tsColumnDefs.push(
|
|
87
|
-
`${escapeTSNames(name)}?: ${postgresToTsType(udt_name) as string} ${is_nullable ? " | null " : ""}
|
|
88
|
+
`${escapeTSNames(name)}?: ${postgresToTsType(udt_name) as string} ${is_nullable ? " | null " : ""};`,
|
|
88
89
|
);
|
|
89
90
|
});
|
|
90
91
|
}
|
|
@@ -192,7 +193,7 @@ export class ViewHandler {
|
|
|
192
193
|
selectParams?: SelectParams,
|
|
193
194
|
_param3_unused?: undefined,
|
|
194
195
|
table_rules?: ParsedTableRule,
|
|
195
|
-
localParams?: LocalParams
|
|
196
|
+
localParams?: LocalParams,
|
|
196
197
|
): Promise<any> {
|
|
197
198
|
try {
|
|
198
199
|
const { limit, ...params } = selectParams ?? {};
|
|
@@ -205,7 +206,7 @@ export class ViewHandler {
|
|
|
205
206
|
{ ...params, limit: 1, returnType: "row" },
|
|
206
207
|
undefined,
|
|
207
208
|
table_rules,
|
|
208
|
-
localParams
|
|
209
|
+
localParams,
|
|
209
210
|
);
|
|
210
211
|
await this._log({
|
|
211
212
|
command: "find",
|
|
@@ -226,58 +227,59 @@ export class ViewHandler {
|
|
|
226
227
|
async subscribe(
|
|
227
228
|
filter: Filter,
|
|
228
229
|
params: SubscribeParams,
|
|
229
|
-
|
|
230
|
+
onData: OnData,
|
|
230
231
|
): Promise<{ unsubscribe: () => any }>;
|
|
231
232
|
|
|
232
233
|
async subscribe(
|
|
233
234
|
filter: Filter,
|
|
234
235
|
params: SubscribeParams,
|
|
235
|
-
|
|
236
|
-
table_rules
|
|
237
|
-
localParams
|
|
238
|
-
): Promise<
|
|
236
|
+
onData?: OnData,
|
|
237
|
+
table_rules?: ParsedTableRule,
|
|
238
|
+
localParams?: LocalParams,
|
|
239
|
+
): Promise<SubscriptionChannels>;
|
|
239
240
|
|
|
240
241
|
async subscribe(
|
|
241
242
|
filter: Filter,
|
|
242
243
|
params: SubscribeParams,
|
|
243
|
-
|
|
244
|
+
onData?: OnData,
|
|
244
245
|
table_rules?: ParsedTableRule,
|
|
245
|
-
localParams?: LocalParams
|
|
246
|
-
): Promise<{ unsubscribe: () => any } |
|
|
247
|
-
|
|
248
|
-
return subscribe.bind(this)(
|
|
246
|
+
localParams?: LocalParams,
|
|
247
|
+
): Promise<{ unsubscribe: () => any } | SubscriptionChannels> {
|
|
248
|
+
const result = await subscribe.bind(this)(
|
|
249
249
|
filter,
|
|
250
250
|
params,
|
|
251
251
|
//@ts-ignore
|
|
252
|
-
|
|
252
|
+
onData,
|
|
253
253
|
table_rules,
|
|
254
|
-
localParams
|
|
254
|
+
localParams,
|
|
255
255
|
);
|
|
256
|
+
return result;
|
|
256
257
|
}
|
|
257
258
|
|
|
258
259
|
/* This should only be called from server */
|
|
259
260
|
subscribeOne(
|
|
260
261
|
filter: Filter,
|
|
261
262
|
params: SubscribeParams,
|
|
262
|
-
|
|
263
|
+
onData: (item: AnyObject | undefined, error?: unknown) => any,
|
|
263
264
|
): Promise<{ unsubscribe: () => any }>;
|
|
264
265
|
subscribeOne(
|
|
265
266
|
filter: Filter,
|
|
266
267
|
params: SubscribeParams,
|
|
267
|
-
|
|
268
|
+
onData: undefined,
|
|
268
269
|
table_rules: ParsedTableRule,
|
|
269
|
-
localParams: LocalParams
|
|
270
|
-
): Promise<
|
|
270
|
+
localParams: LocalParams,
|
|
271
|
+
): Promise<SubscriptionChannels>;
|
|
271
272
|
subscribeOne(
|
|
272
273
|
filter: Filter,
|
|
273
274
|
params: SubscribeParams = {},
|
|
274
|
-
|
|
275
|
+
onData?: (item: AnyObject | undefined, error?: unknown) => void,
|
|
275
276
|
table_rules?: ParsedTableRule,
|
|
276
|
-
localParams?: LocalParams
|
|
277
|
-
): Promise<
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
277
|
+
localParams?: LocalParams,
|
|
278
|
+
): Promise<SubscriptionChannels | { unsubscribe: () => any }> {
|
|
279
|
+
const func =
|
|
280
|
+
localParams || !onData ? undefined : (
|
|
281
|
+
(rows: AnyObject[], error?: unknown) => onData(rows[0], error)
|
|
282
|
+
);
|
|
281
283
|
return this.subscribe(filter, { ...params, limit: 2 }, func, table_rules, localParams);
|
|
282
284
|
}
|
|
283
285
|
|
|
@@ -287,7 +289,7 @@ export class ViewHandler {
|
|
|
287
289
|
getAllowedSelectFields(
|
|
288
290
|
selectParams: FieldFilter = "*",
|
|
289
291
|
allowed_cols: FieldFilter,
|
|
290
|
-
allow_empty = true
|
|
292
|
+
allow_empty = true,
|
|
291
293
|
): string[] {
|
|
292
294
|
const all_columns = this.column_names.slice(0);
|
|
293
295
|
let allowedFields = all_columns.slice(0),
|
|
@@ -317,7 +319,7 @@ export class ViewHandler {
|
|
|
317
319
|
intersectColumns(
|
|
318
320
|
allowedFields: FieldFilter,
|
|
319
321
|
dissallowedFields: FieldFilter,
|
|
320
|
-
removeDisallowedFields = false
|
|
322
|
+
removeDisallowedFields = false,
|
|
321
323
|
): string[] {
|
|
322
324
|
let result: string[] = [];
|
|
323
325
|
if (allowedFields) {
|
|
@@ -338,7 +340,7 @@ export class ViewHandler {
|
|
|
338
340
|
parseFieldFilter(
|
|
339
341
|
fieldParams: FieldFilter = "*",
|
|
340
342
|
allow_empty = true,
|
|
341
|
-
allowed_cols?: string[]
|
|
343
|
+
allowed_cols?: string[],
|
|
342
344
|
): string[] {
|
|
343
345
|
return parseFieldFilter(fieldParams, allow_empty, allowed_cols ?? this.column_names.slice(0));
|
|
344
346
|
}
|
|
@@ -1,68 +1,42 @@
|
|
|
1
1
|
import type { AnyObject, SubscribeParams, SubscriptionChannels } from "prostgles-types";
|
|
2
2
|
import type { ParsedTableRule } from "../../PublishParser/PublishParser";
|
|
3
|
-
import type {
|
|
4
|
-
|
|
5
|
-
LocalParams} from "../DboBuilder";
|
|
6
|
-
import {
|
|
7
|
-
getErrorAsObject,
|
|
8
|
-
getSerializedClientErrorFromPGError
|
|
9
|
-
} from "../DboBuilder";
|
|
3
|
+
import type { Filter, LocalParams } from "../DboBuilder";
|
|
4
|
+
import { getErrorAsObject, getSerializedClientErrorFromPGError } from "../DboBuilder";
|
|
10
5
|
import { getSubscribeRelatedTables } from "../getSubscribeRelatedTables";
|
|
11
6
|
import type { NewQuery } from "../QueryBuilder/QueryBuilder";
|
|
12
7
|
import type { ViewHandler } from "./ViewHandler";
|
|
13
8
|
import { getValidatedSubscribeOptions } from "./getValidatedSubscribeOptions";
|
|
14
9
|
|
|
15
|
-
type OnData = (items: AnyObject[]) => any;
|
|
16
|
-
export type LocalFuncs =
|
|
17
|
-
| {
|
|
18
|
-
onData: OnData;
|
|
19
|
-
onError?: (error: any) => void;
|
|
20
|
-
}
|
|
21
|
-
| OnData;
|
|
10
|
+
export type OnData = (items: AnyObject[], error?: unknown) => any;
|
|
22
11
|
|
|
23
|
-
export const getOnDataFunc = (localFuncs: LocalFuncs | undefined): OnData | undefined => {
|
|
24
|
-
return typeof localFuncs === "function" ? localFuncs : localFuncs?.onData;
|
|
25
|
-
};
|
|
26
12
|
export const matchesLocalFuncs = (
|
|
27
|
-
localFuncs1:
|
|
28
|
-
localFuncs2:
|
|
13
|
+
localFuncs1: OnData | undefined,
|
|
14
|
+
localFuncs2: OnData | undefined,
|
|
29
15
|
) => {
|
|
30
|
-
return localFuncs1 && localFuncs2 &&
|
|
31
|
-
};
|
|
32
|
-
export const parseLocalFuncs = (
|
|
33
|
-
localFuncs1: LocalFuncs | undefined
|
|
34
|
-
): Extract<LocalFuncs, { onData: OnData }> | undefined => {
|
|
35
|
-
return (
|
|
36
|
-
!localFuncs1 ? undefined
|
|
37
|
-
: typeof localFuncs1 === "function" ?
|
|
38
|
-
{
|
|
39
|
-
onData: localFuncs1,
|
|
40
|
-
}
|
|
41
|
-
: localFuncs1
|
|
42
|
-
);
|
|
16
|
+
return localFuncs1 && localFuncs2 && localFuncs1 === localFuncs2;
|
|
43
17
|
};
|
|
44
18
|
|
|
45
19
|
async function subscribe(
|
|
46
20
|
this: ViewHandler,
|
|
47
21
|
filter: Filter,
|
|
48
22
|
params: SubscribeParams,
|
|
49
|
-
|
|
23
|
+
onData: OnData,
|
|
50
24
|
): Promise<{ unsubscribe: () => any }>;
|
|
51
25
|
async function subscribe(
|
|
52
26
|
this: ViewHandler,
|
|
53
27
|
filter: Filter,
|
|
54
28
|
params: SubscribeParams,
|
|
55
|
-
|
|
29
|
+
onData: undefined,
|
|
56
30
|
table_rules: ParsedTableRule | undefined,
|
|
57
|
-
localParams: LocalParams
|
|
31
|
+
localParams: LocalParams,
|
|
58
32
|
): Promise<SubscriptionChannels>;
|
|
59
33
|
async function subscribe(
|
|
60
34
|
this: ViewHandler,
|
|
61
35
|
filter: Filter,
|
|
62
36
|
params: SubscribeParams,
|
|
63
|
-
|
|
37
|
+
onData?: OnData,
|
|
64
38
|
table_rules?: ParsedTableRule,
|
|
65
|
-
localParams?: LocalParams
|
|
39
|
+
localParams?: LocalParams,
|
|
66
40
|
): Promise<{ unsubscribe: () => any } | SubscriptionChannels> {
|
|
67
41
|
const start = Date.now();
|
|
68
42
|
try {
|
|
@@ -74,12 +48,11 @@ async function subscribe(
|
|
|
74
48
|
throw "subscribe not allowed within transactions";
|
|
75
49
|
}
|
|
76
50
|
const clientReq = localParams?.clientReq;
|
|
77
|
-
if (!clientReq && !
|
|
78
|
-
throw " missing data.
|
|
51
|
+
if (!clientReq && !onData) {
|
|
52
|
+
throw " missing data. expecting onData | localParams { socket } ";
|
|
79
53
|
}
|
|
80
|
-
if (clientReq?.socket &&
|
|
81
|
-
|
|
82
|
-
throw " Cannot have localFunc AND socket ";
|
|
54
|
+
if (clientReq?.socket && onData) {
|
|
55
|
+
throw " Cannot have onData and socket ";
|
|
83
56
|
}
|
|
84
57
|
|
|
85
58
|
const { throttle, throttleOpts, skipFirst, actions, skipChangedColumnsCheck, ...selectParams } =
|
|
@@ -93,7 +66,7 @@ async function subscribe(
|
|
|
93
66
|
{ ...selectParams, limit: 0 },
|
|
94
67
|
undefined,
|
|
95
68
|
table_rules,
|
|
96
|
-
{ ...localParams, returnNewQuery: true }
|
|
69
|
+
{ ...localParams, returnNewQuery: true },
|
|
97
70
|
)) as unknown as NewQuery;
|
|
98
71
|
const viewOptions = await getSubscribeRelatedTables.bind(this)({
|
|
99
72
|
filter,
|
|
@@ -114,19 +87,20 @@ async function subscribe(
|
|
|
114
87
|
selectParams: { ...selectParams },
|
|
115
88
|
subscribeOptions: getValidatedSubscribeOptions(
|
|
116
89
|
{ actions, skipFirst, throttle, throttleOpts, skipChangedColumnsCheck },
|
|
117
|
-
table_rules?.subscribe
|
|
90
|
+
table_rules?.subscribe,
|
|
118
91
|
),
|
|
119
92
|
lastPushed: 0,
|
|
120
93
|
tracked_columns,
|
|
121
94
|
} as const;
|
|
122
95
|
|
|
123
96
|
const pubSubManager = await this.dboBuilder.getPubSubManager();
|
|
124
|
-
|
|
97
|
+
|
|
98
|
+
if (!onData) {
|
|
125
99
|
const { socket } = clientReq ?? {};
|
|
126
100
|
const result = await pubSubManager.addSub({
|
|
127
101
|
...commonSubOpts,
|
|
128
102
|
socket,
|
|
129
|
-
|
|
103
|
+
onData: undefined,
|
|
130
104
|
socket_id: socket?.id,
|
|
131
105
|
});
|
|
132
106
|
|
|
@@ -141,13 +115,13 @@ async function subscribe(
|
|
|
141
115
|
const { channelName, sendFirstData } = await pubSubManager.addSub({
|
|
142
116
|
...commonSubOpts,
|
|
143
117
|
socket: undefined,
|
|
144
|
-
|
|
118
|
+
onData,
|
|
145
119
|
socket_id: undefined,
|
|
146
120
|
});
|
|
147
121
|
|
|
148
122
|
const unsubscribe = async () => {
|
|
149
123
|
const pubSubManager = await this.dboBuilder.getPubSubManager();
|
|
150
|
-
pubSubManager.removeSubscription(channelName, { type: "local",
|
|
124
|
+
pubSubManager.removeSubscription(channelName, { type: "local", onData });
|
|
151
125
|
};
|
|
152
126
|
await this._log({
|
|
153
127
|
command: "subscribe",
|
package/lib/Logging.ts
CHANGED
|
@@ -58,6 +58,8 @@ export namespace EventTypes {
|
|
|
58
58
|
tableName: string;
|
|
59
59
|
localParams?: LocalParams;
|
|
60
60
|
};
|
|
61
|
+
type MapValue<M> = M extends Map<any, infer V> ? V : never;
|
|
62
|
+
|
|
61
63
|
export type SyncOrSub = DebugInfo & {
|
|
62
64
|
type: "syncOrSub";
|
|
63
65
|
connectedSocketIds: string[];
|
|
@@ -83,7 +85,7 @@ export namespace EventTypes {
|
|
|
83
85
|
command: "notifListener.Finished";
|
|
84
86
|
op_name: string | undefined;
|
|
85
87
|
condition_ids_str: string | undefined;
|
|
86
|
-
tableTriggers: PubSubManagerTriggers
|
|
88
|
+
tableTriggers: MapValue<PubSubManagerTriggers> | undefined;
|
|
87
89
|
tableSyncs: string;
|
|
88
90
|
state: "ok" | "error" | "no-triggers" | "invalid_condition_ids";
|
|
89
91
|
})
|
|
@@ -20,11 +20,11 @@ import { initialiseEventTriggers } from "./initialiseEventTriggers";
|
|
|
20
20
|
import { refreshTriggers } from "./refreshTriggers";
|
|
21
21
|
|
|
22
22
|
import type { AnyObject, FieldFilter, SelectParams, WAL } from "prostgles-types";
|
|
23
|
-
import { CHANNELS, type SubscribeOptions } from "prostgles-types";
|
|
23
|
+
import { CHANNELS, getSerialisableError, type SubscribeOptions } from "prostgles-types";
|
|
24
24
|
|
|
25
25
|
import { find, pickKeys } from "prostgles-types";
|
|
26
|
-
import type {
|
|
27
|
-
import {
|
|
26
|
+
import type { OnData } from "../DboBuilder/ViewHandler/subscribe";
|
|
27
|
+
import { matchesLocalFuncs } from "../DboBuilder/ViewHandler/subscribe";
|
|
28
28
|
import type { EventTypes } from "../Logging";
|
|
29
29
|
import type { ParsedTableRule } from "../PublishParser/PublishParser";
|
|
30
30
|
import { syncData } from "../SyncReplication";
|
|
@@ -109,7 +109,7 @@ export type SubscriptionParams = {
|
|
|
109
109
|
subscribeOptions: SubscribeOptions;
|
|
110
110
|
tracked_columns: string[] | undefined;
|
|
111
111
|
|
|
112
|
-
|
|
112
|
+
onData?: OnData;
|
|
113
113
|
socket: PRGLIOSocket | undefined;
|
|
114
114
|
|
|
115
115
|
lastPushed: number;
|
|
@@ -125,7 +125,7 @@ export type Subscription = Pick<
|
|
|
125
125
|
| "lastPushed"
|
|
126
126
|
| "channel_name"
|
|
127
127
|
| "is_ready"
|
|
128
|
-
| "
|
|
128
|
+
| "onData"
|
|
129
129
|
| "socket"
|
|
130
130
|
| "socket_id"
|
|
131
131
|
| "table_info"
|
|
@@ -136,7 +136,7 @@ export type Subscription = Pick<
|
|
|
136
136
|
triggers: AddTriggerParams[];
|
|
137
137
|
};
|
|
138
138
|
|
|
139
|
-
export type PubSubManagerTriggers =
|
|
139
|
+
export type PubSubManagerTriggers = Map<string, { condition: string; hash: string }[]>;
|
|
140
140
|
|
|
141
141
|
/**
|
|
142
142
|
* Used to facilitate table subscribe and sync
|
|
@@ -165,7 +165,7 @@ export class PubSubManager {
|
|
|
165
165
|
* Triggers used for sync/sub that reflect prostgles.app_triggers.
|
|
166
166
|
* Updated through refreshTriggers()
|
|
167
167
|
*/
|
|
168
|
-
_triggers: PubSubManagerTriggers =
|
|
168
|
+
_triggers: PubSubManagerTriggers = new Map();
|
|
169
169
|
sockets: Record<string, PRGLIOSocket> = {};
|
|
170
170
|
|
|
171
171
|
subs: Subscription[] = [];
|
|
@@ -218,13 +218,13 @@ export class PubSubManager {
|
|
|
218
218
|
|
|
219
219
|
getClientSubs({
|
|
220
220
|
channel_name,
|
|
221
|
-
|
|
221
|
+
onData,
|
|
222
222
|
socket_id,
|
|
223
|
-
}: Pick<Subscription, "
|
|
223
|
+
}: Pick<Subscription, "onData" | "socket_id" | "channel_name">): Subscription[] {
|
|
224
224
|
return this.subs.filter((s) => {
|
|
225
225
|
return (
|
|
226
226
|
s.channel_name === channel_name &&
|
|
227
|
-
(matchesLocalFuncs(
|
|
227
|
+
(matchesLocalFuncs(onData, s.onData) || (socket_id && s.socket_id === socket_id))
|
|
228
228
|
);
|
|
229
229
|
});
|
|
230
230
|
}
|
|
@@ -236,30 +236,31 @@ export class PubSubManager {
|
|
|
236
236
|
|
|
237
237
|
removeSubscription = (
|
|
238
238
|
channelName: string,
|
|
239
|
-
subInfo: { type: "local";
|
|
239
|
+
subInfo: { type: "local"; onData: OnData } | { type: "ws"; socket: PRGLIOSocket },
|
|
240
240
|
) => {
|
|
241
241
|
const matchingSubIdx = this.subs.findIndex(
|
|
242
242
|
(s) =>
|
|
243
243
|
s.channel_name === channelName &&
|
|
244
244
|
(subInfo.type === "local" ?
|
|
245
|
-
|
|
245
|
+
subInfo.onData === s.onData
|
|
246
246
|
: subInfo.socket.id === s.socket?.id),
|
|
247
247
|
);
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
248
|
+
const matchingSub = this.subs[matchingSubIdx];
|
|
249
|
+
if (matchingSub) {
|
|
250
|
+
/** Ensure we check and refresh related table/view triggers as well */
|
|
251
|
+
const oldActiveTriggers = this.getAllActiveTriggers();
|
|
251
252
|
this.subs.splice(matchingSubIdx, 1);
|
|
252
|
-
const newActiveTriggers = this.
|
|
253
|
+
const newActiveTriggers = this.getAllActiveTriggers();
|
|
254
|
+
const tableNames = new Set(
|
|
255
|
+
[...oldActiveTriggers, ...newActiveTriggers].map((t) => t.tableName),
|
|
256
|
+
);
|
|
253
257
|
if (newActiveTriggers.length < oldActiveTriggers.length) {
|
|
254
|
-
this.deleteOrphanedTriggers(
|
|
258
|
+
this.deleteOrphanedTriggers(tableNames);
|
|
255
259
|
}
|
|
256
260
|
} else {
|
|
257
|
-
console.error(
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
channelName,
|
|
261
|
-
},
|
|
262
|
-
);
|
|
261
|
+
console.error("Could not unsubscribe. Subscription might not have initialised yet", {
|
|
262
|
+
channelName,
|
|
263
|
+
});
|
|
263
264
|
}
|
|
264
265
|
};
|
|
265
266
|
|
|
@@ -272,11 +273,11 @@ export class PubSubManager {
|
|
|
272
273
|
notifListener = notifListener.bind(this);
|
|
273
274
|
|
|
274
275
|
getTriggerInfo = (tableName: string) => {
|
|
275
|
-
const tableTriggerConditions = this._triggers
|
|
276
|
+
const tableTriggerConditions = this._triggers.get(tableName)?.map((triggerInfo, idx) => ({
|
|
276
277
|
idx,
|
|
277
|
-
...
|
|
278
|
-
subs: this.getTriggerSubs(tableName,
|
|
279
|
-
syncs: this.getSyncs(tableName,
|
|
278
|
+
...triggerInfo,
|
|
279
|
+
subs: this.getTriggerSubs(tableName, triggerInfo.condition),
|
|
280
|
+
syncs: this.getSyncs(tableName, triggerInfo.condition),
|
|
280
281
|
}));
|
|
281
282
|
return tableTriggerConditions;
|
|
282
283
|
};
|
|
@@ -287,13 +288,20 @@ export class PubSubManager {
|
|
|
287
288
|
return activeTriggers;
|
|
288
289
|
};
|
|
289
290
|
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
291
|
+
getAllActiveTriggers = () => {
|
|
292
|
+
return Array.from(this._triggers.keys()).flatMap((tableName) => {
|
|
293
|
+
return this.getActiveTriggers(tableName).map((triggerInfo) => ({
|
|
294
|
+
...triggerInfo,
|
|
295
|
+
tableName,
|
|
296
|
+
}));
|
|
297
|
+
});
|
|
298
|
+
};
|
|
299
|
+
|
|
300
|
+
getSubData = async (sub: Subscription) => {
|
|
301
|
+
const { table_info, filter, selectParams: params, table_rules, socket, onData } = sub; //, subOne = false
|
|
294
302
|
const { name: table_name } = table_info;
|
|
295
303
|
const tableHandler = this.dbo[table_name];
|
|
296
|
-
if (!
|
|
304
|
+
if (!onData && !socket) {
|
|
297
305
|
throw new Error("Subscription must have either localFuncs or socket");
|
|
298
306
|
}
|
|
299
307
|
if (!tableHandler?.find) {
|
|
@@ -301,7 +309,7 @@ export class PubSubManager {
|
|
|
301
309
|
}
|
|
302
310
|
|
|
303
311
|
try {
|
|
304
|
-
const data = await tableHandler.find(
|
|
312
|
+
const data = (await tableHandler.find(
|
|
305
313
|
filter,
|
|
306
314
|
params,
|
|
307
315
|
undefined,
|
|
@@ -309,10 +317,10 @@ export class PubSubManager {
|
|
|
309
317
|
socket && {
|
|
310
318
|
clientReq: { socket },
|
|
311
319
|
},
|
|
312
|
-
);
|
|
320
|
+
)) as AnyObject[];
|
|
313
321
|
return { data };
|
|
314
322
|
} catch (err) {
|
|
315
|
-
return { err };
|
|
323
|
+
return { err: getSerialisableError(err) || "Unknown error fetching subscription data" };
|
|
316
324
|
}
|
|
317
325
|
};
|
|
318
326
|
|
|
@@ -401,23 +409,23 @@ export class PubSubManager {
|
|
|
401
409
|
/** Throttle trigger deletes */
|
|
402
410
|
deletingOrphanedTriggers:
|
|
403
411
|
| {
|
|
404
|
-
tableNames: string
|
|
412
|
+
tableNames: Set<string>;
|
|
405
413
|
timeout: NodeJS.Timeout;
|
|
406
414
|
}
|
|
407
415
|
| undefined;
|
|
408
|
-
deleteOrphanedTriggers = (
|
|
416
|
+
deleteOrphanedTriggers = (latestTableNames: Set<string>) => {
|
|
409
417
|
this.deletingOrphanedTriggers ??= {
|
|
410
|
-
tableNames:
|
|
418
|
+
tableNames: latestTableNames,
|
|
411
419
|
timeout: setTimeout(() => {
|
|
412
420
|
const tableNames = this.deletingOrphanedTriggers!.tableNames;
|
|
413
421
|
this.deletingOrphanedTriggers = undefined;
|
|
414
|
-
void deleteOrphanedTriggers.bind(this)(tableNames);
|
|
422
|
+
void deleteOrphanedTriggers.bind(this)(Array.from(tableNames));
|
|
415
423
|
}, 1000),
|
|
416
424
|
};
|
|
417
425
|
|
|
418
|
-
|
|
419
|
-
this.deletingOrphanedTriggers
|
|
420
|
-
}
|
|
426
|
+
latestTableNames.forEach((latestTableName) => {
|
|
427
|
+
this.deletingOrphanedTriggers?.tableNames.add(latestTableName);
|
|
428
|
+
});
|
|
421
429
|
};
|
|
422
430
|
|
|
423
431
|
addingTrigger: any;
|
|
@@ -1,7 +1,12 @@
|
|
|
1
1
|
import type { SubscriptionChannels } from "prostgles-types";
|
|
2
2
|
import type { VoidFunction } from "../SchemaWatch/SchemaWatch";
|
|
3
3
|
import { tout } from "./init/initPubSubManager";
|
|
4
|
-
import type {
|
|
4
|
+
import type {
|
|
5
|
+
BasicCallback,
|
|
6
|
+
PubSubManager,
|
|
7
|
+
Subscription,
|
|
8
|
+
SubscriptionParams,
|
|
9
|
+
} from "./PubSubManager";
|
|
5
10
|
import { parseCondition } from "./PubSubManagerUtils";
|
|
6
11
|
import type { AddTriggerParams } from "./addTrigger";
|
|
7
12
|
|
|
@@ -17,11 +22,11 @@ type AddSubResult = SubscriptionChannels & {
|
|
|
17
22
|
/* The distinct list of {table_name, condition} must have a corresponding trigger in the database */
|
|
18
23
|
export async function addSub(
|
|
19
24
|
this: PubSubManager,
|
|
20
|
-
subscriptionParams: Omit<AddSubscriptionParams, "channel_name" | "parentSubParams"
|
|
25
|
+
subscriptionParams: Omit<AddSubscriptionParams, "channel_name" | "parentSubParams">,
|
|
21
26
|
): Promise<AddSubResult> {
|
|
22
27
|
const {
|
|
23
28
|
socket,
|
|
24
|
-
|
|
29
|
+
onData,
|
|
25
30
|
table_rules,
|
|
26
31
|
filter = {},
|
|
27
32
|
selectParams = {},
|
|
@@ -33,10 +38,10 @@ export async function addSub(
|
|
|
33
38
|
} = subscriptionParams;
|
|
34
39
|
const table_name = table_info.name;
|
|
35
40
|
|
|
36
|
-
if (!socket && !
|
|
41
|
+
if (!socket && !onData) {
|
|
37
42
|
throw "socket AND func missing";
|
|
38
43
|
}
|
|
39
|
-
if (socket &&
|
|
44
|
+
if (socket && onData) {
|
|
40
45
|
throw "addSub: cannot have socket AND func";
|
|
41
46
|
}
|
|
42
47
|
|
|
@@ -50,7 +55,7 @@ export async function addSub(
|
|
|
50
55
|
const newSub: Subscription = {
|
|
51
56
|
channel_name,
|
|
52
57
|
filter,
|
|
53
|
-
|
|
58
|
+
onData,
|
|
54
59
|
selectParams: selectParams,
|
|
55
60
|
lastPushed: 0,
|
|
56
61
|
socket,
|
|
@@ -74,7 +79,7 @@ export async function addSub(
|
|
|
74
79
|
const [matchingSub] = this.getClientSubs(newSub);
|
|
75
80
|
if (matchingSub) {
|
|
76
81
|
console.error(
|
|
77
|
-
`Trying to add a duplicate ${
|
|
82
|
+
`Trying to add a duplicate ${onData ? "local" : "socket"} sub for: ${channel_name}`,
|
|
78
83
|
);
|
|
79
84
|
return result;
|
|
80
85
|
}
|
|
@@ -104,7 +109,7 @@ export async function addSub(
|
|
|
104
109
|
void this.pushSubData(newSub);
|
|
105
110
|
};
|
|
106
111
|
|
|
107
|
-
if (
|
|
112
|
+
if (onData) {
|
|
108
113
|
/**
|
|
109
114
|
* Must ensure sub will start sending data after all triggers are set up.
|
|
110
115
|
* Socket clients are not affected as they need to confirm they are ready to receive data
|
|
@@ -111,7 +111,7 @@ export async function addSync(
|
|
|
111
111
|
}
|
|
112
112
|
});
|
|
113
113
|
} else {
|
|
114
|
-
console.warn("
|
|
114
|
+
console.warn("addSync: Client tried to create a duplicate sync", existing.channel_name);
|
|
115
115
|
}
|
|
116
116
|
|
|
117
117
|
return newSync;
|
|
@@ -8,7 +8,7 @@ import { asValue, DELIMITER, NOTIF_CHANNEL, NOTIF_TYPE } from "../PubSubManagerU
|
|
|
8
8
|
*/
|
|
9
9
|
export const udtNamesWithoutEqualityComparison = ["json", "xml"];
|
|
10
10
|
export const getDataWatchFunctionQuery = (debugMode: boolean | undefined) => {
|
|
11
|
-
|
|
11
|
+
const dataWatchFunctionQuery = `
|
|
12
12
|
|
|
13
13
|
CREATE OR REPLACE FUNCTION ${DB_OBJ_NAMES.data_watch_func}() RETURNS TRIGGER
|
|
14
14
|
AS $$
|
|
@@ -161,6 +161,25 @@ export const getDataWatchFunctionQuery = (debugMode: boolean | undefined) => {
|
|
|
161
161
|
COMMENT ON FUNCTION ${DB_OBJ_NAMES.data_watch_func} IS 'Prostgles internal function used to notify when data in the table changed';
|
|
162
162
|
|
|
163
163
|
`;
|
|
164
|
+
|
|
165
|
+
/** Ensure every execute is followed by EXCEPTION catch to ensure we remove stale schema/faulty triggers */
|
|
166
|
+
const queryLines = dataWatchFunctionQuery
|
|
167
|
+
.split("\n")
|
|
168
|
+
.map((l) => l.trim())
|
|
169
|
+
.filter((l) => l);
|
|
170
|
+
queryLines.forEach((line, lineIndex) => {
|
|
171
|
+
const nextLine = queryLines[lineIndex + 1] ?? "";
|
|
172
|
+
if (
|
|
173
|
+
line.toUpperCase().startsWith("EXECUTE") &&
|
|
174
|
+
!nextLine.toUpperCase().startsWith("EXCEPTION")
|
|
175
|
+
) {
|
|
176
|
+
throw new Error(
|
|
177
|
+
`Every EXECUTE statement in the data watch function must be followed by an EXCEPTION block to catch errors and avoid stale triggers. Problematic line: ${line}`,
|
|
178
|
+
);
|
|
179
|
+
}
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
return dataWatchFunctionQuery;
|
|
164
183
|
};
|
|
165
184
|
|
|
166
185
|
/**
|
|
@@ -208,6 +227,14 @@ IF TG_OP = 'UPDATE' THEN
|
|
|
208
227
|
|
|
209
228
|
BEGIN
|
|
210
229
|
EXECUTE query INTO changed_columns;
|
|
230
|
+
EXCEPTION WHEN OTHERS THEN
|
|
231
|
+
|
|
232
|
+
has_errors := TRUE;
|
|
233
|
+
|
|
234
|
+
GET STACKED DIAGNOSTICS
|
|
235
|
+
err_text = MESSAGE_TEXT,
|
|
236
|
+
err_detail = PG_EXCEPTION_DETAIL,
|
|
237
|
+
err_hint = PG_EXCEPTION_HINT;
|
|
211
238
|
END;
|
|
212
239
|
|
|
213
240
|
/* It is possible to get no changes */
|