prostgles-server 2.0.144 → 2.0.147
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/AuthHandler.d.ts +15 -13
- package/dist/AuthHandler.d.ts.map +1 -1
- package/dist/AuthHandler.js +41 -43
- package/dist/AuthHandler.js.map +1 -1
- package/dist/DBEventsManager.d.ts +6 -5
- package/dist/DBEventsManager.d.ts.map +1 -1
- package/dist/DBEventsManager.js +8 -2
- package/dist/DBEventsManager.js.map +1 -1
- package/dist/DboBuilder.d.ts +62 -50
- package/dist/DboBuilder.d.ts.map +1 -1
- package/dist/DboBuilder.js +229 -170
- package/dist/DboBuilder.js.map +1 -1
- package/dist/FileManager.d.ts +5 -5
- package/dist/FileManager.d.ts.map +1 -1
- package/dist/FileManager.js +48 -21
- package/dist/FileManager.js.map +1 -1
- package/dist/Filtering.d.ts.map +1 -1
- package/dist/Filtering.js +11 -9
- package/dist/Filtering.js.map +1 -1
- package/dist/PostgresNotifListenManager.d.ts +3 -3
- package/dist/PostgresNotifListenManager.d.ts.map +1 -1
- package/dist/PostgresNotifListenManager.js +7 -5
- package/dist/PostgresNotifListenManager.js.map +1 -1
- package/dist/Prostgles.d.ts +6 -9
- package/dist/Prostgles.d.ts.map +1 -1
- package/dist/Prostgles.js +122 -83
- package/dist/Prostgles.js.map +1 -1
- package/dist/PubSubManager.d.ts +9 -9
- package/dist/PubSubManager.d.ts.map +1 -1
- package/dist/PubSubManager.js +10 -9
- package/dist/PubSubManager.js.map +1 -1
- package/dist/QueryBuilder.d.ts +26 -8
- package/dist/QueryBuilder.d.ts.map +1 -1
- package/dist/QueryBuilder.js +114 -46
- package/dist/QueryBuilder.js.map +1 -1
- package/dist/SyncReplication.d.ts +1 -1
- package/dist/SyncReplication.d.ts.map +1 -1
- package/dist/SyncReplication.js +31 -29
- package/dist/SyncReplication.js.map +1 -1
- package/dist/TableConfig.d.ts +0 -1
- package/dist/TableConfig.d.ts.map +1 -1
- package/dist/TableConfig.js +25 -17
- package/dist/TableConfig.js.map +1 -1
- package/dist/shortestPath.d.ts.map +1 -1
- package/dist/shortestPath.js.map +1 -1
- package/dist/utils.d.ts +2 -0
- package/dist/utils.d.ts.map +1 -1
- package/dist/utils.js +6 -0
- package/dist/utils.js.map +1 -1
- package/lib/AuthHandler.ts +50 -40
- package/lib/DBEventsManager.ts +14 -7
- package/lib/DboBuilder.ts +265 -199
- package/lib/FileManager.ts +30 -21
- package/lib/Filtering.ts +19 -16
- package/lib/PostgresNotifListenManager.ts +11 -10
- package/lib/Prostgles.ts +89 -73
- package/lib/PubSubManager.ts +13 -11
- package/lib/QueryBuilder.ts +135 -54
- package/lib/SyncReplication.ts +10 -10
- package/lib/TableConfig.ts +23 -15
- package/lib/shortestPath.ts +6 -4
- package/lib/utils.ts +7 -1
- package/package.json +7 -8
- package/tests/client/PID.txt +1 -1
- package/tests/client/index.js +10 -7
- package/tests/client/index.ts +12 -8
- package/tests/client/package-lock.json +14 -14
- package/tests/client/package.json +2 -2
- package/tests/client/tsconfig.json +2 -2
- package/tests/client_only_queries.js +127 -104
- package/tests/client_only_queries.ts +43 -17
- package/tests/isomorphic_queries.js +44 -6
- package/tests/isomorphic_queries.ts +42 -6
- package/tests/server/package-lock.json +27 -29
- package/tests/server/package.json +2 -2
- package/tests/server/tsconfig.json +2 -2
- package/tsconfig.json +3 -2
package/lib/QueryBuilder.ts
CHANGED
|
@@ -5,9 +5,9 @@
|
|
|
5
5
|
*--------------------------------------------------------------------------------------------*/
|
|
6
6
|
|
|
7
7
|
import { pgp, Filter, LocalParams, isPlainObject, TableHandler, ViewHandler, postgresToTsType } from "./DboBuilder";
|
|
8
|
-
import { TableRule
|
|
8
|
+
import { TableRule } from "./Prostgles";
|
|
9
9
|
import { SelectParamsBasic as SelectParams, isEmpty, FieldFilter, asName, TextFilter_FullTextSearchFilterKeys, TS_PG_Types, ColumnInfo, PG_COLUMN_UDT_DATA_TYPE } from "prostgles-types";
|
|
10
|
-
import { get } from "./utils";
|
|
10
|
+
import { get, isObject } from "./utils";
|
|
11
11
|
|
|
12
12
|
|
|
13
13
|
export type SelectItem = {
|
|
@@ -47,6 +47,65 @@ export const asNameAlias = (field: string, tableAlias?: string) => {
|
|
|
47
47
|
return result;
|
|
48
48
|
}
|
|
49
49
|
|
|
50
|
+
export const parseFunctionObject = (funcData: any): { funcName: string; args: any[] } => {
|
|
51
|
+
const makeErr = (msg: string) => `Function not specified correctly. Expecting { $funcName: ["columnName",...] } object but got: ${JSON.stringify(funcData)} \n ${msg}`
|
|
52
|
+
if(!isObject(funcData)) throw makeErr("");
|
|
53
|
+
const keys = Object.keys(funcData);
|
|
54
|
+
if(keys.length !== 1) throw makeErr("");
|
|
55
|
+
const funcName = keys[0];
|
|
56
|
+
const args = funcData[funcName];
|
|
57
|
+
if(!args || !Array.isArray(args)){
|
|
58
|
+
throw makeErr("Arguments missing or invalid");
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
return { funcName, args };
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export const parseFunction = (funcData: { func: string | FunctionSpec, args: any[], functions: FunctionSpec[]; allowedFields: string[]; }): FunctionSpec => {
|
|
65
|
+
const { func, args, functions, allowedFields } = funcData;
|
|
66
|
+
|
|
67
|
+
/* Function is computed column. No checks needed */
|
|
68
|
+
if(typeof func !== "string"){
|
|
69
|
+
const computedCol = COMPUTED_FIELDS.find(c => c.name === func.name);
|
|
70
|
+
if(!computedCol) throw `Unexpected function: computed column spec not found for ${JSON.stringify(func.name)}`;
|
|
71
|
+
return func;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const funcName = func;
|
|
75
|
+
const makeErr = (msg: string): string => {
|
|
76
|
+
return `Issue with function ${JSON.stringify({ [funcName]: args })}: \n${msg}`
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/* Find function */
|
|
80
|
+
const funcDef = functions.find(f => f.name === funcName);
|
|
81
|
+
|
|
82
|
+
if(!funcDef) {
|
|
83
|
+
const sf = functions.filter(f => f.name.toLowerCase().slice(1).startsWith(funcName.toLowerCase())).sort((a, b) => (a.name.length - b.name.length));
|
|
84
|
+
const hint = (sf.length? `. \n Maybe you meant: \n | ${sf.map(s => s.name + " " + (s.description || "")).join(" \n | ")} ?` : "");
|
|
85
|
+
throw "\n Function " + funcName + " does not exist or is not allowed " + hint;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/* Validate fields */
|
|
89
|
+
const fields = funcDef.getFields(args);
|
|
90
|
+
if(fields !== "*"){
|
|
91
|
+
fields.forEach(fieldKey => {
|
|
92
|
+
if(typeof fieldKey !== "string" || !allowedFields.includes(fieldKey)) {
|
|
93
|
+
throw makeErr(`getFields() => field name ${JSON.stringify(fieldKey)} is invalid or disallowed`)
|
|
94
|
+
}
|
|
95
|
+
});
|
|
96
|
+
if((funcDef.minCols ?? 0) > fields.length){
|
|
97
|
+
throw makeErr(`Less columns provided than necessary (minCols=${funcDef.minCols})`)
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
if(funcDef.numArgs && funcDef.minCols !== 0 && fields !== "*" && Array.isArray(fields) && !fields.length) {
|
|
102
|
+
throw `\n Function "${funcDef.name}" expects at least a field name but has not been provided with one`;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
return funcDef;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
|
|
50
109
|
type GetQueryArgs = {
|
|
51
110
|
allColumns: ColumnInfo[];
|
|
52
111
|
allowedFields: string[];
|
|
@@ -84,17 +143,29 @@ export type FunctionSpec = {
|
|
|
84
143
|
*/
|
|
85
144
|
// returnsBoolean?: boolean;
|
|
86
145
|
|
|
146
|
+
/**
|
|
147
|
+
* Number of arguments expected
|
|
148
|
+
*/
|
|
87
149
|
numArgs: number;
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* If provided then the number of column names provided to the function (from getFields()) must not be less than this
|
|
153
|
+
* By default every function is checked against numArgs
|
|
154
|
+
*/
|
|
155
|
+
minCols?: number;
|
|
156
|
+
|
|
88
157
|
type: "function" | "aggregation" | "computed";
|
|
89
158
|
/**
|
|
90
159
|
* getFields: string[] -> used to validate user supplied field names. It will be fired before querying to validate against allowed columns
|
|
91
160
|
* if not field names are used from arguments then return an empty array
|
|
92
161
|
*/
|
|
93
|
-
getFields: (args: any[]
|
|
162
|
+
getFields: (args: any[]) => "*" | string[];
|
|
94
163
|
/**
|
|
95
164
|
* allowedFields passed for multicol functions (e.g.: $rowhash)
|
|
96
165
|
*/
|
|
97
166
|
getQuery: (params: GetQueryArgs) => string;
|
|
167
|
+
|
|
168
|
+
returnType?: PG_COLUMN_UDT_DATA_TYPE;
|
|
98
169
|
};
|
|
99
170
|
|
|
100
171
|
const MAX_COL_NUM = 1600;
|
|
@@ -187,8 +258,10 @@ let PostGIS_Funcs: FunctionSpec[] = [
|
|
|
187
258
|
|
|
188
259
|
if(!isPlainObject(arg2)) mErr();
|
|
189
260
|
const col = allColumns.find(c => c.name === args[0]);
|
|
261
|
+
if(!col) throw new Error("Col not found: " + args[0])
|
|
190
262
|
|
|
191
|
-
const {
|
|
263
|
+
const {
|
|
264
|
+
lat, lng, srid = 4326,
|
|
192
265
|
geojson, text, use_spheroid,
|
|
193
266
|
distance, spheroid = 'SPHEROID["WGS 84",6378137,298.257223563]',
|
|
194
267
|
debug
|
|
@@ -528,7 +601,7 @@ export const FUNCTIONS: FunctionSpec[] = [
|
|
|
528
601
|
second: "minute"
|
|
529
602
|
};
|
|
530
603
|
|
|
531
|
-
let res = `(date_trunc(${asValue(prevInt[unit] || "hour")}, ${col}) + date_part(${asValue(unit, "::text")}, ${col})::int / ${val} * interval ${asValue(val + " " + unit)})`;
|
|
604
|
+
let res = `(date_trunc(${asValue(prevInt[unit as "month"] || "hour")}, ${col}) + date_part(${asValue(unit, "::text")}, ${col})::int / ${val} * interval ${asValue(val + " " + unit)})`;
|
|
532
605
|
// console.log(res);
|
|
533
606
|
return res;
|
|
534
607
|
}
|
|
@@ -595,7 +668,7 @@ export const FUNCTIONS: FunctionSpec[] = [
|
|
|
595
668
|
} as FunctionSpec)),
|
|
596
669
|
|
|
597
670
|
/* Basic 1 arg col funcs */
|
|
598
|
-
...["upper", "lower", "length", "reverse", "trim", "initcap", "round", "ceil", "floor", "sign", "
|
|
671
|
+
...["upper", "lower", "length", "reverse", "trim", "initcap", "round", "ceil", "floor", "sign", "md5"].map(funcName => ({
|
|
599
672
|
name: "$" + funcName,
|
|
600
673
|
type: "function",
|
|
601
674
|
numArgs: 1,
|
|
@@ -606,6 +679,27 @@ export const FUNCTIONS: FunctionSpec[] = [
|
|
|
606
679
|
}
|
|
607
680
|
} as FunctionSpec)),
|
|
608
681
|
|
|
682
|
+
/* Interval funcs */
|
|
683
|
+
...["age", "difference"].map(funcName => ({
|
|
684
|
+
name: "$" + funcName,
|
|
685
|
+
type: "function",
|
|
686
|
+
numArgs: 1,
|
|
687
|
+
singleColArg: true,
|
|
688
|
+
getFields: (args: any[]) => args,
|
|
689
|
+
getQuery: ({ allowedFields, args, tableAlias }) => {
|
|
690
|
+
const validCols = args.filter(a => typeof a === "string").length
|
|
691
|
+
if(funcName === "difference" && validCols !== 2) throw new Error("Must have two column names")
|
|
692
|
+
if(![1,2].includes(validCols)) throw new Error("Must have one or two column names")
|
|
693
|
+
const [leftField, rightField] = args;
|
|
694
|
+
const leftQ = asNameAlias(leftField, tableAlias);
|
|
695
|
+
const rightQ = rightField? ("," + asNameAlias(rightField, tableAlias)) : "";
|
|
696
|
+
if(funcName === "age"){
|
|
697
|
+
return `${funcName}(${leftQ}, ${rightQ})`;
|
|
698
|
+
} else {
|
|
699
|
+
return `${leftQ} - ${rightQ}`;
|
|
700
|
+
}
|
|
701
|
+
}
|
|
702
|
+
} as FunctionSpec)),
|
|
609
703
|
|
|
610
704
|
/* pgcrypto funcs */
|
|
611
705
|
...["crypt"].map(funcName => ({
|
|
@@ -643,8 +737,9 @@ export const FUNCTIONS: FunctionSpec[] = [
|
|
|
643
737
|
name: "$" + funcName,
|
|
644
738
|
type: "function",
|
|
645
739
|
numArgs: 1,
|
|
740
|
+
minCols: 0,
|
|
646
741
|
singleColArg: false,
|
|
647
|
-
getFields: (args: any[]
|
|
742
|
+
getFields: (args: any[]) => [] as string[], // Fields not validated because we'll use the allowed ones anyway
|
|
648
743
|
getQuery: ({ allowedFields, args, tableAlias }) => {
|
|
649
744
|
let value = asValue(args[0]);
|
|
650
745
|
if(typeof value !== "string") throw "expecting string argument";
|
|
@@ -761,7 +856,7 @@ export const FUNCTIONS: FunctionSpec[] = [
|
|
|
761
856
|
let _cols = validCols.filter(c =>
|
|
762
857
|
/** Exclude numeric columns when the search tern contains a character */
|
|
763
858
|
!hasChars ||
|
|
764
|
-
postgresToTsType(c.colInfo
|
|
859
|
+
postgresToTsType(c.colInfo!.udt_name) !== "number"
|
|
765
860
|
);
|
|
766
861
|
|
|
767
862
|
/** This will break GROUP BY (non-integer constant in GROUP BY) */
|
|
@@ -947,28 +1042,16 @@ export class SelectItemBuilder {
|
|
|
947
1042
|
this.select.push(item);
|
|
948
1043
|
}
|
|
949
1044
|
|
|
950
|
-
private
|
|
951
|
-
const funcDef =
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
throw "\n Function " + funcName + " does not exist or is not allowed " + hint;
|
|
956
|
-
}
|
|
957
|
-
this.addFunction(funcDef, args, alias);
|
|
958
|
-
}
|
|
1045
|
+
private addFunction = (func: FunctionSpec | string, args: any[], alias: string) => {
|
|
1046
|
+
const funcDef = parseFunction({
|
|
1047
|
+
func, args, functions: this.functions,
|
|
1048
|
+
allowedFields: this.allowedFieldsIncludingComputed,
|
|
1049
|
+
});
|
|
959
1050
|
|
|
960
|
-
private addFunction = (funcDef: FunctionSpec, args: any[], alias: string) => {
|
|
961
|
-
if(funcDef.numArgs) {
|
|
962
|
-
const fields = funcDef.getFields(args, this.allowedFields);// && (! || !funcDef.getFields(args) !== "*" !funcDef.getFields(args).filter(f => f).length
|
|
963
|
-
if(fields !== "*" && Array.isArray(fields) && !fields.length ){
|
|
964
|
-
console.log(fields)
|
|
965
|
-
throw `\n Function "${funcDef.name}" is missing a field name argument`;
|
|
966
|
-
}
|
|
967
|
-
}
|
|
968
1051
|
this.addItem({
|
|
969
1052
|
type: funcDef.type,
|
|
970
1053
|
alias,
|
|
971
|
-
getFields: () => funcDef.getFields(args
|
|
1054
|
+
getFields: () => funcDef.getFields(args),
|
|
972
1055
|
getQuery: (tableAlias?: string) => funcDef.getQuery({ allColumns: this.columns, allowedFields: this.allowedFields, args, tableAlias,
|
|
973
1056
|
ctidField: undefined,
|
|
974
1057
|
|
|
@@ -1046,7 +1129,7 @@ export class SelectItemBuilder {
|
|
|
1046
1129
|
|
|
1047
1130
|
} else {
|
|
1048
1131
|
await Promise.all(selectKeys.map(async key => {
|
|
1049
|
-
const val = userSelect[key],
|
|
1132
|
+
const val: any = userSelect[key as keyof typeof userSelect],
|
|
1050
1133
|
throwErr = (extraErr: string = "") => {
|
|
1051
1134
|
console.trace(extraErr)
|
|
1052
1135
|
throw "Unexpected select -> " + JSON.stringify({ [key]: val }) + "\n" + extraErr;
|
|
@@ -1084,14 +1167,13 @@ export class SelectItemBuilder {
|
|
|
1084
1167
|
}
|
|
1085
1168
|
funcName = val;
|
|
1086
1169
|
args = [key];
|
|
1170
|
+
|
|
1171
|
+
/** Function full notation { $funcName: ["colName", ...args] } */
|
|
1087
1172
|
} else {
|
|
1088
|
-
|
|
1089
|
-
if(callKeys.length !== 1 || !Array.isArray(val[callKeys[0]])) throw "\nIssue with select. \nUnexpected function definition. \nExpecting { field_name: func_name } OR { result_key: { func_name: [arg1, arg2 ...] } } \nBut got -> " + JSON.stringify({ [key]: val });
|
|
1090
|
-
funcName = callKeys[0];
|
|
1091
|
-
args = val[funcName];
|
|
1173
|
+
({ funcName, args } = parseFunctionObject(val));
|
|
1092
1174
|
}
|
|
1093
1175
|
|
|
1094
|
-
this.
|
|
1176
|
+
this.addFunction(funcName, args, key);
|
|
1095
1177
|
|
|
1096
1178
|
/* Join */
|
|
1097
1179
|
} else {
|
|
@@ -1114,10 +1196,10 @@ export class SelectItemBuilder {
|
|
|
1114
1196
|
export async function getNewQuery(
|
|
1115
1197
|
_this: TableHandler,
|
|
1116
1198
|
filter: Filter,
|
|
1117
|
-
selectParams: SelectParams & { alias?: string },
|
|
1199
|
+
selectParams: (SelectParams & { alias?: string }) = {},
|
|
1118
1200
|
param3_unused = null,
|
|
1119
|
-
tableRules: TableRule,
|
|
1120
|
-
localParams: LocalParams,
|
|
1201
|
+
tableRules: TableRule | undefined,
|
|
1202
|
+
localParams: LocalParams | undefined,
|
|
1121
1203
|
columns: ColumnInfo[],
|
|
1122
1204
|
): Promise<NewQuery> {
|
|
1123
1205
|
|
|
@@ -1145,7 +1227,6 @@ export async function getNewQuery(
|
|
|
1145
1227
|
|
|
1146
1228
|
// const all_colnames = _this.column_names.slice(0).concat(COMPUTED_FIELDS.map(c => c.name));
|
|
1147
1229
|
|
|
1148
|
-
selectParams = selectParams || {};
|
|
1149
1230
|
const { select: userSelect = "*" } = selectParams,
|
|
1150
1231
|
// allCols = _this.column_names.slice(0),
|
|
1151
1232
|
// allFieldsIncludingComputed = allCols.concat(COMPUTED_FIELDS.map(c => c.name)),
|
|
@@ -1160,10 +1241,10 @@ export async function getNewQuery(
|
|
|
1160
1241
|
// console.log({ key, val })
|
|
1161
1242
|
let j_filter: Filter = {},
|
|
1162
1243
|
j_selectParams: SelectParams = {},
|
|
1163
|
-
j_path: string[],
|
|
1164
|
-
j_alias: string,
|
|
1165
|
-
j_tableRules: TableRule,
|
|
1166
|
-
j_table: string,
|
|
1244
|
+
j_path: string[] | undefined,
|
|
1245
|
+
j_alias: string | undefined,
|
|
1246
|
+
j_tableRules: TableRule | undefined,
|
|
1247
|
+
j_table: string | undefined,
|
|
1167
1248
|
j_isLeftJoin: boolean = true;
|
|
1168
1249
|
|
|
1169
1250
|
if(val === "*"){
|
|
@@ -1199,7 +1280,7 @@ export async function getNewQuery(
|
|
|
1199
1280
|
j_table = key;
|
|
1200
1281
|
}
|
|
1201
1282
|
}
|
|
1202
|
-
|
|
1283
|
+
if(!j_table) throw "j_table missing"
|
|
1203
1284
|
const _thisJoinedTable: any = _this.dboBuilder.dbo[j_table];
|
|
1204
1285
|
if(!_thisJoinedTable) {
|
|
1205
1286
|
throw `Joined table ${JSON.stringify(j_table)} is disallowed or inexistent \nOr you've forgot to put the function arguments into an array`;
|
|
@@ -1208,7 +1289,7 @@ export async function getNewQuery(
|
|
|
1208
1289
|
let isLocal = true;
|
|
1209
1290
|
if(localParams && (localParams.socket || localParams.httpReq)){
|
|
1210
1291
|
isLocal = false;
|
|
1211
|
-
j_tableRules = await _this.dboBuilder.publishParser
|
|
1292
|
+
j_tableRules = await _this.dboBuilder.publishParser?.getValidatedRequestRuleWusr({ tableName: j_table, command: "find", localParams });
|
|
1212
1293
|
}
|
|
1213
1294
|
|
|
1214
1295
|
if(isLocal || j_tableRules){
|
|
@@ -1262,7 +1343,7 @@ export async function getNewQuery(
|
|
|
1262
1343
|
where,
|
|
1263
1344
|
// having: cond.having,
|
|
1264
1345
|
limit: _this.prepareLimitQuery(selectParams.limit, p),
|
|
1265
|
-
orderBy: [_this.prepareSort(selectParams.orderBy, allowedFields, selectParams.alias,
|
|
1346
|
+
orderBy: [_this.prepareSort(selectParams.orderBy, allowedFields, selectParams.alias, undefined, select)],
|
|
1266
1347
|
offset: _this.prepareOffsetQuery(selectParams.offset)
|
|
1267
1348
|
} as NewQuery;
|
|
1268
1349
|
|
|
@@ -1279,19 +1360,19 @@ export function makeQuery(
|
|
|
1279
1360
|
q: NewQuery,
|
|
1280
1361
|
depth: number = 0,
|
|
1281
1362
|
joinFields: string[] = [],
|
|
1282
|
-
selectParams: SelectParams,
|
|
1363
|
+
selectParams: SelectParams = {},
|
|
1283
1364
|
): string {
|
|
1284
1365
|
const PREF = `prostgles`,
|
|
1285
1366
|
joins = q.joins || [],
|
|
1286
1367
|
// aggs = q.aggs || [],
|
|
1287
1368
|
makePref = (q: NewQuery) => !q.tableAlias? q.table : `${q.tableAlias || ""}_${q.table}`,
|
|
1288
|
-
makePrefANON = (joinAlias, table) => asName(!joinAlias? table : `${joinAlias || ""}_${table}`),
|
|
1369
|
+
makePrefANON = (joinAlias: string | undefined, table: string) => asName(!joinAlias? table : `${joinAlias || ""}_${table}`),
|
|
1289
1370
|
makePrefAN = (q: NewQuery) => asName(makePref(q));
|
|
1290
1371
|
|
|
1291
|
-
const indentLine = (numInd, str, indentStr = " ") => new Array(numInd).fill(indentStr).join("") + str;
|
|
1292
|
-
const indStr = (numInd, str: string) => str.split("\n").map(s => indentLine(numInd, s)).join("\n");
|
|
1293
|
-
const indjArr = (numInd, strArr: string[], indentStr = " "): string[] => strArr.map(str => indentLine(numInd, str) );
|
|
1294
|
-
const indJ = (numInd, strArr: string[], separator = " \n ", indentStr = " ") => indjArr(numInd, strArr, indentStr).join(separator);
|
|
1372
|
+
const indentLine = (numInd: number, str: string, indentStr = " ") => new Array(numInd).fill(indentStr).join("") + str;
|
|
1373
|
+
const indStr = (numInd: number, str: string) => str.split("\n").map(s => indentLine(numInd, s)).join("\n");
|
|
1374
|
+
const indjArr = (numInd: number, strArr: string[], indentStr = " "): string[] => strArr.map(str => indentLine(numInd, str) );
|
|
1375
|
+
const indJ = (numInd: number, strArr: string[], separator = " \n ", indentStr = " ") => indjArr(numInd, strArr, indentStr).join(separator);
|
|
1295
1376
|
const selectArrComma = (strArr: string[]): string[] => strArr.map((s, i, arr)=> s + (i < arr.length - 1? " , " : " "));
|
|
1296
1377
|
const prefJCAN = (q: NewQuery, str: string) => asName(`${q.tableAlias || q.table}_${PREF}_${str}`);
|
|
1297
1378
|
|
|
@@ -1300,7 +1381,7 @@ export function makeQuery(
|
|
|
1300
1381
|
const joinInfo = _this.getJoins(q1.table, q2.table, q2.$path, true);
|
|
1301
1382
|
const paths = joinInfo.paths;
|
|
1302
1383
|
|
|
1303
|
-
return
|
|
1384
|
+
return paths.flatMap(({ table, on }, i) => {
|
|
1304
1385
|
const getColName = (col: string, q: NewQuery) => {
|
|
1305
1386
|
if(table === q.table){
|
|
1306
1387
|
const colFromSelect = q.select.find(s => s.getQuery() === asName(col));
|
|
@@ -1347,7 +1428,7 @@ export function makeQuery(
|
|
|
1347
1428
|
/* Rename aggs to avoid collision with join cols */
|
|
1348
1429
|
if(s.type === "aggregation") return asName(`agg_${s.alias}`) + " AS " + asName(s.alias);
|
|
1349
1430
|
return asName(s.alias);
|
|
1350
|
-
}).concat(q2.joins
|
|
1431
|
+
}).concat(q2.joins?.map(j => asName(j.table)) ?? []).join(", ");
|
|
1351
1432
|
|
|
1352
1433
|
const _iiQ = makeQuery(
|
|
1353
1434
|
_this,
|
|
@@ -1384,7 +1465,7 @@ export function makeQuery(
|
|
|
1384
1465
|
}`
|
|
1385
1466
|
];
|
|
1386
1467
|
return jres;
|
|
1387
|
-
})
|
|
1468
|
+
});
|
|
1388
1469
|
}
|
|
1389
1470
|
|
|
1390
1471
|
const getGroupBy = (rootSelectItems: SelectItem[], groupByItems: SelectItem[]): string => {
|
|
@@ -1464,7 +1545,7 @@ export function makeQuery(
|
|
|
1464
1545
|
|
|
1465
1546
|
const rootSelectItems = q.select.filter(s => depth || s.selected)
|
|
1466
1547
|
|
|
1467
|
-
let rootGroupBy: string;
|
|
1548
|
+
let rootGroupBy: string | undefined;
|
|
1468
1549
|
if((selectParams.groupBy || aggs.length || q.joins && q.joins.length) && nonAggs.length){
|
|
1469
1550
|
// console.log({ aggs, nonAggs, joins: q.joins })
|
|
1470
1551
|
// rootGroupBy = getGroupBy(rootSelectItems, depth? rootSelectItems : nonAggs) + (aggs?.length? "" : ", ctid")
|
|
@@ -1528,7 +1609,7 @@ export function makeQuery(
|
|
|
1528
1609
|
, `${q.where} `
|
|
1529
1610
|
])
|
|
1530
1611
|
, `) ${makePrefAN(q)} `
|
|
1531
|
-
, ...
|
|
1612
|
+
, ...joins.flatMap((j, i)=> joinTables(q, j))
|
|
1532
1613
|
])
|
|
1533
1614
|
, ") t1"
|
|
1534
1615
|
])
|
package/lib/SyncReplication.ts
CHANGED
|
@@ -82,10 +82,10 @@ export const syncData = async (_this: PubSubManager, sync: SyncParams, clientDat
|
|
|
82
82
|
throw `dbo.${table_name}.find or .count are missing or not allowed`;
|
|
83
83
|
}
|
|
84
84
|
|
|
85
|
-
const first_rows = await _this.dbo?.[table_name]?.find?.(_filter, { orderBy: (orderByAsc as OrderBy), select: sync_fields, limit, offset },
|
|
86
|
-
const last_rows = first_rows
|
|
85
|
+
const first_rows = await _this.dbo?.[table_name]?.find?.(_filter, { orderBy: (orderByAsc as OrderBy), select: sync_fields, limit, offset }, undefined, table_rules);
|
|
86
|
+
const last_rows = first_rows?.slice(-1);
|
|
87
87
|
// const last_rows = await _this?.dbo[table_name]?.find?.(_filter, { orderBy: (orderByDesc as OrderBy), select: sync_fields, limit: 1, offset: -offset || 0 }, null, table_rules);
|
|
88
|
-
const count = await _this.dbo?.[table_name]?.count?.(_filter,
|
|
88
|
+
const count = await _this.dbo?.[table_name]?.count?.(_filter, undefined, undefined, table_rules);
|
|
89
89
|
|
|
90
90
|
return { s_fr: first_rows?.[0] || null, s_lr: last_rows?.[0] || null, s_count: count }
|
|
91
91
|
},
|
|
@@ -146,7 +146,7 @@ export const syncData = async (_this: PubSubManager, sync: SyncParams, clientDat
|
|
|
146
146
|
offset: offset || 0,
|
|
147
147
|
limit: batch_size
|
|
148
148
|
},
|
|
149
|
-
|
|
149
|
+
undefined,
|
|
150
150
|
table_rules
|
|
151
151
|
);
|
|
152
152
|
|
|
@@ -164,7 +164,7 @@ export const syncData = async (_this: PubSubManager, sync: SyncParams, clientDat
|
|
|
164
164
|
return Promise.all(deleted.map(async d => {
|
|
165
165
|
const id_filter = filterObj(d, id_fields);
|
|
166
166
|
try {
|
|
167
|
-
await (_this.dbo[table_name] as TableHandler).delete(id_filter, undefined,
|
|
167
|
+
await (_this.dbo[table_name] as TableHandler).delete(id_filter, undefined, undefined, table_rules);
|
|
168
168
|
return 1;
|
|
169
169
|
} catch (e){
|
|
170
170
|
console.error(e)
|
|
@@ -195,7 +195,7 @@ export const syncData = async (_this: PubSubManager, sync: SyncParams, clientDat
|
|
|
195
195
|
select: [synced_field, ...id_fields] ,
|
|
196
196
|
orderBy: (orderByAsc as OrderBy),
|
|
197
197
|
},
|
|
198
|
-
|
|
198
|
+
undefined,
|
|
199
199
|
table_rules
|
|
200
200
|
);
|
|
201
201
|
const inserts = data.filter(d => !existingData.find(ed => rowsIdsMatch(ed, d)));
|
|
@@ -216,7 +216,7 @@ export const syncData = async (_this: PubSubManager, sync: SyncParams, clientDat
|
|
|
216
216
|
if(table_rules.insert && inserts.length){
|
|
217
217
|
// const qs = await tbl.insert(inserts, { fixIssues: true }, null, table_rules, { returnQuery: true });
|
|
218
218
|
// console.log("inserts", qs)
|
|
219
|
-
await tbl.insert(inserts, { fixIssues: true },
|
|
219
|
+
await tbl.insert(inserts, { fixIssues: true }, undefined, table_rules);
|
|
220
220
|
inserted = inserts.length;
|
|
221
221
|
}
|
|
222
222
|
|
|
@@ -308,7 +308,7 @@ export const syncData = async (_this: PubSubManager, sync: SyncParams, clientDat
|
|
|
308
308
|
sync_fields.map(key => {
|
|
309
309
|
_filter[key] = c_lr[key];
|
|
310
310
|
});
|
|
311
|
-
server_row = await _this?.dbo?.[table_name]?.find?.(_filter, { select: sync_fields, limit: 1 },
|
|
311
|
+
server_row = await _this?.dbo?.[table_name]?.find?.(_filter, { select: sync_fields, limit: 1 }, undefined, table_rules);
|
|
312
312
|
}
|
|
313
313
|
|
|
314
314
|
// if(rowsFullyMatch(c_lr, s_lr)){ //c_count === s_count &&
|
|
@@ -337,7 +337,7 @@ export const syncData = async (_this: PubSubManager, sync: SyncParams, clientDat
|
|
|
337
337
|
console.error({ syncIssue: "sync.lr[synced_field] is greater than lastRow[synced_field]"})
|
|
338
338
|
}
|
|
339
339
|
sync.lr = lastRow;
|
|
340
|
-
sync.last_synced = +sync.lr[synced_field];
|
|
340
|
+
sync.last_synced = +sync.lr?.[synced_field];
|
|
341
341
|
}
|
|
342
342
|
},
|
|
343
343
|
|
|
@@ -381,7 +381,7 @@ export const syncData = async (_this: PubSubManager, sync: SyncParams, clientDat
|
|
|
381
381
|
});
|
|
382
382
|
await Promise.all(to_delete.map(d => {
|
|
383
383
|
deleted++;
|
|
384
|
-
return (_this.dbo[table_name] as TableHandler).delete(filterObj(d, id_fields), { },
|
|
384
|
+
return (_this.dbo[table_name] as TableHandler).delete(filterObj(d, id_fields), { }, undefined, table_rules);
|
|
385
385
|
}));
|
|
386
386
|
sData = await getServerData(min_synced, offset);
|
|
387
387
|
}
|
package/lib/TableConfig.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { AnyObject, asName } from "prostgles-types";
|
|
2
|
-
import { DboBuilder, JoinInfo, LocalParams } from "./DboBuilder";
|
|
2
|
+
import { DboBuilder, getKeys, JoinInfo, LocalParams } from "./DboBuilder";
|
|
3
3
|
import { asSQLIdentifier, ALLOWED_EXTENSION, ALLOWED_CONTENT_TYPE } from "./FileManager";
|
|
4
4
|
import { DB, DbHandler, Prostgles } from "./Prostgles";
|
|
5
5
|
import { asValue } from "./PubSubManager";
|
|
@@ -184,9 +184,15 @@ export type TableConfig<LANG_IDS = { en: 1 }> = {
|
|
|
184
184
|
export default class TableConfigurator {
|
|
185
185
|
|
|
186
186
|
config?: TableConfig;
|
|
187
|
-
get dbo(): DbHandler {
|
|
188
|
-
|
|
189
|
-
|
|
187
|
+
get dbo(): DbHandler {
|
|
188
|
+
if(!this.prostgles.dbo) throw "this.prostgles.dbo missing"
|
|
189
|
+
return this.prostgles.dbo
|
|
190
|
+
};
|
|
191
|
+
get db(): DB {
|
|
192
|
+
if(!this.prostgles.db) throw "this.prostgles.db missing"
|
|
193
|
+
return this.prostgles.db
|
|
194
|
+
};
|
|
195
|
+
// sidKeyName: string;
|
|
190
196
|
prostgles: Prostgles
|
|
191
197
|
|
|
192
198
|
constructor(prostgles: Prostgles){
|
|
@@ -203,7 +209,7 @@ export default class TableConfigurator {
|
|
|
203
209
|
}
|
|
204
210
|
|
|
205
211
|
getColInfo = (params: {col: string, table: string}): ColExtraInfo | undefined => {
|
|
206
|
-
return this.config[params.table]?.[params.col]?.info;
|
|
212
|
+
return (this.config as any)[params.table]?.[params.col]?.info;
|
|
207
213
|
}
|
|
208
214
|
|
|
209
215
|
checkColVal = (params: {col: string, table: string, value: any }): void => {
|
|
@@ -218,6 +224,7 @@ export default class TableConfigurator {
|
|
|
218
224
|
|
|
219
225
|
getJoinInfo = (sourceTable: string, targetTable: string): JoinInfo | undefined => {
|
|
220
226
|
if(
|
|
227
|
+
this.config &&
|
|
221
228
|
sourceTable in this.config &&
|
|
222
229
|
this.config[sourceTable] &&
|
|
223
230
|
"columns" in this.config[sourceTable]
|
|
@@ -247,9 +254,10 @@ export default class TableConfigurator {
|
|
|
247
254
|
async init(){
|
|
248
255
|
let queries: string[] = [];
|
|
249
256
|
|
|
257
|
+
if(!this.config || !this.prostgles.pgp) throw "config or pgp missing"
|
|
250
258
|
/* Create lookup tables */
|
|
251
259
|
Object.keys(this.config).map(tableName => {
|
|
252
|
-
const tableConf = this.config[tableName];
|
|
260
|
+
const tableConf = this.config![tableName];
|
|
253
261
|
const { dropIfExists = false, dropIfExistsCascade = false } = tableConf;
|
|
254
262
|
if(dropIfExistsCascade){
|
|
255
263
|
queries.push(`DROP TABLE IF EXISTS ${tableName} CASCADE;`);
|
|
@@ -266,8 +274,8 @@ export default class TableConfigurator {
|
|
|
266
274
|
);`);
|
|
267
275
|
|
|
268
276
|
rows.map(row => {
|
|
269
|
-
const values = this.prostgles.pgp
|
|
270
|
-
queries.push(this.prostgles.pgp
|
|
277
|
+
const values = this.prostgles.pgp!.helpers.values(row)
|
|
278
|
+
queries.push(this.prostgles.pgp!.as.format(`INSERT INTO ${tableName} (${["id", ...keys].map(t => asName(t)).join(", ")}) ` + " VALUES ${values:raw} ;", { values} ))
|
|
271
279
|
});
|
|
272
280
|
// console.log("Created lookup table " + tableName)
|
|
273
281
|
}
|
|
@@ -284,7 +292,7 @@ export default class TableConfigurator {
|
|
|
284
292
|
|
|
285
293
|
/* Create referenced columns */
|
|
286
294
|
await Promise.all(Object.keys(this.config).map(async tableName => {
|
|
287
|
-
const tableConf = this.config[tableName];
|
|
295
|
+
const tableConf = this.config![tableName];
|
|
288
296
|
if("columns" in tableConf){
|
|
289
297
|
const getColDef = (name: string, colConf: ColumnConfig): string => {
|
|
290
298
|
const colNameEsc = asName(name);
|
|
@@ -318,13 +326,13 @@ export default class TableConfigurator {
|
|
|
318
326
|
}
|
|
319
327
|
}
|
|
320
328
|
|
|
321
|
-
const colDefs = [];
|
|
329
|
+
const colDefs: string[] = [];
|
|
322
330
|
Object.keys(tableConf.columns).filter(c => !("joinDef" in tableConf.columns[c])).map(colName => {
|
|
323
331
|
const colConf = tableConf.columns[colName];
|
|
324
332
|
|
|
325
333
|
if(!this.dbo[tableName]){
|
|
326
334
|
colDefs.push(getColDef(colName, colConf))
|
|
327
|
-
} else if(!colDefs.length && !this.dbo[tableName].columns
|
|
335
|
+
} else if(!colDefs.length && !this.dbo[tableName].columns?.find(c => colName === c.name)) {
|
|
328
336
|
|
|
329
337
|
if("references" in colConf && colConf.references){
|
|
330
338
|
|
|
@@ -354,13 +362,13 @@ export default class TableConfigurator {
|
|
|
354
362
|
}
|
|
355
363
|
}
|
|
356
364
|
if("constraints" in tableConf && tableConf.constraints){
|
|
357
|
-
|
|
358
|
-
queries.push(`ALTER TABLE ${asName(tableName)} ADD CONSTRAINT ${asName(constraintName)} ${tableConf.constraints[constraintName]} ;`);
|
|
365
|
+
getKeys(tableConf.constraints).map(constraintName => {
|
|
366
|
+
queries.push(`ALTER TABLE ${asName(tableName)} ADD CONSTRAINT ${asName(constraintName)} ${tableConf.constraints![constraintName]} ;`);
|
|
359
367
|
});
|
|
360
368
|
}
|
|
361
369
|
if("indexes" in tableConf && tableConf.indexes){
|
|
362
|
-
|
|
363
|
-
const { concurrently, unique, using, definition, replace } = tableConf.indexes[indexName];
|
|
370
|
+
getKeys(tableConf.indexes).map(indexName => {
|
|
371
|
+
const { concurrently, unique, using, definition, replace } = tableConf.indexes![indexName];
|
|
364
372
|
if(replace || typeof replace !== "boolean" && tableConf.replaceUniqueIndexes){
|
|
365
373
|
queries.push(`DROP INDEX IF EXISTS ${asName(indexName)} ;`);
|
|
366
374
|
}
|
package/lib/shortestPath.ts
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
|
|
1
|
+
import { AnyObject } from "prostgles-types";
|
|
2
|
+
|
|
3
|
+
const shortestDistanceNode = (distances: AnyObject, visited: AnyObject) => {
|
|
2
4
|
let shortest = null;
|
|
3
5
|
|
|
4
6
|
for (let node in distances) {
|
|
@@ -16,18 +18,18 @@ export type Graph = {
|
|
|
16
18
|
|
|
17
19
|
export const findShortestPath = (graph: Graph, startNode: string, endNode: string): { distance: number, path: string[] } => {
|
|
18
20
|
// establish object for recording distances from the start node
|
|
19
|
-
let distances = {};
|
|
21
|
+
let distances: AnyObject = {};
|
|
20
22
|
distances[endNode] = "Infinity";
|
|
21
23
|
distances = Object.assign(distances, graph[startNode]);
|
|
22
24
|
|
|
23
25
|
// track paths
|
|
24
|
-
let parents = { endNode: null };
|
|
26
|
+
let parents: AnyObject = { endNode: null };
|
|
25
27
|
for (let child in graph[startNode]) {
|
|
26
28
|
parents[child] = startNode;
|
|
27
29
|
}
|
|
28
30
|
|
|
29
31
|
// track nodes that have already been visited
|
|
30
|
-
let visited = [];
|
|
32
|
+
let visited: AnyObject = [];
|
|
31
33
|
|
|
32
34
|
// find the nearest node
|
|
33
35
|
let node = shortestDistanceNode(distances, visited);
|
package/lib/utils.ts
CHANGED
|
@@ -1 +1,7 @@
|
|
|
1
|
-
export { get } from "prostgles-types";
|
|
1
|
+
export { get } from "prostgles-types";
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
export function isObject(obj: any): obj is Record<string, any> {
|
|
5
|
+
return Boolean(obj && typeof obj === "object" && !Array.isArray(obj) );
|
|
6
|
+
}
|
|
7
|
+
export const isDefined = <T>(v: T | undefined | void): v is T => v !== undefined && v !== null
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "prostgles-server",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.147",
|
|
4
4
|
"description": "",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -24,20 +24,19 @@
|
|
|
24
24
|
},
|
|
25
25
|
"homepage": "https://github.com/prostgles/prostgles-server-js#readme",
|
|
26
26
|
"dependencies": {
|
|
27
|
-
"@aws-sdk/client-s3": "^3.
|
|
28
|
-
"aws-sdk": "^2.
|
|
27
|
+
"@aws-sdk/client-s3": "^3.95.0",
|
|
28
|
+
"aws-sdk": "^2.1141.0",
|
|
29
29
|
"bluebird": "^3.7.2",
|
|
30
30
|
"file-type": "^16.5.3",
|
|
31
|
-
"
|
|
32
|
-
"
|
|
33
|
-
"
|
|
34
|
-
"prostgles-types": "^1.5.123",
|
|
35
|
-
"sharp": "^0.30.4"
|
|
31
|
+
"pg-promise": "^10.11.1",
|
|
32
|
+
"prostgles-types": "^1.5.124",
|
|
33
|
+
"sharp": "^0.30.5"
|
|
36
34
|
},
|
|
37
35
|
"devDependencies": {
|
|
38
36
|
"@aws-sdk/types": "^3.34.0",
|
|
39
37
|
"@types/bluebird": "^3.5.36",
|
|
40
38
|
"@types/node": "^14.14.35",
|
|
39
|
+
"@types/sharp": "^0.30.2",
|
|
41
40
|
"typescript": "^3.9.7"
|
|
42
41
|
}
|
|
43
42
|
}
|
package/tests/client/PID.txt
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
57757
|