prostgles-server 2.0.145 → 2.0.148
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 +43 -44
- 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 +54 -50
- package/dist/DboBuilder.d.ts.map +1 -1
- package/dist/DboBuilder.js +275 -206
- 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 +59 -31
- package/dist/FileManager.js.map +1 -1
- package/dist/Filtering.d.ts.map +1 -1
- package/dist/Filtering.js +17 -14
- 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 +125 -86
- package/dist/Prostgles.js.map +1 -1
- package/dist/PubSubManager.d.ts +8 -8
- package/dist/PubSubManager.d.ts.map +1 -1
- package/dist/PubSubManager.js +58 -52
- package/dist/PubSubManager.js.map +1 -1
- package/dist/QueryBuilder.d.ts +21 -8
- package/dist/QueryBuilder.d.ts.map +1 -1
- package/dist/QueryBuilder.js +179 -107
- package/dist/QueryBuilder.js.map +1 -1
- package/dist/SyncReplication.js +38 -35
- 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 +36 -28
- package/dist/TableConfig.js.map +1 -1
- package/dist/shortestPath.d.ts.map +1 -1
- package/dist/shortestPath.js +2 -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 +7 -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 +128 -55
- 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 +8 -9
- 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 +43 -6
- package/tests/isomorphic_queries.ts +41 -6
- package/tests/server/package-lock.json +29 -31
- 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,10 +143,13 @@ export type FunctionSpec = {
|
|
|
84
143
|
*/
|
|
85
144
|
// returnsBoolean?: boolean;
|
|
86
145
|
|
|
146
|
+
/**
|
|
147
|
+
* Number of arguments expected
|
|
148
|
+
*/
|
|
87
149
|
numArgs: number;
|
|
88
150
|
|
|
89
151
|
/**
|
|
90
|
-
* If provided then the number of column names provided to the function must not be less than this
|
|
152
|
+
* If provided then the number of column names provided to the function (from getFields()) must not be less than this
|
|
91
153
|
* By default every function is checked against numArgs
|
|
92
154
|
*/
|
|
93
155
|
minCols?: number;
|
|
@@ -97,11 +159,13 @@ export type FunctionSpec = {
|
|
|
97
159
|
* getFields: string[] -> used to validate user supplied field names. It will be fired before querying to validate against allowed columns
|
|
98
160
|
* if not field names are used from arguments then return an empty array
|
|
99
161
|
*/
|
|
100
|
-
getFields: (args: any[]
|
|
162
|
+
getFields: (args: any[]) => "*" | string[];
|
|
101
163
|
/**
|
|
102
164
|
* allowedFields passed for multicol functions (e.g.: $rowhash)
|
|
103
165
|
*/
|
|
104
166
|
getQuery: (params: GetQueryArgs) => string;
|
|
167
|
+
|
|
168
|
+
returnType?: PG_COLUMN_UDT_DATA_TYPE;
|
|
105
169
|
};
|
|
106
170
|
|
|
107
171
|
const MAX_COL_NUM = 1600;
|
|
@@ -194,8 +258,10 @@ let PostGIS_Funcs: FunctionSpec[] = [
|
|
|
194
258
|
|
|
195
259
|
if(!isPlainObject(arg2)) mErr();
|
|
196
260
|
const col = allColumns.find(c => c.name === args[0]);
|
|
261
|
+
if(!col) throw new Error("Col not found: " + args[0])
|
|
197
262
|
|
|
198
|
-
const {
|
|
263
|
+
const {
|
|
264
|
+
lat, lng, srid = 4326,
|
|
199
265
|
geojson, text, use_spheroid,
|
|
200
266
|
distance, spheroid = 'SPHEROID["WGS 84",6378137,298.257223563]',
|
|
201
267
|
debug
|
|
@@ -535,7 +601,7 @@ export const FUNCTIONS: FunctionSpec[] = [
|
|
|
535
601
|
second: "minute"
|
|
536
602
|
};
|
|
537
603
|
|
|
538
|
-
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)})`;
|
|
539
605
|
// console.log(res);
|
|
540
606
|
return res;
|
|
541
607
|
}
|
|
@@ -602,7 +668,7 @@ export const FUNCTIONS: FunctionSpec[] = [
|
|
|
602
668
|
} as FunctionSpec)),
|
|
603
669
|
|
|
604
670
|
/* Basic 1 arg col funcs */
|
|
605
|
-
...["upper", "lower", "length", "reverse", "trim", "initcap", "round", "ceil", "floor", "sign", "
|
|
671
|
+
...["upper", "lower", "length", "reverse", "trim", "initcap", "round", "ceil", "floor", "sign", "md5"].map(funcName => ({
|
|
606
672
|
name: "$" + funcName,
|
|
607
673
|
type: "function",
|
|
608
674
|
numArgs: 1,
|
|
@@ -613,6 +679,27 @@ export const FUNCTIONS: FunctionSpec[] = [
|
|
|
613
679
|
}
|
|
614
680
|
} as FunctionSpec)),
|
|
615
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)),
|
|
616
703
|
|
|
617
704
|
/* pgcrypto funcs */
|
|
618
705
|
...["crypt"].map(funcName => ({
|
|
@@ -652,7 +739,7 @@ export const FUNCTIONS: FunctionSpec[] = [
|
|
|
652
739
|
numArgs: 1,
|
|
653
740
|
minCols: 0,
|
|
654
741
|
singleColArg: false,
|
|
655
|
-
getFields: (args: any[]
|
|
742
|
+
getFields: (args: any[]) => [] as string[], // Fields not validated because we'll use the allowed ones anyway
|
|
656
743
|
getQuery: ({ allowedFields, args, tableAlias }) => {
|
|
657
744
|
let value = asValue(args[0]);
|
|
658
745
|
if(typeof value !== "string") throw "expecting string argument";
|
|
@@ -769,7 +856,7 @@ export const FUNCTIONS: FunctionSpec[] = [
|
|
|
769
856
|
let _cols = validCols.filter(c =>
|
|
770
857
|
/** Exclude numeric columns when the search tern contains a character */
|
|
771
858
|
!hasChars ||
|
|
772
|
-
postgresToTsType(c.colInfo
|
|
859
|
+
postgresToTsType(c.colInfo!.udt_name) !== "number"
|
|
773
860
|
);
|
|
774
861
|
|
|
775
862
|
/** This will break GROUP BY (non-integer constant in GROUP BY) */
|
|
@@ -955,28 +1042,16 @@ export class SelectItemBuilder {
|
|
|
955
1042
|
this.select.push(item);
|
|
956
1043
|
}
|
|
957
1044
|
|
|
958
|
-
private
|
|
959
|
-
const funcDef =
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
throw "\n Function " + funcName + " does not exist or is not allowed " + hint;
|
|
964
|
-
}
|
|
965
|
-
this.addFunction(funcDef, args, alias);
|
|
966
|
-
}
|
|
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
|
+
});
|
|
967
1050
|
|
|
968
|
-
private addFunction = (funcDef: FunctionSpec, args: any[], alias: string) => {
|
|
969
|
-
if(funcDef.numArgs) {
|
|
970
|
-
const fields = funcDef.getFields(args, this.allowedFields);
|
|
971
|
-
if(funcDef.minCols !== 0 && fields !== "*" && Array.isArray(fields) && !fields.length ){
|
|
972
|
-
console.log(fields)
|
|
973
|
-
throw `\n Function "${funcDef.name}" expects at least a field name but has not been provided with one`;
|
|
974
|
-
}
|
|
975
|
-
}
|
|
976
1051
|
this.addItem({
|
|
977
1052
|
type: funcDef.type,
|
|
978
1053
|
alias,
|
|
979
|
-
getFields: () => funcDef.getFields(args
|
|
1054
|
+
getFields: () => funcDef.getFields(args),
|
|
980
1055
|
getQuery: (tableAlias?: string) => funcDef.getQuery({ allColumns: this.columns, allowedFields: this.allowedFields, args, tableAlias,
|
|
981
1056
|
ctidField: undefined,
|
|
982
1057
|
|
|
@@ -1054,7 +1129,7 @@ export class SelectItemBuilder {
|
|
|
1054
1129
|
|
|
1055
1130
|
} else {
|
|
1056
1131
|
await Promise.all(selectKeys.map(async key => {
|
|
1057
|
-
const val = userSelect[key],
|
|
1132
|
+
const val: any = userSelect[key as keyof typeof userSelect],
|
|
1058
1133
|
throwErr = (extraErr: string = "") => {
|
|
1059
1134
|
console.trace(extraErr)
|
|
1060
1135
|
throw "Unexpected select -> " + JSON.stringify({ [key]: val }) + "\n" + extraErr;
|
|
@@ -1092,14 +1167,13 @@ export class SelectItemBuilder {
|
|
|
1092
1167
|
}
|
|
1093
1168
|
funcName = val;
|
|
1094
1169
|
args = [key];
|
|
1170
|
+
|
|
1171
|
+
/** Function full notation { $funcName: ["colName", ...args] } */
|
|
1095
1172
|
} else {
|
|
1096
|
-
|
|
1097
|
-
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 });
|
|
1098
|
-
funcName = callKeys[0];
|
|
1099
|
-
args = val[funcName];
|
|
1173
|
+
({ funcName, args } = parseFunctionObject(val));
|
|
1100
1174
|
}
|
|
1101
1175
|
|
|
1102
|
-
this.
|
|
1176
|
+
this.addFunction(funcName, args, key);
|
|
1103
1177
|
|
|
1104
1178
|
/* Join */
|
|
1105
1179
|
} else {
|
|
@@ -1122,10 +1196,10 @@ export class SelectItemBuilder {
|
|
|
1122
1196
|
export async function getNewQuery(
|
|
1123
1197
|
_this: TableHandler,
|
|
1124
1198
|
filter: Filter,
|
|
1125
|
-
selectParams: SelectParams & { alias?: string },
|
|
1199
|
+
selectParams: (SelectParams & { alias?: string }) = {},
|
|
1126
1200
|
param3_unused = null,
|
|
1127
|
-
tableRules: TableRule,
|
|
1128
|
-
localParams: LocalParams,
|
|
1201
|
+
tableRules: TableRule | undefined,
|
|
1202
|
+
localParams: LocalParams | undefined,
|
|
1129
1203
|
columns: ColumnInfo[],
|
|
1130
1204
|
): Promise<NewQuery> {
|
|
1131
1205
|
|
|
@@ -1153,7 +1227,6 @@ export async function getNewQuery(
|
|
|
1153
1227
|
|
|
1154
1228
|
// const all_colnames = _this.column_names.slice(0).concat(COMPUTED_FIELDS.map(c => c.name));
|
|
1155
1229
|
|
|
1156
|
-
selectParams = selectParams || {};
|
|
1157
1230
|
const { select: userSelect = "*" } = selectParams,
|
|
1158
1231
|
// allCols = _this.column_names.slice(0),
|
|
1159
1232
|
// allFieldsIncludingComputed = allCols.concat(COMPUTED_FIELDS.map(c => c.name)),
|
|
@@ -1168,10 +1241,10 @@ export async function getNewQuery(
|
|
|
1168
1241
|
// console.log({ key, val })
|
|
1169
1242
|
let j_filter: Filter = {},
|
|
1170
1243
|
j_selectParams: SelectParams = {},
|
|
1171
|
-
j_path: string[],
|
|
1172
|
-
j_alias: string,
|
|
1173
|
-
j_tableRules: TableRule,
|
|
1174
|
-
j_table: string,
|
|
1244
|
+
j_path: string[] | undefined,
|
|
1245
|
+
j_alias: string | undefined,
|
|
1246
|
+
j_tableRules: TableRule | undefined,
|
|
1247
|
+
j_table: string | undefined,
|
|
1175
1248
|
j_isLeftJoin: boolean = true;
|
|
1176
1249
|
|
|
1177
1250
|
if(val === "*"){
|
|
@@ -1207,7 +1280,7 @@ export async function getNewQuery(
|
|
|
1207
1280
|
j_table = key;
|
|
1208
1281
|
}
|
|
1209
1282
|
}
|
|
1210
|
-
|
|
1283
|
+
if(!j_table) throw "j_table missing"
|
|
1211
1284
|
const _thisJoinedTable: any = _this.dboBuilder.dbo[j_table];
|
|
1212
1285
|
if(!_thisJoinedTable) {
|
|
1213
1286
|
throw `Joined table ${JSON.stringify(j_table)} is disallowed or inexistent \nOr you've forgot to put the function arguments into an array`;
|
|
@@ -1216,7 +1289,7 @@ export async function getNewQuery(
|
|
|
1216
1289
|
let isLocal = true;
|
|
1217
1290
|
if(localParams && (localParams.socket || localParams.httpReq)){
|
|
1218
1291
|
isLocal = false;
|
|
1219
|
-
j_tableRules = await _this.dboBuilder.publishParser
|
|
1292
|
+
j_tableRules = await _this.dboBuilder.publishParser?.getValidatedRequestRuleWusr({ tableName: j_table, command: "find", localParams });
|
|
1220
1293
|
}
|
|
1221
1294
|
|
|
1222
1295
|
if(isLocal || j_tableRules){
|
|
@@ -1270,7 +1343,7 @@ export async function getNewQuery(
|
|
|
1270
1343
|
where,
|
|
1271
1344
|
// having: cond.having,
|
|
1272
1345
|
limit: _this.prepareLimitQuery(selectParams.limit, p),
|
|
1273
|
-
orderBy: [_this.prepareSort(selectParams.orderBy, allowedFields, selectParams.alias,
|
|
1346
|
+
orderBy: [_this.prepareSort(selectParams.orderBy, allowedFields, selectParams.alias, undefined, select)],
|
|
1274
1347
|
offset: _this.prepareOffsetQuery(selectParams.offset)
|
|
1275
1348
|
} as NewQuery;
|
|
1276
1349
|
|
|
@@ -1287,19 +1360,19 @@ export function makeQuery(
|
|
|
1287
1360
|
q: NewQuery,
|
|
1288
1361
|
depth: number = 0,
|
|
1289
1362
|
joinFields: string[] = [],
|
|
1290
|
-
selectParams: SelectParams,
|
|
1363
|
+
selectParams: SelectParams = {},
|
|
1291
1364
|
): string {
|
|
1292
1365
|
const PREF = `prostgles`,
|
|
1293
1366
|
joins = q.joins || [],
|
|
1294
1367
|
// aggs = q.aggs || [],
|
|
1295
1368
|
makePref = (q: NewQuery) => !q.tableAlias? q.table : `${q.tableAlias || ""}_${q.table}`,
|
|
1296
|
-
makePrefANON = (joinAlias, table) => asName(!joinAlias? table : `${joinAlias || ""}_${table}`),
|
|
1369
|
+
makePrefANON = (joinAlias: string | undefined, table: string) => asName(!joinAlias? table : `${joinAlias || ""}_${table}`),
|
|
1297
1370
|
makePrefAN = (q: NewQuery) => asName(makePref(q));
|
|
1298
1371
|
|
|
1299
|
-
const indentLine = (numInd, str, indentStr = " ") => new Array(numInd).fill(indentStr).join("") + str;
|
|
1300
|
-
const indStr = (numInd, str: string) => str.split("\n").map(s => indentLine(numInd, s)).join("\n");
|
|
1301
|
-
const indjArr = (numInd, strArr: string[], indentStr = " "): string[] => strArr.map(str => indentLine(numInd, str) );
|
|
1302
|
-
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);
|
|
1303
1376
|
const selectArrComma = (strArr: string[]): string[] => strArr.map((s, i, arr)=> s + (i < arr.length - 1? " , " : " "));
|
|
1304
1377
|
const prefJCAN = (q: NewQuery, str: string) => asName(`${q.tableAlias || q.table}_${PREF}_${str}`);
|
|
1305
1378
|
|
|
@@ -1308,7 +1381,7 @@ export function makeQuery(
|
|
|
1308
1381
|
const joinInfo = _this.getJoins(q1.table, q2.table, q2.$path, true);
|
|
1309
1382
|
const paths = joinInfo.paths;
|
|
1310
1383
|
|
|
1311
|
-
return
|
|
1384
|
+
return paths.flatMap(({ table, on }, i) => {
|
|
1312
1385
|
const getColName = (col: string, q: NewQuery) => {
|
|
1313
1386
|
if(table === q.table){
|
|
1314
1387
|
const colFromSelect = q.select.find(s => s.getQuery() === asName(col));
|
|
@@ -1355,7 +1428,7 @@ export function makeQuery(
|
|
|
1355
1428
|
/* Rename aggs to avoid collision with join cols */
|
|
1356
1429
|
if(s.type === "aggregation") return asName(`agg_${s.alias}`) + " AS " + asName(s.alias);
|
|
1357
1430
|
return asName(s.alias);
|
|
1358
|
-
}).concat(q2.joins
|
|
1431
|
+
}).concat(q2.joins?.map(j => asName(j.table)) ?? []).join(", ");
|
|
1359
1432
|
|
|
1360
1433
|
const _iiQ = makeQuery(
|
|
1361
1434
|
_this,
|
|
@@ -1392,7 +1465,7 @@ export function makeQuery(
|
|
|
1392
1465
|
}`
|
|
1393
1466
|
];
|
|
1394
1467
|
return jres;
|
|
1395
|
-
})
|
|
1468
|
+
});
|
|
1396
1469
|
}
|
|
1397
1470
|
|
|
1398
1471
|
const getGroupBy = (rootSelectItems: SelectItem[], groupByItems: SelectItem[]): string => {
|
|
@@ -1472,7 +1545,7 @@ export function makeQuery(
|
|
|
1472
1545
|
|
|
1473
1546
|
const rootSelectItems = q.select.filter(s => depth || s.selected)
|
|
1474
1547
|
|
|
1475
|
-
let rootGroupBy: string;
|
|
1548
|
+
let rootGroupBy: string | undefined;
|
|
1476
1549
|
if((selectParams.groupBy || aggs.length || q.joins && q.joins.length) && nonAggs.length){
|
|
1477
1550
|
// console.log({ aggs, nonAggs, joins: q.joins })
|
|
1478
1551
|
// rootGroupBy = getGroupBy(rootSelectItems, depth? rootSelectItems : nonAggs) + (aggs?.length? "" : ", ctid")
|
|
@@ -1536,7 +1609,7 @@ export function makeQuery(
|
|
|
1536
1609
|
, `${q.where} `
|
|
1537
1610
|
])
|
|
1538
1611
|
, `) ${makePrefAN(q)} `
|
|
1539
|
-
, ...
|
|
1612
|
+
, ...joins.flatMap((j, i)=> joinTables(q, j))
|
|
1540
1613
|
])
|
|
1541
1614
|
, ") t1"
|
|
1542
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.148",
|
|
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",
|
|
41
|
-
"
|
|
39
|
+
"@types/sharp": "^0.30.2",
|
|
40
|
+
"typescript": "^4.7.2"
|
|
42
41
|
}
|
|
43
42
|
}
|
package/tests/client/PID.txt
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
59576
|