@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,27 +1,40 @@
|
|
|
1
1
|
import { parseExpression } from "@babel/parser";
|
|
2
|
-
import { ArrowFunctionExpression, BinaryExpression, CallExpression, CoalesceExpression, Constant, Expression, Identifier, MemberExpression, NullExpression, NumberLiteral, StringLiteral, TemplateLiteral } from "../ast/Expressions.js";
|
|
2
|
+
import { ArrowFunctionExpression, BinaryExpression, CallExpression, CoalesceExpression, ConditionalExpression, Constant, Expression, ExpressionAs, Identifier, MemberExpression, NewObjectExpression, NullExpression, NumberLiteral, ParameterExpression, QuotedLiteral, StringLiteral, TemplateLiteral } from "../ast/Expressions.js";
|
|
3
3
|
import { BabelVisitor } from "./BabelVisitor.js";
|
|
4
4
|
import * as bpe from "@babel/types";
|
|
5
|
-
import
|
|
6
|
-
import {
|
|
5
|
+
import Restructure from "./Restructure.js";
|
|
6
|
+
import { NotSupportedError } from "./NotSupportedError.js";
|
|
7
|
+
import TimedCache from "../../common/cache/TimedCache.js";
|
|
7
8
|
|
|
8
9
|
type IQueryFragment = string | { name?: string, value?: any };
|
|
9
|
-
|
|
10
|
+
|
|
11
|
+
const parsedCache = new TimedCache<string, bpe.Node>();
|
|
10
12
|
|
|
11
13
|
export default class ArrowToExpression extends BabelVisitor<Expression> {
|
|
12
14
|
|
|
13
|
-
public static transform(fx: (p: any) => (x: any) => any) {
|
|
14
|
-
const
|
|
15
|
+
public static transform(fx: (p: any) => (x: any) => any, target?: ParameterExpression) {
|
|
16
|
+
const key = fx.toString();
|
|
17
|
+
const node = parsedCache.getOrCreate(key, fx, (k, f) => {
|
|
18
|
+
const rs = new Restructure();
|
|
19
|
+
return rs.visit(parseExpression(f.toString()));
|
|
20
|
+
});
|
|
21
|
+
return this.transformUncached(node, target);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
private static transformUncached(node: bpe.Node, target?: ParameterExpression) {
|
|
25
|
+
|
|
15
26
|
if (node.type !== "ArrowFunctionExpression") {
|
|
16
27
|
throw new Error("Expecting an arrow function");
|
|
17
28
|
}
|
|
18
29
|
|
|
19
|
-
const
|
|
20
|
-
if (firstParam.type !== "Identifier") {
|
|
21
|
-
throw new Error("Expecting an identifier");
|
|
22
|
-
}
|
|
30
|
+
const params = [] as ParameterExpression[];
|
|
23
31
|
|
|
24
|
-
const
|
|
32
|
+
for (const iterator of node.params) {
|
|
33
|
+
if (iterator.type !== "Identifier") {
|
|
34
|
+
throw new Error("Expecting an identifier");
|
|
35
|
+
}
|
|
36
|
+
params.push(ParameterExpression.create({ name: iterator.name }));
|
|
37
|
+
}
|
|
25
38
|
|
|
26
39
|
let body = node.body;
|
|
27
40
|
if (body.type !== "ArrowFunctionExpression") {
|
|
@@ -33,13 +46,13 @@ export default class ArrowToExpression extends BabelVisitor<Expression> {
|
|
|
33
46
|
throw new Error("Expecting an identifier");
|
|
34
47
|
}
|
|
35
48
|
|
|
36
|
-
|
|
49
|
+
target ??= ParameterExpression.create({ name: firstTarget.name});
|
|
37
50
|
|
|
38
51
|
body = body.body;
|
|
39
52
|
|
|
40
|
-
const visitor = new this(
|
|
53
|
+
const visitor = new this(params, target, firstTarget.name);
|
|
41
54
|
return {
|
|
42
|
-
|
|
55
|
+
params,
|
|
43
56
|
target,
|
|
44
57
|
body: visitor.visit(body)
|
|
45
58
|
};
|
|
@@ -47,11 +60,24 @@ export default class ArrowToExpression extends BabelVisitor<Expression> {
|
|
|
47
60
|
|
|
48
61
|
public readonly leftJoins: string[] = [];
|
|
49
62
|
|
|
63
|
+
private targetStack: Map<any,any> = new Map();
|
|
64
|
+
|
|
50
65
|
protected constructor(
|
|
51
|
-
public
|
|
52
|
-
public target:
|
|
66
|
+
public params: ParameterExpression[],
|
|
67
|
+
public target: ParameterExpression,
|
|
68
|
+
targetName: string
|
|
53
69
|
) {
|
|
54
70
|
super();
|
|
71
|
+
this.targetStack.set("Sql", "Sql");
|
|
72
|
+
for (const iterator of params) {
|
|
73
|
+
this.targetStack.set(iterator.name, iterator);
|
|
74
|
+
}
|
|
75
|
+
if (targetName) {
|
|
76
|
+
this.targetStack.set(targetName, target);
|
|
77
|
+
}
|
|
78
|
+
if (target?.name) {
|
|
79
|
+
this.targetStack.set(target.name, target);
|
|
80
|
+
}
|
|
55
81
|
}
|
|
56
82
|
|
|
57
83
|
visitBigIntLiteral({ value }: bpe.BigIntLiteral) {
|
|
@@ -79,10 +105,24 @@ export default class ArrowToExpression extends BabelVisitor<Expression> {
|
|
|
79
105
|
}
|
|
80
106
|
|
|
81
107
|
visitTemplateLiteral(node: bpe.TemplateLiteral) {
|
|
82
|
-
const value = node.expressions.map((x) => this.visit(x));
|
|
108
|
+
// const value = node.expressions.map((x) => this.visit(x));
|
|
109
|
+
const value = [] as Expression[];
|
|
110
|
+
for (let index = 0; index < node.quasis.length; index++) {
|
|
111
|
+
const { value: { cooked }} = node.quasis[index];
|
|
112
|
+
if (cooked) {
|
|
113
|
+
value.push(StringLiteral.create({ value: cooked }));
|
|
114
|
+
}
|
|
115
|
+
if (index < node.expressions.length) {
|
|
116
|
+
value.push(this.visit(node.expressions[index]));
|
|
117
|
+
}
|
|
118
|
+
}
|
|
83
119
|
return TemplateLiteral.create({ value });
|
|
84
120
|
}
|
|
85
121
|
|
|
122
|
+
visitTemplateElement(node: bpe.TemplateElement): Expression {
|
|
123
|
+
throw new NotSupportedError();
|
|
124
|
+
}
|
|
125
|
+
|
|
86
126
|
visitLogicalExpression(node: bpe.LogicalExpression): Expression {
|
|
87
127
|
const left = this.visit(node.left);
|
|
88
128
|
const right = this.visit(node.right);
|
|
@@ -100,6 +140,10 @@ export default class ArrowToExpression extends BabelVisitor<Expression> {
|
|
|
100
140
|
return BinaryExpression.create({ left, operator, right });
|
|
101
141
|
}
|
|
102
142
|
|
|
143
|
+
visitArrayExpression(node: bpe.ArrayExpression): Expression {
|
|
144
|
+
return Expression.array(node.elements.map((e) => this.visit(e)));
|
|
145
|
+
}
|
|
146
|
+
|
|
103
147
|
visitBinaryExpression(node: bpe.BinaryExpression) {
|
|
104
148
|
let operator = node.operator as string;
|
|
105
149
|
switch(node.operator) {
|
|
@@ -122,7 +166,7 @@ export default class ArrowToExpression extends BabelVisitor<Expression> {
|
|
|
122
166
|
operator = "=";
|
|
123
167
|
break;
|
|
124
168
|
default:
|
|
125
|
-
throw new
|
|
169
|
+
throw new NotSupportedError(`Operator ${operator}`);
|
|
126
170
|
}
|
|
127
171
|
const left = this.visit(node.left);
|
|
128
172
|
const right = this.visit(node.right);
|
|
@@ -130,6 +174,10 @@ export default class ArrowToExpression extends BabelVisitor<Expression> {
|
|
|
130
174
|
}
|
|
131
175
|
|
|
132
176
|
visitCallExpression({ callee, arguments: args }: bpe.CallExpression) {
|
|
177
|
+
|
|
178
|
+
// we need to sanitize callee
|
|
179
|
+
this.sanitize(callee);
|
|
180
|
+
|
|
133
181
|
return CallExpression.create({
|
|
134
182
|
callee: callee ? this.visit(callee) : void 0,
|
|
135
183
|
arguments: args ? args.map((x) => this.visit(x)) : []
|
|
@@ -137,6 +185,10 @@ export default class ArrowToExpression extends BabelVisitor<Expression> {
|
|
|
137
185
|
}
|
|
138
186
|
|
|
139
187
|
visitIdentifier({ name: value }: bpe.Identifier): Expression {
|
|
188
|
+
const scopedName = this.targetStack.get(value);
|
|
189
|
+
if (typeof scopedName === "object") {
|
|
190
|
+
return scopedName;
|
|
191
|
+
}
|
|
140
192
|
return Identifier.create({ value });
|
|
141
193
|
}
|
|
142
194
|
|
|
@@ -150,11 +202,74 @@ export default class ArrowToExpression extends BabelVisitor<Expression> {
|
|
|
150
202
|
}
|
|
151
203
|
|
|
152
204
|
visitArrowFunctionExpression(node: bpe.ArrowFunctionExpression): Expression {
|
|
153
|
-
const params =
|
|
205
|
+
const params = [] as ParameterExpression[];
|
|
206
|
+
const names = [];
|
|
207
|
+
for (const iterator of node.params) {
|
|
208
|
+
if (iterator.type !== "Identifier") {
|
|
209
|
+
throw new NotSupportedError();
|
|
210
|
+
}
|
|
211
|
+
names.push(iterator.name);
|
|
212
|
+
const p = ParameterExpression.create({ name: iterator.name });
|
|
213
|
+
this.targetStack.set(iterator.name, p);
|
|
214
|
+
params.push(p);
|
|
215
|
+
}
|
|
154
216
|
const body = this.visit(node.body);
|
|
217
|
+
for (const name of names) {
|
|
218
|
+
this.targetStack.delete(name);
|
|
219
|
+
}
|
|
155
220
|
return ArrowFunctionExpression.create({
|
|
156
221
|
params,
|
|
157
222
|
body
|
|
158
223
|
});
|
|
159
224
|
}
|
|
225
|
+
|
|
226
|
+
visitObjectExpression(node: bpe.ObjectExpression): Expression {
|
|
227
|
+
const properties = [] as ExpressionAs[];
|
|
228
|
+
for (const iterator of node.properties) {
|
|
229
|
+
switch(iterator.type) {
|
|
230
|
+
case "ObjectProperty":
|
|
231
|
+
switch(iterator.key.type) {
|
|
232
|
+
case "Identifier":
|
|
233
|
+
properties.push( ExpressionAs.create({
|
|
234
|
+
alias: QuotedLiteral.create({ literal: iterator.key.name}),
|
|
235
|
+
expression: this.visit(iterator.value)
|
|
236
|
+
}) );
|
|
237
|
+
break;
|
|
238
|
+
default:
|
|
239
|
+
throw new NotSupportedError();
|
|
240
|
+
}
|
|
241
|
+
continue;
|
|
242
|
+
default:
|
|
243
|
+
throw new NotSupportedError();
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
return NewObjectExpression.create({
|
|
247
|
+
properties
|
|
248
|
+
});
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
visitConditionalExpression(node: bpe.ConditionalExpression): Expression {
|
|
252
|
+
return ConditionalExpression.create({
|
|
253
|
+
test: this.visit(node.test),
|
|
254
|
+
consequent: this.visit(node.consequent),
|
|
255
|
+
alternate: this.visit(node.alternate)
|
|
256
|
+
});
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
private sanitize(node: bpe.Expression | bpe.V8IntrinsicIdentifier) {
|
|
260
|
+
switch(node.type) {
|
|
261
|
+
case "Identifier":
|
|
262
|
+
const name = node.name;
|
|
263
|
+
const scopedName = this.targetStack.get(name);
|
|
264
|
+
if (scopedName === null || scopedName === void 0) {
|
|
265
|
+
throw new Error(`Unknown identifier ${name}`);
|
|
266
|
+
}
|
|
267
|
+
return;
|
|
268
|
+
case "MemberExpression":
|
|
269
|
+
case "OptionalMemberExpression":
|
|
270
|
+
return this.sanitize(node.object);
|
|
271
|
+
}
|
|
272
|
+
throw new Error(`Unexpected expression type ${node.type}`);
|
|
273
|
+
}
|
|
274
|
+
|
|
160
275
|
}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import * as bp from "@babel/parser";
|
|
2
2
|
import * as bpe from "@babel/types";
|
|
3
3
|
|
|
4
|
-
export class BabelVisitor<T> {
|
|
5
|
-
visit(
|
|
6
|
-
const node = nodeAny as bpe.Expression;
|
|
4
|
+
export abstract class BabelVisitor<T> {
|
|
5
|
+
visit(node: bpe.Expression | bpe.Node): T {
|
|
6
|
+
// const node = nodeAny as bpe.Expression;
|
|
7
7
|
switch (node.type) {
|
|
8
8
|
case "BinaryExpression":
|
|
9
9
|
return this.visitBinaryExpression(node);
|
|
@@ -33,54 +33,42 @@ export class BabelVisitor<T> {
|
|
|
33
33
|
return this.visitIdentifier(node);
|
|
34
34
|
case "MemberExpression":
|
|
35
35
|
return this.visitMemberExpression(node);
|
|
36
|
+
case "ObjectExpression":
|
|
37
|
+
return this.visitObjectExpression(node);
|
|
38
|
+
case "ObjectProperty":
|
|
39
|
+
return this.visitObjectProperty(node);
|
|
40
|
+
case "TemplateElement":
|
|
41
|
+
return this.visitTemplateElement(node);
|
|
42
|
+
case "ArrayExpression":
|
|
43
|
+
return this.visitArrayExpression(node);
|
|
36
44
|
case "RegExpLiteral":
|
|
37
45
|
default:
|
|
38
46
|
throw new Error(`Translation from ${node.type} not supported`);
|
|
39
47
|
}
|
|
40
48
|
}
|
|
41
|
-
|
|
42
|
-
|
|
49
|
+
visitArrayExpression(node: bpe.ArrayExpression): T {
|
|
50
|
+
throw new Error("Method not implemented.");
|
|
43
51
|
}
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
visitIdentifier(node: bpe.Identifier): T {
|
|
48
|
-
return;
|
|
49
|
-
}
|
|
50
|
-
visitTemplateLiteral(node: bpe.TemplateLiteral): T {
|
|
51
|
-
return;
|
|
52
|
-
}
|
|
53
|
-
visitNumericLiteral(node: bpe.NumericLiteral): T {
|
|
54
|
-
return;
|
|
55
|
-
}
|
|
56
|
-
visitDecimalLiteral(node: bpe.DecimalLiteral): T {
|
|
57
|
-
return;
|
|
58
|
-
}
|
|
59
|
-
visitBooleanLiteral(node: bpe.BooleanLiteral): T {
|
|
60
|
-
return;
|
|
61
|
-
}
|
|
62
|
-
visitBigIntLiteral(node: bpe.BigIntLiteral): T {
|
|
63
|
-
return;
|
|
64
|
-
}
|
|
65
|
-
visitStringLiteral(node: bpe.StringLiteral): T {
|
|
66
|
-
return;
|
|
67
|
-
}
|
|
68
|
-
visitNullLiteral(node: bpe.NullLiteral): T {
|
|
69
|
-
return;
|
|
70
|
-
}
|
|
71
|
-
visitConditionalExpression(node: bpe.ConditionalExpression): T {
|
|
72
|
-
return;
|
|
52
|
+
abstract visitTemplateElement(node: bpe.TemplateElement): T;
|
|
53
|
+
visitObjectProperty(node: bpe.ObjectProperty): T {
|
|
54
|
+
throw new Error("Method not implemented.");
|
|
73
55
|
}
|
|
56
|
+
abstract visitObjectExpression(node: bpe.ObjectExpression): T;
|
|
57
|
+
abstract visitMemberExpression(node: bpe.MemberExpression): T;
|
|
58
|
+
abstract visitLogicalExpression(node: bpe.LogicalExpression): T;
|
|
59
|
+
abstract visitIdentifier(node: bpe.Identifier): T;
|
|
60
|
+
abstract visitTemplateLiteral(node: bpe.TemplateLiteral): T;
|
|
61
|
+
abstract visitNumericLiteral(node: bpe.NumericLiteral): T;
|
|
62
|
+
abstract visitDecimalLiteral(node: bpe.DecimalLiteral): T;
|
|
63
|
+
abstract visitBooleanLiteral(node: bpe.BooleanLiteral): T;
|
|
64
|
+
abstract visitBigIntLiteral(node: bpe.BigIntLiteral): T;
|
|
65
|
+
abstract visitStringLiteral(node: bpe.StringLiteral): T;
|
|
66
|
+
abstract visitNullLiteral(node: bpe.NullLiteral): T;
|
|
67
|
+
abstract visitConditionalExpression(node: bpe.ConditionalExpression): T;
|
|
74
68
|
|
|
75
|
-
visitArrowFunctionExpression(node: bpe.ArrowFunctionExpression): T
|
|
76
|
-
return;
|
|
77
|
-
}
|
|
69
|
+
abstract visitArrowFunctionExpression(node: bpe.ArrowFunctionExpression): T;
|
|
78
70
|
|
|
79
|
-
visitCallExpression(node: bpe.CallExpression): T
|
|
80
|
-
return;
|
|
81
|
-
}
|
|
71
|
+
abstract visitCallExpression(node: bpe.CallExpression): T;
|
|
82
72
|
|
|
83
|
-
visitBinaryExpression(node: bpe.BinaryExpression): T
|
|
84
|
-
return;
|
|
85
|
-
}
|
|
73
|
+
abstract visitBinaryExpression(node: bpe.BinaryExpression): T;
|
|
86
74
|
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import * as bpe from "@babel/types";
|
|
2
|
+
import { BabelVisitor } from "./BabelVisitor.js";
|
|
3
|
+
import { NotSupportedError } from "./NotSupportedError.js";
|
|
4
|
+
import TransformVisitor from "./TransformVisitor.js";
|
|
5
|
+
|
|
6
|
+
export default class Restructure extends TransformVisitor {
|
|
7
|
+
|
|
8
|
+
private map: Map<string, bpe.Node> = new Map();
|
|
9
|
+
|
|
10
|
+
visitTemplateElement(node: bpe.TemplateElement): bpe.Node {
|
|
11
|
+
return node;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
visitArrowFunctionExpression(node: bpe.ArrowFunctionExpression): bpe.Node {
|
|
15
|
+
|
|
16
|
+
// we need to restructure identifiers from destructure
|
|
17
|
+
|
|
18
|
+
node.params = node.params.map((x) => this.toIdentifier(x));
|
|
19
|
+
|
|
20
|
+
node.body = this.visit(node.body) as bpe.Expression;
|
|
21
|
+
|
|
22
|
+
return node;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
visitIdentifier(node: bpe.Identifier): bpe.Node {
|
|
26
|
+
return this.map.get(node.name) ?? node;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
toIdentifier(x: bpe.Identifier | bpe.Pattern | bpe.RestElement): bpe.Identifier {
|
|
30
|
+
switch(x.type) {
|
|
31
|
+
case "Identifier":
|
|
32
|
+
return x;
|
|
33
|
+
case "ObjectPattern":
|
|
34
|
+
const id = `x${this.map.size + 1}`;
|
|
35
|
+
const idExp = bpe.identifier(id);
|
|
36
|
+
this.convertPattern(idExp, x);
|
|
37
|
+
return idExp;
|
|
38
|
+
case "ArrayPattern":
|
|
39
|
+
case "AssignmentPattern":
|
|
40
|
+
case "RestElement":
|
|
41
|
+
throw new NotSupportedError();
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
convertPattern(parentExp: bpe.Expression, x: bpe.ObjectPattern) {
|
|
46
|
+
for (const iterator of x.properties) {
|
|
47
|
+
switch(iterator.type) {
|
|
48
|
+
case "RestElement":
|
|
49
|
+
throw new NotSupportedError();
|
|
50
|
+
case "ObjectProperty":
|
|
51
|
+
switch(iterator.key.type){
|
|
52
|
+
case "Identifier":
|
|
53
|
+
const childExp = bpe.memberExpression(parentExp, iterator.key);
|
|
54
|
+
this.map.set(iterator.key.name, childExp);
|
|
55
|
+
switch(iterator.value.type) {
|
|
56
|
+
case "ObjectPattern":
|
|
57
|
+
this.convertPattern(childExp, iterator.value);
|
|
58
|
+
break;
|
|
59
|
+
}
|
|
60
|
+
break;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import * as bpe from "@babel/types";
|
|
2
|
+
import { BabelVisitor } from "./BabelVisitor.js";
|
|
3
|
+
|
|
4
|
+
export default class TransformVisitor extends BabelVisitor<bpe.Node> {
|
|
5
|
+
|
|
6
|
+
transform<T>(n:T) {
|
|
7
|
+
if (!n) {
|
|
8
|
+
return n;
|
|
9
|
+
}
|
|
10
|
+
if (Array.isArray(n)) {
|
|
11
|
+
return n.map((x) => this.transform(x));
|
|
12
|
+
}
|
|
13
|
+
return this.visit(n as any);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
visitTemplateElement(node: bpe.TemplateElement): bpe.Node {
|
|
17
|
+
return node;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
visitObjectExpression(node: bpe.ObjectExpression): bpe.Node {
|
|
21
|
+
return bpe.objectExpression(
|
|
22
|
+
this.transform(node.properties)
|
|
23
|
+
);
|
|
24
|
+
}
|
|
25
|
+
visitMemberExpression(node: bpe.MemberExpression): bpe.Node {
|
|
26
|
+
return bpe.memberExpression(this.transform(node.object), this.transform(node.property), node.computed, node.optional);
|
|
27
|
+
}
|
|
28
|
+
visitLogicalExpression(node: bpe.LogicalExpression): bpe.Node {
|
|
29
|
+
return bpe.logicalExpression(node.operator, this.transform(node.left), this.transform(node.right));
|
|
30
|
+
}
|
|
31
|
+
visitIdentifier(node: bpe.Identifier): bpe.Node {
|
|
32
|
+
return node;
|
|
33
|
+
}
|
|
34
|
+
visitTemplateLiteral(node: bpe.TemplateLiteral): bpe.Node {
|
|
35
|
+
return bpe.templateLiteral(this.transform(node.quasis), this.transform(node.expressions));
|
|
36
|
+
}
|
|
37
|
+
visitNumericLiteral(node: bpe.NumericLiteral): bpe.Node {
|
|
38
|
+
return node;
|
|
39
|
+
}
|
|
40
|
+
visitDecimalLiteral(node: bpe.DecimalLiteral): bpe.Node {
|
|
41
|
+
return node;
|
|
42
|
+
}
|
|
43
|
+
visitBooleanLiteral(node: bpe.BooleanLiteral): bpe.Node {
|
|
44
|
+
return node;
|
|
45
|
+
}
|
|
46
|
+
visitBigIntLiteral(node: bpe.BigIntLiteral): bpe.Node {
|
|
47
|
+
return node;
|
|
48
|
+
}
|
|
49
|
+
visitStringLiteral(node: bpe.StringLiteral): bpe.Node {
|
|
50
|
+
return node;
|
|
51
|
+
}
|
|
52
|
+
visitNullLiteral(node: bpe.NullLiteral): bpe.Node {
|
|
53
|
+
return node;
|
|
54
|
+
}
|
|
55
|
+
visitConditionalExpression(node: bpe.ConditionalExpression): bpe.Node {
|
|
56
|
+
return bpe.conditionalExpression(
|
|
57
|
+
this.transform(node.test),
|
|
58
|
+
this.transform(node.consequent),
|
|
59
|
+
this.transform(node.alternate));
|
|
60
|
+
}
|
|
61
|
+
visitArrowFunctionExpression(node: bpe.ArrowFunctionExpression): bpe.Node {
|
|
62
|
+
return bpe.arrowFunctionExpression(this.transform(node.params), this.transform(node.body), node.async);
|
|
63
|
+
}
|
|
64
|
+
visitCallExpression(node: bpe.CallExpression): bpe.Node {
|
|
65
|
+
return bpe.callExpression(this.transform(node.callee), this.transform(node.arguments));
|
|
66
|
+
}
|
|
67
|
+
visitBinaryExpression(node: bpe.BinaryExpression): bpe.Node {
|
|
68
|
+
return bpe.binaryExpression(node.operator, this.transform(node.left), this.transform(node.right));
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
visitObjectProperty(node: bpe.ObjectProperty): bpe.Node {
|
|
72
|
+
|
|
73
|
+
let key = node.key;
|
|
74
|
+
if (node.key.type !== "Identifier") {
|
|
75
|
+
key = this.transform(key);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const value = this.transform(node.value);
|
|
79
|
+
|
|
80
|
+
return bpe.objectProperty(key, value, node.computed, node.shorthand, node.decorators);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
}
|
package/src/sql/ISql.ts
CHANGED
|
@@ -2,6 +2,16 @@ export interface ISql {
|
|
|
2
2
|
|
|
3
3
|
in<T>(a: T, array: T[]): boolean;
|
|
4
4
|
|
|
5
|
+
cast: {
|
|
6
|
+
asNumber(a: any): number;
|
|
7
|
+
asInteger(a: any): number;
|
|
8
|
+
asBigInt(a: any): number;
|
|
9
|
+
asText(a: any): string;
|
|
10
|
+
asDate(a: any): Date;
|
|
11
|
+
asDateTime(a: any): Date;
|
|
12
|
+
asDecimal(a: any): number;
|
|
13
|
+
}
|
|
14
|
+
|
|
5
15
|
text: {
|
|
6
16
|
concat(... fragments: string[]): string;
|
|
7
17
|
like(text: string, test: string): boolean;
|
|
@@ -34,4 +34,16 @@ export default async function(this: TestConfig) {
|
|
|
34
34
|
allHeadphones = await context.products.where({ headPhoneCategory }, (p) => (x) => x.categories.some((pc) => pc.categoryID === p.headPhoneCategory)).count();
|
|
35
35
|
|
|
36
36
|
assert.equal(6, allHeadphones);
|
|
37
|
+
|
|
38
|
+
const first = await context.productCategories.where({ headPhoneCategory }, (p) => (x) => x.category.categoryID === p.headPhoneCategory).first();
|
|
39
|
+
|
|
40
|
+
// delete first one...
|
|
41
|
+
context.products.delete(first);
|
|
42
|
+
|
|
43
|
+
await context.saveChanges();
|
|
44
|
+
|
|
45
|
+
allHeadphones = await context.products.where({ headPhoneCategory }, (p) => (x) => x.categories.some((pc) => pc.categoryID === p.headPhoneCategory)).count();
|
|
46
|
+
|
|
47
|
+
assert.equal(5, allHeadphones);
|
|
48
|
+
|
|
37
49
|
}
|
|
@@ -5,48 +5,64 @@ import { assertSqlMatch, trimInternal } from "../trimInternal.js";
|
|
|
5
5
|
import PostgreSqlDriver from "../../../drivers/postgres/PostgreSqlDriver.js";
|
|
6
6
|
|
|
7
7
|
const sql1 = `SELECT
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
("P1"."productID" = "O0"."productID")
|
|
18
|
-
AND ("O0"."productID" = $2)
|
|
19
|
-
)
|
|
20
|
-
`;
|
|
8
|
+
"p1"."productID",
|
|
9
|
+
"p1"."name",
|
|
10
|
+
"p1"."ownerID",
|
|
11
|
+
"p1"."status"
|
|
12
|
+
FROM "Products" AS "p1"
|
|
13
|
+
WHERE EXISTS (SELECT
|
|
14
|
+
1
|
|
15
|
+
FROM "OrderItems" AS "o"
|
|
16
|
+
WHERE ("p1"."productID" = "o"."productID") AND ("o"."productID" = $1))`;
|
|
21
17
|
|
|
22
18
|
const sql2 = `SELECT
|
|
23
|
-
"
|
|
24
|
-
|
|
19
|
+
"p1"."productID",
|
|
20
|
+
"p1"."name",
|
|
21
|
+
"p1"."ownerID",
|
|
22
|
+
"p1"."status"
|
|
23
|
+
FROM "Products" AS "p1"
|
|
25
24
|
WHERE EXISTS (SELECT
|
|
26
|
-
|
|
27
|
-
FROM "OrderItems" AS "
|
|
28
|
-
WHERE ("
|
|
29
|
-
|
|
30
|
-
FROM "OrderItems" AS "
|
|
31
|
-
WHERE ("
|
|
25
|
+
1
|
|
26
|
+
FROM "OrderItems" AS "o"
|
|
27
|
+
WHERE ("p1"."productID" = "o"."productID") AND ("o"."productID" = $1)) AND EXISTS (SELECT
|
|
28
|
+
1
|
|
29
|
+
FROM "OrderItems" AS "o1"
|
|
30
|
+
WHERE ("p1"."productID" = "o1"."productID") AND ("o1"."amount" > $2))`;
|
|
32
31
|
|
|
33
32
|
const sql3 = `SELECT
|
|
34
|
-
"
|
|
35
|
-
|
|
33
|
+
"p1"."productID",
|
|
34
|
+
"p1"."name",
|
|
35
|
+
"p1"."ownerID",
|
|
36
|
+
"p1"."status"
|
|
37
|
+
FROM "Products" AS "p1"
|
|
36
38
|
WHERE EXISTS (SELECT
|
|
37
|
-
|
|
38
|
-
FROM "OrderItems" AS "
|
|
39
|
-
WHERE ("
|
|
40
|
-
|
|
41
|
-
FROM "OrderItems" AS "
|
|
42
|
-
INNER JOIN "Orders" AS "
|
|
43
|
-
WHERE ("
|
|
39
|
+
1
|
|
40
|
+
FROM "OrderItems" AS "o"
|
|
41
|
+
WHERE ("p1"."productID" = "o"."productID") AND ("o"."productID" = $1)) AND EXISTS (SELECT
|
|
42
|
+
1
|
|
43
|
+
FROM "OrderItems" AS "o1"
|
|
44
|
+
INNER JOIN "Orders" AS "o2" ON "o1"."orderID" = "o2"."orderID"
|
|
45
|
+
WHERE ("p1"."productID" = "o1"."productID") AND ("o2"."orderDate" > $2))`;
|
|
44
46
|
|
|
45
47
|
const productJoin = `SELECT
|
|
46
|
-
"
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
48
|
+
"p1"."productID",
|
|
49
|
+
"p1"."name",
|
|
50
|
+
"p1"."ownerID",
|
|
51
|
+
"p1"."status"
|
|
52
|
+
FROM "Products" AS "p1"
|
|
53
|
+
LEFT JOIN "Users" AS "u" ON "p1"."ownerID" = "u"."userID"
|
|
54
|
+
WHERE "u"."dateCreated" > $1`;
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
const join2 = `SELECT
|
|
58
|
+
"o1"."orderItemID",
|
|
59
|
+
"o1"."orderID",
|
|
60
|
+
"o1"."productID",
|
|
61
|
+
"o1"."priceID",
|
|
62
|
+
"o1"."amount"
|
|
63
|
+
FROM "OrderItems" AS "o1"
|
|
64
|
+
INNER JOIN "Products" AS "p" ON "o1"."productID" = "p"."productID"
|
|
65
|
+
WHERE ("o1"."productID" = $1) OR ("p"."ownerID" = $2)`;
|
|
50
66
|
|
|
51
67
|
export default function() {
|
|
52
68
|
|
|
@@ -68,4 +84,8 @@ export default function() {
|
|
|
68
84
|
query = context.products.where({ date: new Date()}, (p) => (x) => x.owner.dateCreated > p.date);
|
|
69
85
|
r = query.toQuery();
|
|
70
86
|
assertSqlMatch(productJoin, r.text);
|
|
87
|
+
|
|
88
|
+
const q = context.orderItems.where({ o: 1, owner: 1}, (p) => (x) => x.productID === p.o || x.product.ownerID === p.owner);
|
|
89
|
+
r = q.toQuery();
|
|
90
|
+
assertSqlMatch(join2, r.text);
|
|
71
91
|
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import assert from "assert";
|
|
2
|
+
import QueryCompiler from "../../../compiler/QueryCompiler.js";
|
|
3
|
+
|
|
4
|
+
declare let pg_kill: any;
|
|
5
|
+
|
|
6
|
+
export default function () {
|
|
7
|
+
|
|
8
|
+
const compiler = QueryCompiler.instance;
|
|
9
|
+
|
|
10
|
+
const name = "Akash";
|
|
11
|
+
|
|
12
|
+
assert.throws(()=>
|
|
13
|
+
compiler.execute({ name }, (p) => (x) => pg_kill(9))
|
|
14
|
+
);
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import assert from "assert";
|
|
2
|
+
import QueryCompiler from "../../../compiler/QueryCompiler.js";
|
|
3
|
+
import ArrowToExpression from "../../../query/parser/ArrowToExpression.js";
|
|
4
|
+
import { ExpressionAs, Identifier, MemberExpression, NewObjectExpression, QuotedLiteral } from "../../../query/ast/Expressions.js";
|
|
5
|
+
import ExpressionToSql from "../../../query/ast/ExpressionToSql.js";
|
|
6
|
+
|
|
7
|
+
type ICustomer = { firstName: string; lastName: string; emailAddress: string; birthDate: Date };
|
|
8
|
+
|
|
9
|
+
export default function() {
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
const compiler = QueryCompiler.instance;
|
|
13
|
+
|
|
14
|
+
const name = "Akash";
|
|
15
|
+
|
|
16
|
+
let r = compiler.execute({ name }, (p) => ({ firstName, lastName, emailAddress }: ICustomer) => ({ emailAddress, name: `${firstName} ${lastName}` }));
|
|
17
|
+
|
|
18
|
+
assert.strictEqual(`FROM ("x1"."emailAddress" AS "emailAddress",CONCAT("x1"."firstName",$1,"x1"."lastName") AS "name")`, r.text);
|
|
19
|
+
|
|
20
|
+
r = compiler.execute({ name }, (p) => ({ id }) => ({ error: `${id > 0 ? "Error" : ""}` }));
|
|
21
|
+
|
|
22
|
+
assert.strictEqual(`FROM (CONCAT((CASE WHEN "x1"."id" > $1 THEN $2 ELSE $3 END)) AS "error")`, r.text);
|
|
23
|
+
|
|
24
|
+
}
|