prostgles-server 4.2.626 → 4.2.628
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/debug/prostgles-types-4.0.285.tgz +0 -0
- package/dist/Audit/getAuditTableRules.js +1 -1
- package/dist/Audit/getAuditTableRules.js.map +1 -1
- package/dist/DBSchemaBuilder/DBSchemaBuilder.d.ts +4 -4
- package/dist/DBSchemaBuilder/DBSchemaBuilder.d.ts.map +1 -1
- package/dist/DBSchemaBuilder/getClientDBGeneratedSchemas.js +1 -1
- package/dist/DBSchemaBuilder/getClientDBGeneratedSchemas.js.map +1 -1
- package/dist/DboBuilder/QueryBuilder/Functions/Functions.d.ts +3 -1
- package/dist/DboBuilder/QueryBuilder/Functions/Functions.d.ts.map +1 -1
- package/dist/DboBuilder/QueryBuilder/Functions/Functions.js +18 -11
- package/dist/DboBuilder/QueryBuilder/Functions/Functions.js.map +1 -1
- package/dist/DboBuilder/QueryBuilder/Functions/HASHING_FUNCTIONS.d.ts.map +1 -1
- package/dist/DboBuilder/QueryBuilder/Functions/HASHING_FUNCTIONS.js +16 -21
- package/dist/DboBuilder/QueryBuilder/Functions/HASHING_FUNCTIONS.js.map +1 -1
- package/dist/DboBuilder/QueryBuilder/Functions/utils.d.ts +1 -0
- package/dist/DboBuilder/QueryBuilder/Functions/utils.d.ts.map +1 -1
- package/dist/DboBuilder/QueryBuilder/Functions/utils.js +3 -1
- package/dist/DboBuilder/QueryBuilder/Functions/utils.js.map +1 -1
- package/dist/DboBuilder/QueryBuilder/QueryBuilder.d.ts +18 -2
- package/dist/DboBuilder/QueryBuilder/QueryBuilder.d.ts.map +1 -1
- package/dist/DboBuilder/QueryBuilder/QueryBuilder.js +54 -16
- package/dist/DboBuilder/QueryBuilder/QueryBuilder.js.map +1 -1
- package/dist/DboBuilder/QueryBuilder/getNewQuery.d.ts +1 -1
- package/dist/DboBuilder/QueryBuilder/getNewQuery.d.ts.map +1 -1
- package/dist/DboBuilder/QueryBuilder/getNewQuery.js +40 -6
- package/dist/DboBuilder/QueryBuilder/getNewQuery.js.map +1 -1
- package/dist/DboBuilder/QueryBuilder/parseJoinSelect.d.ts.map +1 -1
- package/dist/DboBuilder/QueryBuilder/parseJoinSelect.js +2 -12
- package/dist/DboBuilder/QueryBuilder/parseJoinSelect.js.map +1 -1
- package/dist/DboBuilder/TableHandler/onDeleteFromFileTable.d.ts.map +1 -1
- package/dist/DboBuilder/TableHandler/onDeleteFromFileTable.js +7 -6
- package/dist/DboBuilder/TableHandler/onDeleteFromFileTable.js.map +1 -1
- package/dist/DboBuilder/ViewHandler/find.d.ts.map +1 -1
- package/dist/DboBuilder/ViewHandler/find.js +2 -29
- package/dist/DboBuilder/ViewHandler/find.js.map +1 -1
- package/dist/DboBuilder/ViewHandler/parseComplexFilter.d.ts +4 -1
- package/dist/DboBuilder/ViewHandler/parseComplexFilter.d.ts.map +1 -1
- package/dist/DboBuilder/ViewHandler/parseComplexFilter.js +9 -2
- package/dist/DboBuilder/ViewHandler/parseComplexFilter.js.map +1 -1
- package/dist/DboBuilder/ViewHandler/subscribe.d.ts.map +1 -1
- package/dist/DboBuilder/ViewHandler/subscribe.js +8 -3
- package/dist/DboBuilder/ViewHandler/subscribe.js.map +1 -1
- package/dist/DboBuilder/ViewHandler/validateSelectParams.d.ts +3 -0
- package/dist/DboBuilder/ViewHandler/validateSelectParams.d.ts.map +1 -0
- package/dist/DboBuilder/ViewHandler/validateSelectParams.js +48 -0
- package/dist/DboBuilder/ViewHandler/validateSelectParams.js.map +1 -0
- package/dist/DboBuilder/getCondition.d.ts.map +1 -1
- package/dist/DboBuilder/getCondition.js +9 -3
- package/dist/DboBuilder/getCondition.js.map +1 -1
- package/dist/DboBuilder/getSubscribeRelatedTables.d.ts.map +1 -1
- package/dist/DboBuilder/getSubscribeRelatedTables.js +7 -2
- package/dist/DboBuilder/getSubscribeRelatedTables.js.map +1 -1
- package/dist/PubSubManager/PubSubManager.d.ts +1 -1
- package/dist/PubSubManager/PubSubManager.d.ts.map +1 -1
- package/dist/PubSubManager/PubSubManager.js +1 -1
- package/dist/PubSubManager/PubSubManager.js.map +1 -1
- package/dist/PubSubManager/addSub.js +1 -1
- package/dist/PubSubManager/addSub.js.map +1 -1
- package/dist/StorageClient/fileVersionUtils.d.ts +3 -3
- package/dist/StorageClient/fileVersionUtils.d.ts.map +1 -1
- package/dist/StorageClient/fileVersionUtils.js.map +1 -1
- package/dist/TableConfig/initTableConfig.d.ts.map +1 -1
- package/dist/TableConfig/initTableConfig.js +1 -4
- package/dist/TableConfig/initTableConfig.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/typeTests/DBOFullyTyped.spec.js.map +1 -1
- package/lib/Audit/getAuditTableRules.ts +1 -1
- package/lib/DBSchemaBuilder/DBSchemaBuilder.ts +6 -7
- package/lib/DBSchemaBuilder/getClientDBGeneratedSchemas.ts +1 -1
- package/lib/DboBuilder/QueryBuilder/Functions/Functions.ts +38 -13
- package/lib/DboBuilder/QueryBuilder/Functions/HASHING_FUNCTIONS.ts +28 -24
- package/lib/DboBuilder/QueryBuilder/Functions/utils.ts +10 -0
- package/lib/DboBuilder/QueryBuilder/QueryBuilder.ts +95 -18
- package/lib/DboBuilder/QueryBuilder/getNewQuery.ts +58 -10
- package/lib/DboBuilder/QueryBuilder/parseJoinSelect.ts +2 -13
- package/lib/DboBuilder/TableHandler/onDeleteFromFileTable.ts +9 -8
- package/lib/DboBuilder/ViewHandler/find.ts +2 -32
- package/lib/DboBuilder/ViewHandler/parseComplexFilter.ts +10 -2
- package/lib/DboBuilder/ViewHandler/subscribe.ts +10 -3
- package/lib/DboBuilder/ViewHandler/validateSelectParams.ts +51 -0
- package/lib/DboBuilder/getCondition.ts +11 -3
- package/lib/DboBuilder/getSubscribeRelatedTables.ts +10 -3
- package/lib/PubSubManager/PubSubManager.ts +1 -1
- package/lib/PubSubManager/addSub.ts +1 -1
- package/lib/StorageClient/fileVersionUtils.ts +3 -3
- package/lib/TableConfig/initTableConfig.ts +1 -6
- package/lib/index.ts +5 -1
- package/lib/typeTests/DBOFullyTyped.spec.ts +1 -1
- package/package.json +2 -2
|
@@ -4,7 +4,7 @@ import { asName, includes, isObject, TextFilter_FullTextSearchFilterKeys } from
|
|
|
4
4
|
import { asNameAlias } from "../../../utils/asNameAlias";
|
|
5
5
|
import { HASHING_FUNCTIONS } from "./HASHING_FUNCTIONS";
|
|
6
6
|
import { TEXT_FUNCTIONS } from "./TEXT_FUNCTIONS";
|
|
7
|
-
import { asFunction } from "./utils";
|
|
7
|
+
import { asFunction, getAggregateQuery } from "./utils";
|
|
8
8
|
const pgp = pgPromise();
|
|
9
9
|
|
|
10
10
|
type GetQueryArgs = {
|
|
@@ -13,13 +13,15 @@ type GetQueryArgs = {
|
|
|
13
13
|
args: any[];
|
|
14
14
|
tableAliasRaw?: string;
|
|
15
15
|
ctidField?: string;
|
|
16
|
+
aggregateFilter?: string;
|
|
17
|
+
aggregateOrderBy?: string;
|
|
16
18
|
};
|
|
17
19
|
|
|
18
20
|
export type FieldSpec = {
|
|
19
21
|
name: string;
|
|
20
22
|
type: "column" | "computed";
|
|
21
23
|
/**
|
|
22
|
-
* allowedFields passed for
|
|
24
|
+
* allowedFields passed for multicolumn functions (e.g.: $rowhash)
|
|
23
25
|
*/
|
|
24
26
|
getQuery: (params: Omit<GetQueryArgs, "args">) => string;
|
|
25
27
|
};
|
|
@@ -454,12 +456,18 @@ PostGIS_Funcs = PostGIS_Funcs.concat(
|
|
|
454
456
|
singleColArg: true,
|
|
455
457
|
numArgs: 1,
|
|
456
458
|
getFields: (args: any[]) => [args[0]],
|
|
457
|
-
getQuery: ({ args, tableAliasRaw: tableAlias }) => {
|
|
458
|
-
const
|
|
459
|
+
getQuery: ({ args, tableAliasRaw: tableAlias, aggregateFilter, aggregateOrderBy }) => {
|
|
460
|
+
const escapeTableName = asNameAlias(args[0], tableAlias) + "::geometry";
|
|
459
461
|
if (fname.includes("Extent")) {
|
|
460
|
-
return
|
|
462
|
+
return getAggregateQuery(fname, escapeTableName, aggregateFilter, aggregateOrderBy);
|
|
461
463
|
}
|
|
462
|
-
|
|
464
|
+
const collectedGeometry = getAggregateQuery(
|
|
465
|
+
"ST_Collect",
|
|
466
|
+
escapeTableName,
|
|
467
|
+
aggregateFilter,
|
|
468
|
+
aggregateOrderBy,
|
|
469
|
+
);
|
|
470
|
+
return `${fname.endsWith("_Agg") ? fname.slice(0, -4) : fname}(${collectedGeometry})`;
|
|
463
471
|
},
|
|
464
472
|
};
|
|
465
473
|
return res;
|
|
@@ -753,13 +761,23 @@ export const FUNCTIONS: FunctionSpec[] = [
|
|
|
753
761
|
type: "aggregation",
|
|
754
762
|
numArgs: 1,
|
|
755
763
|
singleColArg: true,
|
|
756
|
-
|
|
757
|
-
|
|
764
|
+
minCols: aggName === "count" ? 0 : undefined,
|
|
765
|
+
getFields: (args: any[]) => (aggName === "count" && !args.length ? [] : [args[0]]),
|
|
766
|
+
getQuery: ({ args, tableAliasRaw: tableAlias, aggregateFilter, aggregateOrderBy }) => {
|
|
758
767
|
let extraArgs = "";
|
|
759
768
|
if (args.length > 1) {
|
|
760
769
|
extraArgs = pgp.as.format(", $1:csv", args.slice(1));
|
|
761
770
|
}
|
|
762
|
-
|
|
771
|
+
const firstArg =
|
|
772
|
+
args.length ? asNameAlias(args[0], tableAlias)
|
|
773
|
+
: aggregateOrderBy ? "1"
|
|
774
|
+
: "*";
|
|
775
|
+
return getAggregateQuery(
|
|
776
|
+
aggName,
|
|
777
|
+
`${firstArg}${extraArgs}`,
|
|
778
|
+
aggregateFilter,
|
|
779
|
+
aggregateOrderBy,
|
|
780
|
+
);
|
|
763
781
|
},
|
|
764
782
|
}),
|
|
765
783
|
),
|
|
@@ -784,8 +802,13 @@ export const FUNCTIONS: FunctionSpec[] = [
|
|
|
784
802
|
singleColArg: true,
|
|
785
803
|
numArgs: 0,
|
|
786
804
|
getFields: (args: any[]) => [],
|
|
787
|
-
getQuery: ({
|
|
788
|
-
return
|
|
805
|
+
getQuery: ({ aggregateFilter, aggregateOrderBy }) => {
|
|
806
|
+
return getAggregateQuery(
|
|
807
|
+
"COUNT",
|
|
808
|
+
aggregateOrderBy ? "1" : "*",
|
|
809
|
+
aggregateFilter,
|
|
810
|
+
aggregateOrderBy,
|
|
811
|
+
);
|
|
789
812
|
},
|
|
790
813
|
}),
|
|
791
814
|
asFunction({
|
|
@@ -794,9 +817,11 @@ export const FUNCTIONS: FunctionSpec[] = [
|
|
|
794
817
|
numArgs: 1,
|
|
795
818
|
singleColArg: true,
|
|
796
819
|
getFields: (args: any[]) => [args[0]],
|
|
797
|
-
getQuery: ({
|
|
820
|
+
getQuery: ({ args, tableAliasRaw: tableAlias, aggregateFilter, aggregateOrderBy }) => {
|
|
798
821
|
const col = asNameAlias(args[0], tableAlias);
|
|
799
|
-
|
|
822
|
+
const max = getAggregateQuery("MAX", col, aggregateFilter, aggregateOrderBy);
|
|
823
|
+
const min = getAggregateQuery("MIN", col, aggregateFilter, aggregateOrderBy);
|
|
824
|
+
return `round( ( ( ${max} - ${min} )::float/${min} ) * 100, 2)`;
|
|
800
825
|
},
|
|
801
826
|
}),
|
|
802
827
|
];
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import pgPromise from "pg-promise";
|
|
2
2
|
import { asNameAlias } from "../../../utils/asNameAlias";
|
|
3
3
|
import type { FunctionSpec } from "./Functions";
|
|
4
|
+
import { getAggregateQuery } from "./utils";
|
|
4
5
|
|
|
5
6
|
const pgp = pgPromise();
|
|
6
7
|
|
|
@@ -33,15 +34,16 @@ export const HASHING_FUNCTIONS: FunctionSpec[] = [
|
|
|
33
34
|
singleColArg: false,
|
|
34
35
|
numArgs: MAX_COL_NUM,
|
|
35
36
|
getFields: (args: any[]) => args,
|
|
36
|
-
getQuery: ({ args, tableAliasRaw: tableAlias }) => {
|
|
37
|
-
const
|
|
38
|
-
"
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
37
|
+
getQuery: ({ args, tableAliasRaw: tableAlias, aggregateFilter, aggregateOrderBy }) => {
|
|
38
|
+
const stringAgg = getAggregateQuery(
|
|
39
|
+
"string_agg",
|
|
40
|
+
args
|
|
41
|
+
.map((fname) => "COALESCE( " + asNameAlias(fname, tableAlias) + "::text, '' )")
|
|
42
|
+
.join(" || ") + ", ','",
|
|
43
|
+
aggregateFilter,
|
|
44
|
+
aggregateOrderBy,
|
|
43
45
|
);
|
|
44
|
-
return
|
|
46
|
+
return pgp.as.format(`md5(${stringAgg})`);
|
|
45
47
|
},
|
|
46
48
|
},
|
|
47
49
|
|
|
@@ -70,15 +72,16 @@ export const HASHING_FUNCTIONS: FunctionSpec[] = [
|
|
|
70
72
|
singleColArg: false,
|
|
71
73
|
numArgs: MAX_COL_NUM,
|
|
72
74
|
getFields: (args: any[]) => args,
|
|
73
|
-
getQuery: ({ args, tableAliasRaw: tableAlias }) => {
|
|
74
|
-
const
|
|
75
|
-
"
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
75
|
+
getQuery: ({ args, tableAliasRaw: tableAlias, aggregateFilter, aggregateOrderBy }) => {
|
|
76
|
+
const stringAgg = getAggregateQuery(
|
|
77
|
+
"string_agg",
|
|
78
|
+
args
|
|
79
|
+
.map((fname) => "COALESCE( " + asNameAlias(fname, tableAlias) + ", '' )")
|
|
80
|
+
.join(" || ") + ", ','",
|
|
81
|
+
aggregateFilter,
|
|
82
|
+
aggregateOrderBy,
|
|
80
83
|
);
|
|
81
|
-
return
|
|
84
|
+
return pgp.as.format(`encode(sha256(${stringAgg}::text::bytea), 'hex')`);
|
|
82
85
|
},
|
|
83
86
|
},
|
|
84
87
|
{
|
|
@@ -106,15 +109,16 @@ export const HASHING_FUNCTIONS: FunctionSpec[] = [
|
|
|
106
109
|
singleColArg: false,
|
|
107
110
|
numArgs: MAX_COL_NUM,
|
|
108
111
|
getFields: (args: any[]) => args,
|
|
109
|
-
getQuery: ({ args, tableAliasRaw: tableAlias }) => {
|
|
110
|
-
const
|
|
111
|
-
"
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
112
|
+
getQuery: ({ args, tableAliasRaw: tableAlias, aggregateFilter, aggregateOrderBy }) => {
|
|
113
|
+
const stringAgg = getAggregateQuery(
|
|
114
|
+
"string_agg",
|
|
115
|
+
args
|
|
116
|
+
.map((fname) => "COALESCE( " + asNameAlias(fname, tableAlias) + ", '' )")
|
|
117
|
+
.join(" || ") + ", ','",
|
|
118
|
+
aggregateFilter,
|
|
119
|
+
aggregateOrderBy,
|
|
116
120
|
);
|
|
117
|
-
return
|
|
121
|
+
return pgp.as.format(`encode(sha512(${stringAgg}::text::bytea), 'hex')`);
|
|
118
122
|
},
|
|
119
123
|
},
|
|
120
124
|
];
|
|
@@ -1,3 +1,13 @@
|
|
|
1
1
|
import type { FunctionSpec } from "./Functions";
|
|
2
2
|
|
|
3
3
|
export const asFunction = (def: FunctionSpec) => def;
|
|
4
|
+
|
|
5
|
+
export const getAggregateQuery = (
|
|
6
|
+
name: string,
|
|
7
|
+
args: string,
|
|
8
|
+
aggregateFilter?: string,
|
|
9
|
+
aggregateOrderBy?: string,
|
|
10
|
+
) =>
|
|
11
|
+
`${name}(${args}${aggregateOrderBy ? ` ${aggregateOrderBy}` : ""})${
|
|
12
|
+
aggregateFilter ? ` FILTER (WHERE ${aggregateFilter})` : ""
|
|
13
|
+
}`;
|
|
@@ -4,13 +4,15 @@
|
|
|
4
4
|
*--------------------------------------------------------------------------------------------*/
|
|
5
5
|
|
|
6
6
|
import type {
|
|
7
|
+
AnyObject,
|
|
7
8
|
ColumnInfo,
|
|
9
|
+
OrderBy,
|
|
8
10
|
PG_COLUMN_UDT_DATA_TYPE,
|
|
9
11
|
Select,
|
|
10
12
|
ValidatedColumnInfo,
|
|
11
13
|
} from "prostgles-types";
|
|
12
|
-
import { getKeys, isEmpty, isObject, postgresToTsType } from "prostgles-types";
|
|
13
|
-
import type { PGIdentifier, SortItem } from "../DboBuilder";
|
|
14
|
+
import { getKeys, includes, isEmpty, isObject, postgresToTsType } from "prostgles-types";
|
|
15
|
+
import type { ExistsFilterConfig, PGIdentifier, SortItem } from "../DboBuilder";
|
|
14
16
|
|
|
15
17
|
import type { Awaitable } from "../../PublishParser/publishTypesAndUtils";
|
|
16
18
|
import { asNameAlias } from "../../utils/asNameAlias";
|
|
@@ -30,6 +32,8 @@ export type SelectItem = {
|
|
|
30
32
|
tsDataType?: ValidatedColumnInfo["tsDataType"];
|
|
31
33
|
alias: string;
|
|
32
34
|
selected: boolean;
|
|
35
|
+
dependencyFields?: string[];
|
|
36
|
+
dependencyExists?: ExistsFilterConfig[];
|
|
33
37
|
} & (
|
|
34
38
|
| {
|
|
35
39
|
type: "column";
|
|
@@ -87,6 +91,36 @@ export const parseFunctionObject = (funcData: unknown): { funcName: string; args
|
|
|
87
91
|
return { funcName, args };
|
|
88
92
|
};
|
|
89
93
|
|
|
94
|
+
type AggregateOptions = {
|
|
95
|
+
$filter?: AnyObject;
|
|
96
|
+
$orderBy?: OrderBy;
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
type ParsedAggregateOptions = {
|
|
100
|
+
filter?: string;
|
|
101
|
+
orderBy?: string;
|
|
102
|
+
dependencyFields: string[];
|
|
103
|
+
dependencyExists: ExistsFilterConfig[];
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
const aggregateOptionKeys = ["$filter", "$orderBy"] as const;
|
|
107
|
+
const parseSelectFunctionObject = (funcData: Record<string, unknown>) => {
|
|
108
|
+
const functionData = Object.fromEntries(
|
|
109
|
+
Object.entries(funcData).filter(([key]) => !includes(aggregateOptionKeys, key)),
|
|
110
|
+
);
|
|
111
|
+
return {
|
|
112
|
+
...parseFunctionObject(functionData),
|
|
113
|
+
aggregateOptions: {
|
|
114
|
+
...(funcData.$filter !== undefined && {
|
|
115
|
+
$filter: funcData.$filter as AnyObject,
|
|
116
|
+
}),
|
|
117
|
+
...(funcData.$orderBy !== undefined && {
|
|
118
|
+
$orderBy: funcData.$orderBy as OrderBy,
|
|
119
|
+
}),
|
|
120
|
+
} satisfies AggregateOptions,
|
|
121
|
+
};
|
|
122
|
+
};
|
|
123
|
+
|
|
90
124
|
export class SelectItemBuilder {
|
|
91
125
|
select: SelectItemValidated[] = [];
|
|
92
126
|
private allFields: string[];
|
|
@@ -97,6 +131,7 @@ export class SelectItemBuilder {
|
|
|
97
131
|
private functions: FunctionSpec[];
|
|
98
132
|
private allowedFieldsIncludingComputed: string[];
|
|
99
133
|
private columns: ColumnInfo[];
|
|
134
|
+
private parseAggregateOptions?: (options: AggregateOptions) => Promise<ParsedAggregateOptions>;
|
|
100
135
|
|
|
101
136
|
constructor(params: {
|
|
102
137
|
allowedFields: string[];
|
|
@@ -106,6 +141,7 @@ export class SelectItemBuilder {
|
|
|
106
141
|
allFields: string[];
|
|
107
142
|
isView: boolean;
|
|
108
143
|
columns: ColumnInfo[];
|
|
144
|
+
parseAggregateOptions?: (options: AggregateOptions) => Promise<ParsedAggregateOptions>;
|
|
109
145
|
}) {
|
|
110
146
|
this.allFields = params.allFields;
|
|
111
147
|
this.allowedFields = params.allowedFields;
|
|
@@ -113,6 +149,7 @@ export class SelectItemBuilder {
|
|
|
113
149
|
this.computedFields = params.computedFields;
|
|
114
150
|
this.functions = params.functions;
|
|
115
151
|
this.columns = params.columns;
|
|
152
|
+
this.parseAggregateOptions = params.parseAggregateOptions;
|
|
116
153
|
this.allowedFieldsIncludingComputed = this.allowedFields.concat(
|
|
117
154
|
this.computedFields.map((cf) => cf.name),
|
|
118
155
|
);
|
|
@@ -164,7 +201,12 @@ export class SelectItemBuilder {
|
|
|
164
201
|
this.select.push({ ...item, fields });
|
|
165
202
|
};
|
|
166
203
|
|
|
167
|
-
private addFunction = (
|
|
204
|
+
private addFunction = (
|
|
205
|
+
func: FunctionSpec | string,
|
|
206
|
+
args: any[],
|
|
207
|
+
alias: string,
|
|
208
|
+
aggregateOptions: AggregateOptions = {},
|
|
209
|
+
): void | Promise<void> => {
|
|
168
210
|
const funcDef = parseFunction({
|
|
169
211
|
func,
|
|
170
212
|
args,
|
|
@@ -172,6 +214,27 @@ export class SelectItemBuilder {
|
|
|
172
214
|
allowedFields: this.allowedFieldsIncludingComputed,
|
|
173
215
|
});
|
|
174
216
|
|
|
217
|
+
if (Object.keys(aggregateOptions).length) {
|
|
218
|
+
if (funcDef.type !== "aggregation") {
|
|
219
|
+
throw `Aggregate options $filter and $orderBy are only allowed on aggregate functions`;
|
|
220
|
+
}
|
|
221
|
+
if (!this.parseAggregateOptions) {
|
|
222
|
+
throw "Aggregate options are not supported in this query";
|
|
223
|
+
}
|
|
224
|
+
return this.parseAggregateOptions(aggregateOptions).then((parsedOptions) => {
|
|
225
|
+
this.addFunctionItem(funcDef, args, alias, parsedOptions);
|
|
226
|
+
});
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
this.addFunctionItem(funcDef, args, alias);
|
|
230
|
+
};
|
|
231
|
+
|
|
232
|
+
private addFunctionItem = (
|
|
233
|
+
funcDef: FunctionSpec,
|
|
234
|
+
args: any[],
|
|
235
|
+
alias: string,
|
|
236
|
+
aggregateOptions?: ParsedAggregateOptions,
|
|
237
|
+
) => {
|
|
175
238
|
const fieldFilter = funcDef.getFields(args);
|
|
176
239
|
this.addItem({
|
|
177
240
|
type: funcDef.type,
|
|
@@ -184,11 +247,15 @@ export class SelectItemBuilder {
|
|
|
184
247
|
args,
|
|
185
248
|
tableAliasRaw: tableAlias,
|
|
186
249
|
ctidField: undefined,
|
|
250
|
+
aggregateFilter: aggregateOptions?.filter,
|
|
251
|
+
aggregateOrderBy: aggregateOptions?.orderBy,
|
|
187
252
|
|
|
188
253
|
/* CTID not available in AFTER trigger */
|
|
189
254
|
// ctidField: this.isView? undefined : "ctid"
|
|
190
255
|
}),
|
|
191
256
|
selected: true,
|
|
257
|
+
dependencyFields: aggregateOptions?.dependencyFields,
|
|
258
|
+
dependencyExists: aggregateOptions?.dependencyExists,
|
|
192
259
|
});
|
|
193
260
|
};
|
|
194
261
|
|
|
@@ -204,7 +271,7 @@ export class SelectItemBuilder {
|
|
|
204
271
|
singleColArg: false,
|
|
205
272
|
getFields: (_args: any[]) => [],
|
|
206
273
|
};
|
|
207
|
-
this.addFunction(cf, [], compCol.name);
|
|
274
|
+
void this.addFunction(cf, [], compCol.name);
|
|
208
275
|
return;
|
|
209
276
|
}
|
|
210
277
|
}
|
|
@@ -271,14 +338,16 @@ export class SelectItemBuilder {
|
|
|
271
338
|
} else {
|
|
272
339
|
await Promise.all(
|
|
273
340
|
selectKeys.map(async (key) => {
|
|
274
|
-
const
|
|
341
|
+
const userSelectValue: unknown = userSelect[key as keyof typeof userSelect];
|
|
275
342
|
const throwErr: (message: string) => never = (message) => {
|
|
276
343
|
console.trace(message);
|
|
277
|
-
throw
|
|
344
|
+
throw (
|
|
345
|
+
"Unexpected select -> " + JSON.stringify({ [key]: userSelectValue }) + "\n" + message
|
|
346
|
+
);
|
|
278
347
|
};
|
|
279
348
|
|
|
280
349
|
/* Included fields */
|
|
281
|
-
if (
|
|
350
|
+
if (userSelectValue === 1 || userSelectValue === true) {
|
|
282
351
|
if (key === "*") {
|
|
283
352
|
this.allowedFields.map((key) => this.addColumn(key, true));
|
|
284
353
|
} else {
|
|
@@ -286,18 +355,26 @@ export class SelectItemBuilder {
|
|
|
286
355
|
}
|
|
287
356
|
|
|
288
357
|
/* Aggregations and functions */
|
|
289
|
-
} else if (typeof
|
|
358
|
+
} else if (typeof userSelectValue === "string" || isObject(userSelectValue)) {
|
|
359
|
+
const functionEntries =
|
|
360
|
+
isObject(userSelectValue) ?
|
|
361
|
+
Object.entries(userSelectValue).filter(
|
|
362
|
+
([key]) => !includes(aggregateOptionKeys, key),
|
|
363
|
+
)
|
|
364
|
+
: [];
|
|
290
365
|
/* Function shorthand notation
|
|
291
366
|
{ id: "$max" } === { id: { $max: ["id"] } } === SELECT MAX(id) AS id
|
|
292
367
|
*/
|
|
293
368
|
if (
|
|
294
|
-
(typeof
|
|
295
|
-
(isObject(
|
|
296
|
-
|
|
297
|
-
Array.isArray(
|
|
369
|
+
(typeof userSelectValue === "string" && userSelectValue !== "*") ||
|
|
370
|
+
(isObject(userSelectValue) &&
|
|
371
|
+
functionEntries.length === 1 &&
|
|
372
|
+
Array.isArray(functionEntries[0]![1]))
|
|
298
373
|
) {
|
|
299
|
-
let funcName: string | undefined,
|
|
300
|
-
|
|
374
|
+
let funcName: string | undefined,
|
|
375
|
+
args: any[] | undefined,
|
|
376
|
+
aggregateOptions: AggregateOptions = {};
|
|
377
|
+
if (typeof userSelectValue === "string") {
|
|
301
378
|
/* Shorthand notation -> it is expected that the key is the column name used as the only argument */
|
|
302
379
|
try {
|
|
303
380
|
this.checkField(key, true);
|
|
@@ -306,22 +383,22 @@ export class SelectItemBuilder {
|
|
|
306
383
|
` Shorthand function notation error: the specified column ( ${key} ) is invalid or disallowed. \n Use correct column name or full aliased function notation, e.g.: -> { alias: { $func_name: ["column_name"] } } `,
|
|
307
384
|
);
|
|
308
385
|
}
|
|
309
|
-
funcName =
|
|
386
|
+
funcName = userSelectValue;
|
|
310
387
|
args = [key];
|
|
311
388
|
|
|
312
389
|
/** Function full notation { $funcName: ["colName", ...args] } */
|
|
313
390
|
} else {
|
|
314
|
-
({ funcName, args } =
|
|
391
|
+
({ funcName, args, aggregateOptions } = parseSelectFunctionObject(userSelectValue));
|
|
315
392
|
}
|
|
316
393
|
|
|
317
|
-
this.addFunction(funcName, args, key);
|
|
394
|
+
await this.addFunction(funcName, args, key, aggregateOptions);
|
|
318
395
|
|
|
319
396
|
/* Join */
|
|
320
397
|
} else {
|
|
321
398
|
if (!joinParse) {
|
|
322
399
|
throw "Joins disallowed";
|
|
323
400
|
}
|
|
324
|
-
const joinSelect =
|
|
401
|
+
const joinSelect = userSelectValue;
|
|
325
402
|
|
|
326
403
|
const parsedJoin = parseJoinSelect(joinSelect);
|
|
327
404
|
|
|
@@ -1,7 +1,13 @@
|
|
|
1
1
|
import type { SelectParams } from "prostgles-types";
|
|
2
|
-
import { asName, isEmpty, omitKeys } from "prostgles-types";
|
|
2
|
+
import { asName, isEmpty, isObject, omitKeys } from "prostgles-types";
|
|
3
3
|
import type { ParsedTableRule } from "../../PublishParser/PublishParser";
|
|
4
|
-
import
|
|
4
|
+
import {
|
|
5
|
+
prepareOrderByQuery,
|
|
6
|
+
type Filter,
|
|
7
|
+
type LocalParams,
|
|
8
|
+
type PGIdentifier,
|
|
9
|
+
type ValidatedTableRules,
|
|
10
|
+
} from "../DboBuilder";
|
|
5
11
|
import type { ViewHandler } from "../ViewHandler/ViewHandler";
|
|
6
12
|
import { parseJoinPath } from "../ViewHandler/parseJoinPath";
|
|
7
13
|
import { prepareSortItems } from "../ViewHandler/prepareSortItems";
|
|
@@ -21,22 +27,31 @@ export const getNewQuery = async (
|
|
|
21
27
|
): Promise<NewQuery> => {
|
|
22
28
|
const { columns } = viewHandler;
|
|
23
29
|
|
|
24
|
-
|
|
30
|
+
const selectRule = tableRules?.select;
|
|
31
|
+
if (tableRules && !selectRule?.fields) {
|
|
25
32
|
throw `INTERNAL ERROR: publish.${viewHandler.name}.select.fields rule missing`;
|
|
26
33
|
}
|
|
27
34
|
|
|
35
|
+
/** Only missing tableRules means this is an unrestricted local query */
|
|
28
36
|
const allowedOrderByFields =
|
|
29
|
-
!
|
|
37
|
+
!selectRule ?
|
|
30
38
|
viewHandler.column_names.slice(0)
|
|
31
|
-
: viewHandler.parseFieldFilter(
|
|
39
|
+
: viewHandler.parseFieldFilter(selectRule.orderByFields);
|
|
32
40
|
const allowedSelectFields =
|
|
33
|
-
!
|
|
41
|
+
!selectRule ?
|
|
34
42
|
viewHandler.column_names.slice(0)
|
|
35
|
-
: viewHandler.parseFieldFilter(
|
|
43
|
+
: viewHandler.parseFieldFilter(selectRule.fields);
|
|
44
|
+
const allowedFilterFields =
|
|
45
|
+
!selectRule ?
|
|
46
|
+
viewHandler.column_names.slice(0)
|
|
47
|
+
: viewHandler.parseFieldFilter(selectRule.filterFields);
|
|
36
48
|
|
|
37
49
|
const joinQueries: NewQueryJoin[] = [];
|
|
38
50
|
|
|
39
51
|
const { select: userSelect = "*" } = selectParams;
|
|
52
|
+
const aggregateTableAlias =
|
|
53
|
+
selectParams.joinExpressionAlias ??
|
|
54
|
+
({ raw: ROOT_TABLE_ALIAS, escaped: ROOT_TABLE_ALIAS } satisfies PGIdentifier);
|
|
40
55
|
|
|
41
56
|
const selectItemBuilder = new SelectItemBuilder({
|
|
42
57
|
allowedFields: allowedSelectFields,
|
|
@@ -46,6 +61,39 @@ export const getNewQuery = async (
|
|
|
46
61
|
functions: FUNCTIONS,
|
|
47
62
|
allFields: viewHandler.column_names.slice(0),
|
|
48
63
|
columns,
|
|
64
|
+
parseAggregateOptions: async ({ $filter, $orderBy }) => {
|
|
65
|
+
if ($filter !== undefined && !isObject($filter)) {
|
|
66
|
+
throw `Invalid aggregate $filter. Expecting an object`;
|
|
67
|
+
}
|
|
68
|
+
const filterInfo =
|
|
69
|
+
$filter === undefined ? undefined : (
|
|
70
|
+
await prepareWhere(viewHandler, {
|
|
71
|
+
filter: $filter,
|
|
72
|
+
select: undefined,
|
|
73
|
+
filterFields: allowedFilterFields,
|
|
74
|
+
tableAlias: aggregateTableAlias,
|
|
75
|
+
localParams,
|
|
76
|
+
addWhere: false,
|
|
77
|
+
tableRule: tableRules,
|
|
78
|
+
})
|
|
79
|
+
);
|
|
80
|
+
const orderByItems = prepareSortItems(
|
|
81
|
+
$orderBy,
|
|
82
|
+
allowedOrderByFields,
|
|
83
|
+
aggregateTableAlias,
|
|
84
|
+
[],
|
|
85
|
+
[],
|
|
86
|
+
);
|
|
87
|
+
return {
|
|
88
|
+
filter: filterInfo?.condition,
|
|
89
|
+
orderBy: prepareOrderByQuery(orderByItems, aggregateTableAlias.raw)[0],
|
|
90
|
+
dependencyFields: [
|
|
91
|
+
...(filterInfo?.columnsUsed ?? []),
|
|
92
|
+
...orderByItems.map(({ key }) => key),
|
|
93
|
+
],
|
|
94
|
+
dependencyExists: filterInfo?.exists ?? [],
|
|
95
|
+
};
|
|
96
|
+
},
|
|
49
97
|
});
|
|
50
98
|
|
|
51
99
|
await selectItemBuilder.parseUserSelectWithJoins(
|
|
@@ -151,9 +199,9 @@ export const getNewQuery = async (
|
|
|
151
199
|
...omitKeys(commonWhereParams, ["forcedFilter", "selectParams"]),
|
|
152
200
|
filter: selectParams.having,
|
|
153
201
|
tableAlias:
|
|
154
|
-
selectParams.joinExpressionAlias ?
|
|
155
|
-
|
|
156
|
-
|
|
202
|
+
selectParams.joinExpressionAlias ?
|
|
203
|
+
source.alias
|
|
204
|
+
: { raw: ROOT_TABLE_ALIAS, escaped: ROOT_TABLE_ALIAS },
|
|
157
205
|
isHaving: true,
|
|
158
206
|
})
|
|
159
207
|
);
|
|
@@ -5,18 +5,7 @@ import type {
|
|
|
5
5
|
RawJoinPath,
|
|
6
6
|
SimpleJoinSelect,
|
|
7
7
|
} from "prostgles-types";
|
|
8
|
-
import { getKeys, includes } from "prostgles-types";
|
|
9
|
-
|
|
10
|
-
const JOIN_KEYS = ["$innerJoin", "$leftJoin"] as const;
|
|
11
|
-
const JOIN_PARAM_KEYS = getKeys({
|
|
12
|
-
$condition: 1,
|
|
13
|
-
filter: 1,
|
|
14
|
-
having: 1,
|
|
15
|
-
limit: 1,
|
|
16
|
-
offset: 1,
|
|
17
|
-
orderBy: 1,
|
|
18
|
-
select: 1,
|
|
19
|
-
} satisfies Record<keyof Omit<DetailedJoinSelect, (typeof JOIN_KEYS)[number]>, 1>);
|
|
8
|
+
import { getKeys, includes, JOIN_KEYS, JOIN_PARAMS } from "prostgles-types";
|
|
20
9
|
|
|
21
10
|
export type ParsedJoin =
|
|
22
11
|
| {
|
|
@@ -47,7 +36,7 @@ export const parseJoinSelect = (joinParams: JoinSelect): ParsedJoin | string =>
|
|
|
47
36
|
} else if (joinKey) {
|
|
48
37
|
/* Full option join { field_name: db.innerJoin.table_name(filter, select) } */
|
|
49
38
|
const invalidParams = Object.keys(joinParams).filter(
|
|
50
|
-
(k) => !includes([...
|
|
39
|
+
(k) => !includes([...JOIN_PARAMS, ...JOIN_KEYS], k),
|
|
51
40
|
);
|
|
52
41
|
if (invalidParams.length) {
|
|
53
42
|
throw "Invalid join params: " + invalidParams.join(", ");
|
|
@@ -36,32 +36,33 @@ export const onDeleteFromFileTable = async (
|
|
|
36
36
|
|
|
37
37
|
const fileIds =
|
|
38
38
|
config.versioning ?
|
|
39
|
-
|
|
39
|
+
await getFileTableHandler(dbx, config).find(filterOpts.filter, {
|
|
40
40
|
select: { id: 1 },
|
|
41
41
|
limit: null,
|
|
42
|
-
})
|
|
42
|
+
})
|
|
43
43
|
: [];
|
|
44
44
|
const versionFiles =
|
|
45
45
|
fileIds.length ?
|
|
46
|
-
|
|
46
|
+
await getFileRevisionsTableHandler(dbx, config).find(
|
|
47
47
|
{ file_id: { $in: fileIds.map(({ id }) => id) } },
|
|
48
48
|
{ select: { storage_key: 1 }, limit: null },
|
|
49
|
-
)
|
|
49
|
+
)
|
|
50
50
|
: [];
|
|
51
51
|
|
|
52
|
-
const
|
|
52
|
+
const KEY = "_prostgles_storage_key" as const;
|
|
53
|
+
const files = await t.any<Record<string, unknown> & { [KEY]: string }>(
|
|
53
54
|
`DELETE FROM ${asName(tableName)} ${filterOpts.where}
|
|
54
55
|
${returningQuery ? `${returningQuery},` : "RETURNING"}
|
|
55
|
-
COALESCE(storage_key, id) AS
|
|
56
|
+
COALESCE(storage_key, id) AS ${KEY}`,
|
|
56
57
|
);
|
|
57
58
|
const storageKeys = new Set([
|
|
58
59
|
...versionFiles.map(({ storage_key }) => storage_key),
|
|
59
|
-
...files.map((
|
|
60
|
+
...files.map((row) => row[KEY]),
|
|
60
61
|
]);
|
|
61
62
|
for (const storageKey of storageKeys) {
|
|
62
63
|
onCommit(({ db }) => deleteUnreferencedFile(db, config, storageKey));
|
|
63
64
|
}
|
|
64
|
-
const result = files.map(({
|
|
65
|
+
const result = files.map(({ [KEY]: _, ...row }) => row);
|
|
65
66
|
if (!returningQuery) return undefined;
|
|
66
67
|
return (isOneOrNone ? result[0] : result) as AnyObject[] | undefined;
|
|
67
68
|
};
|
|
@@ -11,6 +11,7 @@ import {
|
|
|
11
11
|
import { getNewQuery } from "../QueryBuilder/getNewQuery";
|
|
12
12
|
import { getSelectQuery } from "../QueryBuilder/getSelectQuery";
|
|
13
13
|
import { getReturnTypeQuery } from "./getReturnTypeQuery";
|
|
14
|
+
import { validateSelectParams } from "./validateSelectParams";
|
|
14
15
|
import type { ViewHandler } from "./ViewHandler";
|
|
15
16
|
|
|
16
17
|
export type Param3 = {
|
|
@@ -30,44 +31,13 @@ export const find = async function (
|
|
|
30
31
|
|
|
31
32
|
const command = limit === 1 && returnType === "row" ? "findOne" : "find";
|
|
32
33
|
try {
|
|
33
|
-
|
|
34
|
-
row: 1,
|
|
35
|
-
statement: 1,
|
|
36
|
-
value: 1,
|
|
37
|
-
values: 1,
|
|
38
|
-
"statement-no-rls": 1,
|
|
39
|
-
"statement-where": 1,
|
|
40
|
-
} satisfies Record<Required<SelectParams>["returnType"], 1>);
|
|
34
|
+
validateSelectParams(selectParams);
|
|
41
35
|
|
|
42
36
|
const { returnType } = selectParams || {};
|
|
43
|
-
if (returnType && !allowedReturnTypes.includes(returnType)) {
|
|
44
|
-
throw `returnType (${returnType}) can only be ${allowedReturnTypes.join(" OR ")}`;
|
|
45
|
-
}
|
|
46
37
|
|
|
47
38
|
const { testRule = false } = localParams || {};
|
|
48
39
|
|
|
49
40
|
if (testRule) return [];
|
|
50
|
-
if (selectParams) {
|
|
51
|
-
const validParamNames = Object.keys({
|
|
52
|
-
select: 1,
|
|
53
|
-
orderBy: 1,
|
|
54
|
-
offset: 1,
|
|
55
|
-
limit: 1,
|
|
56
|
-
returnType: 1,
|
|
57
|
-
groupBy: 1,
|
|
58
|
-
having: 1,
|
|
59
|
-
abortSignal: 1,
|
|
60
|
-
} satisfies Record<keyof SelectParams, 1>);
|
|
61
|
-
|
|
62
|
-
const invalidParams = Object.keys(selectParams).filter((k) => !validParamNames.includes(k));
|
|
63
|
-
if (invalidParams.length)
|
|
64
|
-
throw (
|
|
65
|
-
"Invalid params: " +
|
|
66
|
-
invalidParams.join(", ") +
|
|
67
|
-
" \n Expecting: " +
|
|
68
|
-
validParamNames.join(", ")
|
|
69
|
-
);
|
|
70
|
-
}
|
|
71
41
|
|
|
72
42
|
/* Validate publish */
|
|
73
43
|
if (tableRules) {
|