@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.
Files changed (69) hide show
  1. package/.github/workflows/node.yml +7 -2
  2. package/.vscode/settings.json +1 -0
  3. package/README.md +57 -27
  4. package/package.json +2 -2
  5. package/src/common/EntityAccessError.ts +10 -0
  6. package/src/common/IDisposable.ts +25 -0
  7. package/src/common/ImmutableObject.ts +53 -0
  8. package/src/common/Logger.ts +59 -0
  9. package/src/common/TypeInfo.ts +3 -0
  10. package/src/common/cache/TimedCache.ts +2 -2
  11. package/src/common/usingAsync.ts +42 -12
  12. package/src/compiler/QueryCompiler.ts +28 -30
  13. package/src/compiler/postgres/PostgreSqlMethodTransformer.ts +23 -0
  14. package/src/compiler/sql-server/SqlServerSqlMethodTransformer.ts +23 -0
  15. package/src/decorators/ForeignKey.ts +1 -1
  16. package/src/decorators/IClassOf.ts +2 -1
  17. package/src/decorators/IColumn.ts +2 -0
  18. package/src/decorators/parser/NameParser.ts +15 -0
  19. package/src/di/di.ts +224 -0
  20. package/src/drivers/base/BaseDriver.ts +28 -3
  21. package/src/drivers/sql-server/ExpressionToSqlServer.ts +34 -9
  22. package/src/drivers/sql-server/SqlServerDriver.ts +7 -16
  23. package/src/entity-query/EntityType.ts +45 -3
  24. package/src/model/EntityContext.ts +167 -22
  25. package/src/model/EntityQuery.ts +118 -59
  26. package/src/model/EntitySource.ts +38 -46
  27. package/src/model/IFilterWithParameter.ts +5 -0
  28. package/src/model/SourceExpression.ts +21 -25
  29. package/src/model/{ChangeEntry.ts → changes/ChangeEntry.ts} +35 -5
  30. package/src/model/{ChangeSet.ts → changes/ChangeSet.ts} +16 -11
  31. package/src/model/events/ContextEvents.ts +26 -0
  32. package/src/model/events/EntityEvents.ts +96 -0
  33. package/src/model/{IdentityService.ts → identity/IdentityService.ts} +9 -1
  34. package/src/model/identity/RelationMapper.ts +71 -0
  35. package/src/model/symbols.ts +1 -0
  36. package/src/model/verification/VerificationSession.ts +173 -0
  37. package/src/query/ast/DebugStringVisitor.ts +175 -0
  38. package/src/query/ast/ExpressionToSql.ts +277 -119
  39. package/src/query/ast/Expressions.ts +130 -13
  40. package/src/query/ast/IStringTransformer.ts +19 -5
  41. package/src/query/ast/ParameterScope.ts +97 -0
  42. package/src/query/ast/ReplaceParameter.ts +40 -0
  43. package/src/query/ast/Types.ts +0 -0
  44. package/src/query/ast/Visitor.ts +26 -5
  45. package/src/query/expander/QueryExpander.ts +147 -0
  46. package/src/query/parser/ArrowToExpression.ts +134 -19
  47. package/src/query/parser/BabelVisitor.ts +31 -43
  48. package/src/query/parser/NotSupportedError.ts +5 -0
  49. package/src/query/parser/Restructure.ts +66 -0
  50. package/src/query/parser/TransformVisitor.ts +83 -0
  51. package/src/sql/ISql.ts +10 -0
  52. package/src/tests/db-tests/tests/select-items.ts +12 -0
  53. package/src/tests/expressions/left-joins/child-joins.ts +54 -34
  54. package/src/tests/expressions/sanitize/sanitize-test.ts +17 -0
  55. package/src/tests/expressions/select/select.ts +24 -0
  56. package/src/tests/expressions/simple/parse-arrow.ts +10 -0
  57. package/src/tests/model/ShoppingContext.ts +7 -3
  58. package/src/tests/model/createContext.ts +68 -17
  59. package/src/tests/security/ShoppingContextEvents.ts +20 -0
  60. package/src/tests/security/events/OrderEvents.ts +72 -0
  61. package/src/tests/security/events/ProductEvents.ts +92 -0
  62. package/src/tests/security/events/UserEvents.ts +28 -0
  63. package/src/tests/security/events/UserInfo.ts +7 -0
  64. package/src/tests/security/tests/include-items.ts +19 -0
  65. package/src/tests/security/tests/place-order.ts +104 -0
  66. package/test.js +11 -4
  67. package/tsconfig.json +2 -0
  68. package/src/decorators/parser/MemberParser.ts +0 -8
  69. 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 EntityType from "../../entity-query/EntityType.js";
6
- import { EntitySource } from "../../model/EntitySource.js";
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
- type IQueryFragments = IQueryFragment[];
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 node = parseExpression(fx.toString());
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 firstParam = node.params[0];
20
- if (firstParam.type !== "Identifier") {
21
- throw new Error("Expecting an identifier");
22
- }
30
+ const params = [] as ParameterExpression[];
23
31
 
24
- const paramName = firstParam.name;
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
- const target = firstTarget.name;
49
+ target ??= ParameterExpression.create({ name: firstTarget.name});
37
50
 
38
51
  body = body.body;
39
52
 
40
- const visitor = new this(paramName, target);
53
+ const visitor = new this(params, target, firstTarget.name);
41
54
  return {
42
- param: paramName,
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 param: string,
52
- public target: string
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 Error(`Operator ${operator} not supported`);
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 = node.params.map((x) => this.visit(x));
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(nodeAny: bpe.Expression | bpe.Node): T {
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
- visitMemberExpression(node: bpe.MemberExpression): T {
42
- return;
49
+ visitArrayExpression(node: bpe.ArrayExpression): T {
50
+ throw new Error("Method not implemented.");
43
51
  }
44
- visitLogicalExpression(node: bpe.LogicalExpression): T {
45
- return;
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,5 @@
1
+ export class NotSupportedError extends Error {
2
+ constructor(message?: string) {
3
+ super(message ? `${message} Not Supported`: "Not Supported");
4
+ }
5
+ }
@@ -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
- "P1"."productID",
9
- "P1"."name",
10
- "P1"."ownerID"
11
- FROM "Products" AS "P1"
12
- WHERE
13
- EXISTS (SELECT
14
- $1 AS "o1"
15
- FROM "OrderItems" AS "O0"
16
- WHERE
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
- "P1"."productID","P1"."name","P1"."ownerID"
24
- FROM "Products" AS "P1"
19
+ "p1"."productID",
20
+ "p1"."name",
21
+ "p1"."ownerID",
22
+ "p1"."status"
23
+ FROM "Products" AS "p1"
25
24
  WHERE EXISTS (SELECT
26
- $1 AS "o1"
27
- FROM "OrderItems" AS "O0"
28
- WHERE ("P1"."productID" = "O0"."productID") AND ("O0"."productID" = $2)) AND EXISTS (SELECT
29
- $3 AS "o1"
30
- FROM "OrderItems" AS "O1"
31
- WHERE ("P1"."productID" = "O1"."productID") AND ("O1"."amount" > $4))`;
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
- "P1"."productID","P1"."name","P1"."ownerID"
35
- FROM "Products" AS "P1"
33
+ "p1"."productID",
34
+ "p1"."name",
35
+ "p1"."ownerID",
36
+ "p1"."status"
37
+ FROM "Products" AS "p1"
36
38
  WHERE EXISTS (SELECT
37
- $1 AS "o1"
38
- FROM "OrderItems" AS "O0"
39
- WHERE ("P1"."productID" = "O0"."productID") AND ("O0"."productID" = $2)) AND EXISTS (SELECT
40
- $3 AS "o1"
41
- FROM "OrderItems" AS "O1"
42
- INNER JOIN "Orders" AS "O2" ON "O1"."orderID" = "O2"."orderID"
43
- WHERE ("P1"."productID" = "O1"."productID") AND ("O2"."orderDate" > $4))`;
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
- "P1"."productID","P1"."name","P1"."ownerID"
47
- FROM "Products" AS "P1"
48
- LEFT JOIN "Users" AS "U0" ON "P1"."ownerID" = "U0"."userID"
49
- WHERE "U0"."dateCreated" > $1`;
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
+ }