prostgles-server 4.2.157 → 4.2.159
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/Auth/AuthHandler.js +2 -2
- package/dist/Auth/AuthHandler.js.map +1 -1
- package/dist/Auth/AuthTypes.d.ts +4 -8
- package/dist/Auth/AuthTypes.d.ts.map +1 -1
- package/dist/Auth/setAuthProviders.d.ts +1 -1
- package/dist/Auth/setAuthProviders.d.ts.map +1 -1
- package/dist/Auth/setAuthProviders.js +6 -7
- package/dist/Auth/setAuthProviders.js.map +1 -1
- package/dist/Auth/setEmailProvider.d.ts +1 -1
- package/dist/Auth/setEmailProvider.d.ts.map +1 -1
- package/dist/Auth/setEmailProvider.js +22 -2
- package/dist/Auth/setEmailProvider.js.map +1 -1
- package/dist/Auth/setupAuthRoutes.js +1 -1
- package/dist/Auth/setupAuthRoutes.js.map +1 -1
- package/dist/Prostgles.d.ts +1 -0
- package/dist/Prostgles.d.ts.map +1 -1
- package/dist/Prostgles.js +6 -0
- package/dist/Prostgles.js.map +1 -1
- package/dist/initProstgles.d.ts.map +1 -1
- package/dist/initProstgles.js +2 -6
- package/dist/initProstgles.js.map +1 -1
- package/package.json +1 -1
- package/lib/Auth/AuthHandler.ts +0 -436
- package/lib/Auth/AuthTypes.ts +0 -285
- package/lib/Auth/getSafeReturnURL.ts +0 -35
- package/lib/Auth/sendEmail.ts +0 -83
- package/lib/Auth/setAuthProviders.ts +0 -129
- package/lib/Auth/setEmailProvider.ts +0 -63
- package/lib/Auth/setupAuthRoutes.ts +0 -161
- package/lib/DBEventsManager.ts +0 -178
- package/lib/DBSchemaBuilder.ts +0 -225
- package/lib/DboBuilder/DboBuilder.ts +0 -319
- package/lib/DboBuilder/DboBuilderTypes.ts +0 -361
- package/lib/DboBuilder/QueryBuilder/Functions.ts +0 -1153
- package/lib/DboBuilder/QueryBuilder/QueryBuilder.ts +0 -288
- package/lib/DboBuilder/QueryBuilder/getJoinQuery.ts +0 -263
- package/lib/DboBuilder/QueryBuilder/getNewQuery.ts +0 -271
- package/lib/DboBuilder/QueryBuilder/getSelectQuery.ts +0 -136
- package/lib/DboBuilder/QueryBuilder/prepareHaving.ts +0 -22
- package/lib/DboBuilder/QueryStreamer.ts +0 -250
- package/lib/DboBuilder/TableHandler/DataValidator.ts +0 -428
- package/lib/DboBuilder/TableHandler/TableHandler.ts +0 -205
- package/lib/DboBuilder/TableHandler/delete.ts +0 -115
- package/lib/DboBuilder/TableHandler/insert.ts +0 -183
- package/lib/DboBuilder/TableHandler/insertTest.ts +0 -78
- package/lib/DboBuilder/TableHandler/onDeleteFromFileTable.ts +0 -62
- package/lib/DboBuilder/TableHandler/runInsertUpdateQuery.ts +0 -134
- package/lib/DboBuilder/TableHandler/update.ts +0 -126
- package/lib/DboBuilder/TableHandler/updateBatch.ts +0 -49
- package/lib/DboBuilder/TableHandler/updateFile.ts +0 -48
- package/lib/DboBuilder/TableHandler/upsert.ts +0 -34
- package/lib/DboBuilder/ViewHandler/ViewHandler.ts +0 -393
- package/lib/DboBuilder/ViewHandler/count.ts +0 -38
- package/lib/DboBuilder/ViewHandler/find.ts +0 -153
- package/lib/DboBuilder/ViewHandler/getExistsCondition.ts +0 -73
- package/lib/DboBuilder/ViewHandler/getExistsFilters.ts +0 -74
- package/lib/DboBuilder/ViewHandler/getInfo.ts +0 -32
- package/lib/DboBuilder/ViewHandler/getTableJoinQuery.ts +0 -84
- package/lib/DboBuilder/ViewHandler/parseComplexFilter.ts +0 -96
- package/lib/DboBuilder/ViewHandler/parseFieldFilter.ts +0 -105
- package/lib/DboBuilder/ViewHandler/parseJoinPath.ts +0 -208
- package/lib/DboBuilder/ViewHandler/prepareSortItems.ts +0 -163
- package/lib/DboBuilder/ViewHandler/prepareWhere.ts +0 -90
- package/lib/DboBuilder/ViewHandler/size.ts +0 -37
- package/lib/DboBuilder/ViewHandler/subscribe.ts +0 -118
- package/lib/DboBuilder/ViewHandler/validateViewRules.ts +0 -70
- package/lib/DboBuilder/dboBuilderUtils.ts +0 -222
- package/lib/DboBuilder/getColumns.ts +0 -114
- package/lib/DboBuilder/getCondition.ts +0 -201
- package/lib/DboBuilder/getSubscribeRelatedTables.ts +0 -190
- package/lib/DboBuilder/getTablesForSchemaPostgresSQL.ts +0 -426
- package/lib/DboBuilder/insertNestedRecords.ts +0 -355
- package/lib/DboBuilder/parseUpdateRules.ts +0 -187
- package/lib/DboBuilder/prepareShortestJoinPaths.ts +0 -186
- package/lib/DboBuilder/runSQL.ts +0 -182
- package/lib/DboBuilder/runTransaction.ts +0 -50
- package/lib/DboBuilder/sqlErrCodeToMsg.ts +0 -254
- package/lib/DboBuilder/uploadFile.ts +0 -69
- package/lib/Event_Trigger_Tags.ts +0 -118
- package/lib/FileManager/FileManager.ts +0 -358
- package/lib/FileManager/getValidatedFileType.ts +0 -69
- package/lib/FileManager/initFileManager.ts +0 -187
- package/lib/FileManager/upload.ts +0 -62
- package/lib/FileManager/uploadStream.ts +0 -79
- package/lib/Filtering.ts +0 -463
- package/lib/JSONBValidation/validate_jsonb_schema_sql.ts +0 -502
- package/lib/JSONBValidation/validation.ts +0 -143
- package/lib/Logging.ts +0 -127
- package/lib/PostgresNotifListenManager.ts +0 -143
- package/lib/Prostgles.ts +0 -479
- package/lib/ProstglesTypes.ts +0 -196
- package/lib/PubSubManager/PubSubManager.ts +0 -609
- package/lib/PubSubManager/addSub.ts +0 -138
- package/lib/PubSubManager/addSync.ts +0 -141
- package/lib/PubSubManager/getCreatePubSubManagerError.ts +0 -72
- package/lib/PubSubManager/getPubSubManagerInitQuery.ts +0 -662
- package/lib/PubSubManager/initPubSubManager.ts +0 -79
- package/lib/PubSubManager/notifListener.ts +0 -173
- package/lib/PubSubManager/orphanTriggerCheck.ts +0 -70
- package/lib/PubSubManager/pushSubData.ts +0 -55
- package/lib/PublishParser/PublishParser.ts +0 -162
- package/lib/PublishParser/getFileTableRules.ts +0 -124
- package/lib/PublishParser/getSchemaFromPublish.ts +0 -141
- package/lib/PublishParser/getTableRulesWithoutFileTable.ts +0 -177
- package/lib/PublishParser/publishTypesAndUtils.ts +0 -399
- package/lib/RestApi.ts +0 -127
- package/lib/SchemaWatch/SchemaWatch.ts +0 -90
- package/lib/SchemaWatch/createSchemaWatchEventTrigger.ts +0 -3
- package/lib/SchemaWatch/getValidatedWatchSchemaType.ts +0 -45
- package/lib/SchemaWatch/getWatchSchemaTagList.ts +0 -27
- package/lib/SyncReplication.ts +0 -557
- package/lib/TableConfig/TableConfig.ts +0 -468
- package/lib/TableConfig/getColumnDefinitionQuery.ts +0 -111
- package/lib/TableConfig/getConstraintDefinitionQueries.ts +0 -95
- package/lib/TableConfig/getFutureTableSchema.ts +0 -64
- package/lib/TableConfig/getPGIndexes.ts +0 -53
- package/lib/TableConfig/getTableColumnQueries.ts +0 -129
- package/lib/TableConfig/initTableConfig.ts +0 -326
- package/lib/index.ts +0 -13
- package/lib/initProstgles.ts +0 -322
- package/lib/onSocketConnected.ts +0 -102
- package/lib/runClientRequest.ts +0 -129
- package/lib/shortestPath.ts +0 -122
- package/lib/typeTests/DBoGenerated.d.ts +0 -320
- package/lib/typeTests/dboTypeCheck.ts +0 -81
- package/lib/utils.ts +0 -15
- package/tests/client/hooks.spec.ts +0 -205
- package/tests/client/index.ts +0 -139
- package/tests/client/package-lock.json +0 -637
- package/tests/client/package.json +0 -26
- package/tests/client/renderReactHook.ts +0 -177
- package/tests/client/tsconfig.json +0 -15
- package/tests/client/useProstgles.spec.ts +0 -120
- package/tests/clientFileTests.spec.ts +0 -102
- package/tests/clientOnlyQueries.spec.ts +0 -667
- package/tests/clientRestApi.spec.ts +0 -82
- package/tests/config_test/DBoGenerated.d.ts +0 -407
- package/tests/config_test/index.html +0 -109
- package/tests/config_test/index.js +0 -86
- package/tests/config_test/index.js.map +0 -1
- package/tests/config_test/index.ts +0 -91
- package/tests/config_test/init.sql +0 -48
- package/tests/config_test/package.json +0 -29
- package/tests/config_test/tsconfig.json +0 -23
- package/tests/config_testDBoGenerated.d.ts +0 -407
- package/tests/isomorphicQueries.spec.ts +0 -1493
- package/tests/server/DBoGenerated.d.ts +0 -537
- package/tests/server/index.html +0 -73
- package/tests/server/index.ts +0 -289
- package/tests/server/init.sql +0 -224
- package/tests/server/package-lock.json +0 -2164
- package/tests/server/package.json +0 -25
- package/tests/server/publishTypeCheck.ts +0 -136
- package/tests/server/server.ts +0 -35
- package/tests/server/testPublish.ts +0 -147
- package/tests/server/testTableConfig.ts +0 -156
- package/tests/server/tsconfig.json +0 -22
- package/tests/serverOnlyQueries.spec.ts +0 -32
- package/tests/test.sh +0 -20
|
@@ -1,288 +0,0 @@
|
|
|
1
|
-
|
|
2
|
-
/*---------------------------------------------------------------------------------------------
|
|
3
|
-
* Copyright (c) Stefan L. All rights reserved.
|
|
4
|
-
* Licensed under the MIT License. See LICENSE in the project root for license information.
|
|
5
|
-
*--------------------------------------------------------------------------------------------*/
|
|
6
|
-
|
|
7
|
-
import { asName, ColumnInfo, getKeys, isEmpty, isObject, JoinSelect, PG_COLUMN_UDT_DATA_TYPE, Select, ValidatedColumnInfo } from "prostgles-types";
|
|
8
|
-
import { isPlainObject, postgresToTsType, SortItem } from "../DboBuilder";
|
|
9
|
-
|
|
10
|
-
import { ParsedJoinPath } from "../ViewHandler/parseJoinPath";
|
|
11
|
-
import { ViewHandler } from "../ViewHandler/ViewHandler";
|
|
12
|
-
import { COMPUTED_FIELDS, FieldSpec, FunctionSpec, parseFunction } from "./Functions";
|
|
13
|
-
|
|
14
|
-
export type SelectItem = {
|
|
15
|
-
getFields: (args?: any[]) => string[] | "*";
|
|
16
|
-
getQuery: (tableAlias?: string) => string;
|
|
17
|
-
columnPGDataType?: string;
|
|
18
|
-
column_udt_type?: PG_COLUMN_UDT_DATA_TYPE;
|
|
19
|
-
tsDataType?: ValidatedColumnInfo["tsDataType"]
|
|
20
|
-
alias: string;
|
|
21
|
-
selected: boolean;
|
|
22
|
-
} & ({
|
|
23
|
-
type: "column";
|
|
24
|
-
columnName: string;
|
|
25
|
-
} | {
|
|
26
|
-
type: "function" | "aggregation" | "joinedColumn" | "computed";
|
|
27
|
-
columnName?: undefined;
|
|
28
|
-
});
|
|
29
|
-
export type SelectItemValidated = SelectItem & { fields: string[]; }
|
|
30
|
-
export type WhereOptions = Awaited<ReturnType<ViewHandler["prepareWhere"]>>;
|
|
31
|
-
export type NewQueryRoot = {
|
|
32
|
-
/**
|
|
33
|
-
* All fields from the table will be in nested SELECT and GROUP BY to allow order/filter by fields not in select
|
|
34
|
-
*/
|
|
35
|
-
allFields: string[];
|
|
36
|
-
|
|
37
|
-
/**
|
|
38
|
-
* Contains user selection and all the allowed columns. Allowed columns not selected are marked with selected: false
|
|
39
|
-
*/
|
|
40
|
-
select: SelectItem[];
|
|
41
|
-
|
|
42
|
-
table: string;
|
|
43
|
-
where: string;
|
|
44
|
-
whereOpts: WhereOptions;
|
|
45
|
-
orderByItems: SortItem[];
|
|
46
|
-
having: string;
|
|
47
|
-
limit: number | null;
|
|
48
|
-
offset: number;
|
|
49
|
-
isLeftJoin: boolean;
|
|
50
|
-
tableAlias?: string;
|
|
51
|
-
};
|
|
52
|
-
|
|
53
|
-
export type NewQueryJoin = (NewQuery & {
|
|
54
|
-
joinPath: ParsedJoinPath[];
|
|
55
|
-
joinAlias: string;
|
|
56
|
-
});
|
|
57
|
-
export type NewQuery = NewQueryRoot & {
|
|
58
|
-
joins?: NewQueryJoin[];
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
export const asNameAlias = (field: string, tableAlias?: string) => {
|
|
62
|
-
const result = asName(field);
|
|
63
|
-
if(tableAlias) return asName(tableAlias) + "." + result;
|
|
64
|
-
return result;
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
export const parseFunctionObject = (funcData: any): { funcName: string; args: any[] } => {
|
|
68
|
-
const makeErr = (msg: string) => `Function not specified correctly. Expecting { $funcName: ["columnName" | <value>, ...args] } object but got: ${JSON.stringify(funcData)} \n ${msg}`
|
|
69
|
-
if(!isObject(funcData)) throw makeErr("");
|
|
70
|
-
const keys = getKeys(funcData);
|
|
71
|
-
if(keys.length !== 1) throw makeErr("");
|
|
72
|
-
const funcName = keys[0]!;
|
|
73
|
-
const args = funcData[funcName];
|
|
74
|
-
if(!args || !Array.isArray(args)){
|
|
75
|
-
throw makeErr("Arguments missing or invalid");
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
return { funcName, args };
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
export class SelectItemBuilder {
|
|
83
|
-
|
|
84
|
-
select: SelectItemValidated[] = [];
|
|
85
|
-
private allFields: string[];
|
|
86
|
-
|
|
87
|
-
private allowedFields: string[];
|
|
88
|
-
private allowedOrderByFields: string[];
|
|
89
|
-
private computedFields: FieldSpec[];
|
|
90
|
-
private functions: FunctionSpec[];
|
|
91
|
-
private allowedFieldsIncludingComputed: string[];
|
|
92
|
-
private isView: boolean;
|
|
93
|
-
private columns: ColumnInfo[];
|
|
94
|
-
|
|
95
|
-
constructor(params: { allowedFields: string[]; allowedOrderByFields: string[]; computedFields: FieldSpec[]; functions: FunctionSpec[]; allFields: string[]; isView: boolean; columns: ColumnInfo[]; }){
|
|
96
|
-
this.allFields = params.allFields;
|
|
97
|
-
this.allowedFields = params.allowedFields;
|
|
98
|
-
this.allowedOrderByFields = params.allowedOrderByFields;
|
|
99
|
-
this.computedFields = params.computedFields;
|
|
100
|
-
this.isView = params.isView;
|
|
101
|
-
this.functions = params.functions;
|
|
102
|
-
this.columns = params.columns;
|
|
103
|
-
this.allowedFieldsIncludingComputed = this.allowedFields.concat(this.computedFields? this.computedFields.map(cf => cf.name) : []);
|
|
104
|
-
if(!this.allowedFields.length){
|
|
105
|
-
if(!this.columns.length){
|
|
106
|
-
throw "This view/table has no columns. Cannot select anything";
|
|
107
|
-
}
|
|
108
|
-
throw "allowedFields empty/missing";
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
/* Check for conflicting computed column names */
|
|
112
|
-
const conflictingCol = this.allFields.find(fieldName => this.computedFields.find(cf => cf.name === fieldName));
|
|
113
|
-
if(conflictingCol){
|
|
114
|
-
throw "INTERNAL ERROR: Cannot have duplicate column names ( " + conflictingCol + " ). One or more computed column names are colliding with table columns ones";
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
private checkField = (f: string, isSelected: boolean) => {
|
|
119
|
-
const allowedSelectedFields = this.allowedFieldsIncludingComputed;
|
|
120
|
-
const allowedNonSelectedFields = [...this.allowedFieldsIncludingComputed, ...this.allowedOrderByFields];
|
|
121
|
-
|
|
122
|
-
/** Not selected items can be part of the orderBy fields */
|
|
123
|
-
const allowedFields = isSelected? allowedSelectedFields : allowedNonSelectedFields;
|
|
124
|
-
if(!allowedFields.includes(f)){
|
|
125
|
-
throw "Field " + f + " is invalid or dissallowed. \nAllowed fields: " + allowedFields.join(", ");
|
|
126
|
-
}
|
|
127
|
-
return f;
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
private addItem = (item: SelectItem) => {
|
|
131
|
-
let fields = item.getFields();
|
|
132
|
-
// console.trace(fields)
|
|
133
|
-
if(fields === "*") fields = this.allowedFields.slice(0);
|
|
134
|
-
fields.map(f => this.checkField(f, item.selected));
|
|
135
|
-
|
|
136
|
-
if(this.select.find(s => s.alias === item.alias)){
|
|
137
|
-
throw `Cannot specify duplicate columns ( ${item.alias} ). Perhaps you're using "*" with column names?`;
|
|
138
|
-
}
|
|
139
|
-
this.select.push({ ...item, fields });
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
private addFunction = (func: FunctionSpec | string, args: any[], alias: string) => {
|
|
143
|
-
const funcDef = parseFunction({
|
|
144
|
-
func, args, functions: this.functions,
|
|
145
|
-
allowedFields: this.allowedFieldsIncludingComputed,
|
|
146
|
-
});
|
|
147
|
-
|
|
148
|
-
this.addItem({
|
|
149
|
-
type: funcDef.type,
|
|
150
|
-
alias,
|
|
151
|
-
getFields: () => funcDef.getFields(args),
|
|
152
|
-
getQuery: (tableAlias?: string) => funcDef.getQuery({ allColumns: this.columns, allowedFields: this.allowedFields, args, tableAlias,
|
|
153
|
-
ctidField: undefined,
|
|
154
|
-
|
|
155
|
-
/* CTID not available in AFTER trigger */
|
|
156
|
-
// ctidField: this.isView? undefined : "ctid"
|
|
157
|
-
}),
|
|
158
|
-
selected: true
|
|
159
|
-
});
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
addColumn = (fieldName: string, selected: boolean) => {
|
|
163
|
-
|
|
164
|
-
/* Check if computed col */
|
|
165
|
-
if(selected){
|
|
166
|
-
const compCol = COMPUTED_FIELDS.find(cf => cf.name === fieldName);
|
|
167
|
-
if(compCol && !this.select.find(s => s.alias === fieldName)){
|
|
168
|
-
const cf: FunctionSpec = {
|
|
169
|
-
...compCol,
|
|
170
|
-
type: "computed",
|
|
171
|
-
numArgs: 0,
|
|
172
|
-
singleColArg: false,
|
|
173
|
-
getFields: (_args: any[]) => []
|
|
174
|
-
}
|
|
175
|
-
this.addFunction(cf, [], compCol.name)
|
|
176
|
-
return;
|
|
177
|
-
}
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
const colDef = this.columns.find(c => c.name === fieldName);
|
|
181
|
-
const alias = selected? fieldName : ("not_selected_" + fieldName);
|
|
182
|
-
this.addItem({
|
|
183
|
-
type: "column",
|
|
184
|
-
columnName: fieldName,
|
|
185
|
-
columnPGDataType: colDef?.data_type,
|
|
186
|
-
column_udt_type: colDef?.udt_name,
|
|
187
|
-
tsDataType: colDef && postgresToTsType(colDef.udt_name),
|
|
188
|
-
alias,
|
|
189
|
-
getQuery: (tableAlias) => asNameAlias(fieldName, tableAlias),
|
|
190
|
-
getFields: () => [fieldName],
|
|
191
|
-
selected
|
|
192
|
-
});
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
parseUserSelect = async (userSelect: Select, joinParse?: (key: string, val: JoinSelect, throwErr: (msg: string) => any) => any) => {
|
|
196
|
-
|
|
197
|
-
/* [col1, col2, col3] */
|
|
198
|
-
if(Array.isArray(userSelect)){
|
|
199
|
-
if(userSelect.find(key => typeof key !== "string")) throw "Invalid array select. Expecting an array of strings";
|
|
200
|
-
|
|
201
|
-
userSelect.map(key => this.addColumn(key, true))
|
|
202
|
-
|
|
203
|
-
/* Empty select */
|
|
204
|
-
} else if(userSelect === ""){
|
|
205
|
-
return [];
|
|
206
|
-
|
|
207
|
-
} else if(userSelect === "*"){
|
|
208
|
-
this.allowedFields.map(key => this.addColumn(key, true) );
|
|
209
|
-
|
|
210
|
-
} else if(isPlainObject(userSelect) && !isEmpty(userSelect)){
|
|
211
|
-
const selectKeys = Object.keys(userSelect),
|
|
212
|
-
selectValues = Object.values(userSelect);
|
|
213
|
-
|
|
214
|
-
/* Cannot include and exclude at the same time */
|
|
215
|
-
if(
|
|
216
|
-
selectValues.filter(v => [0, false].includes(v)).length
|
|
217
|
-
){
|
|
218
|
-
if(selectValues.filter(v => ![0, false].includes(v)).length ){
|
|
219
|
-
throw "\nCannot include and exclude fields at the same time";
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
/* Exclude only */
|
|
223
|
-
this.allowedFields.filter(f => !selectKeys.includes(f)).map(key => this.addColumn(key, true) )
|
|
224
|
-
|
|
225
|
-
} else {
|
|
226
|
-
await Promise.all(selectKeys.map(async key => {
|
|
227
|
-
const val: any = userSelect[key as keyof typeof userSelect],
|
|
228
|
-
throwErr = (extraErr = "") => {
|
|
229
|
-
console.trace(extraErr)
|
|
230
|
-
throw "Unexpected select -> " + JSON.stringify({ [key]: val }) + "\n" + extraErr;
|
|
231
|
-
};
|
|
232
|
-
|
|
233
|
-
/* Included fields */
|
|
234
|
-
if([1, true].includes(val)){
|
|
235
|
-
if(key === "*"){
|
|
236
|
-
this.allowedFields.map(key => this.addColumn(key, true) )
|
|
237
|
-
} else {
|
|
238
|
-
this.addColumn(key, true);
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
/* Aggs and functions */
|
|
242
|
-
} else if(typeof val === "string" || isObject(val)) {
|
|
243
|
-
|
|
244
|
-
/* Function shorthand notation
|
|
245
|
-
{ id: "$max" } === { id: { $max: ["id"] } } === SELECT MAX(id) AS id
|
|
246
|
-
*/
|
|
247
|
-
if(
|
|
248
|
-
(typeof val === "string" && val !== "*") ||
|
|
249
|
-
isPlainObject(val) && Object.keys(val).length === 1 && Array.isArray(Object.values(val)[0])
|
|
250
|
-
){
|
|
251
|
-
|
|
252
|
-
let funcName: string | undefined, args: any[] | undefined;
|
|
253
|
-
if(typeof val === "string") {
|
|
254
|
-
/* Shorthand notation -> it is expected that the key is the column name used as the only argument */
|
|
255
|
-
try {
|
|
256
|
-
this.checkField(key, true)
|
|
257
|
-
} catch (err){
|
|
258
|
-
throwErr(` Shorthand function notation error: the specifield column ( ${key} ) is invalid or dissallowed. \n Use correct column name or full aliased function notation, e.g.: -> { alias: { $func_name: ["column_name"] } } `)
|
|
259
|
-
}
|
|
260
|
-
funcName = val;
|
|
261
|
-
args = [key];
|
|
262
|
-
|
|
263
|
-
/** Function full notation { $funcName: ["colName", ...args] } */
|
|
264
|
-
} else {
|
|
265
|
-
({ funcName, args } = parseFunctionObject(val));
|
|
266
|
-
}
|
|
267
|
-
|
|
268
|
-
this.addFunction(funcName, args, key);
|
|
269
|
-
|
|
270
|
-
/* Join */
|
|
271
|
-
} else {
|
|
272
|
-
|
|
273
|
-
if(!joinParse) {
|
|
274
|
-
throw "Joins dissalowed";
|
|
275
|
-
}
|
|
276
|
-
await joinParse(key, val as JoinSelect, throwErr);
|
|
277
|
-
|
|
278
|
-
}
|
|
279
|
-
|
|
280
|
-
} else throwErr();
|
|
281
|
-
|
|
282
|
-
}));
|
|
283
|
-
}
|
|
284
|
-
} else throw "Unexpected select -> " + JSON.stringify(userSelect);
|
|
285
|
-
|
|
286
|
-
}
|
|
287
|
-
|
|
288
|
-
}
|
|
@@ -1,263 +0,0 @@
|
|
|
1
|
-
import { isDefined, asName } from "prostgles-types";
|
|
2
|
-
import { ParsedJoinPath, parseJoinPath } from "../ViewHandler/parseJoinPath";
|
|
3
|
-
import { NewQuery, NewQueryJoin, SelectItem, asNameAlias } from "./QueryBuilder";
|
|
4
|
-
import { ROOT_TABLE_ALIAS, ROOT_TABLE_ROW_NUM_ID, indentLines } from "./getSelectQuery";
|
|
5
|
-
import { ViewHandler } from "../ViewHandler/ViewHandler";
|
|
6
|
-
import { getJoinOnCondition } from "../ViewHandler/getTableJoinQuery";
|
|
7
|
-
import { prepareOrderByQuery } from "../DboBuilder";
|
|
8
|
-
|
|
9
|
-
type Args = {
|
|
10
|
-
q1: NewQuery;
|
|
11
|
-
q2: NewQueryJoin;
|
|
12
|
-
selectParamsGroupBy: boolean;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
/**
|
|
16
|
-
* Rename all join columns to prevent name clash
|
|
17
|
-
*/
|
|
18
|
-
export const getJoinCol = (colName: string) => {
|
|
19
|
-
const alias = asName("prgl_join_col__" + colName);
|
|
20
|
-
return {
|
|
21
|
-
alias,
|
|
22
|
-
rootSelect: `${asName(colName)} AS ${alias}`,
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
export const JSON_AGG_FIELD_NAME = "prostgles_json_agg_result_field";
|
|
27
|
-
/**
|
|
28
|
-
* Used for LIMIT and for sorting
|
|
29
|
-
*/
|
|
30
|
-
export const NESTED_ROWID_FIELD_NAME = "prostgles_rowid_field";
|
|
31
|
-
|
|
32
|
-
const getJoinTable = (tableName: string, pathIndex: number, isLastTableAlias: string | undefined) => {
|
|
33
|
-
const rawAlias = isLastTableAlias ?? `p${pathIndex} ${tableName}`;
|
|
34
|
-
return {
|
|
35
|
-
// name: asName(tableName), /** table names are already escaped */
|
|
36
|
-
name: tableName,
|
|
37
|
-
alias: asName(rawAlias),
|
|
38
|
-
rawAlias,
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
type GetJoinQueryResult = {
|
|
43
|
-
resultAlias: string;
|
|
44
|
-
// queryLines: string[];
|
|
45
|
-
firstJoinTableJoinFields: string[];
|
|
46
|
-
isOrJoin: boolean;
|
|
47
|
-
type: "cte";
|
|
48
|
-
joinLines: string[];
|
|
49
|
-
cteLines: string[];
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
/**
|
|
53
|
-
Returns join query. All inner join tables will be prefixed with path index unless it's the final target table which is aliased using the q2 tableAlias
|
|
54
|
-
|
|
55
|
-
LEFT JOIN (
|
|
56
|
-
SELECT [target table select + join fields]
|
|
57
|
-
FROM first_join/target_table
|
|
58
|
-
JOIN ..next_joins ON ...
|
|
59
|
-
JOIN target_table
|
|
60
|
-
) target_table
|
|
61
|
-
ON ...condition
|
|
62
|
-
*/
|
|
63
|
-
export const getJoinQuery = (viewHandler: ViewHandler, { q1, q2 }: Args): GetJoinQueryResult => {
|
|
64
|
-
const paths = parseJoinPath({
|
|
65
|
-
rootTable: q1.table,
|
|
66
|
-
rawPath: q2.joinPath,
|
|
67
|
-
viewHandler: viewHandler,
|
|
68
|
-
allowMultiOrJoin: true,
|
|
69
|
-
addShortestJoinIfMissing: true,
|
|
70
|
-
});
|
|
71
|
-
|
|
72
|
-
const targetTableAliasRaw = q2.tableAlias || q2.table;
|
|
73
|
-
const targetTableAlias = asName(targetTableAliasRaw);
|
|
74
|
-
|
|
75
|
-
const firstJoinTablePath = paths[0]!;
|
|
76
|
-
const firstJoinTableJoinFields = firstJoinTablePath.on.flatMap(condObj => Object.entries(condObj).map(([source, target]) => target));
|
|
77
|
-
const { rootSelectItems, jsonAggLimit } = getNestedSelectFields({
|
|
78
|
-
q: q2,
|
|
79
|
-
firstJoinTableAlias: getJoinTable(firstJoinTablePath.table, 0, paths.length === 1? targetTableAliasRaw : undefined).rawAlias,
|
|
80
|
-
_joinFields: firstJoinTableJoinFields
|
|
81
|
-
});
|
|
82
|
-
|
|
83
|
-
const joinType = q2.isLeftJoin? "LEFT" : "INNER";
|
|
84
|
-
|
|
85
|
-
const isOrJoin = firstJoinTablePath.on.length > 1;
|
|
86
|
-
const joinCondition = getJoinOnCondition({
|
|
87
|
-
on: firstJoinTablePath.on,
|
|
88
|
-
leftAlias: asName(q1.tableAlias || q1.table),
|
|
89
|
-
rightAlias: targetTableAlias,
|
|
90
|
-
getRightColName: (col) => getJoinCol(col).alias
|
|
91
|
-
});
|
|
92
|
-
|
|
93
|
-
const joinFields = rootSelectItems.filter(s => s.isJoinCol).map(s => s.alias);
|
|
94
|
-
const selectedFields = rootSelectItems.filter(s => s.selected).map(s => asNameAlias(s.alias, targetTableAliasRaw));
|
|
95
|
-
const rootNestedSort = q1.orderByItems.filter(d => d.nested?.joinAlias === q2.joinAlias);
|
|
96
|
-
const jsonAggSort = prepareOrderByQuery(q2.orderByItems, targetTableAliasRaw).join(", ");
|
|
97
|
-
const jsonAgg = `json_agg((SELECT x FROM (SELECT ${selectedFields}) as x )${jsonAggSort}) ${jsonAggLimit} as ${JSON_AGG_FIELD_NAME}`;
|
|
98
|
-
|
|
99
|
-
const { innerQuery } = getInnerJoinQuery({ paths, q1, q2, rootSelectItems, targetTableAliasRaw });
|
|
100
|
-
|
|
101
|
-
const requiredJoinFields = joinFields.map(field => getJoinCol(field).alias);
|
|
102
|
-
/**
|
|
103
|
-
* Used to prevent duplicates in case of OR filters
|
|
104
|
-
*/
|
|
105
|
-
const rootTableIdField = `${ROOT_TABLE_ALIAS}.${ROOT_TABLE_ROW_NUM_ID}`;
|
|
106
|
-
const wrappingQuery = [
|
|
107
|
-
`SELECT `,
|
|
108
|
-
...indentLines([
|
|
109
|
-
...(isOrJoin? [rootTableIdField]: requiredJoinFields),
|
|
110
|
-
jsonAgg,
|
|
111
|
-
...rootNestedSort.map(d => d.nested!.wrapperQuerySortItem)
|
|
112
|
-
], { appendCommas: true }),
|
|
113
|
-
`FROM (`,
|
|
114
|
-
...indentLines(innerQuery),
|
|
115
|
-
`) ${targetTableAlias}`,
|
|
116
|
-
...(isOrJoin? [
|
|
117
|
-
`LEFT JOIN ${q1.table} ${ROOT_TABLE_ALIAS}`,
|
|
118
|
-
`ON ${joinCondition}`
|
|
119
|
-
] : []),
|
|
120
|
-
`GROUP BY ${isOrJoin? rootTableIdField : requiredJoinFields}`,
|
|
121
|
-
];
|
|
122
|
-
|
|
123
|
-
/**
|
|
124
|
-
* This is done to prevent join cte names clashing with actual table names
|
|
125
|
-
*/
|
|
126
|
-
const targetTableAliasTempRename = asName(`${targetTableAlias}_prostgles_join_temp_rename`)
|
|
127
|
-
const cteLines = [
|
|
128
|
-
`${targetTableAliasTempRename} AS (`,
|
|
129
|
-
...indentLines(wrappingQuery),
|
|
130
|
-
`)`
|
|
131
|
-
];
|
|
132
|
-
|
|
133
|
-
const joinLines = [
|
|
134
|
-
`${joinType} JOIN ( SELECT * FROM ${targetTableAliasTempRename} ) as ${targetTableAlias}`,
|
|
135
|
-
isOrJoin?
|
|
136
|
-
`ON ${targetTableAlias}.${ROOT_TABLE_ROW_NUM_ID} = ${rootTableIdField}` :
|
|
137
|
-
`ON ${joinCondition}`
|
|
138
|
-
];
|
|
139
|
-
|
|
140
|
-
return {
|
|
141
|
-
type: "cte",
|
|
142
|
-
resultAlias: JSON_AGG_FIELD_NAME,
|
|
143
|
-
// queryLines,
|
|
144
|
-
joinLines,
|
|
145
|
-
cteLines,
|
|
146
|
-
isOrJoin,
|
|
147
|
-
firstJoinTableJoinFields,
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
/**
|
|
153
|
-
* prepares the
|
|
154
|
-
*/
|
|
155
|
-
const getInnerJoinQuery = ({ paths, q1, q2, targetTableAliasRaw, rootSelectItems }: {
|
|
156
|
-
paths: ParsedJoinPath[];
|
|
157
|
-
q1: NewQuery;
|
|
158
|
-
q2: NewQueryJoin;
|
|
159
|
-
targetTableAliasRaw: string;
|
|
160
|
-
rootSelectItems: SelectItemNested[];
|
|
161
|
-
}) => {
|
|
162
|
-
|
|
163
|
-
const innerQuery = paths.flatMap((path, i) => {
|
|
164
|
-
|
|
165
|
-
const isLast = i === paths.length - 1;
|
|
166
|
-
const targetQueryExtraQueries: string[] = [];
|
|
167
|
-
|
|
168
|
-
const prevTable = getJoinTable(!i? (q1.tableAlias? asName(q1.tableAlias) : q1.table) : paths[i-1]!.table, i-1, undefined);
|
|
169
|
-
|
|
170
|
-
const table = getJoinTable(path.table, i, isLast? targetTableAliasRaw : undefined);
|
|
171
|
-
|
|
172
|
-
if(isLast){
|
|
173
|
-
if(q2.where){
|
|
174
|
-
targetQueryExtraQueries.push(q2.where);
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
/* If aggs exist need to set groupBy add joinFields into select */
|
|
178
|
-
const aggs = q2.select.filter(s => s.type === "aggregation")
|
|
179
|
-
if (aggs.length) {
|
|
180
|
-
const groupByFields = rootSelectItems.map((c, i) => (c.isJoinCol || c.selected && c.type !== "aggregation")? `${i+1}` : undefined ).filter(isDefined);
|
|
181
|
-
if(groupByFields.length){
|
|
182
|
-
targetQueryExtraQueries.push(`GROUP BY ${groupByFields}`)
|
|
183
|
-
}
|
|
184
|
-
if(q2.having){
|
|
185
|
-
targetQueryExtraQueries.push(`HAVING ${q2.having}`)
|
|
186
|
-
}
|
|
187
|
-
}
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
const isFirst = !i;
|
|
191
|
-
if(isFirst){
|
|
192
|
-
return [
|
|
193
|
-
`SELECT `,
|
|
194
|
-
` /* Join fields + select */`,
|
|
195
|
-
...indentLines(rootSelectItems.map(s => s.query), { appendCommas: true }),
|
|
196
|
-
`FROM ${table.name} ${table.alias}`,
|
|
197
|
-
...targetQueryExtraQueries
|
|
198
|
-
]
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
return [
|
|
202
|
-
`INNER JOIN ${table.name} ${table.alias}`,
|
|
203
|
-
`ON ${getJoinOnCondition({
|
|
204
|
-
on: path.on,
|
|
205
|
-
leftAlias: prevTable.alias,
|
|
206
|
-
rightAlias: table.alias,
|
|
207
|
-
})}`,
|
|
208
|
-
...targetQueryExtraQueries
|
|
209
|
-
]
|
|
210
|
-
});
|
|
211
|
-
|
|
212
|
-
return { innerQuery }
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
type GetSelectFieldsArgs = {
|
|
217
|
-
q: NewQueryJoin;
|
|
218
|
-
firstJoinTableAlias: string;
|
|
219
|
-
_joinFields: string[];
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
export type SelectItemNested = SelectItem & { query: string; isJoinCol: boolean; };
|
|
223
|
-
const getNestedSelectFields = ({ q, firstJoinTableAlias, _joinFields }: GetSelectFieldsArgs) => {
|
|
224
|
-
const targetTableAlias = (q.tableAlias || q.table);
|
|
225
|
-
|
|
226
|
-
const requiredJoinFields = Array.from(new Set(_joinFields))
|
|
227
|
-
const selectedFields = q.select.filter(s => s.selected);
|
|
228
|
-
const rootSelectItems: SelectItemNested[] = selectedFields
|
|
229
|
-
.map(s => ({
|
|
230
|
-
...s,
|
|
231
|
-
isJoinCol: false,
|
|
232
|
-
query: s.getQuery(targetTableAlias) + " AS " + asName(s.alias)
|
|
233
|
-
}))
|
|
234
|
-
.concat(requiredJoinFields.map(f => ({
|
|
235
|
-
type: "column",
|
|
236
|
-
columnName: f,
|
|
237
|
-
alias: f,
|
|
238
|
-
getFields: () => [f],
|
|
239
|
-
getQuery: (tableAlias) => asNameAlias(f, tableAlias),
|
|
240
|
-
selected: false,
|
|
241
|
-
isJoinCol: true,
|
|
242
|
-
query: `${asName(firstJoinTableAlias)}.${getJoinCol(f).rootSelect}`,
|
|
243
|
-
})));
|
|
244
|
-
|
|
245
|
-
const getQuery = (tableAlias?: string) => {
|
|
246
|
-
const partitionBy = `PARTITION BY ${requiredJoinFields.map(f => asNameAlias(f, tableAlias))}`;
|
|
247
|
-
return `ROW_NUMBER() OVER(${partitionBy}) AS ${NESTED_ROWID_FIELD_NAME}`
|
|
248
|
-
};
|
|
249
|
-
rootSelectItems.push({
|
|
250
|
-
type: "computed",
|
|
251
|
-
selected: false,
|
|
252
|
-
alias: NESTED_ROWID_FIELD_NAME,
|
|
253
|
-
getFields: () => [],
|
|
254
|
-
getQuery,
|
|
255
|
-
query: getQuery(firstJoinTableAlias),
|
|
256
|
-
isJoinCol: false,
|
|
257
|
-
})
|
|
258
|
-
|
|
259
|
-
return {
|
|
260
|
-
rootSelectItems,
|
|
261
|
-
jsonAggLimit: q.limit? `FILTER (WHERE ${NESTED_ROWID_FIELD_NAME} <= ${q.limit})` : ""
|
|
262
|
-
};
|
|
263
|
-
}
|