prostgles-server 3.0.78 → 3.0.79

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 (75) hide show
  1. package/dist/DboBuilder/QueryBuilder/Functions.d.ts.map +1 -1
  2. package/dist/DboBuilder/QueryBuilder/Functions.js +18 -0
  3. package/dist/DboBuilder/QueryBuilder/Functions.js.map +1 -1
  4. package/dist/DboBuilder/TableHandler.d.ts.map +1 -1
  5. package/dist/DboBuilder/TableHandler.js +1 -1
  6. package/dist/DboBuilder/TableHandler.js.map +1 -1
  7. package/dist/DboBuilder/ViewHandler.d.ts +6 -12
  8. package/dist/DboBuilder/ViewHandler.d.ts.map +1 -1
  9. package/dist/DboBuilder/ViewHandler.js +5 -207
  10. package/dist/DboBuilder/ViewHandler.js.map +1 -1
  11. package/dist/DboBuilder/delete.d.ts.map +1 -1
  12. package/dist/DboBuilder/delete.js +1 -1
  13. package/dist/DboBuilder/delete.js.map +1 -1
  14. package/dist/DboBuilder/getCondition.d.ts +19 -0
  15. package/dist/DboBuilder/getCondition.d.ts.map +1 -0
  16. package/dist/DboBuilder/getCondition.js +236 -0
  17. package/dist/DboBuilder/getCondition.js.map +1 -0
  18. package/dist/DboBuilder/insert.d.ts.map +1 -1
  19. package/dist/DboBuilder/insert.js +2 -2
  20. package/dist/DboBuilder/insert.js.map +1 -1
  21. package/dist/DboBuilder/subscribe.d.ts.map +1 -1
  22. package/dist/DboBuilder/subscribe.js +1 -1
  23. package/dist/DboBuilder/subscribe.js.map +1 -1
  24. package/dist/DboBuilder/update.d.ts.map +1 -1
  25. package/dist/DboBuilder/update.js +2 -2
  26. package/dist/DboBuilder/update.js.map +1 -1
  27. package/dist/DboBuilder.d.ts +2 -4
  28. package/dist/DboBuilder.d.ts.map +1 -1
  29. package/dist/DboBuilder.js +3 -4
  30. package/dist/DboBuilder.js.map +1 -1
  31. package/dist/Filtering.d.ts.map +1 -1
  32. package/dist/Filtering.js +94 -73
  33. package/dist/Filtering.js.map +1 -1
  34. package/dist/PubSubManager/initPubSubManager.d.ts.map +1 -1
  35. package/dist/PubSubManager/initPubSubManager.js +10 -2
  36. package/dist/PubSubManager/initPubSubManager.js.map +1 -1
  37. package/lib/DboBuilder/QueryBuilder/Functions.d.ts.map +1 -1
  38. package/lib/DboBuilder/QueryBuilder/Functions.js +18 -0
  39. package/lib/DboBuilder/QueryBuilder/Functions.ts +23 -0
  40. package/lib/DboBuilder/TableHandler.d.ts.map +1 -1
  41. package/lib/DboBuilder/TableHandler.js +1 -1
  42. package/lib/DboBuilder/TableHandler.ts +2 -2
  43. package/lib/DboBuilder/ViewHandler.d.ts +1 -14
  44. package/lib/DboBuilder/ViewHandler.d.ts.map +1 -1
  45. package/lib/DboBuilder/ViewHandler.js +5 -207
  46. package/lib/DboBuilder/ViewHandler.ts +9 -248
  47. package/lib/DboBuilder/delete.d.ts.map +1 -1
  48. package/lib/DboBuilder/delete.js +1 -1
  49. package/lib/DboBuilder/delete.ts +2 -2
  50. package/lib/DboBuilder/getCondition.d.ts +19 -0
  51. package/lib/DboBuilder/getCondition.d.ts.map +1 -0
  52. package/lib/DboBuilder/getCondition.js +235 -0
  53. package/lib/DboBuilder/getCondition.ts +279 -0
  54. package/lib/DboBuilder/insert.d.ts.map +1 -1
  55. package/lib/DboBuilder/insert.js +2 -2
  56. package/lib/DboBuilder/insert.ts +3 -3
  57. package/lib/DboBuilder/subscribe.d.ts.map +1 -1
  58. package/lib/DboBuilder/subscribe.js +1 -1
  59. package/lib/DboBuilder/subscribe.ts +2 -2
  60. package/lib/DboBuilder/update.d.ts.map +1 -1
  61. package/lib/DboBuilder/update.js +2 -2
  62. package/lib/DboBuilder/update.ts +3 -3
  63. package/lib/DboBuilder.d.ts +2 -4
  64. package/lib/DboBuilder.d.ts.map +1 -1
  65. package/lib/DboBuilder.js +3 -4
  66. package/lib/DboBuilder.ts +5 -13
  67. package/lib/Filtering.d.ts.map +1 -1
  68. package/lib/Filtering.js +94 -73
  69. package/lib/Filtering.ts +104 -79
  70. package/lib/PubSubManager/initPubSubManager.d.ts.map +1 -1
  71. package/lib/PubSubManager/initPubSubManager.js +10 -2
  72. package/lib/PubSubManager/initPubSubManager.ts +11 -2
  73. package/package.json +4 -4
  74. package/tests/client/PID.txt +1 -1
  75. package/tests/server/package-lock.json +7 -7
@@ -0,0 +1,235 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getCondition = void 0;
4
+ const prostgles_types_1 = require("prostgles-types");
5
+ const DboBuilder_1 = require("../DboBuilder");
6
+ const PubSubManager_1 = require("../PubSubManager/PubSubManager");
7
+ const Functions_1 = require("./QueryBuilder/Functions");
8
+ const QueryBuilder_1 = require("./QueryBuilder/QueryBuilder");
9
+ const Filtering_1 = require("../Filtering");
10
+ const FILTER_FUNCS = Functions_1.FUNCTIONS.filter(f => f.canBeUsedForFilter);
11
+ /**
12
+ * parses a single filter
13
+ * @example
14
+ * { fff: 2 } => "fff" = 2
15
+ * { fff: { $ilike: 'abc' } } => "fff" ilike 'abc'
16
+ */
17
+ async function getCondition(params) {
18
+ const { filter, select, allowed_colnames, tableAlias, localParams, tableRules } = params;
19
+ let data = { ...filter };
20
+ /* Exists join filter */
21
+ const ERR = "Invalid exists filter. \nExpecting somethibng like: \n | { $exists: { tableName.tableName2: Filter } } \n | { $exists: { \"**.tableName3\": Filter } }\n | { path: string[]; filter: AnyObject }";
22
+ const SP_WILDCARD = "**";
23
+ let existsKeys = (0, prostgles_types_1.getKeys)(data)
24
+ .filter(k => prostgles_types_1.EXISTS_KEYS.includes(k) && Object.keys(data[k] ?? {}).length)
25
+ .map(key => {
26
+ const isJoined = key.toLowerCase().includes("join");
27
+ const filterValue = data[key];
28
+ /**
29
+ * type ExistsJoined =
30
+ * | { "table1.table2": { column: filterValue } }
31
+ * | { path: string[]; filter: AnyObject }
32
+ */
33
+ const dataKeys = Object.keys(filterValue);
34
+ const isDetailed = dataKeys.length === 2 && dataKeys.every(key => ["path", "filter"].includes(key));
35
+ const firstKey = dataKeys[0];
36
+ /**
37
+ * Prevent some errors with table names that contain "."
38
+ */
39
+ const firstKeyIsATable = !!this.dboBuilder.dbo[firstKey];
40
+ let tables = isDetailed ? filterValue.path : (firstKeyIsATable ? [firstKey] : firstKey.split("."));
41
+ let f2 = isDetailed ? filterValue.filter : filterValue[firstKey];
42
+ let shortestJoin = false;
43
+ if (!isJoined) {
44
+ if (tables.length !== 1)
45
+ throw "Expecting single table in exists filter. Example: { $exists: { tableName: Filter } }";
46
+ }
47
+ else {
48
+ /* First part can be the ** param meaning shortest join. Will be overriden by anything in tableConfig */
49
+ if (!tables.length) {
50
+ throw ERR + "\nBut got: " + filterValue;
51
+ }
52
+ if (tables[0] === SP_WILDCARD) {
53
+ tables = tables.slice(1);
54
+ shortestJoin = true;
55
+ }
56
+ }
57
+ return {
58
+ key,
59
+ existType: key,
60
+ isJoined,
61
+ shortestJoin,
62
+ f2,
63
+ tables
64
+ };
65
+ });
66
+ /* Exists with exact path */
67
+ // Object.keys(data).map(k => {
68
+ // let isthis = isPlainObject(data[k]) && !this.column_names.includes(k) && !k.split(".").find(kt => !this.dboBuilder.dbo[kt]);
69
+ // if(isthis) {
70
+ // existsKeys.push({
71
+ // key: k,
72
+ // notJoined: false,
73
+ // exactPaths: k.split(".")
74
+ // });
75
+ // }
76
+ // });
77
+ let funcConds = [];
78
+ const funcFilterkeys = FILTER_FUNCS.filter(f => {
79
+ return f.name in data;
80
+ });
81
+ funcFilterkeys.map(f => {
82
+ const funcArgs = data[f.name];
83
+ if (!Array.isArray(funcArgs))
84
+ throw `A function filter must contain an array. E.g: { $funcFilterName: ["col1"] } \n but got: ${JSON.stringify((0, prostgles_types_1.pickKeys)(data, [f.name]))} `;
85
+ const fields = this.parseFieldFilter(f.getFields(funcArgs), true, allowed_colnames);
86
+ const dissallowedCols = fields.filter(fname => !allowed_colnames.includes(fname));
87
+ if (dissallowedCols.length) {
88
+ throw `Invalid/disallowed columns found in function filter: ${dissallowedCols}`;
89
+ }
90
+ funcConds.push(f.getQuery({ args: funcArgs, allColumns: this.columns, allowedFields: allowed_colnames, tableAlias }));
91
+ });
92
+ let existsCond = "";
93
+ if (existsKeys.length) {
94
+ existsCond = (await Promise.all(existsKeys.map(async (k) => await this.prepareExistCondition(k, localParams)))).join(" AND ");
95
+ }
96
+ /* Computed field queries */
97
+ const p = this.getValidatedRules(tableRules, localParams);
98
+ const computedFields = p.allColumns.filter(c => c.type === "computed");
99
+ let computedColConditions = [];
100
+ Object.keys(data || {}).map(key => {
101
+ const compCol = computedFields.find(cf => cf.name === key);
102
+ if (compCol) {
103
+ computedColConditions.push(compCol.getQuery({
104
+ tableAlias,
105
+ allowedFields: p.select.fields,
106
+ allColumns: this.columns,
107
+ /* CTID not available in AFTER trigger */
108
+ // ctidField: this.is_view? undefined : "ctid"
109
+ ctidField: undefined,
110
+ }) + ` = ${DboBuilder_1.pgp.as.format("$1", [data[key]])}`);
111
+ delete data[key];
112
+ }
113
+ });
114
+ let allowedSelect = [];
115
+ /* Select aliases take precedence over col names. This is to ensure filters work correctly and even on computed cols*/
116
+ if (select) {
117
+ /* Allow filtering by selected fields/funcs */
118
+ allowedSelect = select.filter(s => {
119
+ /* */
120
+ if (["function", "computed", "column"].includes(s.type)) {
121
+ if (s.type !== "column" || allowed_colnames.includes(s.alias)) {
122
+ return true;
123
+ }
124
+ }
125
+ return false;
126
+ });
127
+ }
128
+ /* Add remaining allowed fields */
129
+ allowedSelect = allowedSelect.concat(p.allColumns.filter(c => allowed_colnames.includes(c.name) &&
130
+ !allowedSelect.find(s => s.alias === c.name)).map(f => ({
131
+ type: f.type,
132
+ alias: f.name,
133
+ getQuery: (tableAlias) => f.getQuery({
134
+ tableAlias,
135
+ allColumns: this.columns,
136
+ allowedFields: allowed_colnames
137
+ }),
138
+ selected: false,
139
+ getFields: () => [f.name],
140
+ column_udt_type: f.type === "column" ? this.columns.find(c => c.name === f.name)?.udt_name : undefined
141
+ })));
142
+ /* Parse complex filters
143
+ {
144
+ $filter: [
145
+ { $func: [...] },
146
+ "=",
147
+ value | { $func: [..] }
148
+ ]
149
+ }
150
+ */
151
+ const complexFilters = [];
152
+ const complexFilterKey = "$filter";
153
+ const allowedComparators = [">", "<", "=", "<=", ">=", "<>", "!="];
154
+ if (complexFilterKey in data) {
155
+ /**
156
+ * { $funcName: [arg1, arg2] }
157
+ * { $column: "column_name" }
158
+ */
159
+ const getFuncQuery = (funcData) => {
160
+ if ((0, prostgles_types_1.isObject)(funcData) && "$column" in funcData) {
161
+ const column = funcData["$column"];
162
+ if (typeof column !== "string") {
163
+ throw `expecting: \n { $column: "column_name" } received:\n ${JSON.stringify(funcData)}`;
164
+ }
165
+ if (!allowed_colnames.includes(column)) {
166
+ throw `Dissallowed or Invalid column ${column}. Allowed columns: ${allowed_colnames}`;
167
+ }
168
+ return (0, QueryBuilder_1.asNameAlias)(column, tableAlias);
169
+ }
170
+ const { funcName, args } = (0, QueryBuilder_1.parseFunctionObject)(funcData);
171
+ const funcDef = (0, Functions_1.parseFunction)({ func: funcName, args, functions: Functions_1.FUNCTIONS, allowedFields: allowed_colnames });
172
+ return funcDef.getQuery({ args, tableAlias, allColumns: this.columns, allowedFields: allowed_colnames });
173
+ };
174
+ const complexFilter = data[complexFilterKey];
175
+ if (!Array.isArray(complexFilter)) {
176
+ throw `Invalid $filter. Must contain an array of at least element but got: ${JSON.stringify(complexFilter)} `;
177
+ }
178
+ const [leftFilter, comparator, rightFilterOrValue] = complexFilter;
179
+ const leftVal = getFuncQuery(leftFilter);
180
+ let result = leftVal;
181
+ if (comparator) {
182
+ if (!allowedComparators.includes(comparator)) {
183
+ throw `Invalid $filter. comparator ${JSON.stringify(comparator)} is not valid. Expecting one of: ${allowedComparators}`;
184
+ }
185
+ if (!rightFilterOrValue) {
186
+ throw "Invalid $filter. Expecting a value or function after the comparator";
187
+ }
188
+ const rightVal = (0, prostgles_types_1.isObject)(rightFilterOrValue) ? getFuncQuery(rightFilterOrValue) : (0, PubSubManager_1.asValue)(rightFilterOrValue);
189
+ if (leftVal === rightVal) {
190
+ throw "Invalid $filter. Cannot compare two identical function signatures: " + JSON.stringify(leftFilter);
191
+ }
192
+ result += ` ${comparator} ${rightVal}`;
193
+ }
194
+ complexFilters.push(result);
195
+ }
196
+ /* Parse join filters
197
+ { $joinFilter: { $ST_DWithin: [table.col, foreignTable.col, distance] }
198
+ will make an exists filter
199
+ */
200
+ let filterKeys = Object.keys(data).filter(k => k !== complexFilterKey && !funcFilterkeys.find(ek => ek.name === k) && !computedFields.find(cf => cf.name === k) && !existsKeys.find(ek => ek.key === k));
201
+ // if(allowed_colnames){
202
+ // const aliasedColumns = (select || []).filter(s =>
203
+ // ["function", "computed", "column"].includes(s.type) && allowed_colnames.includes(s.alias) ||
204
+ // s.getFields().find(f => allowed_colnames.includes(f))
205
+ // ).map(s => s.alias);
206
+ // const validCols = [...allowed_colnames, ...aliasedColumns];
207
+ // }
208
+ const validFieldNames = allowedSelect.map(s => s.alias);
209
+ const invalidColumn = filterKeys
210
+ .find(fName => !validFieldNames.find(c => c === fName ||
211
+ (fName.startsWith(c) && (fName.slice(c.length).includes("->") ||
212
+ fName.slice(c.length).includes(".")))));
213
+ if (invalidColumn) {
214
+ throw `Table: ${this.name} -> disallowed/inexistent columns in filter: ${invalidColumn} \n Expecting one of: ${allowedSelect.map(s => s.type === "column" ? s.getQuery() : s.alias).join(", ")}`;
215
+ }
216
+ /* TODO: Allow filter funcs */
217
+ // const singleFuncs = FUNCTIONS.filter(f => f.singleColArg);
218
+ const f = (0, prostgles_types_1.pickKeys)(data, filterKeys);
219
+ const q = (0, Filtering_1.parseFilterItem)({
220
+ filter: f,
221
+ tableAlias,
222
+ pgp: DboBuilder_1.pgp,
223
+ select: allowedSelect
224
+ });
225
+ let templates = [q].filter(q => q);
226
+ if (existsCond)
227
+ templates.push(existsCond);
228
+ templates = templates.concat(funcConds);
229
+ templates = templates.concat(computedColConditions);
230
+ templates = templates.concat(complexFilters);
231
+ /* sorted to ensure duplicate subscription channels are not created due to different condition order */
232
+ return templates.sort()
233
+ .join(" AND \n");
234
+ }
235
+ exports.getCondition = getCondition;
@@ -0,0 +1,279 @@
1
+ import { AnyObject, EXISTS_KEY, EXISTS_KEYS, getKeys, isObject, pickKeys } from "prostgles-types";
2
+ import { ExistsFilterConfig, LocalParams, makeErrorFromPGError, pgp } from "../DboBuilder";
3
+ import { TableRule } from "../PublishParser";
4
+ import { asValue } from "../PubSubManager/PubSubManager";
5
+ import { FUNCTIONS, parseFunction } from "./QueryBuilder/Functions";
6
+ import { asNameAlias, parseFunctionObject, SelectItem } from "./QueryBuilder/QueryBuilder";
7
+ import { ViewHandler } from "./ViewHandler";
8
+ import { parseFilterItem } from "../Filtering";
9
+
10
+
11
+ const FILTER_FUNCS = FUNCTIONS.filter(f => f.canBeUsedForFilter);
12
+
13
+ /**
14
+ * parses a single filter
15
+ * @example
16
+ * { fff: 2 } => "fff" = 2
17
+ * { fff: { $ilike: 'abc' } } => "fff" ilike 'abc'
18
+ */
19
+ export async function getCondition(this: ViewHandler, params: { filter: any, select?: SelectItem[], allowed_colnames: string[], tableAlias?: string, localParams?: LocalParams, tableRules?: TableRule }) {
20
+ const { filter, select, allowed_colnames, tableAlias, localParams, tableRules } = params;
21
+
22
+
23
+ let data = { ... (filter as any) } as any;
24
+
25
+ /* Exists join filter */
26
+ const ERR = "Invalid exists filter. \nExpecting somethibng like: \n | { $exists: { tableName.tableName2: Filter } } \n | { $exists: { \"**.tableName3\": Filter } }\n | { path: string[]; filter: AnyObject }"
27
+ const SP_WILDCARD = "**";
28
+ let existsKeys: ExistsFilterConfig[] = getKeys(data)
29
+ .filter(k => EXISTS_KEYS.includes(k as EXISTS_KEY) && Object.keys(data[k] ?? {}).length)
30
+ .map(key => {
31
+
32
+ const isJoined = key.toLowerCase().includes("join");
33
+
34
+ const filterValue = data[key];
35
+ /**
36
+ * type ExistsJoined =
37
+ * | { "table1.table2": { column: filterValue } }
38
+ * | { path: string[]; filter: AnyObject }
39
+ */
40
+ const dataKeys = Object.keys(filterValue);
41
+ const isDetailed = dataKeys.length === 2 && dataKeys.every(key => ["path", "filter"].includes(key));
42
+
43
+ const firstKey = dataKeys[0];
44
+
45
+ /**
46
+ * Prevent some errors with table names that contain "."
47
+ */
48
+ const firstKeyIsATable = !!this.dboBuilder.dbo[firstKey];
49
+ let tables = isDetailed? filterValue.path : (firstKeyIsATable? [firstKey] : firstKey.split("."));
50
+ let f2 = isDetailed? filterValue.filter : filterValue[firstKey];
51
+ let shortestJoin = false;
52
+
53
+ if (!isJoined) {
54
+ if (tables.length !== 1) throw "Expecting single table in exists filter. Example: { $exists: { tableName: Filter } }"
55
+ } else {
56
+ /* First part can be the ** param meaning shortest join. Will be overriden by anything in tableConfig */
57
+
58
+ if (!tables.length) {
59
+ throw ERR + "\nBut got: " + filterValue;
60
+ }
61
+
62
+ if (tables[0] === SP_WILDCARD) {
63
+ tables = tables.slice(1);
64
+ shortestJoin = true;
65
+ }
66
+ }
67
+
68
+ return {
69
+ key,
70
+ existType: key as EXISTS_KEY,
71
+ isJoined,
72
+ shortestJoin,
73
+ f2,
74
+ tables
75
+ }
76
+ });
77
+ /* Exists with exact path */
78
+ // Object.keys(data).map(k => {
79
+ // let isthis = isPlainObject(data[k]) && !this.column_names.includes(k) && !k.split(".").find(kt => !this.dboBuilder.dbo[kt]);
80
+ // if(isthis) {
81
+ // existsKeys.push({
82
+ // key: k,
83
+ // notJoined: false,
84
+ // exactPaths: k.split(".")
85
+ // });
86
+ // }
87
+ // });
88
+ let funcConds: string[] = [];
89
+ const funcFilterkeys = FILTER_FUNCS.filter(f => {
90
+ return f.name in data;
91
+ });
92
+ funcFilterkeys.map(f => {
93
+ const funcArgs = data[f.name];
94
+ if (!Array.isArray(funcArgs)) throw `A function filter must contain an array. E.g: { $funcFilterName: ["col1"] } \n but got: ${JSON.stringify(pickKeys(data, [f.name]))} `;
95
+ const fields = this.parseFieldFilter(f.getFields(funcArgs), true, allowed_colnames);
96
+
97
+ const dissallowedCols = fields.filter(fname => !allowed_colnames.includes(fname))
98
+ if (dissallowedCols.length) {
99
+ throw `Invalid/disallowed columns found in function filter: ${dissallowedCols}`
100
+ }
101
+ funcConds.push(f.getQuery({ args: funcArgs, allColumns: this.columns, allowedFields: allowed_colnames, tableAlias }));
102
+ })
103
+
104
+
105
+ let existsCond = "";
106
+ if (existsKeys.length) {
107
+ existsCond = (await Promise.all(existsKeys.map(async k => await this.prepareExistCondition(k, localParams)))).join(" AND ");
108
+ }
109
+
110
+ /* Computed field queries */
111
+ const p = this.getValidatedRules(tableRules, localParams);
112
+ const computedFields = p.allColumns.filter(c => c.type === "computed");
113
+ let computedColConditions: string[] = [];
114
+ Object.keys(data || {}).map(key => {
115
+ const compCol = computedFields.find(cf => cf.name === key);
116
+ if (compCol) {
117
+ computedColConditions.push(
118
+ compCol.getQuery({
119
+ tableAlias,
120
+ allowedFields: p.select.fields,
121
+ allColumns: this.columns,
122
+
123
+ /* CTID not available in AFTER trigger */
124
+ // ctidField: this.is_view? undefined : "ctid"
125
+
126
+ ctidField: undefined,
127
+ }) + ` = ${pgp.as.format("$1", [(data as any)[key]])}`
128
+ );
129
+ delete (data as any)[key];
130
+ }
131
+ });
132
+
133
+ let allowedSelect: SelectItem[] = [];
134
+ /* Select aliases take precedence over col names. This is to ensure filters work correctly and even on computed cols*/
135
+ if (select) {
136
+ /* Allow filtering by selected fields/funcs */
137
+ allowedSelect = select.filter(s => {
138
+ /* */
139
+ if (["function", "computed", "column"].includes(s.type)) {
140
+ if (s.type !== "column" || allowed_colnames.includes(s.alias)) {
141
+ return true;
142
+ }
143
+ }
144
+ return false;
145
+ })
146
+ }
147
+
148
+ /* Add remaining allowed fields */
149
+ allowedSelect = allowedSelect.concat(
150
+ p.allColumns.filter(c =>
151
+ allowed_colnames.includes(c.name) &&
152
+ !allowedSelect.find(s => s.alias === c.name)
153
+ ).map(f => ({
154
+ type: f.type,
155
+ alias: f.name,
156
+ getQuery: (tableAlias) => f.getQuery({
157
+ tableAlias,
158
+ allColumns: this.columns,
159
+ allowedFields: allowed_colnames
160
+ }),
161
+ selected: false,
162
+ getFields: () => [f.name],
163
+ column_udt_type: f.type === "column" ? this.columns.find(c => c.name === f.name)?.udt_name : undefined
164
+ }))
165
+ );
166
+
167
+ /* Parse complex filters
168
+ {
169
+ $filter: [
170
+ { $func: [...] },
171
+ "=",
172
+ value | { $func: [..] }
173
+ ]
174
+ }
175
+ */
176
+ const complexFilters: string[] = [];
177
+ const complexFilterKey = "$filter";
178
+ const allowedComparators = [">", "<", "=", "<=", ">=", "<>", "!="]
179
+ if (complexFilterKey in data) {
180
+
181
+ /**
182
+ * { $funcName: [arg1, arg2] }
183
+ * { $column: "column_name" }
184
+ */
185
+ const getFuncQuery = (funcData: AnyObject): string => {
186
+ if(isObject(funcData) && "$column" in funcData){
187
+ const column = funcData["$column"]
188
+ if(typeof column !== "string"){
189
+ throw `expecting: \n { $column: "column_name" } received:\n ${JSON.stringify(funcData)}`;
190
+ }
191
+ if(!allowed_colnames.includes(column)){
192
+ throw `Dissallowed or Invalid column ${column}. Allowed columns: ${allowed_colnames}`;
193
+ }
194
+ return asNameAlias(column, tableAlias)
195
+ }
196
+ const { funcName, args } = parseFunctionObject(funcData);
197
+ const funcDef = parseFunction({ func: funcName, args, functions: FUNCTIONS, allowedFields: allowed_colnames });
198
+ return funcDef.getQuery({ args, tableAlias, allColumns: this.columns, allowedFields: allowed_colnames });
199
+ }
200
+
201
+ const complexFilter = data[complexFilterKey];
202
+ if (!Array.isArray(complexFilter)) {
203
+ throw `Invalid $filter. Must contain an array of at least element but got: ${JSON.stringify(complexFilter)} `
204
+ }
205
+
206
+ const [leftFilter, comparator, rightFilterOrValue] = complexFilter;
207
+
208
+ const leftVal = getFuncQuery(leftFilter);
209
+ let result = leftVal;
210
+ if (comparator) {
211
+ if (!allowedComparators.includes(comparator)) {
212
+ throw `Invalid $filter. comparator ${JSON.stringify(comparator)} is not valid. Expecting one of: ${allowedComparators}`;
213
+ }
214
+ if (!rightFilterOrValue) {
215
+ throw "Invalid $filter. Expecting a value or function after the comparator";
216
+ }
217
+ const rightVal = isObject(rightFilterOrValue) ? getFuncQuery(rightFilterOrValue) : asValue(rightFilterOrValue);
218
+ if (leftVal === rightVal){
219
+ throw "Invalid $filter. Cannot compare two identical function signatures: " + JSON.stringify(leftFilter);
220
+ }
221
+ result += ` ${comparator} ${rightVal}`;
222
+ }
223
+ complexFilters.push(result);
224
+ }
225
+
226
+
227
+ /* Parse join filters
228
+ { $joinFilter: { $ST_DWithin: [table.col, foreignTable.col, distance] }
229
+ will make an exists filter
230
+ */
231
+
232
+ let filterKeys = Object.keys(data).filter(k => k !== complexFilterKey && !funcFilterkeys.find(ek => ek.name === k) && !computedFields.find(cf => cf.name === k) && !existsKeys.find(ek => ek.key === k));
233
+ // if(allowed_colnames){
234
+ // const aliasedColumns = (select || []).filter(s =>
235
+ // ["function", "computed", "column"].includes(s.type) && allowed_colnames.includes(s.alias) ||
236
+ // s.getFields().find(f => allowed_colnames.includes(f))
237
+ // ).map(s => s.alias);
238
+ // const validCols = [...allowed_colnames, ...aliasedColumns];
239
+
240
+ // }
241
+ const validFieldNames = allowedSelect.map(s => s.alias);
242
+ const invalidColumn = filterKeys
243
+ .find(fName => !validFieldNames.find(c =>
244
+ c === fName ||
245
+ (
246
+ fName.startsWith(c) && (
247
+ fName.slice(c.length).includes("->") ||
248
+ fName.slice(c.length).includes(".")
249
+ )
250
+ )
251
+ ));
252
+
253
+ if (invalidColumn) {
254
+ throw `Table: ${this.name} -> disallowed/inexistent columns in filter: ${invalidColumn} \n Expecting one of: ${allowedSelect.map(s => s.type === "column" ? s.getQuery() : s.alias).join(", ")}`;
255
+ }
256
+
257
+ /* TODO: Allow filter funcs */
258
+ // const singleFuncs = FUNCTIONS.filter(f => f.singleColArg);
259
+
260
+ const f = pickKeys(data, filterKeys);
261
+ const q = parseFilterItem({
262
+ filter: f,
263
+ tableAlias,
264
+ pgp,
265
+ select: allowedSelect
266
+ });
267
+
268
+ let templates: string[] = [q].filter(q => q);
269
+
270
+ if (existsCond) templates.push(existsCond);
271
+ templates = templates.concat(funcConds);
272
+ templates = templates.concat(computedColConditions);
273
+ templates = templates.concat(complexFilters);
274
+
275
+ /* sorted to ensure duplicate subscription channels are not created due to different condition order */
276
+ return templates.sort()
277
+ .join(" AND \n");
278
+
279
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"insert.d.ts","sourceRoot":"","sources":["insert.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAqC,YAAY,EAAY,MAAM,iBAAiB,CAAC;AACvG,OAAO,EAAiB,WAAW,EAA4B,MAAM,eAAe,CAAC;AACrF,OAAO,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAE7C,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAE9C,wBAAsB,MAAM,CAAC,IAAI,EAAE,YAAY,EAAE,SAAS,EAAE,CAAC,SAAS,GAAG,SAAS,EAAE,CAAC,EAAE,MAAM,CAAC,EAAE,YAAY,EAAE,aAAa,CAAC,EAAE,SAAS,EAAE,UAAU,CAAC,EAAE,SAAS,EAAE,WAAW,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,GAAG,GAAG,GAAG,EAAE,GAAG,OAAO,CAAC,CAiL1N"}
1
+ {"version":3,"file":"insert.d.ts","sourceRoot":"","sources":["insert.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAqC,YAAY,EAAY,MAAM,iBAAiB,CAAC;AACvG,OAAO,EAAiB,WAAW,EAAyC,MAAM,eAAe,CAAC;AAClG,OAAO,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAE7C,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAE9C,wBAAsB,MAAM,CAAC,IAAI,EAAE,YAAY,EAAE,SAAS,EAAE,CAAC,SAAS,GAAG,SAAS,EAAE,CAAC,EAAE,MAAM,CAAC,EAAE,YAAY,EAAE,aAAa,CAAC,EAAE,SAAS,EAAE,UAAU,CAAC,EAAE,SAAS,EAAE,WAAW,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,GAAG,GAAG,GAAG,EAAE,GAAG,OAAO,CAAC,CAiL1N"}
@@ -129,10 +129,10 @@ async function insert(rowOrRows, param2, param3_unused, tableRules, localParams)
129
129
  const tx = localParams?.tx?.t || this.t;
130
130
  const allowedFieldKeys = this.parseFieldFilter(fields);
131
131
  if (tx) {
132
- result = await tx[queryType](query).catch((err) => (0, DboBuilder_1.makeErr)(err, localParams, this, allowedFieldKeys));
132
+ result = await tx[queryType](query).catch((err) => (0, DboBuilder_1.makeErrorFromPGError)(err, localParams, this, allowedFieldKeys));
133
133
  }
134
134
  else {
135
- result = await this.db.tx(t => t[queryType](query)).catch(err => (0, DboBuilder_1.makeErr)(err, localParams, this, allowedFieldKeys));
135
+ result = await this.db.tx(t => t[queryType](query)).catch(err => (0, DboBuilder_1.makeErrorFromPGError)(err, localParams, this, allowedFieldKeys));
136
136
  }
137
137
  if (tableRules?.[ACTION]?.postValidate) {
138
138
  if (!finalDBtx)
@@ -1,6 +1,6 @@
1
1
  import pgPromise from "pg-promise";
2
2
  import { AnyObject, asName, FieldFilter, get, getKeys, InsertParams, isObject } from "prostgles-types";
3
- import { isPlainObject, LocalParams, makeErr, parseError, pgp } from "../DboBuilder";
3
+ import { isPlainObject, LocalParams, makeErrorFromPGError, parseError, pgp } from "../DboBuilder";
4
4
  import { TableRule } from "../PublishParser";
5
5
  import { asValue, omitKeys, pickKeys } from "../PubSubManager/PubSubManager";
6
6
  import { TableHandler } from "./TableHandler";
@@ -149,9 +149,9 @@ export async function insert(this: TableHandler, rowOrRows: (AnyObject | AnyObje
149
149
 
150
150
  const allowedFieldKeys = this.parseFieldFilter(fields);
151
151
  if (tx) {
152
- result = await (tx as any)[queryType](query).catch((err: any) => makeErr(err, localParams, this, allowedFieldKeys));
152
+ result = await (tx as any)[queryType](query).catch((err: any) => makeErrorFromPGError(err, localParams, this, allowedFieldKeys));
153
153
  } else {
154
- result = await this.db.tx(t => (t as any)[queryType](query)).catch(err => makeErr(err, localParams, this, allowedFieldKeys));
154
+ result = await this.db.tx(t => (t as any)[queryType](query)).catch(err => makeErrorFromPGError(err, localParams, this, allowedFieldKeys));
155
155
  }
156
156
 
157
157
  if(tableRules?.[ACTION]?.postValidate){
@@ -1 +1 @@
1
- {"version":3,"file":"subscribe.d.ts","sourceRoot":"","sources":["subscribe.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAU,eAAe,EAAE,MAAM,iBAAiB,CAAC;AACrE,OAAO,EAAE,MAAM,EAAE,WAAW,EAAuB,MAAM,eAAe,CAAC;AACzE,OAAO,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAE7C,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAE5C,oBAAY,SAAS,GAAG,CAAC,KAAK,EAAE,SAAS,EAAE,KAAK,GAAG,CAAC;AAEpD,iBAAe,SAAS,CAAC,IAAI,EAAE,WAAW,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,eAAe,EAAE,SAAS,EAAE,SAAS,GAAG,OAAO,CAAC;IAAE,WAAW,EAAE,MAAM,GAAG,CAAA;CAAE,CAAC,CAAA;AAC/I,iBAAe,SAAS,CAAC,IAAI,EAAE,WAAW,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,eAAe,EAAE,SAAS,EAAE,SAAS,EAAE,WAAW,EAAE,SAAS,GAAG,SAAS,EAAE,WAAW,EAAE,WAAW,GAAG,OAAO,CAAC,MAAM,CAAC,CAAA;AA4NzL,OAAO,EAAE,SAAS,EAAE,CAAA"}
1
+ {"version":3,"file":"subscribe.d.ts","sourceRoot":"","sources":["subscribe.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAU,eAAe,EAAE,MAAM,iBAAiB,CAAC;AACrE,OAAO,EAAE,MAAM,EAAE,WAAW,EAAoC,MAAM,eAAe,CAAC;AACtF,OAAO,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAE7C,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAE5C,oBAAY,SAAS,GAAG,CAAC,KAAK,EAAE,SAAS,EAAE,KAAK,GAAG,CAAC;AAEpD,iBAAe,SAAS,CAAC,IAAI,EAAE,WAAW,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,eAAe,EAAE,SAAS,EAAE,SAAS,GAAG,OAAO,CAAC;IAAE,WAAW,EAAE,MAAM,GAAG,CAAA;CAAE,CAAC,CAAA;AAC/I,iBAAe,SAAS,CAAC,IAAI,EAAE,WAAW,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,eAAe,EAAE,SAAS,EAAE,SAAS,EAAE,WAAW,EAAE,SAAS,GAAG,SAAS,EAAE,WAAW,EAAE,WAAW,GAAG,OAAO,CAAC,MAAM,CAAC,CAAA;AA4NzL,OAAO,EAAE,SAAS,EAAE,CAAA"}
@@ -43,7 +43,7 @@ async function subscribe(filter, params, localFunc, table_rules, localParams) {
43
43
  def = def.slice(0, -1);
44
44
  }
45
45
  if (!def || typeof def !== "string") {
46
- throw (0, DboBuilder_1.makeErr)("Could get view definition");
46
+ throw (0, DboBuilder_1.makeErrorFromPGError)("Could get view definition");
47
47
  }
48
48
  const { fields } = await this.dboBuilder.dbo.sql(`SELECT * FROM ( \n ${def} \n ) prostgles_subscribe_view_definition LIMIT 0`, {});
49
49
  const tableColumns = fields.filter(f => f.tableName && f.columnName);
@@ -1,5 +1,5 @@
1
1
  import { AnyObject, asName, SubscribeParams } from "prostgles-types";
2
- import { Filter, LocalParams, makeErr, parseError } from "../DboBuilder";
2
+ import { Filter, LocalParams, makeErrorFromPGError, parseError } from "../DboBuilder";
3
3
  import { TableRule } from "../PublishParser";
4
4
  import { log, omitKeys, ViewSubscriptionOptions } from "../PubSubManager/PubSubManager";
5
5
  import { ViewHandler } from "./ViewHandler";
@@ -62,7 +62,7 @@ async function subscribe(this: ViewHandler, filter: Filter, params: SubscribePar
62
62
  def = def.slice(0, -1);
63
63
  }
64
64
  if (!def || typeof def !== "string") {
65
- throw makeErr("Could get view definition");
65
+ throw makeErrorFromPGError("Could get view definition");
66
66
  }
67
67
  const { fields } = await this.dboBuilder.dbo.sql!(`SELECT * FROM ( \n ${def} \n ) prostgles_subscribe_view_definition LIMIT 0`, {});
68
68
  const tableColumns = fields.filter(f => f.tableName && f.columnName);
@@ -1 +1 @@
1
- {"version":3,"file":"update.d.ts","sourceRoot":"","sources":["update.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAkC,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAC1F,OAAO,EAAE,MAAM,EAAiB,WAAW,EAA8B,MAAM,eAAe,CAAC;AAC/F,OAAO,EAAE,SAAS,EAAe,MAAM,kBAAkB,CAAC;AAE1D,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAG9C,wBAAsB,MAAM,CAAC,IAAI,EAAE,YAAY,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,CAAC,EAAE,YAAY,EAAE,UAAU,CAAC,EAAE,SAAS,EAAE,WAAW,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,SAAS,GAAG,IAAI,CAAC,CA+JzL"}
1
+ {"version":3,"file":"update.d.ts","sourceRoot":"","sources":["update.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAkC,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAC1F,OAAO,EAAE,MAAM,EAAiB,WAAW,EAA2C,MAAM,eAAe,CAAC;AAC5G,OAAO,EAAE,SAAS,EAAe,MAAM,kBAAkB,CAAC;AAE1D,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAG9C,wBAAsB,MAAM,CAAC,IAAI,EAAE,YAAY,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,CAAC,EAAE,YAAY,EAAE,UAAU,CAAC,EAAE,SAAS,EAAE,WAAW,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,SAAS,GAAG,IAAI,CAAC,CA+JzL"}
@@ -108,10 +108,10 @@ async function update(filter, _newData, params, tableRules, localParams) {
108
108
  return query;
109
109
  let result;
110
110
  if (this.t) {
111
- result = await (this.t)[qType](query).catch((err) => (0, DboBuilder_1.makeErr)(err, localParams, this, fields));
111
+ result = await (this.t)[qType](query).catch((err) => (0, DboBuilder_1.makeErrorFromPGError)(err, localParams, this, fields));
112
112
  }
113
113
  else {
114
- result = await this.db.tx(t => t[qType](query)).catch(err => (0, DboBuilder_1.makeErr)(err, localParams, this, fields));
114
+ result = await this.db.tx(t => t[qType](query)).catch(err => (0, DboBuilder_1.makeErrorFromPGError)(err, localParams, this, fields));
115
115
  }
116
116
  /** TODO: Delete old file at the end in case new file update fails */
117
117
  // await oldFileDelete();
@@ -1,5 +1,5 @@
1
1
  import { AnyObject, getKeys, isObject, unpatchText, UpdateParams } from "prostgles-types";
2
- import { Filter, isPlainObject, LocalParams, makeErr, Media, parseError } from "../DboBuilder";
2
+ import { Filter, isPlainObject, LocalParams, makeErrorFromPGError, Media, parseError } from "../DboBuilder";
3
3
  import { TableRule, ValidateRow } from "../PublishParser";
4
4
  import { omitKeys, pickKeys } from "../PubSubManager/PubSubManager";
5
5
  import { TableHandler } from "./TableHandler";
@@ -129,9 +129,9 @@ export async function update(this: TableHandler, filter: Filter, _newData: AnyOb
129
129
 
130
130
  let result;
131
131
  if (this.t) {
132
- result = await (this.t)[qType](query).catch((err: any) => makeErr(err, localParams, this, fields));
132
+ result = await (this.t)[qType](query).catch((err: any) => makeErrorFromPGError(err, localParams, this, fields));
133
133
  } else {
134
- result = await this.db.tx(t => (t as any)[qType](query)).catch(err => makeErr(err, localParams, this, fields));
134
+ result = await this.db.tx(t => (t as any)[qType](query)).catch(err => makeErrorFromPGError(err, localParams, this, fields));
135
135
  }
136
136
 
137
137
  /** TODO: Delete old file at the end in case new file update fails */
@@ -1,6 +1,6 @@
1
1
  import * as pgPromise from 'pg-promise';
2
2
  import pg = require('pg-promise/typescript/pg-subset');
3
- import { ColumnInfo, SQLOptions, DbJoinMaker, PG_COLUMN_UDT_DATA_TYPE, TS_PG_Types, TableInfo as TInfo, SQLHandler, AnyObject, ProstglesError } from "prostgles-types";
3
+ import { ColumnInfo, SQLOptions, DbJoinMaker, PG_COLUMN_UDT_DATA_TYPE, TS_PG_Types, TableInfo as TInfo, SQLHandler, AnyObject, ProstglesError, EXISTS_KEY } from "prostgles-types";
4
4
  export declare type SortItem = {
5
5
  asc: boolean;
6
6
  nulls?: "first" | "last";
@@ -181,9 +181,7 @@ export declare type ValidatedTableRules = CommonTableRules & {
181
181
  returningFields: string[];
182
182
  };
183
183
  };
184
- export declare function makeErr(err: any, localParams?: LocalParams, view?: ViewHandler, allowedKeys?: string[]): Promise<never>;
185
- export declare const EXISTS_KEYS: readonly ["$exists", "$notExists", "$existsJoined", "$notExistsJoined"];
186
- export declare type EXISTS_KEY = typeof EXISTS_KEYS[number];
184
+ export declare function makeErrorFromPGError(err: any, localParams?: LocalParams, view?: ViewHandler, allowedKeys?: string[]): Promise<never>;
187
185
  /**
188
186
  * Ensure the error is an Object and has
189
187
  */