prostgles-server 3.0.150 → 3.0.152

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.
@@ -1,9 +1,9 @@
1
- import { AnyObject, asName, SubscribeParams } from "prostgles-types";
2
- import { Filter, LocalParams, makeErrorFromPGError, parseError } from "../DboBuilder";
1
+ import { AnyObject, SubscribeParams } from "prostgles-types";
2
+ import { Filter, LocalParams, parseError } from "../DboBuilder";
3
3
  import { TableRule } from "../PublishParser";
4
- import { log, omitKeys, ViewSubscriptionOptions } from "../PubSubManager/PubSubManager";
5
- import { NewQuery } from "./QueryBuilder/QueryBuilder";
4
+ import { omitKeys } from "../PubSubManager/PubSubManager";
6
5
  import { ViewHandler } from "./ViewHandler";
6
+ import { getSubscribeRelatedTables } from "./getSubscribeRelatedTables";
7
7
 
8
8
  export type LocalFunc = (items: AnyObject[]) => any;
9
9
 
@@ -39,210 +39,48 @@ async function subscribe(this: ViewHandler, filter: Filter, params: SubscribePar
39
39
  if (filterSize * 4 > 2704) {
40
40
  throw "filter too big. Might exceed the btree version 4 maximum 2704. Use a primary key or a $rowhash filter instead"
41
41
  }
42
+ if (!this.dboBuilder.prostgles.isSuperUser) {
43
+ throw "Subscribe not possible. Must be superuser to add triggers 1856";
44
+ }
42
45
 
43
- if (!localFunc) {
44
-
45
- if (!this.dboBuilder.prostgles.isSuperUser) {
46
- throw "Subscribe not possible. Must be superuser to add triggers 1856";
47
- }
48
-
49
- return await this.find(filter, { ...selectParams, limit: 0 }, undefined, table_rules, localParams)
50
- .then(async _isValid => {
51
-
52
- let viewOptions: ViewSubscriptionOptions | undefined = undefined;
53
-
54
- if (this.is_view) {
55
- const viewName = this.name;
56
- const viewNameEscaped = this.escapedName;
57
- const { current_schema } = await this.db.oneOrNone("SELECT current_schema")
58
-
59
- /** Get list of used columns and their parent tables */
60
- let { def } = (await this.db.oneOrNone("SELECT pg_get_viewdef(${viewName}) as def", { viewName })) as { def: string };
61
- def = def.trim();
62
- if (def.endsWith(";")) {
63
- def = def.slice(0, -1);
64
- }
65
- if (!def || typeof def !== "string") {
66
- throw makeErrorFromPGError("Could get view definition");
67
- }
68
- const { fields } = await this.dboBuilder.dbo.sql!(`SELECT * FROM ( \n ${def} \n ) prostgles_subscribe_view_definition LIMIT 0`, {});
69
- const tableColumns = fields.filter(f => f.tableName && f.columnName);
70
-
71
- /** Create exists filters for each table */
72
- const tableIds: string[] = Array.from(new Set(tableColumns.map(tc => tc.tableID!.toString())));
73
- viewOptions = {
74
- type: "view",
75
- viewName,
76
- definition: def,
77
- relatedTables: []
78
- }
79
- viewOptions.relatedTables = await Promise.all(tableIds.map(async tableID => {
80
- const table = this.dboBuilder.USER_TABLES!.find(t => t.relid === +tableID)!;
81
- let tableCols = tableColumns.filter(tc => tc.tableID!.toString() === tableID);
82
-
83
- /** If table has primary keys and they are all in this view then use only primary keys */
84
- if (table?.pkey_columns?.every(pkey => tableCols.some(c => c.columnName === pkey))) {
85
- tableCols = tableCols.filter(c => table?.pkey_columns?.includes(c.columnName!))
86
- } else {
87
- /** Exclude non comparable data types */
88
- tableCols = tableCols.filter(c => !["json", "xml"].includes(c.udt_name))
89
- }
90
-
91
- const { relname: tableName, schemaname: tableSchema } = table;
92
-
93
- if(tableCols.length){
94
-
95
- const tableNameEscaped = tableSchema === current_schema ? table.relname : [tableSchema, tableName].map(v => JSON.stringify(v)).join(".");
96
-
97
- const fullCondition = `EXISTS (
98
- SELECT 1
99
- FROM ${viewNameEscaped}
100
- WHERE ${tableCols.map(c => `${tableNameEscaped}.${JSON.stringify(c.columnName)} = ${viewNameEscaped}.${JSON.stringify(c.name)}`).join(" AND \n")}
101
- AND ${condition || "TRUE"}
102
- )`;
103
-
104
- try {
105
- const { count } = await this.db.oneOrNone(`
106
- WITH ${asName(tableName)} AS (
107
- SELECT *
108
- FROM ${asName(tableName)}
109
- LIMIT 0
110
- )
111
-
112
- SELECT COUNT(*) as count
113
- FROM (
114
- ${def}
115
- ) prostgles_view_ref_table_test
116
- `);
117
-
118
- const relatedTableSubscription = {
119
- tableName: tableName!,
120
- tableNameEscaped,
121
- condition: fullCondition,
122
- }
123
-
124
- if(count.toString() === '0'){
125
- return relatedTableSubscription;
126
- }
127
- } catch(e){
128
- log(`Could not not override subscribed view (${this.name}) table (${tableName}). Will not check condition`, e);
129
- }
130
- }
131
-
132
- return {
133
- tableName,
134
- tableNameEscaped: JSON.stringify(tableName),// [table.schemaname, table.relname].map(v => JSON.stringify(v)).join("."),
135
- condition: "TRUE"
136
- }
137
-
138
- }))
139
-
140
- /** Get list of remaining used inner tables */
141
- const allUsedTables: { table_name: string; table_schema: string; }[] = await this.db.any(
142
- "SELECT distinct table_name, table_schema FROM information_schema.view_column_usage WHERE view_name = ${viewName}",
143
- { viewName }
144
- );
46
+ /** Ensure request is valid */
47
+ await this.find(filter, { ...selectParams, limit: 0 }, undefined, table_rules, localParams);
145
48
 
146
- /** Remaining tables will have listeners on all records (condition = "TRUE") */
147
- const remainingInnerTables = allUsedTables.filter(at => !tableColumns.some(dc => dc.tableName === at.table_name && dc.tableSchema === at.table_schema));
148
- viewOptions.relatedTables = [
149
- ...viewOptions.relatedTables,
150
- ...remainingInnerTables.map(t => ({
151
- tableName: t.table_name,
152
- tableNameEscaped: [t.table_name, t.table_schema].map(v => JSON.stringify(v)).join("."),
153
- condition: "TRUE"
154
- }))
155
- ];
49
+ const viewOptions = await getSubscribeRelatedTables.bind(this)({ filter, selectParams, table_rules, localParams, condition, filterOpts })
156
50
 
157
- if (!viewOptions.relatedTables.length) {
158
- throw "Could not subscribe to this view: no related tables found";
159
- }
160
- }
51
+ const commonSubOpts = {
52
+ table_info: this.tableOrViewInfo,
53
+ viewOptions,
54
+ table_rules,
55
+ condition,
56
+ table_name: this.name,
57
+ filter: { ...filter },
58
+ params: { ...selectParams },
59
+ throttle,
60
+ last_throttled: 0,
61
+ } as const;
161
62
 
162
- /** Any joined table used within select or filter must also be added a trigger for this recordset */
163
- if(!this.is_view){
164
- const newQuery = await this.find(filter, { ...selectParams, limit: 0 }, undefined, table_rules, { ...localParams, returnNewQuery: true }) as unknown as NewQuery;
165
- viewOptions = {
166
- type: "table",
167
- relatedTables: []
168
- }
169
- /**
170
- * Avoid nested exists error. Will affect performance
171
- */
172
- const nonExistsFilter = filterOpts.exists.length? {} : filter;
173
- for await (const j of (newQuery.joins ?? [])) {
174
- if(!viewOptions!.relatedTables.find(rt => rt.tableName === j.table)){
175
- viewOptions.relatedTables.push({
176
- tableName: j.table,
177
- tableNameEscaped: asName(j.table),
178
- condition: (await this.dboBuilder.dbo[j.table]!.prepareWhere!({
179
- filter: {
180
- $existsJoined: {
181
-
182
- [[this.name, ...j.$path ?? [].slice(0).reverse()].join(".")]: nonExistsFilter
183
- }
184
- },
185
- addKeywords: false,
186
- localParams: undefined,
187
- tableRule: undefined
188
- })).where
189
- })
190
- }
191
- }
192
- for await(const e of newQuery.whereOpts.exists) {
193
- const eTable = e.tables.at(-1)!
194
- viewOptions.relatedTables.push({
195
- tableName: eTable,
196
- tableNameEscaped: asName(eTable),
197
- condition: (await this.dboBuilder.dbo[eTable]!.prepareWhere!({
198
- filter: {
199
- $existsJoined: {
200
- [[this.name, ...e.tables ?? [].slice(0, -1).reverse()].join(".")]: nonExistsFilter
201
- }
202
- },
203
- addKeywords: false,
204
- localParams: undefined,
205
- tableRule: undefined
206
- })).where
207
- });
208
- }
209
- if(!viewOptions.relatedTables.length){
210
- viewOptions = undefined;
211
- }
212
- }
63
+ if (!localFunc) {
213
64
 
214
- const { socket } = localParams ?? {};
215
- const pubSubManager = await this.dboBuilder.getPubSubManager();
216
- return pubSubManager.addSub({
217
- table_info: this.tableOrViewInfo,
218
- socket,
219
- table_rules,
220
- table_name: this.name,
221
- condition: condition,
222
- viewOptions,
223
- func: undefined,
224
- filter: { ...filter },
225
- params: { ...selectParams },
226
- socket_id: socket?.id,
227
- throttle,
228
- last_throttled: 0,
229
- }).then(channelName => ({ channelName }));
230
- }) as any;
65
+ const { socket } = localParams ?? {};
66
+ const pubSubManager = await this.dboBuilder.getPubSubManager();
67
+ return pubSubManager.addSub({
68
+ ...commonSubOpts,
69
+ socket,
70
+ func: undefined,
71
+ socket_id: socket?.id,
72
+ }).then(channelName => ({ channelName })) as any;
73
+
231
74
  } else {
75
+
232
76
  const pubSubManager = await this.dboBuilder.getPubSubManager();
233
- pubSubManager.addSub({
234
- table_info: this.tableOrViewInfo,
235
- socket: undefined,
236
- table_rules,
237
- condition,
238
- func: localFunc,
239
- filter: { ...filter },
240
- params: { ...selectParams },
241
- socket_id: undefined,
242
- table_name: this.name,
243
- throttle,
244
- last_throttled: 0,
77
+ pubSubManager.addSub({
78
+ ...commonSubOpts,
79
+ socket: undefined,
80
+ func: localFunc,
81
+ socket_id: undefined,
245
82
  }).then(channelName => ({ channelName }));
83
+
246
84
  const unsubscribe = async () => {
247
85
  const pubSubManager = await this.dboBuilder.getPubSubManager();
248
86
  pubSubManager.removeLocalSub(this.name, condition, localFunc)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "prostgles-server",
3
- "version": "3.0.150",
3
+ "version": "3.0.152",
4
4
  "description": "",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -1 +1 @@
1
- 11319
1
+ 18959
@@ -21,7 +21,7 @@
21
21
  },
22
22
  "../..": {
23
23
  "name": "prostgles-server",
24
- "version": "3.0.149",
24
+ "version": "3.0.151",
25
25
  "license": "MIT",
26
26
  "dependencies": {
27
27
  "@aws-sdk/client-s3": "^3.272.0",