@entity-access/entity-access 1.0.1 → 1.0.5
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 +19 -4
- 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/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/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 +48 -9
- package/src/drivers/sql-server/SqlServerDriver.ts +7 -16
- package/src/entity-query/EntityType.ts +1 -1
- package/src/model/EntityContext.ts +167 -22
- package/src/model/EntityQuery.ts +76 -60
- package/src/model/EntitySource.ts +39 -33
- package/src/model/IFilterWithParameter.ts +3 -0
- package/src/model/SourceExpression.ts +21 -25
- package/src/model/{ChangeEntry.ts → changes/ChangeEntry.ts} +33 -3
- package/src/model/{ChangeSet.ts → changes/ChangeSet.ts} +12 -11
- package/src/model/events/ContextEvents.ts +26 -0
- package/src/model/events/EntityEvents.ts +92 -0
- package/src/model/{IdentityService.ts → identity/IdentityService.ts} +1 -1
- package/src/model/symbols.ts +1 -0
- package/src/model/verification/VerificationSession.ts +173 -0
- package/src/query/ast/DebugStringVisitor.ts +128 -0
- package/src/query/ast/ExpressionToSql.ts +289 -119
- package/src/query/ast/Expressions.ts +111 -12
- package/src/query/ast/IStringTransformer.ts +16 -4
- package/src/query/ast/ReplaceParameter.ts +40 -0
- package/src/query/ast/Visitor.ts +20 -5
- package/src/query/parser/ArrowToExpression.ts +116 -16
- package/src/query/parser/BabelVisitor.ts +27 -44
- 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 +19 -26
- 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 +45 -13
- package/src/tests/security/ShoppingContextEvents.ts +20 -0
- package/src/tests/security/events/OrderEvents.ts +66 -0
- package/src/tests/security/events/ProductEvents.ts +92 -0
- package/src/tests/security/events/UserEvents.ts +18 -0
- package/src/tests/security/events/UserInfo.ts +7 -0
- package/src/tests/security/tests/place-order.ts +71 -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,6 +1,11 @@
|
|
|
1
1
|
name: Build
|
|
2
2
|
|
|
3
|
-
on:
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches-ignore:
|
|
6
|
+
- 'feature/**'
|
|
7
|
+
tags-ignore:
|
|
8
|
+
- '*'
|
|
4
9
|
|
|
5
10
|
jobs:
|
|
6
11
|
build:
|
|
@@ -38,7 +43,7 @@ jobs:
|
|
|
38
43
|
- run: npm install
|
|
39
44
|
- run: npm install -D typescript
|
|
40
45
|
- run: tsc
|
|
41
|
-
- run: npm run test
|
|
46
|
+
- run: npm run test
|
|
42
47
|
env:
|
|
43
48
|
POSTGRES_HOST: localhost
|
|
44
49
|
POSTGRES_PORT: 5432
|
package/.vscode/settings.json
CHANGED
package/README.md
CHANGED
|
@@ -5,7 +5,7 @@ Inspired from Entity Framework Core, Entity Access is ORM for JavaScript runtime
|
|
|
5
5
|
|
|
6
6
|
# Project Status
|
|
7
7
|
1. Beta - Postgres Driver
|
|
8
|
-
2.
|
|
8
|
+
2. Beta - Sql Server Driver
|
|
9
9
|
|
|
10
10
|
## Features
|
|
11
11
|
1. Unit of Work and Repository Pattern
|
|
@@ -15,6 +15,7 @@ Inspired from Entity Framework Core, Entity Access is ORM for JavaScript runtime
|
|
|
15
15
|
5. Postgres Driver
|
|
16
16
|
6. Sql Server Driver
|
|
17
17
|
7. Automatic parameterization to safeguard sql injection attacks.
|
|
18
|
+
8. Context Filters - This is a new concept where you can setup filters that will be used against saving/retrieving data.
|
|
18
19
|
|
|
19
20
|
## Upcoming Features
|
|
20
21
|
1. Include
|
|
@@ -38,6 +39,22 @@ db.orders.add({
|
|
|
38
39
|
|
|
39
40
|
// save all in single transaction
|
|
40
41
|
await db.saveChanges();
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
const existingOrderItem1: OrderItem;
|
|
45
|
+
const existingOrderItem2: OrderItem;
|
|
46
|
+
|
|
47
|
+
existingOrderItem2.status = "cancelled";
|
|
48
|
+
existingOrderItem1.status = "cancelled";
|
|
49
|
+
// executes update statements in single transaction
|
|
50
|
+
await db.saveChanges();
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
db.orderItems.delete(existingOrderItem1);
|
|
54
|
+
db.orderItems.delete(existingOrderItem2);
|
|
55
|
+
// executes delete statements in single transaction
|
|
56
|
+
await db.saveChanges();
|
|
57
|
+
|
|
41
58
|
```
|
|
42
59
|
|
|
43
60
|
### Arrow function based query features
|
|
@@ -52,11 +69,34 @@ Simple Query
|
|
|
52
69
|
```typescript
|
|
53
70
|
const db = new ShoppingContext();
|
|
54
71
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
72
|
+
// find customer from orderID
|
|
73
|
+
const q = db.customers
|
|
74
|
+
// first we will send parameters
|
|
75
|
+
.where({ orderID },
|
|
76
|
+
// second we will write an arrow
|
|
77
|
+
// accepting parameters
|
|
78
|
+
(p) =>
|
|
79
|
+
// this is the arrow which will
|
|
80
|
+
// be converted to SQL
|
|
81
|
+
// you can write very limited set of
|
|
82
|
+
// expressions in this arrow function
|
|
83
|
+
(x) => x.orders.some(
|
|
84
|
+
// This will results in exists or join
|
|
85
|
+
// based on relations declared
|
|
86
|
+
(order) => order.orderID === p.orderID );
|
|
87
|
+
const customer = await q.first();
|
|
88
|
+
```
|
|
89
|
+
Above expression will result in following filter expression
|
|
90
|
+
```sql
|
|
91
|
+
SELECT c.firstName, c.lastName, c.customerID
|
|
92
|
+
FROM customers as c
|
|
93
|
+
EXISTS (
|
|
94
|
+
SELECT 1
|
|
95
|
+
FROM Orders as o1
|
|
96
|
+
WHERE x.customerID = o1.orderID
|
|
97
|
+
AND o1.orderID = $1
|
|
98
|
+
)
|
|
99
|
+
LIMIT 1;
|
|
60
100
|
```
|
|
61
101
|
|
|
62
102
|
Query with Like operator
|
|
@@ -124,42 +164,32 @@ class OrderItem {
|
|
|
124
164
|
|
|
125
165
|
### Compare operators
|
|
126
166
|
|
|
167
|
+
Only handful of operators are supported as of now.
|
|
168
|
+
1. Equality Both `==`, `===`, will result in simple `=` operator in SQL. There is no type check performed and no conversion is performed to speed up
|
|
169
|
+
execution. However, typescript will give you warning and compilation for mismatch of operands and you can convert them as needed. But for conversion
|
|
170
|
+
use only `Sql.*` functions.
|
|
171
|
+
2. Above also applies for operators `!=` and `!==`, they will result in `<>` in SQL.
|
|
172
|
+
3. `+`, `-`, `*`, `/` operators will be sent to SQL as it is.
|
|
173
|
+
4. For precedence, we recommend using brackets in the arrow functions as there might be differences in underlying database engine's preferences and you may not get correct results.
|
|
174
|
+
5. Template Literals, will be sent to SQL as concat, however, please use
|
|
175
|
+
conversion of non string to string type if underlying provider does not support direct conversion.
|
|
176
|
+
6. Conversion methods, `Sql.cast.as*` methods will provide conversion from any type to desired type. `Sql.cast.asText` will convert to number to text.
|
|
177
|
+
|
|
127
178
|
#### Equality
|
|
128
179
|
Both strict and non strict equality will result in
|
|
129
180
|
simple equality comparison in SQL. Database provider
|
|
130
181
|
may or may not convert them correctly, so we recommend
|
|
131
182
|
using helper functions to convert before comparison.
|
|
132
183
|
```typescript
|
|
133
|
-
// find all customer from orderID
|
|
134
184
|
const q = db.customers
|
|
135
|
-
// first we will send parameters
|
|
136
185
|
.where({ orderID },
|
|
137
|
-
// second we will write an arrow
|
|
138
|
-
// accepting parameters
|
|
139
186
|
(p) =>
|
|
140
|
-
// this is the arrow which will
|
|
141
|
-
// be converted to SQL
|
|
142
|
-
// you can write very limited set of
|
|
143
|
-
// expressions in this arrow function
|
|
144
187
|
(x) => x.orders.some(
|
|
145
|
-
// This will results in exists or join
|
|
146
|
-
// based on what level of nested
|
|
147
|
-
// foreign key references are available
|
|
148
188
|
(order) => order.orderID === p.orderID )
|
|
149
189
|
)
|
|
150
190
|
|
|
151
191
|
```
|
|
152
192
|
|
|
153
|
-
Above expression will result in following filter expression
|
|
154
|
-
```sql
|
|
155
|
-
EXISTS (
|
|
156
|
-
SELECT 1
|
|
157
|
-
FROM Orders as o1
|
|
158
|
-
WHERE x.customerID = o1.orderID
|
|
159
|
-
AND o1.orderID = $1
|
|
160
|
-
)
|
|
161
|
-
```
|
|
162
|
-
|
|
163
193
|
#### Like
|
|
164
194
|
|
|
165
195
|
To use `LIKE` operator, `Sql.text.like` method must be used
|
package/package.json
CHANGED
|
@@ -1,15 +1,30 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@entity-access/entity-access",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.5",
|
|
4
4
|
"description": "",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"scripts": {
|
|
7
|
+
"test-plain": "node --enable-source-maps ./test.js no-db",
|
|
7
8
|
"test": "node --enable-source-maps ./test.js",
|
|
8
|
-
"
|
|
9
|
+
"postversion": "git push --follow-tags"
|
|
9
10
|
},
|
|
10
11
|
"type": "module",
|
|
11
|
-
"
|
|
12
|
-
|
|
12
|
+
"repository": {
|
|
13
|
+
"type": "git",
|
|
14
|
+
"url": "git+https://github.com/entity-access/entity-access.git"
|
|
15
|
+
},
|
|
16
|
+
"author": "Akash Kava",
|
|
17
|
+
"license": "MIT",
|
|
18
|
+
"bugs": {
|
|
19
|
+
"url": "https://github.com/entity-access/entity-access/issues"
|
|
20
|
+
},
|
|
21
|
+
"keywords": [
|
|
22
|
+
"entity",
|
|
23
|
+
"framework",
|
|
24
|
+
"access",
|
|
25
|
+
"orm"
|
|
26
|
+
],
|
|
27
|
+
"homepage": "https://github.com/entity-access/entity-access#readme",
|
|
13
28
|
"dependencies": {
|
|
14
29
|
"@babel/parser": "^7.22.5",
|
|
15
30
|
"@babel/types": "^7.22.5",
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/* eslint-disable no-console */
|
|
2
|
+
/* eslint-disable @typescript-eslint/naming-convention */
|
|
3
|
+
declare global {
|
|
4
|
+
interface SymbolConstructor {
|
|
5
|
+
readonly disposable?: unique symbol;
|
|
6
|
+
readonly asyncDisposable?: unique symbol;
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
// @ts-expect-error readonly
|
|
11
|
+
Symbol.disposable ??= Symbol("@@disposable");
|
|
12
|
+
// @ts-expect-error readonly
|
|
13
|
+
Symbol.asyncDisposable ??= Symbol("@@asyncDisposable");
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
export interface IDisposable {
|
|
17
|
+
dispose?();
|
|
18
|
+
[Symbol.disposable]?();
|
|
19
|
+
[Symbol.asyncDisposable]?();
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function disposeDisposable(d: IDisposable) {
|
|
23
|
+
const f = d[Symbol.disposable] ?? d[Symbol.asyncDisposable];
|
|
24
|
+
f?.()?.catch((error) => console.error(error));
|
|
25
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { IClassOf } from "../decorators/IClassOf.js";
|
|
2
|
+
|
|
3
|
+
function clone() {
|
|
4
|
+
Object.getPrototypeOf(this).constructor.create({ ... this });
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export default class ImmutableObject {
|
|
8
|
+
|
|
9
|
+
public static create<T extends ImmutableObject>(this:IClassOf<T>, p: Partial<T>): T {
|
|
10
|
+
(p as any).type = this.name;
|
|
11
|
+
for(const [key, value] of Object.entries(p)) {
|
|
12
|
+
if (!value) {
|
|
13
|
+
continue;
|
|
14
|
+
}
|
|
15
|
+
// if it is array, we should make it immutable
|
|
16
|
+
if (Array.isArray(value)) {
|
|
17
|
+
const array = [];
|
|
18
|
+
let index = 0;
|
|
19
|
+
for (const iterator of value) {
|
|
20
|
+
Object.defineProperty(array, index++, {
|
|
21
|
+
value: iterator,
|
|
22
|
+
writable: false,
|
|
23
|
+
enumerable: true
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
Object.defineProperty(array, "clone", {
|
|
27
|
+
value: clone,
|
|
28
|
+
enumerable: false,
|
|
29
|
+
writable: false
|
|
30
|
+
});
|
|
31
|
+
Object.defineProperty(p, key, {
|
|
32
|
+
value: array,
|
|
33
|
+
enumerable: true,
|
|
34
|
+
writable: false
|
|
35
|
+
});
|
|
36
|
+
continue;
|
|
37
|
+
}
|
|
38
|
+
Object.defineProperty(p, key, {
|
|
39
|
+
value,
|
|
40
|
+
enumerable: true,
|
|
41
|
+
writable: false
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
Object.setPrototypeOf(p, Object.getPrototypeOf(this));
|
|
45
|
+
Object.defineProperty(p, "clone", {
|
|
46
|
+
value: clone,
|
|
47
|
+
enumerable: false,
|
|
48
|
+
writable: false
|
|
49
|
+
});
|
|
50
|
+
return p as T;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { RegisterSingleton } from "../di/di.js";
|
|
2
|
+
import { IDisposable } from "./IDisposable.js";
|
|
3
|
+
|
|
4
|
+
/* eslint-disable no-console */
|
|
5
|
+
@RegisterSingleton
|
|
6
|
+
export default class Logger implements IDisposable {
|
|
7
|
+
|
|
8
|
+
public static instance = new Logger();
|
|
9
|
+
|
|
10
|
+
log(a) {
|
|
11
|
+
console.log(a);
|
|
12
|
+
return this;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
error(a) {
|
|
16
|
+
console.error(a);
|
|
17
|
+
return this;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
newSession() {
|
|
21
|
+
return new SessionLogger(this);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
dispose() {
|
|
25
|
+
// do nothing...
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
class SessionLogger extends Logger {
|
|
30
|
+
private items = [];
|
|
31
|
+
|
|
32
|
+
constructor(private parent: Logger) {
|
|
33
|
+
super();
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
log(a) {
|
|
37
|
+
this.items.push({ log: a});
|
|
38
|
+
return this;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
error(a: any) {
|
|
42
|
+
this.items.push({ error: a});
|
|
43
|
+
return this;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
dispose(): void {
|
|
47
|
+
for (const { log, error } of this.items) {
|
|
48
|
+
if (log) {
|
|
49
|
+
this.parent.log(log);
|
|
50
|
+
continue;
|
|
51
|
+
}
|
|
52
|
+
if (error) {
|
|
53
|
+
this.parent.error(error);
|
|
54
|
+
continue;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
}
|
package/src/common/usingAsync.ts
CHANGED
|
@@ -1,24 +1,54 @@
|
|
|
1
|
+
import { IDisposable } from "./IDisposable.js";
|
|
1
2
|
|
|
2
|
-
export
|
|
3
|
+
export type IDisposableObject = IDisposable & { end?(): any; close?():any };
|
|
3
4
|
|
|
4
|
-
|
|
5
|
-
dispose?(): any;
|
|
6
|
-
close?(): any;
|
|
5
|
+
export type IDisposableObjectType = IDisposableObject | IDisposableObject[];
|
|
7
6
|
|
|
8
|
-
}
|
|
9
7
|
|
|
8
|
+
const disposeAll = async (all: (IDisposable & { end?(): any; close?():any })[]) => {
|
|
9
|
+
let error = null;
|
|
10
|
+
// we need to dispose in reverse order...
|
|
11
|
+
while (all.length) {
|
|
12
|
+
const d = all.pop();
|
|
13
|
+
try {
|
|
14
|
+
const f = d.dispose ?? d.end ?? d.close ?? d[Symbol.disposable] ?? d[Symbol.asyncDisposable];
|
|
15
|
+
const r = f?.apply(d);
|
|
16
|
+
if (r?.then) {
|
|
17
|
+
await r;
|
|
18
|
+
}
|
|
19
|
+
} catch (e) {
|
|
20
|
+
error = error
|
|
21
|
+
? new Error(`${error.stack ?? error}\r\n${e.stack ?? e}`)
|
|
22
|
+
: error;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
if (error) {
|
|
26
|
+
throw error;
|
|
27
|
+
}
|
|
28
|
+
};
|
|
10
29
|
|
|
11
|
-
export default async function usingAsync<T
|
|
30
|
+
export default async function usingAsync<T>(fx: (registry: IDisposableObject[]) => T | Promise<T>) {
|
|
31
|
+
const registry = [] as IDisposableObject[];
|
|
12
32
|
try {
|
|
13
|
-
const r = fx(
|
|
33
|
+
const r = fx(registry) as any;
|
|
14
34
|
if (r?.then) {
|
|
15
35
|
await r;
|
|
16
36
|
}
|
|
17
|
-
return r;
|
|
18
37
|
} finally {
|
|
19
|
-
|
|
20
|
-
if (r?.then) {
|
|
21
|
-
await r;
|
|
22
|
-
}
|
|
38
|
+
await disposeAll(registry);
|
|
23
39
|
}
|
|
24
40
|
}
|
|
41
|
+
|
|
42
|
+
export class DisposableScope {
|
|
43
|
+
|
|
44
|
+
private disposables: IDisposableObject[] = [];
|
|
45
|
+
|
|
46
|
+
public register(d: IDisposableObject) {
|
|
47
|
+
this.disposables.push(d);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
async dispose() {
|
|
51
|
+
await disposeAll(this.disposables);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
}
|
|
@@ -1,14 +1,19 @@
|
|
|
1
|
-
import { IClassOf } from "../decorators/IClassOf.js";
|
|
2
1
|
import ExpressionToSql from "../query/ast/ExpressionToSql.js";
|
|
3
|
-
import { ISqlMethodTransformer, IStringTransformer,
|
|
4
|
-
import { Expression,
|
|
2
|
+
import { ISqlMethodTransformer, IStringTransformer, ITextQuery } from "../query/ast/IStringTransformer.js";
|
|
3
|
+
import { Expression, ParameterExpression, SelectStatement } from "../query/ast/Expressions.js";
|
|
5
4
|
import SqlLiteral from "../query/ast/SqlLiteral.js";
|
|
6
5
|
import ArrowToExpression from "../query/parser/ArrowToExpression.js";
|
|
7
6
|
import PostgreSqlMethodTransformer from "./postgres/PostgreSqlMethodTransformer.js";
|
|
8
|
-
import
|
|
9
|
-
import
|
|
10
|
-
|
|
11
|
-
|
|
7
|
+
import EntityQuery from "../model/EntityQuery.js";
|
|
8
|
+
import ReplaceParameter from "../query/ast/ReplaceParameter.js";
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
export class CompiledQuery {
|
|
12
|
+
constructor(
|
|
13
|
+
public root: ParameterExpression,
|
|
14
|
+
public target: ParameterExpression,
|
|
15
|
+
public textQuery: ITextQuery) {}
|
|
16
|
+
}
|
|
12
17
|
|
|
13
18
|
export default class QueryCompiler {
|
|
14
19
|
|
|
@@ -38,47 +43,40 @@ export default class QueryCompiler {
|
|
|
38
43
|
this.sqlMethodTransformer = sqlMethodTransformer;
|
|
39
44
|
}
|
|
40
45
|
|
|
41
|
-
public execute<P = any, T = any>(parameters: P, fx: (p: P) => (x: T) => any, source?:
|
|
42
|
-
const {
|
|
43
|
-
const exp = new this.expressionToSql(source,
|
|
46
|
+
public execute<P = any, T = any>(parameters: P, fx: (p: P) => (x: T) => any, source?: EntityQuery) {
|
|
47
|
+
const { params, target , body } = this.arrowToExpression.transform(fx, source?.selectStatement.as);
|
|
48
|
+
const exp = new this.expressionToSql(source, params[0], target, this);
|
|
44
49
|
const query = exp.visit(body);
|
|
45
50
|
return this.invoke(query, parameters);
|
|
46
51
|
}
|
|
47
52
|
|
|
48
|
-
public
|
|
49
|
-
const {
|
|
50
|
-
|
|
51
|
-
const visited = exp.visit(body);
|
|
52
|
-
return PlaceholderExpression.create({
|
|
53
|
-
expression: () => visited.map((x) => typeof x === "function" ? () => x(p) : x)
|
|
54
|
-
});
|
|
53
|
+
public compile(source: EntityQuery, fx: (p) => (x) => any) {
|
|
54
|
+
const { params, target , body } = this.arrowToExpression.transform(fx, source?.selectStatement.as);
|
|
55
|
+
return { params, target, body };
|
|
55
56
|
}
|
|
56
57
|
|
|
57
|
-
public
|
|
58
|
-
const {
|
|
59
|
-
const exp = new this.expressionToSql(source,
|
|
60
|
-
|
|
58
|
+
public compileToSql( source: EntityQuery , fx: (p) => (x) => any) {
|
|
59
|
+
const { params, target , body } = this.arrowToExpression.transform(fx, source?.selectStatement.as);
|
|
60
|
+
const exp = new this.expressionToSql(source, params[0], target, this);
|
|
61
|
+
const textQuery = exp.visit(body);
|
|
62
|
+
return new CompiledQuery(exp.root,exp.target,textQuery);
|
|
61
63
|
}
|
|
62
64
|
|
|
63
|
-
public compileExpression(exp: Expression) {
|
|
64
|
-
const toSql = new this.expressionToSql(null,
|
|
65
|
+
public compileExpression(source: EntityQuery, exp: Expression) {
|
|
66
|
+
const toSql = new this.expressionToSql(source ?? null, null, (exp as SelectStatement)?.as ?? source?.selectStatement?.as, this);
|
|
65
67
|
const query = toSql.visit(exp);
|
|
66
68
|
return this.invoke(query);
|
|
67
69
|
}
|
|
68
70
|
|
|
69
|
-
private invoke(query:
|
|
71
|
+
private invoke(query: ITextQuery, p: any = {}) {
|
|
70
72
|
let text = "";
|
|
71
73
|
const values = [];
|
|
72
74
|
for (const iterator of query) {
|
|
73
|
-
if (typeof iterator
|
|
75
|
+
if (typeof iterator !== "function") {
|
|
74
76
|
text += iterator;
|
|
75
77
|
continue;
|
|
76
78
|
}
|
|
77
|
-
|
|
78
|
-
if (typeof iterator === "function") {
|
|
79
|
-
value = iterator(p);
|
|
80
|
-
}
|
|
81
|
-
|
|
79
|
+
const value = iterator(p);
|
|
82
80
|
values.push(value);
|
|
83
81
|
text += "$" + values.length;
|
|
84
82
|
}
|
|
@@ -6,6 +6,29 @@ export const PostgreSqlHelper: ISqlHelpers = {
|
|
|
6
6
|
in(a, array) {
|
|
7
7
|
return prepareAny `${a} IN ${array}`;
|
|
8
8
|
},
|
|
9
|
+
cast: {
|
|
10
|
+
asBigInt(a) {
|
|
11
|
+
return prepareAny `(${a} ::bigint)`;
|
|
12
|
+
},
|
|
13
|
+
asDate(a) {
|
|
14
|
+
return prepareAny `(${a} ::date)`;
|
|
15
|
+
},
|
|
16
|
+
asDateTime(a) {
|
|
17
|
+
return prepareAny `(${a} ::timestamp)`;
|
|
18
|
+
},
|
|
19
|
+
asInteger(a) {
|
|
20
|
+
return prepareAny `(${a} ::int)`;
|
|
21
|
+
},
|
|
22
|
+
asNumber(a) {
|
|
23
|
+
return prepareAny `(${a} ::double)`;
|
|
24
|
+
},
|
|
25
|
+
asText(a) {
|
|
26
|
+
return prepareAny `(${a} ::text)`;
|
|
27
|
+
},
|
|
28
|
+
asDecimal(a) {
|
|
29
|
+
return prepareAny `(${a} ::decimal(18,2))`;
|
|
30
|
+
},
|
|
31
|
+
},
|
|
9
32
|
date: {
|
|
10
33
|
addDays(d, n) {
|
|
11
34
|
return prepareAny `(${d} + (${n} * interval '1 day'))`;
|
|
@@ -6,6 +6,29 @@ export const SqlServerSqlHelper: ISqlHelpers = {
|
|
|
6
6
|
in(a, array) {
|
|
7
7
|
return prepareAny `${a} IN ${array}`;
|
|
8
8
|
},
|
|
9
|
+
cast: {
|
|
10
|
+
asBigInt(a) {
|
|
11
|
+
return prepareAny `CAST(${a} as bigint)`;
|
|
12
|
+
},
|
|
13
|
+
asDate(a) {
|
|
14
|
+
return prepareAny `CAST(${a} as date)`;
|
|
15
|
+
},
|
|
16
|
+
asDateTime(a) {
|
|
17
|
+
return prepareAny `CAST(${a} as datetime2)`;
|
|
18
|
+
},
|
|
19
|
+
asInteger(a) {
|
|
20
|
+
return prepareAny `CAST(${a} as int)`;
|
|
21
|
+
},
|
|
22
|
+
asNumber(a) {
|
|
23
|
+
return prepareAny `CAST(${a} as double)`;
|
|
24
|
+
},
|
|
25
|
+
asText(a) {
|
|
26
|
+
return prepareAny `CAST(${a} as varchar(max))`;
|
|
27
|
+
},
|
|
28
|
+
asDecimal(a) {
|
|
29
|
+
return prepareAny `CAST(${a} as decimal(18,2))`;
|
|
30
|
+
},
|
|
31
|
+
},
|
|
9
32
|
date: {
|
|
10
33
|
addDays(d, n) {
|
|
11
34
|
return prepareAny `DateAdd(DAY, ${d}, ${n})`;
|
|
@@ -1 +1,2 @@
|
|
|
1
|
-
export type IClassOf<T> =
|
|
1
|
+
export type IClassOf<T> = new (... a: any[]) => T;
|
|
2
|
+
export type IAbstractClassOf<T> = abstract new (... a: any[]) => T;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
const parsedName = Symbol("parsedName");
|
|
2
|
+
|
|
3
|
+
export default class NameParser {
|
|
4
|
+
public static parseMember(text: ((a: any) => any)) {
|
|
5
|
+
let name = text[parsedName];
|
|
6
|
+
if (!name) {
|
|
7
|
+
const t: string = text.toString();
|
|
8
|
+
|
|
9
|
+
const index = t.lastIndexOf(".");
|
|
10
|
+
name = t.substring(index + 1);
|
|
11
|
+
text[parsedName] = name;
|
|
12
|
+
}
|
|
13
|
+
return name;
|
|
14
|
+
}
|
|
15
|
+
}
|