@entity-access/entity-access 1.0.2 → 1.0.6
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/.github/workflows/node.yml +7 -2
- package/.vscode/settings.json +1 -0
- package/README.md +57 -27
- package/package.json +2 -2
- package/src/common/EntityAccessError.ts +10 -0
- package/src/common/IDisposable.ts +25 -0
- package/src/common/ImmutableObject.ts +53 -0
- package/src/common/Logger.ts +59 -0
- package/src/common/TypeInfo.ts +3 -0
- package/src/common/cache/TimedCache.ts +2 -2
- package/src/common/usingAsync.ts +42 -12
- package/src/compiler/QueryCompiler.ts +28 -30
- package/src/compiler/postgres/PostgreSqlMethodTransformer.ts +23 -0
- package/src/compiler/sql-server/SqlServerSqlMethodTransformer.ts +23 -0
- package/src/decorators/ForeignKey.ts +1 -1
- package/src/decorators/IClassOf.ts +2 -1
- package/src/decorators/IColumn.ts +2 -0
- package/src/decorators/parser/NameParser.ts +15 -0
- package/src/di/di.ts +224 -0
- package/src/drivers/base/BaseDriver.ts +28 -3
- package/src/drivers/sql-server/ExpressionToSqlServer.ts +34 -9
- package/src/drivers/sql-server/SqlServerDriver.ts +7 -16
- package/src/entity-query/EntityType.ts +45 -3
- package/src/model/EntityContext.ts +167 -22
- package/src/model/EntityQuery.ts +118 -59
- package/src/model/EntitySource.ts +38 -46
- package/src/model/IFilterWithParameter.ts +5 -0
- package/src/model/SourceExpression.ts +21 -25
- package/src/model/{ChangeEntry.ts → changes/ChangeEntry.ts} +35 -5
- package/src/model/{ChangeSet.ts → changes/ChangeSet.ts} +16 -11
- package/src/model/events/ContextEvents.ts +26 -0
- package/src/model/events/EntityEvents.ts +96 -0
- package/src/model/{IdentityService.ts → identity/IdentityService.ts} +9 -1
- package/src/model/identity/RelationMapper.ts +71 -0
- package/src/model/symbols.ts +1 -0
- package/src/model/verification/VerificationSession.ts +173 -0
- package/src/query/ast/DebugStringVisitor.ts +175 -0
- package/src/query/ast/ExpressionToSql.ts +277 -119
- package/src/query/ast/Expressions.ts +130 -13
- package/src/query/ast/IStringTransformer.ts +19 -5
- package/src/query/ast/ParameterScope.ts +97 -0
- package/src/query/ast/ReplaceParameter.ts +40 -0
- package/src/query/ast/Types.ts +0 -0
- package/src/query/ast/Visitor.ts +26 -5
- package/src/query/expander/QueryExpander.ts +147 -0
- package/src/query/parser/ArrowToExpression.ts +134 -19
- package/src/query/parser/BabelVisitor.ts +31 -43
- package/src/query/parser/NotSupportedError.ts +5 -0
- package/src/query/parser/Restructure.ts +66 -0
- package/src/query/parser/TransformVisitor.ts +83 -0
- package/src/sql/ISql.ts +10 -0
- package/src/tests/db-tests/tests/select-items.ts +12 -0
- package/src/tests/expressions/left-joins/child-joins.ts +54 -34
- package/src/tests/expressions/sanitize/sanitize-test.ts +17 -0
- package/src/tests/expressions/select/select.ts +24 -0
- package/src/tests/expressions/simple/parse-arrow.ts +10 -0
- package/src/tests/model/ShoppingContext.ts +7 -3
- package/src/tests/model/createContext.ts +68 -17
- package/src/tests/security/ShoppingContextEvents.ts +20 -0
- package/src/tests/security/events/OrderEvents.ts +72 -0
- package/src/tests/security/events/ProductEvents.ts +92 -0
- package/src/tests/security/events/UserEvents.ts +28 -0
- package/src/tests/security/events/UserInfo.ts +7 -0
- package/src/tests/security/tests/include-items.ts +19 -0
- package/src/tests/security/tests/place-order.ts +104 -0
- package/test.js +11 -4
- package/tsconfig.json +2 -0
- package/src/decorators/parser/MemberParser.ts +0 -8
- package/src/model/EntitySchema.ts +0 -21
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import { IColumn } from "../../decorators/IColumn.js";
|
|
2
1
|
import { IClassOf } from "../../decorators/IClassOf.js";
|
|
3
|
-
import {
|
|
2
|
+
import { ITextQuery } from "./IStringTransformer.js";
|
|
4
3
|
import type EntityType from "../../entity-query/EntityType.js";
|
|
4
|
+
import DebugStringVisitor from "./DebugStringVisitor.js";
|
|
5
|
+
import { IEntityRelation } from "../../decorators/IColumn.js";
|
|
5
6
|
|
|
6
7
|
const flattenedSelf = Symbol("flattenedSelf");
|
|
7
8
|
|
|
@@ -24,9 +25,68 @@ const flattenedSelf = Symbol("flattenedSelf");
|
|
|
24
25
|
|
|
25
26
|
export abstract class Expression {
|
|
26
27
|
|
|
28
|
+
static array(elements: Expression[]) {
|
|
29
|
+
return ArrayExpression.create({ elements });
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
static as(expression: Expression, alias: QuotedLiteral | string) {
|
|
33
|
+
if (typeof alias === "string") {
|
|
34
|
+
alias = Expression.quotedLiteral(alias);
|
|
35
|
+
}
|
|
36
|
+
return ExpressionAs.create({
|
|
37
|
+
expression,
|
|
38
|
+
alias
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
static templateLiteral(value: Expression[]) {
|
|
43
|
+
return TemplateLiteral.create({ value });
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
static constant(value: string) {
|
|
48
|
+
return Constant.create({ value });
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
static quotedLiteral(name: string) {
|
|
52
|
+
return QuotedLiteral.create({ literal: name });
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
static parameter(name: string) {
|
|
56
|
+
return ParameterExpression.create({ name });
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
static identifier(name: string) {
|
|
60
|
+
return Identifier.create({ value: name });
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
static logicalAnd(left: Expression, right: Expression): BinaryExpression {
|
|
64
|
+
return BinaryExpression.create({ left, operator: "AND", right});
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
static member(target: Expression, identifier: string |Expression): MemberExpression {
|
|
68
|
+
return MemberExpression.create({
|
|
69
|
+
target,
|
|
70
|
+
property: typeof identifier === "string"
|
|
71
|
+
? this.identifier(identifier)
|
|
72
|
+
: identifier
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
static equal(left: Expression, right: Expression) {
|
|
77
|
+
return BinaryExpression.create({ left, right, operator: "="});
|
|
78
|
+
}
|
|
79
|
+
|
|
27
80
|
static create<T extends Expression>(this: IClassOf<T>, p: Partial<Omit<T, "type">>) {
|
|
28
81
|
(p as any).type = (this as any).name;
|
|
29
82
|
Object.setPrototypeOf(p, this.prototype);
|
|
83
|
+
Object.defineProperty(p, "debugView", {
|
|
84
|
+
get() {
|
|
85
|
+
return DebugStringVisitor.expressionToString(this);
|
|
86
|
+
},
|
|
87
|
+
enumerable: true,
|
|
88
|
+
configurable: true
|
|
89
|
+
});
|
|
30
90
|
return p as T;
|
|
31
91
|
}
|
|
32
92
|
|
|
@@ -68,9 +128,14 @@ export abstract class Expression {
|
|
|
68
128
|
|
|
69
129
|
}
|
|
70
130
|
|
|
71
|
-
export class
|
|
72
|
-
readonly type = "
|
|
73
|
-
|
|
131
|
+
export class ArrayExpression extends Expression {
|
|
132
|
+
readonly type = "ArrayExpression";
|
|
133
|
+
elements: Expression[];
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
export class PartialExpression extends Expression {
|
|
137
|
+
readonly type = "PartialExpression";
|
|
138
|
+
query: ITextQuery;
|
|
74
139
|
}
|
|
75
140
|
|
|
76
141
|
export class BinaryExpression extends Expression {
|
|
@@ -112,6 +177,26 @@ export class CallExpression extends Expression {
|
|
|
112
177
|
arguments: Expression[];
|
|
113
178
|
}
|
|
114
179
|
|
|
180
|
+
export class ParameterExpression extends Expression {
|
|
181
|
+
readonly type = "ParameterExpression";
|
|
182
|
+
name: string;
|
|
183
|
+
/**
|
|
184
|
+
* Default value if any...
|
|
185
|
+
*/
|
|
186
|
+
value: any;
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* Entity Model representing the variable
|
|
190
|
+
*/
|
|
191
|
+
model: EntityType;
|
|
192
|
+
|
|
193
|
+
quotedLiteral: (x: string) => string;
|
|
194
|
+
|
|
195
|
+
toString() {
|
|
196
|
+
return this.quotedLiteral?.(this.name) ?? this.name;
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
115
200
|
export class MemberExpression extends Expression {
|
|
116
201
|
readonly type = "MemberExpression";
|
|
117
202
|
target: Expression;
|
|
@@ -121,19 +206,21 @@ export class MemberExpression extends Expression {
|
|
|
121
206
|
|
|
122
207
|
export class ArrowFunctionExpression extends Expression {
|
|
123
208
|
readonly type = "ArrowFunctionExpression";
|
|
124
|
-
params:
|
|
209
|
+
params: ParameterExpression[];
|
|
125
210
|
body: Expression;
|
|
126
211
|
}
|
|
127
212
|
|
|
128
213
|
export type TableSource = SelectStatement | QuotedLiteral | ExpressionAs | TableLiteral;
|
|
129
214
|
|
|
215
|
+
export type Expand = { [key: string]: string | Expand };
|
|
216
|
+
|
|
130
217
|
export class SelectStatement extends Expression {
|
|
131
218
|
|
|
132
219
|
readonly type = "SelectStatement";
|
|
133
220
|
|
|
134
|
-
source: TableSource;
|
|
221
|
+
source: TableSource | ValuesStatement;
|
|
135
222
|
|
|
136
|
-
as:
|
|
223
|
+
as: ParameterExpression;
|
|
137
224
|
|
|
138
225
|
fields: (Expression | QuotedLiteral | ExpressionAs)[];
|
|
139
226
|
|
|
@@ -147,13 +234,30 @@ export class SelectStatement extends Expression {
|
|
|
147
234
|
|
|
148
235
|
offset: number;
|
|
149
236
|
|
|
237
|
+
model: EntityType;
|
|
238
|
+
|
|
239
|
+
// include relations...
|
|
240
|
+
include: SelectStatement[];
|
|
241
|
+
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
export class NewObjectExpression extends Expression {
|
|
245
|
+
readonly type = "NewObjectExpression";
|
|
246
|
+
properties: ExpressionAs[];
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
export class ConditionalExpression extends Expression {
|
|
250
|
+
readonly type = "ConditionalExpression";
|
|
251
|
+
test: Expression;
|
|
252
|
+
consequent: Expression;
|
|
253
|
+
alternate: Expression;
|
|
150
254
|
}
|
|
151
255
|
|
|
152
256
|
export class JoinExpression extends Expression {
|
|
153
257
|
readonly type = "JoinExpression";
|
|
154
258
|
joinType: "LEFT" | "INNER";
|
|
155
|
-
source: SelectStatement | QuotedLiteral | ExpressionAs;
|
|
156
|
-
as: QuotedLiteral;
|
|
259
|
+
source: SelectStatement | QuotedLiteral | ExpressionAs | TableLiteral;
|
|
260
|
+
as: QuotedLiteral | ParameterExpression;
|
|
157
261
|
where: Expression;
|
|
158
262
|
model: EntityType;
|
|
159
263
|
}
|
|
@@ -200,8 +304,17 @@ export class BigIntLiteral extends Expression {
|
|
|
200
304
|
public value: bigint;
|
|
201
305
|
}
|
|
202
306
|
|
|
307
|
+
export class TemplateElement extends Expression {
|
|
308
|
+
readonly type = "TemplateElement";
|
|
309
|
+
public value: {
|
|
310
|
+
raw: string;
|
|
311
|
+
cooked: string;
|
|
312
|
+
};
|
|
313
|
+
}
|
|
314
|
+
|
|
203
315
|
export class TemplateLiteral extends Expression {
|
|
204
316
|
readonly type = "TemplateLiteral";
|
|
317
|
+
public quasis: TemplateElement[];
|
|
205
318
|
public value: Expression[];
|
|
206
319
|
}
|
|
207
320
|
|
|
@@ -260,7 +373,7 @@ export class UpdateStatement extends Expression {
|
|
|
260
373
|
|
|
261
374
|
export class DeleteStatement extends Expression {
|
|
262
375
|
readonly type = "DeleteStatement";
|
|
263
|
-
table: TableLiteral;
|
|
376
|
+
table: TableLiteral | QuotedLiteral;
|
|
264
377
|
where: Expression;
|
|
265
378
|
}
|
|
266
379
|
|
|
@@ -289,6 +402,10 @@ export type ExpressionType =
|
|
|
289
402
|
CoalesceExpression|
|
|
290
403
|
ExistsExpression|
|
|
291
404
|
Identifier |
|
|
292
|
-
|
|
293
|
-
|
|
405
|
+
ArrowFunctionExpression |
|
|
406
|
+
ConditionalExpression |
|
|
407
|
+
NewObjectExpression |
|
|
408
|
+
ParameterExpression |
|
|
409
|
+
ArrayExpression |
|
|
410
|
+
TemplateElement
|
|
294
411
|
;
|
|
@@ -1,10 +1,22 @@
|
|
|
1
|
-
export type
|
|
2
|
-
export type
|
|
1
|
+
export type ITextQueryFragment = string | ((p: any) => any) | { toString(): string };
|
|
2
|
+
export type ITextQuery = ITextQueryFragment[];
|
|
3
3
|
|
|
4
4
|
export type IStringTransformer = (s: string) => string;
|
|
5
5
|
|
|
6
6
|
export type ISqlMethodTransformer = (callee: string, args: string[]) => string;
|
|
7
7
|
|
|
8
|
+
export class QueryParameter {
|
|
9
|
+
|
|
10
|
+
static create(name: () => string, quotedLiteral: (n: string) => string ) {
|
|
11
|
+
return new QueryParameter(name, quotedLiteral);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
constructor(public name: () => string, public quotedLiteral: (n: string) => string) {}
|
|
15
|
+
|
|
16
|
+
toString() {
|
|
17
|
+
return this.quotedLiteral(this.name());
|
|
18
|
+
}
|
|
19
|
+
}
|
|
8
20
|
|
|
9
21
|
export const prepareAny = (a: TemplateStringsArray, ... p: any[]): any => {
|
|
10
22
|
const r = [];
|
|
@@ -46,7 +58,7 @@ const addNonEmptyFlat = (array: any[], item: any) => {
|
|
|
46
58
|
array.push(item);
|
|
47
59
|
};
|
|
48
60
|
|
|
49
|
-
export const prepare = (a: TemplateStringsArray, ... p: (
|
|
61
|
+
export const prepare = (a: TemplateStringsArray, ... p: (ITextQueryFragment | ITextQuery)[]): ITextQuery => {
|
|
50
62
|
|
|
51
63
|
const r = [];
|
|
52
64
|
for (let index = 0; index < a.length; index++) {
|
|
@@ -60,12 +72,14 @@ export const prepare = (a: TemplateStringsArray, ... p: (ITextOrFunction | IText
|
|
|
60
72
|
return r.flat(2);
|
|
61
73
|
};
|
|
62
74
|
|
|
63
|
-
export const prepareJoin = (a: (
|
|
75
|
+
export const prepareJoin = (a: (ITextQueryFragment | ITextQuery)[], sep: string = ","): ITextQuery => {
|
|
64
76
|
const r = [];
|
|
65
77
|
let first = true;
|
|
66
78
|
for (const iterator of a) {
|
|
67
79
|
if (!first) {
|
|
68
|
-
|
|
80
|
+
if (sep) {
|
|
81
|
+
r.push(sep);
|
|
82
|
+
}
|
|
69
83
|
}
|
|
70
84
|
first = false;
|
|
71
85
|
addNonEmptyFlat(r, iterator);
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import type EntityType from "../../entity-query/EntityType.js";
|
|
2
|
+
import type { ParameterExpression, SelectStatement } from "./Expressions.js";
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
export interface IMappingModel {
|
|
6
|
+
parameter: ParameterExpression;
|
|
7
|
+
selectStatement?: SelectStatement;
|
|
8
|
+
name?: string;
|
|
9
|
+
model?: EntityType;
|
|
10
|
+
replace?: ParameterExpression;
|
|
11
|
+
isRuntimeParam?: boolean;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* This class will provide scope for ever parameter along with type mapping, unique name and
|
|
16
|
+
* and replace parameter
|
|
17
|
+
*/
|
|
18
|
+
export default class ParameterScope {
|
|
19
|
+
|
|
20
|
+
private map:Map<ParameterExpression, IMappingModel> = new Map();
|
|
21
|
+
|
|
22
|
+
private names: Set<string> = new Set();
|
|
23
|
+
|
|
24
|
+
public get(p: ParameterExpression) {
|
|
25
|
+
const model = this.map.get(p);
|
|
26
|
+
return model;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
public nameOf(p: ParameterExpression) {
|
|
30
|
+
const model = this.map.get(p);
|
|
31
|
+
return model.name;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
public alias(
|
|
35
|
+
originalParameter: ParameterExpression,
|
|
36
|
+
alias: ParameterExpression,
|
|
37
|
+
selectStatement: SelectStatement
|
|
38
|
+
) {
|
|
39
|
+
const model = this.map.get(originalParameter);
|
|
40
|
+
if (!model.selectStatement) {
|
|
41
|
+
model.selectStatement = selectStatement;
|
|
42
|
+
} else {
|
|
43
|
+
selectStatement = model.selectStatement;
|
|
44
|
+
}
|
|
45
|
+
return this.create({ parameter: alias, selectStatement, name: model.name, replace: originalParameter });
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
create(model: IMappingModel) {
|
|
49
|
+
|
|
50
|
+
const existing = this.map.get(model.parameter);
|
|
51
|
+
if (existing) {
|
|
52
|
+
existing.replace = model.replace;
|
|
53
|
+
existing.selectStatement = model.selectStatement;
|
|
54
|
+
existing.model ??= existing.selectStatement.model;
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
if (!model.replace) {
|
|
59
|
+
// create name...
|
|
60
|
+
model.name = this.createName(model.parameter.model?.name?.[0] ?? model.parameter.name, model.parameter.name );
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
if (!model.model) {
|
|
64
|
+
if (model.parameter.model) {
|
|
65
|
+
model.model = model.parameter.model;
|
|
66
|
+
} else if (model.selectStatement){
|
|
67
|
+
model.model = model.selectStatement.model;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
this.map.set(model.parameter, model);
|
|
72
|
+
}
|
|
73
|
+
createName(prefix: string, name?: string): string {
|
|
74
|
+
if (name) {
|
|
75
|
+
name = name.toLocaleLowerCase();
|
|
76
|
+
if (!this.names.has(name)) {
|
|
77
|
+
this.names.add(name);
|
|
78
|
+
return name;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
let index = 1;
|
|
82
|
+
prefix = prefix.toLocaleLowerCase();
|
|
83
|
+
while(true) {
|
|
84
|
+
name = `${prefix}${index++}`;
|
|
85
|
+
if (this.names.has(name)) {
|
|
86
|
+
continue;
|
|
87
|
+
}
|
|
88
|
+
this.names.add(name);
|
|
89
|
+
return name;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
delete(param1: ParameterExpression) {
|
|
94
|
+
// this.map.delete(param1);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { Expression } from "@babel/types";
|
|
2
|
+
|
|
3
|
+
export default class ReplaceParameter {
|
|
4
|
+
|
|
5
|
+
public static replace(tree: Expression | Expression[], from: Expression, to: Expression) {
|
|
6
|
+
if (!tree) {
|
|
7
|
+
return tree;
|
|
8
|
+
}
|
|
9
|
+
if (tree === from) {
|
|
10
|
+
return to;
|
|
11
|
+
}
|
|
12
|
+
if (Array.isArray(tree)) {
|
|
13
|
+
const copy = [];
|
|
14
|
+
let index = 0;
|
|
15
|
+
for (const iterator of tree) {
|
|
16
|
+
Object.defineProperty(copy, index++, {
|
|
17
|
+
value: this.replace(iterator, from, to),
|
|
18
|
+
writable: false,
|
|
19
|
+
enumerable: true
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
return tree;
|
|
23
|
+
}
|
|
24
|
+
if (!(tree as any).type) {
|
|
25
|
+
return tree;
|
|
26
|
+
}
|
|
27
|
+
const treeCopy = {};
|
|
28
|
+
for (const key in tree) {
|
|
29
|
+
if (Object.prototype.hasOwnProperty.call(tree, key)) {
|
|
30
|
+
const element = tree[key];
|
|
31
|
+
Object.defineProperty(treeCopy, key, {
|
|
32
|
+
value: this.replace(element, from, to)
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
Object.setPrototypeOf(treeCopy, Object.getPrototypeOf(tree));
|
|
37
|
+
return treeCopy;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
}
|
|
File without changes
|
package/src/query/ast/Visitor.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { NotSupportedError } from "../parser/NotSupportedError.js";
|
|
2
|
+
import { ArrayExpression, ArrowFunctionExpression, BigIntLiteral, BinaryExpression, BooleanLiteral, CallExpression, CoalesceExpression, ConditionalExpression, Constant, DeleteStatement, ExistsExpression, Expression, ExpressionAs, ExpressionType, Identifier, InsertStatement, JoinExpression, MemberExpression, NewObjectExpression, NullExpression, NumberLiteral, OrderByExpression, ParameterExpression, QuotedLiteral, ReturnUpdated, SelectStatement, StringLiteral, TableLiteral, TemplateElement, TemplateLiteral, UpdateStatement, ValuesStatement } from "./Expressions.js";
|
|
2
3
|
|
|
3
4
|
|
|
4
5
|
export default abstract class Visitor<T = any> {
|
|
@@ -56,18 +57,38 @@ export default abstract class Visitor<T = any> {
|
|
|
56
57
|
return this.visitCoalesceExpression(e);
|
|
57
58
|
case "ExistsExpression":
|
|
58
59
|
return this.visitExistsExpression(e);
|
|
59
|
-
case "PlaceholderExpression":
|
|
60
|
-
return this.visitPlaceholderExpression(e);
|
|
61
60
|
case "ArrowFunctionExpression":
|
|
62
61
|
return this.visitArrowFunctionExpression(e);
|
|
62
|
+
case "ConditionalExpression":
|
|
63
|
+
return this.visitConditionalExpression(e);
|
|
64
|
+
case "NewObjectExpression":
|
|
65
|
+
return this.visitNewObjectExpression(e);
|
|
66
|
+
case "TemplateElement":
|
|
67
|
+
return this.visitTemplateElement(e);
|
|
68
|
+
case "ParameterExpression":
|
|
69
|
+
return this.visitParameterExpression(e);
|
|
70
|
+
case "ArrayExpression":
|
|
71
|
+
return this.visitArrayExpression(e);
|
|
63
72
|
}
|
|
64
73
|
const c: never = e;
|
|
65
74
|
throw new Error(`${e1.type} Not implemented`);
|
|
66
75
|
}
|
|
67
|
-
|
|
76
|
+
visitArrayExpression(e: ArrayExpression): T {
|
|
77
|
+
throw new NotSupportedError("Array Expression");
|
|
78
|
+
}
|
|
79
|
+
visitParameterExpression(e: ParameterExpression): T {
|
|
68
80
|
return;
|
|
69
81
|
}
|
|
70
|
-
|
|
82
|
+
visitTemplateElement(e: TemplateElement): T {
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
visitNewObjectExpression(e: NewObjectExpression): T {
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
visitConditionalExpression(e: ConditionalExpression): T {
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
visitArrowFunctionExpression(e: ArrowFunctionExpression): T {
|
|
71
92
|
return;
|
|
72
93
|
}
|
|
73
94
|
visitExistsExpression(e: ExistsExpression): T {
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
import EntityType from "../../entity-query/EntityType.js";
|
|
2
|
+
import EntityContext from "../../model/EntityContext.js";
|
|
3
|
+
import EntityQuery from "../../model/EntityQuery.js";
|
|
4
|
+
import { ArrowFunctionExpression, ExistsExpression, Expression, ExpressionType, JoinExpression, ParameterExpression, SelectStatement, TableSource } from "../ast/Expressions.js";
|
|
5
|
+
import ArrowToExpression from "../parser/ArrowToExpression.js";
|
|
6
|
+
import { NotSupportedError } from "../parser/NotSupportedError.js";
|
|
7
|
+
|
|
8
|
+
export class QueryExpander {
|
|
9
|
+
static expand(context: EntityContext, select: SelectStatement, p) {
|
|
10
|
+
const qe = new QueryExpander(context, select);
|
|
11
|
+
const expression = ArrowToExpression.transform(`(_____________________x) => ${p}` as any);
|
|
12
|
+
qe.expandNode(select, select.model, expression.body as ExpressionType);
|
|
13
|
+
return select;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
constructor(
|
|
17
|
+
private context: EntityContext,
|
|
18
|
+
private select: SelectStatement
|
|
19
|
+
) {
|
|
20
|
+
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
expandNode(parent: SelectStatement, model: EntityType, node: ExpressionType): [SelectStatement, EntityType] {
|
|
24
|
+
|
|
25
|
+
if (node.type === "ArrayExpression") {
|
|
26
|
+
for (const iterator of node.elements) {
|
|
27
|
+
this.expandNode(parent, model, iterator as ExpressionType);
|
|
28
|
+
}
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
if(node.type === "CallExpression") {
|
|
33
|
+
const callee = node.callee as ExpressionType;
|
|
34
|
+
if (callee.type !== "MemberExpression") {
|
|
35
|
+
throw new NotSupportedError(callee.type);
|
|
36
|
+
}
|
|
37
|
+
const property = callee.property as ExpressionType;
|
|
38
|
+
if (property.type !== "Identifier") {
|
|
39
|
+
throw new NotSupportedError(property.type);
|
|
40
|
+
}
|
|
41
|
+
if (property.value !== "forEach") {
|
|
42
|
+
throw new NotSupportedError(property.value);
|
|
43
|
+
}
|
|
44
|
+
const [expandedSelect, expandedType] = this.expandNode(parent, model, callee.target as ExpressionType);
|
|
45
|
+
|
|
46
|
+
const arrow = node.arguments[0];
|
|
47
|
+
if (!arrow || arrow.type !== "ArrowFunctionExpression") {
|
|
48
|
+
throw new NotSupportedError(arrow?.type ?? "Empty Expression");
|
|
49
|
+
}
|
|
50
|
+
this.expandNode(expandedSelect, expandedType, (arrow as ArrowFunctionExpression).body as ExpressionType);
|
|
51
|
+
return [expandedSelect, expandedType];
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
if (node.type !== "MemberExpression") {
|
|
55
|
+
throw new NotSupportedError(node.type);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const p = node.property as ExpressionType;
|
|
59
|
+
if (p.type !== "Identifier") {
|
|
60
|
+
throw new NotSupportedError(p.type);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const target = node.target as ExpressionType;
|
|
64
|
+
if (target.type === "MemberExpression") {
|
|
65
|
+
const [mepSelect, mepType] = this.expandNode(parent, model, target);
|
|
66
|
+
parent = mepSelect;
|
|
67
|
+
model = mepType;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const mp = model.getProperty(p.value);
|
|
71
|
+
if (!mp.relation) {
|
|
72
|
+
throw new NotSupportedError(`No relation found ${p.value} in ${model.name}`);
|
|
73
|
+
}
|
|
74
|
+
const { relation } = mp;
|
|
75
|
+
const { relatedTypeClass: propertyType } = relation;
|
|
76
|
+
|
|
77
|
+
let query = this.context.query(propertyType);
|
|
78
|
+
const events = this.context.eventsFor(propertyType, false);
|
|
79
|
+
if (events) {
|
|
80
|
+
query = events.includeFilter(query, model, p.value) ?? query;
|
|
81
|
+
}
|
|
82
|
+
const select = { ... (query as EntityQuery).selectStatement };
|
|
83
|
+
|
|
84
|
+
let where: Expression;
|
|
85
|
+
let joinWhere: Expression;
|
|
86
|
+
|
|
87
|
+
const fk = relation.fkColumn ?? relation.relatedRelation.fkColumn;
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
if(relation.isInverseRelation) {
|
|
91
|
+
|
|
92
|
+
joinWhere = Expression.equal(
|
|
93
|
+
Expression.member(
|
|
94
|
+
parent.as,
|
|
95
|
+
Expression.quotedLiteral(fk.columnName)
|
|
96
|
+
),
|
|
97
|
+
Expression.member(
|
|
98
|
+
select.as,
|
|
99
|
+
Expression.quotedLiteral(model.keys[0].columnName)
|
|
100
|
+
)
|
|
101
|
+
);
|
|
102
|
+
// load parent..
|
|
103
|
+
where = parent.where
|
|
104
|
+
? Expression.logicalAnd(joinWhere, parent.where)
|
|
105
|
+
: joinWhere;
|
|
106
|
+
|
|
107
|
+
const joins = (select.joins ??= []);
|
|
108
|
+
joins.push(JoinExpression.create({
|
|
109
|
+
source: parent.source as TableSource,
|
|
110
|
+
as: parent.as,
|
|
111
|
+
model,
|
|
112
|
+
where
|
|
113
|
+
}));
|
|
114
|
+
(this.select.include ??= []).push(select);
|
|
115
|
+
return [select, relation.relatedEntity];
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
joinWhere = Expression.equal(
|
|
119
|
+
Expression.member(
|
|
120
|
+
parent.as,
|
|
121
|
+
Expression.quotedLiteral(fk.columnName)
|
|
122
|
+
),
|
|
123
|
+
Expression.member(
|
|
124
|
+
select.as,
|
|
125
|
+
Expression.quotedLiteral(relation.relatedEntity.keys[0].columnName)
|
|
126
|
+
)
|
|
127
|
+
);
|
|
128
|
+
|
|
129
|
+
parent = { ... parent };
|
|
130
|
+
|
|
131
|
+
parent.where = parent.where
|
|
132
|
+
? Expression.logicalAnd(parent.where, joinWhere)
|
|
133
|
+
: joinWhere;
|
|
134
|
+
|
|
135
|
+
const existsWhere = ExistsExpression.create({
|
|
136
|
+
target: parent
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
select.where = select.where
|
|
140
|
+
? Expression.logicalAnd(select.where, existsWhere)
|
|
141
|
+
: existsWhere;
|
|
142
|
+
|
|
143
|
+
(this.select.include ??= []).push(select);
|
|
144
|
+
|
|
145
|
+
return [select, relation.relatedEntity];
|
|
146
|
+
}
|
|
147
|
+
}
|