prostgles-server 3.0.38 → 3.0.40

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 (118) hide show
  1. package/dist/DBSchemaBuilder.d.ts.map +1 -1
  2. package/dist/DBSchemaBuilder.js.map +1 -1
  3. package/dist/DboBuilder/QueryBuilder/QueryBuilder.d.ts +2 -1
  4. package/dist/DboBuilder/QueryBuilder/QueryBuilder.d.ts.map +1 -1
  5. package/dist/DboBuilder/QueryBuilder/QueryBuilder.js +47 -2
  6. package/dist/DboBuilder/QueryBuilder/QueryBuilder.js.map +1 -1
  7. package/dist/DboBuilder/QueryBuilder/makeSelectQuery.d.ts +1 -1
  8. package/dist/DboBuilder/QueryBuilder/makeSelectQuery.d.ts.map +1 -1
  9. package/dist/DboBuilder/QueryBuilder/makeSelectQuery.js.map +1 -1
  10. package/dist/DboBuilder/TableHandler.d.ts +62 -0
  11. package/dist/DboBuilder/TableHandler.d.ts.map +1 -0
  12. package/dist/DboBuilder/TableHandler.js +304 -0
  13. package/dist/DboBuilder/TableHandler.js.map +1 -0
  14. package/dist/DboBuilder/ViewHandler.d.ts +137 -0
  15. package/dist/DboBuilder/ViewHandler.d.ts.map +1 -0
  16. package/dist/DboBuilder/ViewHandler.js +1292 -0
  17. package/dist/DboBuilder/ViewHandler.js.map +1 -0
  18. package/dist/DboBuilder/delete.d.ts +2 -1
  19. package/dist/DboBuilder/delete.d.ts.map +1 -1
  20. package/dist/DboBuilder/delete.js.map +1 -1
  21. package/dist/DboBuilder/getColumns.d.ts +12 -0
  22. package/dist/DboBuilder/getColumns.d.ts.map +1 -0
  23. package/dist/DboBuilder/getColumns.js +95 -0
  24. package/dist/DboBuilder/getColumns.js.map +1 -0
  25. package/dist/DboBuilder/insert.d.ts +2 -1
  26. package/dist/DboBuilder/insert.d.ts.map +1 -1
  27. package/dist/DboBuilder/insert.js +1 -1
  28. package/dist/DboBuilder/insert.js.map +1 -1
  29. package/dist/DboBuilder/insertDataParse.d.ts +2 -1
  30. package/dist/DboBuilder/insertDataParse.d.ts.map +1 -1
  31. package/dist/DboBuilder/insertDataParse.js +2 -3
  32. package/dist/DboBuilder/insertDataParse.js.map +1 -1
  33. package/dist/DboBuilder/parseUpdateRules.d.ts +18 -0
  34. package/dist/DboBuilder/parseUpdateRules.d.ts.map +1 -0
  35. package/dist/DboBuilder/parseUpdateRules.js +119 -0
  36. package/dist/DboBuilder/parseUpdateRules.js.map +1 -0
  37. package/dist/DboBuilder/update.d.ts +2 -1
  38. package/dist/DboBuilder/update.d.ts.map +1 -1
  39. package/dist/DboBuilder/update.js.map +1 -1
  40. package/dist/DboBuilder/uploadFile.d.ts +2 -1
  41. package/dist/DboBuilder/uploadFile.d.ts.map +1 -1
  42. package/dist/DboBuilder/uploadFile.js.map +1 -1
  43. package/dist/DboBuilder.d.ts +5 -185
  44. package/dist/DboBuilder.d.ts.map +1 -1
  45. package/dist/DboBuilder.js +7 -1773
  46. package/dist/DboBuilder.js.map +1 -1
  47. package/dist/FileManager.d.ts.map +1 -1
  48. package/dist/FileManager.js +4 -4
  49. package/dist/FileManager.js.map +1 -1
  50. package/dist/PubSubManager.d.ts.map +1 -1
  51. package/dist/PubSubManager.js +4 -0
  52. package/dist/PubSubManager.js.map +1 -1
  53. package/dist/PublishParser.d.ts.map +1 -1
  54. package/dist/PublishParser.js.map +1 -1
  55. package/dist/index.js +0 -38
  56. package/dist/index.js.map +1 -1
  57. package/lib/DBSchemaBuilder.d.ts.map +1 -1
  58. package/lib/DBSchemaBuilder.ts +1 -1
  59. package/lib/DboBuilder/QueryBuilder/QueryBuilder.d.ts +2 -1
  60. package/lib/DboBuilder/QueryBuilder/QueryBuilder.d.ts.map +1 -1
  61. package/lib/DboBuilder/QueryBuilder/QueryBuilder.js +47 -2
  62. package/lib/DboBuilder/QueryBuilder/QueryBuilder.ts +55 -2
  63. package/lib/DboBuilder/QueryBuilder/makeSelectQuery.d.ts +1 -1
  64. package/lib/DboBuilder/QueryBuilder/makeSelectQuery.d.ts.map +1 -1
  65. package/lib/DboBuilder/QueryBuilder/makeSelectQuery.ts +2 -1
  66. package/lib/DboBuilder/TableHandler.d.ts +54 -0
  67. package/lib/DboBuilder/TableHandler.d.ts.map +1 -0
  68. package/lib/DboBuilder/TableHandler.js +303 -0
  69. package/lib/DboBuilder/TableHandler.ts +365 -0
  70. package/lib/DboBuilder/ViewHandler.d.ts +133 -0
  71. package/lib/DboBuilder/ViewHandler.d.ts.map +1 -0
  72. package/lib/DboBuilder/ViewHandler.js +1291 -0
  73. package/lib/DboBuilder/ViewHandler.ts +1542 -0
  74. package/lib/DboBuilder/delete.d.ts +2 -1
  75. package/lib/DboBuilder/delete.d.ts.map +1 -1
  76. package/lib/DboBuilder/delete.ts +2 -1
  77. package/lib/DboBuilder/getColumns.d.ts +12 -0
  78. package/lib/DboBuilder/getColumns.d.ts.map +1 -0
  79. package/lib/DboBuilder/getColumns.js +94 -0
  80. package/lib/DboBuilder/getColumns.ts +133 -0
  81. package/lib/DboBuilder/insert.d.ts +2 -1
  82. package/lib/DboBuilder/insert.d.ts.map +1 -1
  83. package/lib/DboBuilder/insert.js +1 -1
  84. package/lib/DboBuilder/insert.ts +3 -2
  85. package/lib/DboBuilder/insertDataParse.d.ts +2 -1
  86. package/lib/DboBuilder/insertDataParse.d.ts.map +1 -1
  87. package/lib/DboBuilder/insertDataParse.js +2 -3
  88. package/lib/DboBuilder/insertDataParse.ts +6 -5
  89. package/lib/DboBuilder/parseUpdateRules.d.ts +18 -0
  90. package/lib/DboBuilder/parseUpdateRules.d.ts.map +1 -0
  91. package/lib/DboBuilder/parseUpdateRules.js +118 -0
  92. package/lib/DboBuilder/parseUpdateRules.ts +156 -0
  93. package/lib/DboBuilder/update.d.ts +2 -1
  94. package/lib/DboBuilder/update.d.ts.map +1 -1
  95. package/lib/DboBuilder/update.ts +2 -1
  96. package/lib/DboBuilder/uploadFile.d.ts +2 -1
  97. package/lib/DboBuilder/uploadFile.d.ts.map +1 -1
  98. package/lib/DboBuilder/uploadFile.ts +2 -1
  99. package/lib/DboBuilder.d.ts +5 -185
  100. package/lib/DboBuilder.d.ts.map +1 -1
  101. package/lib/DboBuilder.js +7 -1773
  102. package/lib/DboBuilder.ts +170 -2296
  103. package/lib/FileManager.d.ts.map +1 -1
  104. package/lib/FileManager.js +4 -4
  105. package/lib/FileManager.ts +3 -1
  106. package/lib/PubSubManager.d.ts.map +1 -1
  107. package/lib/PubSubManager.js +4 -0
  108. package/lib/PubSubManager.ts +6 -1
  109. package/lib/PublishParser.d.ts.map +1 -1
  110. package/lib/PublishParser.ts +3 -1
  111. package/lib/SyncReplication.ts +1 -1
  112. package/lib/index.js +0 -38
  113. package/lib/index.ts +1 -53
  114. package/package.json +2 -2
  115. package/tests/client/PID.txt +1 -1
  116. package/tests/client_only_queries.ts +1 -1
  117. package/tests/server/package-lock.json +409 -250
  118. package/tests/server/package.json +1 -1
@@ -0,0 +1,303 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.TableHandler = void 0;
4
+ const prostgles_types_1 = require("prostgles-types");
5
+ const DboBuilder_1 = require("../DboBuilder");
6
+ const PubSubManager_1 = require("../PubSubManager");
7
+ const delete_1 = require("./delete");
8
+ const insert_1 = require("./insert");
9
+ const insertDataParse_1 = require("./insertDataParse");
10
+ const QueryBuilder_1 = require("./QueryBuilder/QueryBuilder");
11
+ const update_1 = require("./update");
12
+ const ViewHandler_1 = require("./ViewHandler");
13
+ const parseUpdateRules_1 = require("./parseUpdateRules");
14
+ class TableHandler extends ViewHandler_1.ViewHandler {
15
+ constructor(db, tableOrViewInfo, dboBuilder, t, dbTX, joinPaths) {
16
+ super(db, tableOrViewInfo, dboBuilder, t, dbTX, joinPaths);
17
+ this.parseUpdateRules = parseUpdateRules_1.parseUpdateRules.bind(this);
18
+ this.update = update_1.update.bind(this);
19
+ this.insertDataParse = insertDataParse_1.insertDataParse;
20
+ this.prepareReturning = async (returning, allowedFields) => {
21
+ let result = [];
22
+ if (returning) {
23
+ let sBuilder = new QueryBuilder_1.SelectItemBuilder({
24
+ allFields: this.column_names.slice(0),
25
+ allowedFields,
26
+ allowedOrderByFields: allowedFields,
27
+ computedFields: QueryBuilder_1.COMPUTED_FIELDS,
28
+ functions: QueryBuilder_1.FUNCTIONS.filter(f => f.type === "function" && f.singleColArg),
29
+ isView: this.is_view,
30
+ columns: this.columns,
31
+ });
32
+ await sBuilder.parseUserSelect(returning);
33
+ return sBuilder.select;
34
+ }
35
+ return result;
36
+ };
37
+ this.remove = this.delete;
38
+ this.io_stats = {
39
+ since: Date.now(),
40
+ queries: 0,
41
+ throttle_queries_per_sec: 500,
42
+ batching: null
43
+ };
44
+ this.is_view = false;
45
+ this.is_media = dboBuilder.prostgles.isMedia(this.name);
46
+ }
47
+ /* TO DO: Maybe finished query batching */
48
+ willBatch(query) {
49
+ const now = Date.now();
50
+ if (this.io_stats.since < Date.now()) {
51
+ this.io_stats.since = Date.now();
52
+ this.io_stats.queries = 0;
53
+ }
54
+ else {
55
+ this.io_stats.queries++;
56
+ }
57
+ if (this.io_stats.queries > this.io_stats.throttle_queries_per_sec) {
58
+ return true;
59
+ }
60
+ }
61
+ async subscribe(filter, params = {}, localFunc, table_rules, localParams) {
62
+ try {
63
+ if (this.is_view)
64
+ throw "Cannot subscribe to a view";
65
+ if (this.t)
66
+ throw "subscribe not allowed within transactions";
67
+ if (!localParams && !localFunc)
68
+ throw " missing data. provide -> localFunc | localParams { socket } ";
69
+ if (localParams && localParams.socket && localFunc) {
70
+ console.error({ localParams, localFunc });
71
+ throw " Cannot have localFunc AND socket ";
72
+ }
73
+ const { filterFields, forcedFilter } = table_rules?.select || {}, filterOpts = await this.prepareWhere({ filter, forcedFilter, addKeywords: false, filterFields, tableAlias: undefined, localParams, tableRule: table_rules }), condition = filterOpts.where, throttle = params?.throttle || 0, selectParams = (0, PubSubManager_1.omitKeys)(params || {}, ["throttle"]);
74
+ // const { subOne = false } = localParams || {};
75
+ const filterSize = JSON.stringify(filter || {}).length;
76
+ if (filterSize * 4 > 2704) {
77
+ throw "filter too big. Might exceed the btree version 4 maximum 2704";
78
+ }
79
+ if (!localFunc) {
80
+ if (!this.dboBuilder.prostgles.isSuperUser)
81
+ throw "Subscribe not possible. Must be superuser to add triggers 1856";
82
+ return await this.find(filter, { ...selectParams, limit: 0 }, undefined, table_rules, localParams)
83
+ .then(async (isValid) => {
84
+ const { socket } = localParams ?? {};
85
+ const pubSubManager = await this.dboBuilder.getPubSubManager();
86
+ return pubSubManager.addSub({
87
+ table_info: this.tableOrViewInfo,
88
+ socket,
89
+ table_rules,
90
+ condition: condition,
91
+ func: undefined,
92
+ filter: { ...filter },
93
+ params: { ...selectParams },
94
+ socket_id: socket?.id,
95
+ table_name: this.name,
96
+ throttle,
97
+ last_throttled: 0,
98
+ // subOne
99
+ }).then(channelName => ({ channelName }));
100
+ });
101
+ }
102
+ else {
103
+ const pubSubManager = await this.dboBuilder.getPubSubManager();
104
+ pubSubManager.addSub({
105
+ table_info: this.tableOrViewInfo,
106
+ socket: undefined,
107
+ table_rules,
108
+ condition,
109
+ func: localFunc,
110
+ filter: { ...filter },
111
+ params: { ...selectParams },
112
+ socket_id: undefined,
113
+ table_name: this.name,
114
+ throttle,
115
+ last_throttled: 0,
116
+ // subOne
117
+ }).then(channelName => ({ channelName }));
118
+ const unsubscribe = async () => {
119
+ const pubSubManager = await this.dboBuilder.getPubSubManager();
120
+ pubSubManager.removeLocalSub(this.name, condition, localFunc);
121
+ };
122
+ let res = Object.freeze({ unsubscribe });
123
+ return res;
124
+ }
125
+ }
126
+ catch (e) {
127
+ if (localParams && localParams.testRule)
128
+ throw e;
129
+ throw (0, DboBuilder_1.parseError)(e, `dbo.${this.name}.subscribe()`);
130
+ }
131
+ }
132
+ subscribeOne(filter, params = {}, localFunc, table_rules, localParams) {
133
+ let func = localParams ? undefined : (rows) => localFunc(rows[0]);
134
+ return this.subscribe(filter, { ...params, limit: 2 }, func, table_rules, localParams);
135
+ }
136
+ async updateBatch(data, params, tableRules, localParams) {
137
+ try {
138
+ const queries = await Promise.all(data.map(async ([filter, data]) => await this.update(filter, data, { ...(params || {}), returning: undefined }, tableRules, { ...(localParams || {}), returnQuery: true })));
139
+ const keys = (data && data.length) ? Object.keys(data[0]) : [];
140
+ return this.db.tx(t => {
141
+ const _queries = queries.map(q => t.none(q));
142
+ return t.batch(_queries);
143
+ }).catch(err => (0, DboBuilder_1.makeErr)(err, localParams, this, keys));
144
+ }
145
+ catch (e) {
146
+ if (localParams && localParams.testRule)
147
+ throw e;
148
+ throw (0, DboBuilder_1.parseError)(e, `dbo.${this.name}.update()`);
149
+ }
150
+ }
151
+ validateNewData({ row, forcedData, allowedFields, tableRules, fixIssues = false }) {
152
+ const synced_field = (tableRules ?? {})?.sync?.synced_field;
153
+ /* Update synced_field if sync is on and missing */
154
+ if (synced_field && !row[synced_field]) {
155
+ row[synced_field] = Date.now();
156
+ }
157
+ let data = this.prepareFieldValues(row, forcedData, allowedFields, fixIssues);
158
+ const dataKeys = (0, prostgles_types_1.getKeys)(data);
159
+ dataKeys.map(col => {
160
+ this.dboBuilder.prostgles?.tableConfigurator?.checkColVal({ table: this.name, col, value: data[col] });
161
+ const colConfig = this.dboBuilder.prostgles?.tableConfigurator?.getColumnConfig(this.name, col);
162
+ if (colConfig && (0, prostgles_types_1.isObject)(colConfig) && "isText" in colConfig && data[col]) {
163
+ if (colConfig.lowerCased) {
164
+ data[col] = data[col].toString().toLowerCase();
165
+ }
166
+ if (colConfig.trimmed) {
167
+ data[col] = data[col].toString().trim();
168
+ }
169
+ }
170
+ });
171
+ return { data, allowedCols: this.columns.filter(c => dataKeys.includes(c.name)).map(c => c.name) };
172
+ }
173
+ async insert(rowOrRows, param2, param3_unused, tableRules, _localParams) {
174
+ return insert_1.insert.bind(this)(rowOrRows, param2, param3_unused, tableRules, _localParams);
175
+ }
176
+ makeReturnQuery(items) {
177
+ if (items?.length)
178
+ return " RETURNING " + items.map(s => s.getQuery() + " AS " + (0, prostgles_types_1.asName)(s.alias)).join(", ");
179
+ return "";
180
+ }
181
+ async delete(filter, params, param3_unused, table_rules, localParams) {
182
+ return delete_1._delete.bind(this)(filter, params, param3_unused, table_rules, localParams);
183
+ }
184
+ ;
185
+ remove(filter, params, param3_unused, tableRules, localParams) {
186
+ return this.delete(filter, params, param3_unused, tableRules, localParams);
187
+ }
188
+ async upsert(filter, newData, params, table_rules, localParams) {
189
+ try {
190
+ /* Do it within a transaction to ensure consisency */
191
+ if (!this.t) {
192
+ return this.dboBuilder.getTX(dbTX => _upsert(dbTX[this.name]));
193
+ }
194
+ else {
195
+ return _upsert(this);
196
+ }
197
+ async function _upsert(tblH) {
198
+ return tblH.find(filter, { select: "", limit: 1 }, undefined, table_rules, localParams)
199
+ .then(exists => {
200
+ if (exists && exists.length) {
201
+ return tblH.update(filter, newData, params, table_rules, localParams);
202
+ }
203
+ else {
204
+ return tblH.insert({ ...newData, ...filter }, params, undefined, table_rules, localParams);
205
+ }
206
+ });
207
+ }
208
+ }
209
+ catch (e) {
210
+ if (localParams && localParams.testRule)
211
+ throw e;
212
+ throw (0, DboBuilder_1.parseError)(e, `dbo.${this.name}.upsert()`);
213
+ }
214
+ }
215
+ ;
216
+ /* External request. Cannot sync from server */
217
+ async sync(filter, params, param3_unused, table_rules, localParams) {
218
+ if (!localParams)
219
+ throw "Sync not allowed within the same server code";
220
+ const { socket } = localParams;
221
+ if (!socket)
222
+ throw "INTERNAL ERROR: socket missing";
223
+ if (!table_rules || !table_rules.sync || !table_rules.select)
224
+ throw "INTERNAL ERROR: sync or select rules missing";
225
+ if (this.t)
226
+ throw "Sync not allowed within transactions";
227
+ const ALLOWED_PARAMS = ["select"];
228
+ const invalidParams = Object.keys(params || {}).filter(k => !ALLOWED_PARAMS.includes(k));
229
+ if (invalidParams.length)
230
+ throw "Invalid or dissallowed params found: " + invalidParams.join(", ");
231
+ try {
232
+ let { id_fields, synced_field, allow_delete } = table_rules.sync;
233
+ const syncFields = [...id_fields, synced_field];
234
+ if (!id_fields || !synced_field) {
235
+ const err = "INTERNAL ERROR: id_fields OR synced_field missing from publish";
236
+ console.error(err);
237
+ throw err;
238
+ }
239
+ id_fields = this.parseFieldFilter(id_fields, false);
240
+ let allowedSelect = this.parseFieldFilter(table_rules?.select.fields ?? false);
241
+ if (syncFields.find(f => !allowedSelect.includes(f))) {
242
+ throw `INTERNAL ERROR: sync field missing from publish.${this.name}.select.fields`;
243
+ }
244
+ let select = this.getAllowedSelectFields((params || {})?.select || "*", allowedSelect, false);
245
+ if (!select.length)
246
+ throw "Empty select not allowed";
247
+ /* Add sync fields if missing */
248
+ syncFields.map(sf => {
249
+ if (!select.includes(sf))
250
+ select.push(sf);
251
+ });
252
+ /* Step 1: parse command and params */
253
+ return this.find(filter, { select, limit: 0 }, undefined, table_rules, localParams)
254
+ .then(async (isValid) => {
255
+ const { filterFields, forcedFilter } = table_rules?.select || {};
256
+ const condition = (await this.prepareWhere({ filter, forcedFilter, filterFields, addKeywords: false, localParams, tableRule: table_rules })).where;
257
+ // let final_filter = getFindFilter(filter, table_rules);
258
+ const pubSubManager = await this.dboBuilder.getPubSubManager();
259
+ return pubSubManager.addSync({
260
+ table_info: this.tableOrViewInfo,
261
+ condition,
262
+ id_fields, synced_field,
263
+ allow_delete,
264
+ socket,
265
+ table_rules,
266
+ filter: { ...filter },
267
+ params: { select }
268
+ }).then(channelName => ({ channelName, id_fields, synced_field }));
269
+ });
270
+ }
271
+ catch (e) {
272
+ if (localParams && localParams.testRule)
273
+ throw e;
274
+ throw (0, DboBuilder_1.parseError)(e, `dbo.${this.name}.sync()`);
275
+ }
276
+ /*
277
+ REPLICATION
278
+
279
+ 1 Sync proccess (NO DELETES ALLOWED):
280
+
281
+ Client sends:
282
+ "sync-request"
283
+ { min_id, max_id, count, max_synced }
284
+
285
+ Server sends:
286
+ "sync-pull"
287
+ { from_synced }
288
+
289
+ Client sends:
290
+ "sync-push"
291
+ { data } -> WHERE synced >= from_synced
292
+
293
+ Server upserts:
294
+ WHERE not exists synced = synced AND id = id
295
+ UNTIL
296
+
297
+ Server sends
298
+ "sync-push"
299
+ { data } -> WHERE synced >= from_synced
300
+ */
301
+ }
302
+ }
303
+ exports.TableHandler = TableHandler;
@@ -0,0 +1,365 @@
1
+ import pgPromise from "pg-promise";
2
+ import { AnyObject, asName, DeleteParams, FieldFilter, getKeys, InsertParams, isDefined, isObject, Select, SelectParams, SubscribeParams, UpdateParams } from "prostgles-types";
3
+ import { DboBuilder, Filter, LocalParams, makeErr, parseError, TableHandlers, TableSchema } from "../DboBuilder";
4
+ import { DB } from "../Prostgles";
5
+ import { SyncRule, TableRule, UpdateRule, ValidateRow, ValidateUpdateRow } from "../PublishParser";
6
+ import { omitKeys } from "../PubSubManager";
7
+ import { _delete } from "./delete";
8
+ import { insert } from "./insert";
9
+ import { insertDataParse } from "./insertDataParse";
10
+ import { COMPUTED_FIELDS, FUNCTIONS, SelectItem, SelectItemBuilder } from "./QueryBuilder/QueryBuilder";
11
+ import { update } from "./update";
12
+ import { JoinPaths, ViewHandler } from "./ViewHandler";
13
+ import { parseUpdateRules } from "./parseUpdateRules";
14
+
15
+
16
+ type ValidatedParams = {
17
+ row: AnyObject;
18
+ forcedData?: AnyObject;
19
+ allowedFields?: FieldFilter;
20
+ tableRules?: TableRule;
21
+ fixIssues: boolean;
22
+ }
23
+
24
+ export class TableHandler extends ViewHandler {
25
+ io_stats: {
26
+ throttle_queries_per_sec: number;
27
+ since: number,
28
+ queries: number,
29
+ batching: string[] | null
30
+ }
31
+
32
+ constructor(db: DB, tableOrViewInfo: TableSchema, dboBuilder: DboBuilder, t?: pgPromise.ITask<{}>, dbTX?: TableHandlers, joinPaths?: JoinPaths) {
33
+ super(db, tableOrViewInfo, dboBuilder, t, dbTX, joinPaths);
34
+
35
+ this.remove = this.delete;
36
+
37
+ this.io_stats = {
38
+ since: Date.now(),
39
+ queries: 0,
40
+ throttle_queries_per_sec: 500,
41
+ batching: null
42
+ };
43
+ this.is_view = false;
44
+ this.is_media = dboBuilder.prostgles.isMedia(this.name)
45
+ }
46
+
47
+ /* TO DO: Maybe finished query batching */
48
+ willBatch(query: string) {
49
+ const now = Date.now();
50
+ if (this.io_stats.since < Date.now()) {
51
+ this.io_stats.since = Date.now();
52
+ this.io_stats.queries = 0;
53
+ } else {
54
+ this.io_stats.queries++;
55
+ }
56
+
57
+ if (this.io_stats.queries > this.io_stats.throttle_queries_per_sec) {
58
+
59
+ return true;
60
+ }
61
+ }
62
+
63
+ async subscribe(filter: Filter, params: SubscribeParams, localFunc: (items: AnyObject[]) => any): Promise<{ unsubscribe: () => any }>
64
+ async subscribe(filter: Filter, params: SubscribeParams, localFunc?: (items: AnyObject[]) => any, table_rules?: TableRule, localParams?: LocalParams): Promise<string>
65
+ async subscribe(filter: Filter, params: SubscribeParams = {}, localFunc?: (items: AnyObject[]) => any, table_rules?: TableRule, localParams?: LocalParams):
66
+ Promise<string | { unsubscribe: () => any }> {
67
+ try {
68
+ if (this.is_view) throw "Cannot subscribe to a view";
69
+ if (this.t) throw "subscribe not allowed within transactions";
70
+ if (!localParams && !localFunc) throw " missing data. provide -> localFunc | localParams { socket } ";
71
+ if (localParams && localParams.socket && localFunc) {
72
+ console.error({ localParams, localFunc })
73
+ throw " Cannot have localFunc AND socket ";
74
+ }
75
+
76
+ const { filterFields, forcedFilter } = table_rules?.select || {},
77
+ filterOpts = await this.prepareWhere({ filter, forcedFilter, addKeywords: false, filterFields, tableAlias: undefined, localParams, tableRule: table_rules }),
78
+ condition = filterOpts.where,
79
+ throttle = params?.throttle || 0,
80
+ selectParams = omitKeys(params || {}, ["throttle"]);
81
+
82
+ // const { subOne = false } = localParams || {};
83
+ const filterSize = JSON.stringify(filter || {}).length;
84
+ if (filterSize * 4 > 2704) {
85
+ throw "filter too big. Might exceed the btree version 4 maximum 2704"
86
+ }
87
+
88
+ if (!localFunc) {
89
+ if (!this.dboBuilder.prostgles.isSuperUser) throw "Subscribe not possible. Must be superuser to add triggers 1856";
90
+ return await this.find(filter, { ...selectParams, limit: 0 }, undefined, table_rules, localParams)
91
+ .then(async isValid => {
92
+
93
+ const { socket } = localParams ?? {};
94
+ const pubSubManager = await this.dboBuilder.getPubSubManager();
95
+ return pubSubManager.addSub({
96
+ table_info: this.tableOrViewInfo,
97
+ socket,
98
+ table_rules,
99
+ condition: condition,
100
+ func: undefined,
101
+ filter: { ...filter },
102
+ params: { ...selectParams },
103
+ socket_id: socket?.id,
104
+ table_name: this.name,
105
+ throttle,
106
+ last_throttled: 0,
107
+ // subOne
108
+ }).then(channelName => ({ channelName }));
109
+ }) as string;
110
+ } else {
111
+ const pubSubManager = await this.dboBuilder.getPubSubManager();
112
+ pubSubManager.addSub({
113
+ table_info: this.tableOrViewInfo,
114
+ socket: undefined,
115
+ table_rules,
116
+ condition,
117
+ func: localFunc,
118
+ filter: { ...filter },
119
+ params: { ...selectParams },
120
+ socket_id: undefined,
121
+ table_name: this.name,
122
+ throttle,
123
+ last_throttled: 0,
124
+ // subOne
125
+ }).then(channelName => ({ channelName }));
126
+ const unsubscribe = async () => {
127
+ const pubSubManager = await this.dboBuilder.getPubSubManager();
128
+ pubSubManager.removeLocalSub(this.name, condition, localFunc)
129
+ };
130
+ let res: { unsubscribe: () => any } = Object.freeze({ unsubscribe })
131
+ return res;
132
+ }
133
+ } catch (e) {
134
+ if (localParams && localParams.testRule) throw e;
135
+ throw parseError(e, `dbo.${this.name}.subscribe()`);
136
+ }
137
+ }
138
+
139
+ /* This should only be called from server */
140
+ subscribeOne(filter: Filter, params: SubscribeParams, localFunc: (item: AnyObject) => any): Promise<{ unsubscribe: () => any }>
141
+ subscribeOne(filter: Filter, params: SubscribeParams, localFunc: (item: AnyObject) => any, table_rules?: TableRule, localParams?: LocalParams): Promise<string>
142
+ subscribeOne(filter: Filter, params: SubscribeParams = {}, localFunc: (item: AnyObject) => any, table_rules?: TableRule, localParams?: LocalParams):
143
+ Promise<string | { unsubscribe: () => any }> {
144
+ let func = localParams ? undefined : (rows: AnyObject[]) => localFunc(rows[0]);
145
+ return this.subscribe(filter, { ...params, limit: 2 }, func, table_rules, localParams);
146
+ }
147
+
148
+
149
+ async updateBatch(data: [Filter, AnyObject][], params?: UpdateParams, tableRules?: TableRule, localParams?: LocalParams): Promise<any> {
150
+ try {
151
+ const queries = await Promise.all(
152
+ data.map(async ([filter, data]) =>
153
+ await this.update(
154
+ filter,
155
+ data,
156
+ { ...(params || {}), returning: undefined },
157
+ tableRules,
158
+ { ...(localParams || {}), returnQuery: true }
159
+ )
160
+ )
161
+ );
162
+ const keys = (data && data.length) ? Object.keys(data[0]) : [];
163
+ return this.db.tx(t => {
164
+ const _queries = queries.map(q => t.none(q as unknown as string))
165
+ return t.batch(_queries)
166
+ }).catch(err => makeErr(err, localParams, this, keys));
167
+ } catch (e) {
168
+ if (localParams && localParams.testRule) throw e;
169
+ throw parseError(e, `dbo.${this.name}.update()`);
170
+ }
171
+ }
172
+
173
+ parseUpdateRules = parseUpdateRules.bind(this);
174
+
175
+ update = update.bind(this);
176
+
177
+ validateNewData({ row, forcedData, allowedFields, tableRules, fixIssues = false }: ValidatedParams): { data: any; allowedCols: string[] } {
178
+ const synced_field = (tableRules ?? {})?.sync?.synced_field;
179
+
180
+ /* Update synced_field if sync is on and missing */
181
+ if (synced_field && !row[synced_field]) {
182
+ row[synced_field] = Date.now();
183
+ }
184
+
185
+ let data = this.prepareFieldValues(row, forcedData, allowedFields, fixIssues);
186
+ const dataKeys = getKeys(data);
187
+
188
+ dataKeys.map(col => {
189
+ this.dboBuilder.prostgles?.tableConfigurator?.checkColVal({ table: this.name, col, value: data[col] });
190
+ const colConfig = this.dboBuilder.prostgles?.tableConfigurator?.getColumnConfig(this.name, col);
191
+ if (colConfig && isObject(colConfig) && "isText" in colConfig && data[col]) {
192
+ if (colConfig.lowerCased) {
193
+ data[col] = data[col].toString().toLowerCase()
194
+ }
195
+ if (colConfig.trimmed) {
196
+ data[col] = data[col].toString().trim()
197
+ }
198
+ }
199
+ })
200
+
201
+ return { data, allowedCols: this.columns.filter(c => dataKeys.includes(c.name)).map(c => c.name) }
202
+ }
203
+
204
+ insertDataParse = insertDataParse;
205
+ async insert(rowOrRows: (AnyObject | AnyObject[]), param2?: InsertParams, param3_unused?: undefined, tableRules?: TableRule, _localParams?: LocalParams): Promise<any | any[] | boolean> {
206
+ return insert.bind(this)(rowOrRows, param2, param3_unused, tableRules, _localParams)
207
+ }
208
+
209
+ prepareReturning = async (returning: Select | undefined, allowedFields: string[]): Promise<SelectItem[]> => {
210
+ let result: SelectItem[] = [];
211
+ if (returning) {
212
+ let sBuilder = new SelectItemBuilder({
213
+ allFields: this.column_names.slice(0),
214
+ allowedFields,
215
+ allowedOrderByFields: allowedFields,
216
+ computedFields: COMPUTED_FIELDS,
217
+ functions: FUNCTIONS.filter(f => f.type === "function" && f.singleColArg),
218
+ isView: this.is_view,
219
+ columns: this.columns,
220
+ });
221
+ await sBuilder.parseUserSelect(returning);
222
+
223
+ return sBuilder.select;
224
+ }
225
+
226
+ return result;
227
+ }
228
+
229
+ makeReturnQuery(items?: SelectItem[]) {
230
+ if (items?.length) return " RETURNING " + items.map(s => s.getQuery() + " AS " + asName(s.alias)).join(", ");
231
+ return "";
232
+ }
233
+
234
+ async delete(filter?: Filter, params?: DeleteParams, param3_unused?: undefined, table_rules?: TableRule, localParams?: LocalParams): Promise<any> {
235
+ return _delete.bind(this)(filter, params, param3_unused, table_rules, localParams);
236
+ };
237
+
238
+ remove(filter: Filter, params?: UpdateParams, param3_unused?: undefined, tableRules?: TableRule, localParams?: LocalParams) {
239
+ return this.delete(filter, params, param3_unused, tableRules, localParams);
240
+ }
241
+
242
+ async upsert(filter: Filter, newData: AnyObject, params?: UpdateParams, table_rules?: TableRule, localParams?: LocalParams): Promise<any> {
243
+ try {
244
+ /* Do it within a transaction to ensure consisency */
245
+ if (!this.t) {
246
+ return this.dboBuilder.getTX(dbTX => _upsert(dbTX[this.name] as TableHandler))
247
+ } else {
248
+ return _upsert(this);
249
+ }
250
+
251
+ async function _upsert(tblH: TableHandler) {
252
+ return tblH.find(filter, { select: "", limit: 1 }, undefined, table_rules, localParams)
253
+ .then(exists => {
254
+ if (exists && exists.length) {
255
+ return tblH.update(filter, newData, params, table_rules, localParams);
256
+ } else {
257
+ return tblH.insert({ ...newData, ...filter }, params, undefined, table_rules, localParams);
258
+ }
259
+ });
260
+ }
261
+ } catch (e) {
262
+ if (localParams && localParams.testRule) throw e;
263
+ throw parseError(e, `dbo.${this.name}.upsert()`);
264
+ }
265
+ };
266
+
267
+ /* External request. Cannot sync from server */
268
+ async sync(filter: Filter, params: SelectParams, param3_unused: undefined, table_rules: TableRule, localParams: LocalParams) {
269
+ if (!localParams) throw "Sync not allowed within the same server code";
270
+ const { socket } = localParams;
271
+ if (!socket) throw "INTERNAL ERROR: socket missing";
272
+
273
+
274
+ if (!table_rules || !table_rules.sync || !table_rules.select) throw "INTERNAL ERROR: sync or select rules missing";
275
+
276
+ if (this.t) throw "Sync not allowed within transactions";
277
+
278
+ const ALLOWED_PARAMS = ["select"];
279
+ const invalidParams = Object.keys(params || {}).filter(k => !ALLOWED_PARAMS.includes(k));
280
+ if (invalidParams.length) throw "Invalid or dissallowed params found: " + invalidParams.join(", ");
281
+
282
+ try {
283
+
284
+
285
+ let { id_fields, synced_field, allow_delete }: SyncRule = table_rules.sync;
286
+ const syncFields = [...id_fields, synced_field];
287
+
288
+ if (!id_fields || !synced_field) {
289
+ const err = "INTERNAL ERROR: id_fields OR synced_field missing from publish";
290
+ console.error(err);
291
+ throw err;
292
+ }
293
+
294
+ id_fields = this.parseFieldFilter(id_fields, false);
295
+
296
+ let allowedSelect = this.parseFieldFilter(table_rules?.select.fields ?? false);
297
+ if (syncFields.find(f => !allowedSelect.includes(f))) {
298
+ throw `INTERNAL ERROR: sync field missing from publish.${this.name}.select.fields`;
299
+ }
300
+ let select = this.getAllowedSelectFields(
301
+ (params || {})?.select || "*",
302
+ allowedSelect,
303
+ false
304
+ );
305
+ if (!select.length) throw "Empty select not allowed";
306
+
307
+ /* Add sync fields if missing */
308
+ syncFields.map(sf => {
309
+ if (!select.includes(sf)) select.push(sf);
310
+ });
311
+
312
+ /* Step 1: parse command and params */
313
+ return this.find(filter, { select, limit: 0 }, undefined, table_rules, localParams)
314
+ .then(async isValid => {
315
+
316
+ const { filterFields, forcedFilter } = table_rules?.select || {};
317
+ const condition = (await this.prepareWhere({ filter, forcedFilter, filterFields, addKeywords: false, localParams, tableRule: table_rules })).where;
318
+
319
+ // let final_filter = getFindFilter(filter, table_rules);
320
+ const pubSubManager = await this.dboBuilder.getPubSubManager();
321
+ return pubSubManager.addSync({
322
+ table_info: this.tableOrViewInfo,
323
+ condition,
324
+ id_fields, synced_field,
325
+ allow_delete,
326
+ socket,
327
+ table_rules,
328
+ filter: { ...filter },
329
+ params: { select }
330
+ }).then(channelName => ({ channelName, id_fields, synced_field }));
331
+ });
332
+
333
+ } catch (e) {
334
+ if (localParams && localParams.testRule) throw e;
335
+ throw parseError(e, `dbo.${this.name}.sync()`);
336
+ }
337
+
338
+ /*
339
+ REPLICATION
340
+
341
+ 1 Sync proccess (NO DELETES ALLOWED):
342
+
343
+ Client sends:
344
+ "sync-request"
345
+ { min_id, max_id, count, max_synced }
346
+
347
+ Server sends:
348
+ "sync-pull"
349
+ { from_synced }
350
+
351
+ Client sends:
352
+ "sync-push"
353
+ { data } -> WHERE synced >= from_synced
354
+
355
+ Server upserts:
356
+ WHERE not exists synced = synced AND id = id
357
+ UNTIL
358
+
359
+ Server sends
360
+ "sync-push"
361
+ { data } -> WHERE synced >= from_synced
362
+ */
363
+ }
364
+
365
+ }