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.
- package/dist/DboBuilder/QueryBuilder/Functions.d.ts.map +1 -1
- package/dist/DboBuilder/QueryBuilder/Functions.js +18 -0
- package/dist/DboBuilder/QueryBuilder/Functions.js.map +1 -1
- package/dist/DboBuilder/TableHandler.d.ts.map +1 -1
- package/dist/DboBuilder/TableHandler.js +1 -1
- package/dist/DboBuilder/TableHandler.js.map +1 -1
- package/dist/DboBuilder/ViewHandler.d.ts +6 -12
- package/dist/DboBuilder/ViewHandler.d.ts.map +1 -1
- package/dist/DboBuilder/ViewHandler.js +5 -207
- package/dist/DboBuilder/ViewHandler.js.map +1 -1
- package/dist/DboBuilder/delete.d.ts.map +1 -1
- package/dist/DboBuilder/delete.js +1 -1
- package/dist/DboBuilder/delete.js.map +1 -1
- package/dist/DboBuilder/getCondition.d.ts +19 -0
- package/dist/DboBuilder/getCondition.d.ts.map +1 -0
- package/dist/DboBuilder/getCondition.js +236 -0
- package/dist/DboBuilder/getCondition.js.map +1 -0
- package/dist/DboBuilder/insert.d.ts.map +1 -1
- package/dist/DboBuilder/insert.js +2 -2
- package/dist/DboBuilder/insert.js.map +1 -1
- package/dist/DboBuilder/subscribe.d.ts.map +1 -1
- package/dist/DboBuilder/subscribe.js +1 -1
- package/dist/DboBuilder/subscribe.js.map +1 -1
- package/dist/DboBuilder/update.d.ts.map +1 -1
- package/dist/DboBuilder/update.js +2 -2
- package/dist/DboBuilder/update.js.map +1 -1
- package/dist/DboBuilder.d.ts +2 -4
- package/dist/DboBuilder.d.ts.map +1 -1
- package/dist/DboBuilder.js +3 -4
- package/dist/DboBuilder.js.map +1 -1
- package/dist/Filtering.d.ts.map +1 -1
- package/dist/Filtering.js +94 -73
- package/dist/Filtering.js.map +1 -1
- package/dist/PubSubManager/initPubSubManager.d.ts.map +1 -1
- package/dist/PubSubManager/initPubSubManager.js +10 -2
- package/dist/PubSubManager/initPubSubManager.js.map +1 -1
- package/lib/DboBuilder/QueryBuilder/Functions.d.ts.map +1 -1
- package/lib/DboBuilder/QueryBuilder/Functions.js +18 -0
- package/lib/DboBuilder/QueryBuilder/Functions.ts +23 -0
- package/lib/DboBuilder/TableHandler.d.ts.map +1 -1
- package/lib/DboBuilder/TableHandler.js +1 -1
- package/lib/DboBuilder/TableHandler.ts +2 -2
- package/lib/DboBuilder/ViewHandler.d.ts +1 -14
- package/lib/DboBuilder/ViewHandler.d.ts.map +1 -1
- package/lib/DboBuilder/ViewHandler.js +5 -207
- package/lib/DboBuilder/ViewHandler.ts +9 -248
- package/lib/DboBuilder/delete.d.ts.map +1 -1
- package/lib/DboBuilder/delete.js +1 -1
- package/lib/DboBuilder/delete.ts +2 -2
- package/lib/DboBuilder/getCondition.d.ts +19 -0
- package/lib/DboBuilder/getCondition.d.ts.map +1 -0
- package/lib/DboBuilder/getCondition.js +235 -0
- package/lib/DboBuilder/getCondition.ts +279 -0
- package/lib/DboBuilder/insert.d.ts.map +1 -1
- package/lib/DboBuilder/insert.js +2 -2
- package/lib/DboBuilder/insert.ts +3 -3
- package/lib/DboBuilder/subscribe.d.ts.map +1 -1
- package/lib/DboBuilder/subscribe.js +1 -1
- package/lib/DboBuilder/subscribe.ts +2 -2
- package/lib/DboBuilder/update.d.ts.map +1 -1
- package/lib/DboBuilder/update.js +2 -2
- package/lib/DboBuilder/update.ts +3 -3
- package/lib/DboBuilder.d.ts +2 -4
- package/lib/DboBuilder.d.ts.map +1 -1
- package/lib/DboBuilder.js +3 -4
- package/lib/DboBuilder.ts +5 -13
- package/lib/Filtering.d.ts.map +1 -1
- package/lib/Filtering.js +94 -73
- package/lib/Filtering.ts +104 -79
- package/lib/PubSubManager/initPubSubManager.d.ts.map +1 -1
- package/lib/PubSubManager/initPubSubManager.js +10 -2
- package/lib/PubSubManager/initPubSubManager.ts +11 -2
- package/package.json +4 -4
- package/tests/client/PID.txt +1 -1
- package/tests/server/package-lock.json +7 -7
|
@@ -2,16 +2,15 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.ViewHandler = void 0;
|
|
4
4
|
const makeSelectQuery_1 = require("../DboBuilder/QueryBuilder/makeSelectQuery");
|
|
5
|
+
const getCondition_1 = require("./getCondition");
|
|
5
6
|
const runSQL_1 = require("../DboBuilder/runSQL");
|
|
6
7
|
const prostgles_types_1 = require("prostgles-types");
|
|
7
8
|
const DboBuilder_1 = require("../DboBuilder");
|
|
8
9
|
const PubSubManager_1 = require("../PubSubManager/PubSubManager");
|
|
9
10
|
const QueryBuilder_1 = require("./QueryBuilder/QueryBuilder");
|
|
10
11
|
const Functions_1 = require("./QueryBuilder/Functions");
|
|
11
|
-
const Filtering_1 = require("../Filtering");
|
|
12
12
|
const getColumns_1 = require("./getColumns");
|
|
13
13
|
const subscribe_1 = require("./subscribe");
|
|
14
|
-
const FILTER_FUNCS = Functions_1.FUNCTIONS.filter(f => f.canBeUsedForFilter);
|
|
15
14
|
class ColSet {
|
|
16
15
|
opts;
|
|
17
16
|
constructor(columns, tableName) {
|
|
@@ -579,7 +578,7 @@ class ViewHandler {
|
|
|
579
578
|
if (["row", "value"].includes(returnType)) {
|
|
580
579
|
return (this.t || this.db).oneOrNone(_query).then(data => {
|
|
581
580
|
return (data && returnType === "value") ? Object.values(data)[0] : data;
|
|
582
|
-
}).catch(err => (0, DboBuilder_1.
|
|
581
|
+
}).catch(err => (0, DboBuilder_1.makeErrorFromPGError)(err, localParams, this));
|
|
583
582
|
}
|
|
584
583
|
else {
|
|
585
584
|
return (this.t || this.db).any(_query).then(data => {
|
|
@@ -587,7 +586,7 @@ class ViewHandler {
|
|
|
587
586
|
return data.map(d => Object.values(d)[0]);
|
|
588
587
|
}
|
|
589
588
|
return data;
|
|
590
|
-
}).catch(err => (0, DboBuilder_1.
|
|
589
|
+
}).catch(err => (0, DboBuilder_1.makeErrorFromPGError)(err, localParams, this));
|
|
591
590
|
}
|
|
592
591
|
}
|
|
593
592
|
catch (e) {
|
|
@@ -780,7 +779,7 @@ class ViewHandler {
|
|
|
780
779
|
throw { stack: ["prepareExistCondition()"], message: `Invalid or dissallowed table: ${t}` };
|
|
781
780
|
});
|
|
782
781
|
/* Nested $exists not allowed */
|
|
783
|
-
if (f2 && Object.keys(f2).find(fk =>
|
|
782
|
+
if (f2 && Object.keys(f2).find(fk => prostgles_types_1.EXISTS_KEYS.includes(fk))) {
|
|
784
783
|
throw { stack: ["prepareExistCondition()"], message: "Nested exists dissallowed" };
|
|
785
784
|
}
|
|
786
785
|
const makeTableChain = (finalFilter) => {
|
|
@@ -859,208 +858,7 @@ class ViewHandler {
|
|
|
859
858
|
}
|
|
860
859
|
return res;
|
|
861
860
|
}
|
|
862
|
-
|
|
863
|
-
* parses a single filter
|
|
864
|
-
* @example
|
|
865
|
-
* { fff: 2 } => "fff" = 2
|
|
866
|
-
* { fff: { $ilike: 'abc' } } => "fff" ilike 'abc'
|
|
867
|
-
*/
|
|
868
|
-
async getCondition(params) {
|
|
869
|
-
const { filter, select, allowed_colnames, tableAlias, localParams, tableRules } = params;
|
|
870
|
-
let data = { ...filter };
|
|
871
|
-
/* Exists join filter */
|
|
872
|
-
const ERR = "Invalid exists filter. \nExpecting somethibng like: \n | { $exists: { tableName.tableName2: Filter } } \n | { $exists: { \"**.tableName3\": Filter } }\n | { path: string[]; filter: AnyObject }";
|
|
873
|
-
const SP_WILDCARD = "**";
|
|
874
|
-
let existsKeys = Object.keys(data)
|
|
875
|
-
.filter(k => DboBuilder_1.EXISTS_KEYS.includes(k) && Object.keys(data[k] || {}).length)
|
|
876
|
-
.map(key => {
|
|
877
|
-
const isJoined = DboBuilder_1.EXISTS_KEYS.slice(-2).includes(key);
|
|
878
|
-
const filterValue = data[key];
|
|
879
|
-
/**
|
|
880
|
-
* type ExistsJoined =
|
|
881
|
-
* | { "table1.table2": { column: filterValue } }
|
|
882
|
-
* | { path: string[]; filter: AnyObject }
|
|
883
|
-
*/
|
|
884
|
-
const dataKeys = Object.keys(filterValue);
|
|
885
|
-
const isDetailed = dataKeys.length === 2 && dataKeys.every(key => ["path", "filter"].includes(key));
|
|
886
|
-
const firstKey = dataKeys[0];
|
|
887
|
-
/**
|
|
888
|
-
* Prevent some errors with table names that contain "."
|
|
889
|
-
*/
|
|
890
|
-
const firstKeyIsATable = !!this.dboBuilder.dbo[firstKey];
|
|
891
|
-
let tables = isDetailed ? filterValue.path : (firstKeyIsATable ? [firstKey] : firstKey.split("."));
|
|
892
|
-
let f2 = isDetailed ? filterValue.filter : filterValue[firstKey];
|
|
893
|
-
let shortestJoin = false;
|
|
894
|
-
if (!isJoined) {
|
|
895
|
-
if (tables.length !== 1)
|
|
896
|
-
throw "Expecting single table in exists filter. Example: { $exists: { tableName: Filter } }";
|
|
897
|
-
}
|
|
898
|
-
else {
|
|
899
|
-
/* First part can be the ** param meaning shortest join. Will be overriden by anything in tableConfig */
|
|
900
|
-
if (!tables.length) {
|
|
901
|
-
throw ERR + "\nBut got: " + filterValue;
|
|
902
|
-
}
|
|
903
|
-
if (tables[0] === SP_WILDCARD) {
|
|
904
|
-
tables = tables.slice(1);
|
|
905
|
-
shortestJoin = true;
|
|
906
|
-
}
|
|
907
|
-
}
|
|
908
|
-
return {
|
|
909
|
-
key,
|
|
910
|
-
existType: key,
|
|
911
|
-
isJoined,
|
|
912
|
-
shortestJoin,
|
|
913
|
-
f2,
|
|
914
|
-
tables
|
|
915
|
-
};
|
|
916
|
-
});
|
|
917
|
-
/* Exists with exact path */
|
|
918
|
-
// Object.keys(data).map(k => {
|
|
919
|
-
// let isthis = isPlainObject(data[k]) && !this.column_names.includes(k) && !k.split(".").find(kt => !this.dboBuilder.dbo[kt]);
|
|
920
|
-
// if(isthis) {
|
|
921
|
-
// existsKeys.push({
|
|
922
|
-
// key: k,
|
|
923
|
-
// notJoined: false,
|
|
924
|
-
// exactPaths: k.split(".")
|
|
925
|
-
// });
|
|
926
|
-
// }
|
|
927
|
-
// });
|
|
928
|
-
let funcConds = [];
|
|
929
|
-
const funcFilterkeys = FILTER_FUNCS.filter(f => {
|
|
930
|
-
return f.name in data;
|
|
931
|
-
});
|
|
932
|
-
funcFilterkeys.map(f => {
|
|
933
|
-
const funcArgs = data[f.name];
|
|
934
|
-
if (!Array.isArray(funcArgs))
|
|
935
|
-
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]))} `;
|
|
936
|
-
const fields = this.parseFieldFilter(f.getFields(funcArgs), true, allowed_colnames);
|
|
937
|
-
const dissallowedCols = fields.filter(fname => !allowed_colnames.includes(fname));
|
|
938
|
-
if (dissallowedCols.length) {
|
|
939
|
-
throw `Invalid/disallowed columns found in function filter: ${dissallowedCols}`;
|
|
940
|
-
}
|
|
941
|
-
funcConds.push(f.getQuery({ args: funcArgs, allColumns: this.columns, allowedFields: allowed_colnames, tableAlias }));
|
|
942
|
-
});
|
|
943
|
-
let existsCond = "";
|
|
944
|
-
if (existsKeys.length) {
|
|
945
|
-
existsCond = (await Promise.all(existsKeys.map(async (k) => await this.prepareExistCondition(k, localParams)))).join(" AND ");
|
|
946
|
-
}
|
|
947
|
-
/* Computed field queries */
|
|
948
|
-
const p = this.getValidatedRules(tableRules, localParams);
|
|
949
|
-
const computedFields = p.allColumns.filter(c => c.type === "computed");
|
|
950
|
-
let computedColConditions = [];
|
|
951
|
-
Object.keys(data || {}).map(key => {
|
|
952
|
-
const compCol = computedFields.find(cf => cf.name === key);
|
|
953
|
-
if (compCol) {
|
|
954
|
-
computedColConditions.push(compCol.getQuery({
|
|
955
|
-
tableAlias,
|
|
956
|
-
allowedFields: p.select.fields,
|
|
957
|
-
allColumns: this.columns,
|
|
958
|
-
/* CTID not available in AFTER trigger */
|
|
959
|
-
// ctidField: this.is_view? undefined : "ctid"
|
|
960
|
-
ctidField: undefined,
|
|
961
|
-
}) + ` = ${DboBuilder_1.pgp.as.format("$1", [data[key]])}`);
|
|
962
|
-
delete data[key];
|
|
963
|
-
}
|
|
964
|
-
});
|
|
965
|
-
let allowedSelect = [];
|
|
966
|
-
/* Select aliases take precedence over col names. This is to ensure filters work correctly and even on computed cols*/
|
|
967
|
-
if (select) {
|
|
968
|
-
/* Allow filtering by selected fields/funcs */
|
|
969
|
-
allowedSelect = select.filter(s => {
|
|
970
|
-
/* */
|
|
971
|
-
if (["function", "computed", "column"].includes(s.type)) {
|
|
972
|
-
if (s.type !== "column" || allowed_colnames.includes(s.alias)) {
|
|
973
|
-
return true;
|
|
974
|
-
}
|
|
975
|
-
}
|
|
976
|
-
return false;
|
|
977
|
-
});
|
|
978
|
-
}
|
|
979
|
-
/* Add remaining allowed fields */
|
|
980
|
-
allowedSelect = allowedSelect.concat(p.allColumns.filter(c => allowed_colnames.includes(c.name) &&
|
|
981
|
-
!allowedSelect.find(s => s.alias === c.name)).map(f => ({
|
|
982
|
-
type: f.type,
|
|
983
|
-
alias: f.name,
|
|
984
|
-
getQuery: (tableAlias) => f.getQuery({
|
|
985
|
-
tableAlias,
|
|
986
|
-
allColumns: this.columns,
|
|
987
|
-
allowedFields: allowed_colnames
|
|
988
|
-
}),
|
|
989
|
-
selected: false,
|
|
990
|
-
getFields: () => [f.name],
|
|
991
|
-
column_udt_type: f.type === "column" ? this.columns.find(c => c.name === f.name)?.udt_name : undefined
|
|
992
|
-
})));
|
|
993
|
-
/* Parse complex filters
|
|
994
|
-
{ $filter: [{ $func: [...] }, "=", value | { $func: [..] }] }
|
|
995
|
-
*/
|
|
996
|
-
const complexFilters = [];
|
|
997
|
-
const complexFilterKey = "$filter";
|
|
998
|
-
const allowedComparators = [">", "<", "=", "<=", ">=", "<>", "!="];
|
|
999
|
-
if (complexFilterKey in data) {
|
|
1000
|
-
const getFuncQuery = (funcData) => {
|
|
1001
|
-
const { funcName, args } = (0, QueryBuilder_1.parseFunctionObject)(funcData);
|
|
1002
|
-
const funcDef = (0, Functions_1.parseFunction)({ func: funcName, args, functions: Functions_1.FUNCTIONS, allowedFields: allowed_colnames });
|
|
1003
|
-
return funcDef.getQuery({ args, tableAlias, allColumns: this.columns, allowedFields: allowed_colnames });
|
|
1004
|
-
};
|
|
1005
|
-
const complexFilter = data[complexFilterKey];
|
|
1006
|
-
if (!Array.isArray(complexFilter))
|
|
1007
|
-
throw `Invalid $filter. Must contain an array of at least element but got: ${JSON.stringify(complexFilter)} `;
|
|
1008
|
-
const leftFilter = complexFilter[0];
|
|
1009
|
-
const comparator = complexFilter[1];
|
|
1010
|
-
const rightFilterOrValue = complexFilter[2];
|
|
1011
|
-
const leftVal = getFuncQuery(leftFilter);
|
|
1012
|
-
let result = leftVal;
|
|
1013
|
-
if (comparator) {
|
|
1014
|
-
if (!allowedComparators.includes(comparator))
|
|
1015
|
-
throw `Invalid $filter. comparator ${JSON.stringify(comparator)} is not valid. Expecting one of: ${allowedComparators}`;
|
|
1016
|
-
if (!rightFilterOrValue)
|
|
1017
|
-
throw "Invalid $filter. Expecting a value or function after the comparator";
|
|
1018
|
-
const rightVal = (0, prostgles_types_1.isObject)(rightFilterOrValue) ? getFuncQuery(rightFilterOrValue) : (0, PubSubManager_1.asValue)(rightFilterOrValue);
|
|
1019
|
-
if (leftVal === rightVal)
|
|
1020
|
-
throw "Invalid $filter. Cannot compare two identical function signatures: " + JSON.stringify(leftFilter);
|
|
1021
|
-
result += ` ${comparator} ${rightVal}`;
|
|
1022
|
-
}
|
|
1023
|
-
complexFilters.push(result);
|
|
1024
|
-
}
|
|
1025
|
-
/* Parse join filters
|
|
1026
|
-
{ $joinFilter: { $ST_DWithin: [table.col, foreignTable.col, distance] }
|
|
1027
|
-
will make an exists filter
|
|
1028
|
-
*/
|
|
1029
|
-
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));
|
|
1030
|
-
// if(allowed_colnames){
|
|
1031
|
-
// const aliasedColumns = (select || []).filter(s =>
|
|
1032
|
-
// ["function", "computed", "column"].includes(s.type) && allowed_colnames.includes(s.alias) ||
|
|
1033
|
-
// s.getFields().find(f => allowed_colnames.includes(f))
|
|
1034
|
-
// ).map(s => s.alias);
|
|
1035
|
-
// const validCols = [...allowed_colnames, ...aliasedColumns];
|
|
1036
|
-
// }
|
|
1037
|
-
const validFieldNames = allowedSelect.map(s => s.alias);
|
|
1038
|
-
const invalidColumn = filterKeys
|
|
1039
|
-
.find(fName => !validFieldNames.find(c => c === fName ||
|
|
1040
|
-
(fName.startsWith(c) && (fName.slice(c.length).includes("->") ||
|
|
1041
|
-
fName.slice(c.length).includes(".")))));
|
|
1042
|
-
if (invalidColumn) {
|
|
1043
|
-
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(", ")}`;
|
|
1044
|
-
}
|
|
1045
|
-
/* TODO: Allow filter funcs */
|
|
1046
|
-
// const singleFuncs = FUNCTIONS.filter(f => f.singleColArg);
|
|
1047
|
-
const f = (0, prostgles_types_1.pickKeys)(data, filterKeys);
|
|
1048
|
-
const q = (0, Filtering_1.parseFilterItem)({
|
|
1049
|
-
filter: f,
|
|
1050
|
-
tableAlias,
|
|
1051
|
-
pgp: DboBuilder_1.pgp,
|
|
1052
|
-
select: allowedSelect
|
|
1053
|
-
});
|
|
1054
|
-
let templates = [q].filter(q => q);
|
|
1055
|
-
if (existsCond)
|
|
1056
|
-
templates.push(existsCond);
|
|
1057
|
-
templates = templates.concat(funcConds);
|
|
1058
|
-
templates = templates.concat(computedColConditions);
|
|
1059
|
-
templates = templates.concat(complexFilters);
|
|
1060
|
-
/* sorted to ensure duplicate subscription channels are not created due to different condition order */
|
|
1061
|
-
return templates.sort()
|
|
1062
|
-
.join(" AND \n");
|
|
1063
|
-
}
|
|
861
|
+
getCondition = getCondition_1.getCondition.bind(this);
|
|
1064
862
|
/* This relates only to SELECT */
|
|
1065
863
|
prepareSortItems(orderBy, allowed_cols, tableAlias, select) {
|
|
1066
864
|
const throwErr = () => {
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { makeSelectQuery } from "../DboBuilder/QueryBuilder/makeSelectQuery"
|
|
1
|
+
import { makeSelectQuery } from "../DboBuilder/QueryBuilder/makeSelectQuery";
|
|
2
|
+
import { getCondition } from "./getCondition";
|
|
2
3
|
|
|
3
4
|
import * as pgPromise from 'pg-promise';
|
|
4
5
|
import { canRunSQL, runSQL } from "../DboBuilder/runSQL";
|
|
@@ -10,12 +11,12 @@ import {
|
|
|
10
11
|
TableInfo as TInfo,
|
|
11
12
|
AnyObject,
|
|
12
13
|
isObject, isDefined, getKeys,
|
|
13
|
-
_PG_geometric, pickKeys, SubscribeParams
|
|
14
|
+
_PG_geometric, pickKeys, SubscribeParams, EXISTS_KEYS, EXISTS_KEY
|
|
14
15
|
} from "prostgles-types";
|
|
15
16
|
import { DB, DBHandlerServer, Join } from "../Prostgles";
|
|
16
17
|
import {
|
|
17
|
-
DboBuilder, escapeTSNames, ExistsFilterConfig,
|
|
18
|
-
JoinInfo, LocalParams,
|
|
18
|
+
DboBuilder, escapeTSNames, ExistsFilterConfig,Filter, isPlainObject,
|
|
19
|
+
JoinInfo, LocalParams, makeErrorFromPGError, parseError, pgp, postgresToTsType, SortItem,
|
|
19
20
|
TableHandlers, TableSchema, ValidatedTableRules
|
|
20
21
|
} from "../DboBuilder";
|
|
21
22
|
import { Graph } from "../shortestPath";
|
|
@@ -23,8 +24,7 @@ import { TableRule, UpdateRule, ValidateRow } from "../PublishParser";
|
|
|
23
24
|
import { asValue, omitKeys } from "../PubSubManager/PubSubManager";
|
|
24
25
|
import { TableHandler } from "./TableHandler";
|
|
25
26
|
import { asNameAlias, getNewQuery, parseFunctionObject, SelectItem, SelectItemValidated } from "./QueryBuilder/QueryBuilder";
|
|
26
|
-
import { COMPUTED_FIELDS, FieldSpec
|
|
27
|
-
import { parseFilterItem } from "../Filtering";
|
|
27
|
+
import { COMPUTED_FIELDS, FieldSpec } from "./QueryBuilder/Functions";
|
|
28
28
|
import { getColumns } from "./getColumns";
|
|
29
29
|
import { LocalFunc, subscribe } from "./subscribe";
|
|
30
30
|
export type JoinPaths = {
|
|
@@ -34,8 +34,6 @@ export type JoinPaths = {
|
|
|
34
34
|
}[];
|
|
35
35
|
|
|
36
36
|
|
|
37
|
-
const FILTER_FUNCS = FUNCTIONS.filter(f => f.canBeUsedForFilter);
|
|
38
|
-
|
|
39
37
|
|
|
40
38
|
class ColSet {
|
|
41
39
|
opts: {
|
|
@@ -684,14 +682,14 @@ export class ViewHandler {
|
|
|
684
682
|
if (["row", "value"].includes(returnType!)) {
|
|
685
683
|
return (this.t || this.db).oneOrNone(_query).then(data => {
|
|
686
684
|
return (data && returnType === "value") ? Object.values(data)[0] : data;
|
|
687
|
-
}).catch(err =>
|
|
685
|
+
}).catch(err => makeErrorFromPGError(err, localParams, this));
|
|
688
686
|
} else {
|
|
689
687
|
return (this.t || this.db).any(_query).then(data => {
|
|
690
688
|
if (returnType === "values") {
|
|
691
689
|
return data.map(d => Object.values(d)[0]);
|
|
692
690
|
}
|
|
693
691
|
return data;
|
|
694
|
-
}).catch(err =>
|
|
692
|
+
}).catch(err => makeErrorFromPGError(err, localParams, this));
|
|
695
693
|
}
|
|
696
694
|
|
|
697
695
|
} catch (e) {
|
|
@@ -1027,244 +1025,7 @@ export class ViewHandler {
|
|
|
1027
1025
|
return res;
|
|
1028
1026
|
}
|
|
1029
1027
|
|
|
1030
|
-
|
|
1031
|
-
* parses a single filter
|
|
1032
|
-
* @example
|
|
1033
|
-
* { fff: 2 } => "fff" = 2
|
|
1034
|
-
* { fff: { $ilike: 'abc' } } => "fff" ilike 'abc'
|
|
1035
|
-
*/
|
|
1036
|
-
async getCondition(params: { filter: any, select?: SelectItem[], allowed_colnames: string[], tableAlias?: string, localParams?: LocalParams, tableRules?: TableRule }) {
|
|
1037
|
-
const { filter, select, allowed_colnames, tableAlias, localParams, tableRules } = params;
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
let data = { ... (filter as any) } as any;
|
|
1041
|
-
|
|
1042
|
-
/* Exists join filter */
|
|
1043
|
-
const ERR = "Invalid exists filter. \nExpecting somethibng like: \n | { $exists: { tableName.tableName2: Filter } } \n | { $exists: { \"**.tableName3\": Filter } }\n | { path: string[]; filter: AnyObject }"
|
|
1044
|
-
const SP_WILDCARD = "**";
|
|
1045
|
-
let existsKeys: ExistsFilterConfig[] = Object.keys(data)
|
|
1046
|
-
.filter(k => EXISTS_KEYS.includes(k as EXISTS_KEY) && Object.keys(data[k] || {}).length)
|
|
1047
|
-
.map(key => {
|
|
1048
|
-
|
|
1049
|
-
const isJoined = EXISTS_KEYS.slice(-2).includes(key as EXISTS_KEY);
|
|
1050
|
-
|
|
1051
|
-
const filterValue = data[key];
|
|
1052
|
-
/**
|
|
1053
|
-
* type ExistsJoined =
|
|
1054
|
-
* | { "table1.table2": { column: filterValue } }
|
|
1055
|
-
* | { path: string[]; filter: AnyObject }
|
|
1056
|
-
*/
|
|
1057
|
-
const dataKeys = Object.keys(filterValue);
|
|
1058
|
-
const isDetailed = dataKeys.length === 2 && dataKeys.every(key => ["path", "filter"].includes(key));
|
|
1059
|
-
|
|
1060
|
-
const firstKey = dataKeys[0];
|
|
1061
|
-
|
|
1062
|
-
/**
|
|
1063
|
-
* Prevent some errors with table names that contain "."
|
|
1064
|
-
*/
|
|
1065
|
-
const firstKeyIsATable = !!this.dboBuilder.dbo[firstKey];
|
|
1066
|
-
let tables = isDetailed? filterValue.path : (firstKeyIsATable? [firstKey] : firstKey.split("."));
|
|
1067
|
-
let f2 = isDetailed? filterValue.filter : filterValue[firstKey];
|
|
1068
|
-
let shortestJoin = false;
|
|
1069
|
-
|
|
1070
|
-
if (!isJoined) {
|
|
1071
|
-
if (tables.length !== 1) throw "Expecting single table in exists filter. Example: { $exists: { tableName: Filter } }"
|
|
1072
|
-
} else {
|
|
1073
|
-
/* First part can be the ** param meaning shortest join. Will be overriden by anything in tableConfig */
|
|
1074
|
-
|
|
1075
|
-
if (!tables.length) {
|
|
1076
|
-
throw ERR + "\nBut got: " + filterValue;
|
|
1077
|
-
}
|
|
1078
|
-
|
|
1079
|
-
if (tables[0] === SP_WILDCARD) {
|
|
1080
|
-
tables = tables.slice(1);
|
|
1081
|
-
shortestJoin = true;
|
|
1082
|
-
}
|
|
1083
|
-
}
|
|
1084
|
-
|
|
1085
|
-
return {
|
|
1086
|
-
key,
|
|
1087
|
-
existType: key as EXISTS_KEY,
|
|
1088
|
-
isJoined,
|
|
1089
|
-
shortestJoin,
|
|
1090
|
-
f2,
|
|
1091
|
-
tables
|
|
1092
|
-
}
|
|
1093
|
-
});
|
|
1094
|
-
/* Exists with exact path */
|
|
1095
|
-
// Object.keys(data).map(k => {
|
|
1096
|
-
// let isthis = isPlainObject(data[k]) && !this.column_names.includes(k) && !k.split(".").find(kt => !this.dboBuilder.dbo[kt]);
|
|
1097
|
-
// if(isthis) {
|
|
1098
|
-
// existsKeys.push({
|
|
1099
|
-
// key: k,
|
|
1100
|
-
// notJoined: false,
|
|
1101
|
-
// exactPaths: k.split(".")
|
|
1102
|
-
// });
|
|
1103
|
-
// }
|
|
1104
|
-
// });
|
|
1105
|
-
let funcConds: string[] = [];
|
|
1106
|
-
const funcFilterkeys = FILTER_FUNCS.filter(f => {
|
|
1107
|
-
return f.name in data;
|
|
1108
|
-
});
|
|
1109
|
-
funcFilterkeys.map(f => {
|
|
1110
|
-
const funcArgs = data[f.name];
|
|
1111
|
-
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]))} `;
|
|
1112
|
-
const fields = this.parseFieldFilter(f.getFields(funcArgs), true, allowed_colnames);
|
|
1113
|
-
|
|
1114
|
-
const dissallowedCols = fields.filter(fname => !allowed_colnames.includes(fname))
|
|
1115
|
-
if (dissallowedCols.length) {
|
|
1116
|
-
throw `Invalid/disallowed columns found in function filter: ${dissallowedCols}`
|
|
1117
|
-
}
|
|
1118
|
-
funcConds.push(f.getQuery({ args: funcArgs, allColumns: this.columns, allowedFields: allowed_colnames, tableAlias }));
|
|
1119
|
-
})
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
let existsCond = "";
|
|
1123
|
-
if (existsKeys.length) {
|
|
1124
|
-
existsCond = (await Promise.all(existsKeys.map(async k => await this.prepareExistCondition(k, localParams)))).join(" AND ");
|
|
1125
|
-
}
|
|
1126
|
-
|
|
1127
|
-
/* Computed field queries */
|
|
1128
|
-
const p = this.getValidatedRules(tableRules, localParams);
|
|
1129
|
-
const computedFields = p.allColumns.filter(c => c.type === "computed");
|
|
1130
|
-
let computedColConditions: string[] = [];
|
|
1131
|
-
Object.keys(data || {}).map(key => {
|
|
1132
|
-
const compCol = computedFields.find(cf => cf.name === key);
|
|
1133
|
-
if (compCol) {
|
|
1134
|
-
computedColConditions.push(
|
|
1135
|
-
compCol.getQuery({
|
|
1136
|
-
tableAlias,
|
|
1137
|
-
allowedFields: p.select.fields,
|
|
1138
|
-
allColumns: this.columns,
|
|
1139
|
-
|
|
1140
|
-
/* CTID not available in AFTER trigger */
|
|
1141
|
-
// ctidField: this.is_view? undefined : "ctid"
|
|
1142
|
-
|
|
1143
|
-
ctidField: undefined,
|
|
1144
|
-
}) + ` = ${pgp.as.format("$1", [(data as any)[key]])}`
|
|
1145
|
-
);
|
|
1146
|
-
delete (data as any)[key];
|
|
1147
|
-
}
|
|
1148
|
-
});
|
|
1149
|
-
|
|
1150
|
-
let allowedSelect: SelectItem[] = [];
|
|
1151
|
-
/* Select aliases take precedence over col names. This is to ensure filters work correctly and even on computed cols*/
|
|
1152
|
-
if (select) {
|
|
1153
|
-
/* Allow filtering by selected fields/funcs */
|
|
1154
|
-
allowedSelect = select.filter(s => {
|
|
1155
|
-
/* */
|
|
1156
|
-
if (["function", "computed", "column"].includes(s.type)) {
|
|
1157
|
-
if (s.type !== "column" || allowed_colnames.includes(s.alias)) {
|
|
1158
|
-
return true;
|
|
1159
|
-
}
|
|
1160
|
-
}
|
|
1161
|
-
return false;
|
|
1162
|
-
})
|
|
1163
|
-
}
|
|
1164
|
-
|
|
1165
|
-
/* Add remaining allowed fields */
|
|
1166
|
-
allowedSelect = allowedSelect.concat(
|
|
1167
|
-
p.allColumns.filter(c =>
|
|
1168
|
-
allowed_colnames.includes(c.name) &&
|
|
1169
|
-
!allowedSelect.find(s => s.alias === c.name)
|
|
1170
|
-
).map(f => ({
|
|
1171
|
-
type: f.type,
|
|
1172
|
-
alias: f.name,
|
|
1173
|
-
getQuery: (tableAlias) => f.getQuery({
|
|
1174
|
-
tableAlias,
|
|
1175
|
-
allColumns: this.columns,
|
|
1176
|
-
allowedFields: allowed_colnames
|
|
1177
|
-
}),
|
|
1178
|
-
selected: false,
|
|
1179
|
-
getFields: () => [f.name],
|
|
1180
|
-
column_udt_type: f.type === "column" ? this.columns.find(c => c.name === f.name)?.udt_name : undefined
|
|
1181
|
-
}))
|
|
1182
|
-
);
|
|
1183
|
-
|
|
1184
|
-
/* Parse complex filters
|
|
1185
|
-
{ $filter: [{ $func: [...] }, "=", value | { $func: [..] }] }
|
|
1186
|
-
*/
|
|
1187
|
-
const complexFilters: string[] = [];
|
|
1188
|
-
const complexFilterKey = "$filter";
|
|
1189
|
-
const allowedComparators = [">", "<", "=", "<=", ">=", "<>", "!="]
|
|
1190
|
-
if (complexFilterKey in data) {
|
|
1191
|
-
const getFuncQuery = (funcData: any): string => {
|
|
1192
|
-
const { funcName, args } = parseFunctionObject(funcData);
|
|
1193
|
-
const funcDef = parseFunction({ func: funcName, args, functions: FUNCTIONS, allowedFields: allowed_colnames });
|
|
1194
|
-
return funcDef.getQuery({ args, tableAlias, allColumns: this.columns, allowedFields: allowed_colnames });
|
|
1195
|
-
}
|
|
1196
|
-
|
|
1197
|
-
const complexFilter = data[complexFilterKey];
|
|
1198
|
-
if (!Array.isArray(complexFilter)) throw `Invalid $filter. Must contain an array of at least element but got: ${JSON.stringify(complexFilter)} `
|
|
1199
|
-
const leftFilter = complexFilter[0];
|
|
1200
|
-
const comparator = complexFilter[1];
|
|
1201
|
-
const rightFilterOrValue = complexFilter[2];
|
|
1202
|
-
const leftVal = getFuncQuery(leftFilter);
|
|
1203
|
-
let result = leftVal;
|
|
1204
|
-
if (comparator) {
|
|
1205
|
-
if (!allowedComparators.includes(comparator)) throw `Invalid $filter. comparator ${JSON.stringify(comparator)} is not valid. Expecting one of: ${allowedComparators}`
|
|
1206
|
-
if (!rightFilterOrValue) throw "Invalid $filter. Expecting a value or function after the comparator";
|
|
1207
|
-
const rightVal = isObject(rightFilterOrValue) ? getFuncQuery(rightFilterOrValue) : asValue(rightFilterOrValue);
|
|
1208
|
-
if (leftVal === rightVal) throw "Invalid $filter. Cannot compare two identical function signatures: " + JSON.stringify(leftFilter);
|
|
1209
|
-
result += ` ${comparator} ${rightVal}`;
|
|
1210
|
-
}
|
|
1211
|
-
complexFilters.push(result);
|
|
1212
|
-
}
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
/* Parse join filters
|
|
1216
|
-
{ $joinFilter: { $ST_DWithin: [table.col, foreignTable.col, distance] }
|
|
1217
|
-
will make an exists filter
|
|
1218
|
-
*/
|
|
1219
|
-
|
|
1220
|
-
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));
|
|
1221
|
-
// if(allowed_colnames){
|
|
1222
|
-
// const aliasedColumns = (select || []).filter(s =>
|
|
1223
|
-
// ["function", "computed", "column"].includes(s.type) && allowed_colnames.includes(s.alias) ||
|
|
1224
|
-
// s.getFields().find(f => allowed_colnames.includes(f))
|
|
1225
|
-
// ).map(s => s.alias);
|
|
1226
|
-
// const validCols = [...allowed_colnames, ...aliasedColumns];
|
|
1227
|
-
|
|
1228
|
-
// }
|
|
1229
|
-
const validFieldNames = allowedSelect.map(s => s.alias);
|
|
1230
|
-
const invalidColumn = filterKeys
|
|
1231
|
-
.find(fName => !validFieldNames.find(c =>
|
|
1232
|
-
c === fName ||
|
|
1233
|
-
(
|
|
1234
|
-
fName.startsWith(c) && (
|
|
1235
|
-
fName.slice(c.length).includes("->") ||
|
|
1236
|
-
fName.slice(c.length).includes(".")
|
|
1237
|
-
)
|
|
1238
|
-
)
|
|
1239
|
-
));
|
|
1240
|
-
|
|
1241
|
-
if (invalidColumn) {
|
|
1242
|
-
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(", ")}`;
|
|
1243
|
-
}
|
|
1244
|
-
|
|
1245
|
-
/* TODO: Allow filter funcs */
|
|
1246
|
-
// const singleFuncs = FUNCTIONS.filter(f => f.singleColArg);
|
|
1247
|
-
|
|
1248
|
-
const f = pickKeys(data, filterKeys);
|
|
1249
|
-
const q = parseFilterItem({
|
|
1250
|
-
filter: f,
|
|
1251
|
-
tableAlias,
|
|
1252
|
-
pgp,
|
|
1253
|
-
select: allowedSelect
|
|
1254
|
-
});
|
|
1255
|
-
|
|
1256
|
-
let templates: string[] = [q].filter(q => q);
|
|
1257
|
-
|
|
1258
|
-
if (existsCond) templates.push(existsCond);
|
|
1259
|
-
templates = templates.concat(funcConds);
|
|
1260
|
-
templates = templates.concat(computedColConditions);
|
|
1261
|
-
templates = templates.concat(complexFilters);
|
|
1262
|
-
|
|
1263
|
-
/* sorted to ensure duplicate subscription channels are not created due to different condition order */
|
|
1264
|
-
return templates.sort()
|
|
1265
|
-
.join(" AND \n");
|
|
1266
|
-
|
|
1267
|
-
}
|
|
1028
|
+
getCondition = getCondition.bind(this);
|
|
1268
1029
|
|
|
1269
1030
|
/* This relates only to SELECT */
|
|
1270
1031
|
prepareSortItems(orderBy: OrderBy | undefined, allowed_cols: string[], tableAlias: string | undefined, select: SelectItemValidated[]): SortItem[] {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"delete.d.ts","sourceRoot":"","sources":["delete.ts"],"names":[],"mappings":"AACA,OAAO,EAAqB,YAAY,EAAe,MAAM,iBAAiB,CAAC;AAC/E,OAAO,EAAE,MAAM,EAAE,WAAW,
|
|
1
|
+
{"version":3,"file":"delete.d.ts","sourceRoot":"","sources":["delete.ts"],"names":[],"mappings":"AACA,OAAO,EAAqB,YAAY,EAAe,MAAM,iBAAiB,CAAC;AAC/E,OAAO,EAAE,MAAM,EAAE,WAAW,EAAoC,MAAM,eAAe,CAAC;AACtF,OAAO,EAAc,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAEzD,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAE9C,wBAAsB,OAAO,CAAC,IAAI,EAAE,YAAY,EAAE,MAAM,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,YAAY,EAAE,aAAa,CAAC,EAAE,SAAS,EAAE,WAAW,CAAC,EAAE,SAAS,EAAE,WAAW,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,GAAG,CAAC,CA6HrL"}
|
package/lib/DboBuilder/delete.js
CHANGED
|
@@ -112,7 +112,7 @@ async function _delete(filter, params, param3_unused, table_rules, localParams)
|
|
|
112
112
|
}
|
|
113
113
|
}
|
|
114
114
|
}
|
|
115
|
-
return dbHandler[queryType](_query).catch((err) => (0, DboBuilder_1.
|
|
115
|
+
return dbHandler[queryType](_query).catch((err) => (0, DboBuilder_1.makeErrorFromPGError)(err, localParams));
|
|
116
116
|
}
|
|
117
117
|
catch (e) {
|
|
118
118
|
// console.trace(e)
|
package/lib/DboBuilder/delete.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import pgPromise from "pg-promise";
|
|
2
2
|
import { AnyObject, asName, DeleteParams, FieldFilter } from "prostgles-types";
|
|
3
|
-
import { Filter, LocalParams,
|
|
3
|
+
import { Filter, LocalParams, makeErrorFromPGError, parseError } from "../DboBuilder";
|
|
4
4
|
import { DeleteRule, TableRule } from "../PublishParser";
|
|
5
5
|
import { pickKeys } from "../PubSubManager/PubSubManager";
|
|
6
6
|
import { TableHandler } from "./TableHandler";
|
|
@@ -124,7 +124,7 @@ export async function _delete(this: TableHandler, filter?: Filter, params?: Dele
|
|
|
124
124
|
}
|
|
125
125
|
}
|
|
126
126
|
|
|
127
|
-
return dbHandler[queryType](_query).catch((err: any) =>
|
|
127
|
+
return dbHandler[queryType](_query).catch((err: any) => makeErrorFromPGError(err, localParams));
|
|
128
128
|
} catch (e) {
|
|
129
129
|
// console.trace(e)
|
|
130
130
|
if (localParams && localParams.testRule) throw e;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { LocalParams } from "../DboBuilder";
|
|
2
|
+
import { TableRule } from "../PublishParser";
|
|
3
|
+
import { SelectItem } from "./QueryBuilder/QueryBuilder";
|
|
4
|
+
import { ViewHandler } from "./ViewHandler";
|
|
5
|
+
/**
|
|
6
|
+
* parses a single filter
|
|
7
|
+
* @example
|
|
8
|
+
* { fff: 2 } => "fff" = 2
|
|
9
|
+
* { fff: { $ilike: 'abc' } } => "fff" ilike 'abc'
|
|
10
|
+
*/
|
|
11
|
+
export declare function getCondition(this: ViewHandler, params: {
|
|
12
|
+
filter: any;
|
|
13
|
+
select?: SelectItem[];
|
|
14
|
+
allowed_colnames: string[];
|
|
15
|
+
tableAlias?: string;
|
|
16
|
+
localParams?: LocalParams;
|
|
17
|
+
tableRules?: TableRule;
|
|
18
|
+
}): Promise<string>;
|
|
19
|
+
//# sourceMappingURL=getCondition.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"getCondition.d.ts","sourceRoot":"","sources":["getCondition.ts"],"names":[],"mappings":"AACA,OAAO,EAAsB,WAAW,EAA6B,MAAM,eAAe,CAAC;AAC3F,OAAO,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAG7C,OAAO,EAAoC,UAAU,EAAE,MAAM,6BAA6B,CAAC;AAC3F,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAM1C;;;;;GAKG;AACH,wBAAsB,YAAY,CAAC,IAAI,EAAE,WAAW,EAAE,MAAM,EAAE;IAAE,MAAM,EAAE,GAAG,CAAC;IAAC,MAAM,CAAC,EAAE,UAAU,EAAE,CAAC;IAAC,gBAAgB,EAAE,MAAM,EAAE,CAAC;IAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAAC,WAAW,CAAC,EAAE,WAAW,CAAC;IAAC,UAAU,CAAC,EAAE,SAAS,CAAA;CAAE,mBAoQvM"}
|