@entity-access/entity-access 1.0.165 → 1.0.166
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/dist/decorators/IColumn.d.ts +2 -0
- package/dist/decorators/IColumn.d.ts.map +1 -1
- package/dist/decorators/IForeignKeyConstraint.d.ts +12 -0
- package/dist/decorators/IForeignKeyConstraint.d.ts.map +1 -0
- package/dist/decorators/IForeignKeyConstraint.js +2 -0
- package/dist/decorators/IForeignKeyConstraint.js.map +1 -0
- package/dist/decorators/Relate.d.ts +5 -0
- package/dist/decorators/Relate.d.ts.map +1 -1
- package/dist/decorators/Relate.js +2 -1
- package/dist/decorators/Relate.js.map +1 -1
- package/dist/drivers/base/BaseDriver.d.ts +2 -1
- package/dist/drivers/base/BaseDriver.d.ts.map +1 -1
- package/dist/drivers/base/BaseDriver.js +60 -1
- package/dist/drivers/base/BaseDriver.js.map +1 -1
- package/dist/drivers/sql-server/ExpressionToSqlServer.d.ts +2 -1
- package/dist/drivers/sql-server/ExpressionToSqlServer.d.ts.map +1 -1
- package/dist/drivers/sql-server/ExpressionToSqlServer.js +35 -2
- package/dist/drivers/sql-server/ExpressionToSqlServer.js.map +1 -1
- package/dist/entity-query/EntityType.d.ts.map +1 -1
- package/dist/entity-query/EntityType.js +8 -0
- package/dist/entity-query/EntityType.js.map +1 -1
- package/dist/migrations/Migrations.d.ts +3 -1
- package/dist/migrations/Migrations.d.ts.map +1 -1
- package/dist/migrations/Migrations.js +17 -0
- package/dist/migrations/Migrations.js.map +1 -1
- package/dist/migrations/postgres/PostgresAutomaticMigrations.d.ts +2 -0
- package/dist/migrations/postgres/PostgresAutomaticMigrations.d.ts.map +1 -1
- package/dist/migrations/postgres/PostgresAutomaticMigrations.js +39 -0
- package/dist/migrations/postgres/PostgresAutomaticMigrations.js.map +1 -1
- package/dist/migrations/sql-server/SqlServerAutomaticMigrations.d.ts +2 -0
- package/dist/migrations/sql-server/SqlServerAutomaticMigrations.d.ts.map +1 -1
- package/dist/migrations/sql-server/SqlServerAutomaticMigrations.js +39 -0
- package/dist/migrations/sql-server/SqlServerAutomaticMigrations.js.map +1 -1
- package/dist/model/EntitySource.d.ts +2 -0
- package/dist/model/EntitySource.d.ts.map +1 -1
- package/dist/model/EntitySource.js +7 -0
- package/dist/model/EntitySource.js.map +1 -1
- package/dist/query/ast/ExpressionToSql.d.ts +2 -1
- package/dist/query/ast/ExpressionToSql.d.ts.map +1 -1
- package/dist/query/ast/ExpressionToSql.js +28 -1
- package/dist/query/ast/ExpressionToSql.js.map +1 -1
- package/dist/query/ast/Expressions.d.ts +8 -1
- package/dist/query/ast/Expressions.d.ts.map +1 -1
- package/dist/query/ast/Expressions.js +6 -0
- package/dist/query/ast/Expressions.js.map +1 -1
- package/dist/query/ast/Visitor.d.ts +2 -1
- package/dist/query/ast/Visitor.d.ts.map +1 -1
- package/dist/query/ast/Visitor.js +5 -0
- package/dist/query/ast/Visitor.js.map +1 -1
- package/dist/tests/model/ShoppingContext.d.ts.map +1 -1
- package/dist/tests/model/ShoppingContext.js +4 -1
- package/dist/tests/model/ShoppingContext.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/workflows/WorkflowStorage.d.ts +4 -1
- package/dist/workflows/WorkflowStorage.d.ts.map +1 -1
- package/dist/workflows/WorkflowStorage.js +24 -13
- package/dist/workflows/WorkflowStorage.js.map +1 -1
- package/package.json +3 -3
- package/src/decorators/IColumn.ts +3 -0
- package/src/decorators/IForeignKeyConstraint.ts +19 -0
- package/src/decorators/Relate.ts +8 -1
- package/src/drivers/base/BaseDriver.ts +70 -1
- package/src/drivers/sql-server/ExpressionToSqlServer.ts +45 -3
- package/src/entity-query/EntityType.ts +9 -0
- package/src/migrations/Migrations.ts +25 -2
- package/src/migrations/postgres/PostgresAutomaticMigrations.ts +50 -0
- package/src/migrations/sql-server/SqlServerAutomaticMigrations.ts +47 -0
- package/src/model/EntitySource.ts +13 -1
- package/src/query/ast/ExpressionToSql.ts +37 -2
- package/src/query/ast/Expressions.ts +10 -1
- package/src/query/ast/Visitor.ts +6 -1
- package/src/tests/model/ShoppingContext.ts +4 -1
- package/src/workflows/WorkflowStorage.ts +27 -16
- package/tsconfig.json +1 -1
package/src/decorators/Relate.ts
CHANGED
|
@@ -1,12 +1,15 @@
|
|
|
1
1
|
import type { IClassOf } from "./IClassOf.js";
|
|
2
|
+
import { IForeignKeyConstraint } from "./IForeignKeyConstraint.js";
|
|
2
3
|
import SchemaRegistry from "./SchemaRegistry.js";
|
|
3
4
|
import NameParser from "./parser/NameParser.js";
|
|
4
5
|
|
|
6
|
+
|
|
5
7
|
export interface IRelatedType<T, TRelated> {
|
|
6
8
|
property: (item: T) => TRelated;
|
|
7
9
|
inverseProperty: (item: TRelated) => T[];
|
|
8
10
|
inverseKey?: (item: TRelated) => any;
|
|
9
11
|
dotNotCreateIndex?: boolean;
|
|
12
|
+
foreignKeyConstraint?: IForeignKeyConstraint;
|
|
10
13
|
}
|
|
11
14
|
|
|
12
15
|
export interface IRelatedTypeOne<T, TRelated> {
|
|
@@ -14,6 +17,7 @@ export interface IRelatedTypeOne<T, TRelated> {
|
|
|
14
17
|
inverseProperty: (item: TRelated) => T;
|
|
15
18
|
inverseKey?: (item: TRelated) => any;
|
|
16
19
|
dotNotCreateIndex?: boolean;
|
|
20
|
+
foreignKeyConstraint?: IForeignKeyConstraint;
|
|
17
21
|
}
|
|
18
22
|
|
|
19
23
|
export interface IRelatedTypeWithType<T, TRelated> {
|
|
@@ -22,6 +26,7 @@ export interface IRelatedTypeWithType<T, TRelated> {
|
|
|
22
26
|
inverseProperty: (item: TRelated) => T[];
|
|
23
27
|
inverseKey?: (item: TRelated) => any;
|
|
24
28
|
dotNotCreateIndex?: boolean;
|
|
29
|
+
foreignKeyConstraint?: IForeignKeyConstraint;
|
|
25
30
|
}
|
|
26
31
|
|
|
27
32
|
export interface IRelatedTypeOneWithType<T, TRelated> {
|
|
@@ -30,6 +35,7 @@ export interface IRelatedTypeOneWithType<T, TRelated> {
|
|
|
30
35
|
inverseProperty: (item: TRelated) => T;
|
|
31
36
|
inverseKey?: (item: TRelated) => any;
|
|
32
37
|
dotNotCreateIndex?: boolean;
|
|
38
|
+
foreignKeyConstraint?: IForeignKeyConstraint;
|
|
33
39
|
}
|
|
34
40
|
export function RelateTo<T, TRelated>(p: IRelatedTypeWithType<T, TRelated>): (target: T, key: string) => any;
|
|
35
41
|
export function RelateTo<T, TRelated>(c: IClassOf<TRelated>, p: IRelatedType<T, TRelated>): (target: T, key: string) => any;
|
|
@@ -43,7 +49,7 @@ export function RelateTo(c, p?): any {
|
|
|
43
49
|
}
|
|
44
50
|
}
|
|
45
51
|
|
|
46
|
-
const { property, inverseKey: invKey, inverseProperty, dotNotCreateIndex } = p;
|
|
52
|
+
const { property, inverseKey: invKey, inverseProperty, dotNotCreateIndex, foreignKeyConstraint } = p;
|
|
47
53
|
|
|
48
54
|
return (target: any, foreignKey: string): any => {
|
|
49
55
|
|
|
@@ -60,6 +66,7 @@ export function RelateTo(c, p?): any {
|
|
|
60
66
|
relatedTypeClassFactory: p.type,
|
|
61
67
|
relatedName: NameParser.parseMember(inverseProperty),
|
|
62
68
|
relatedKey: invKey ? NameParser.parseMember(invKey) : void 0,
|
|
69
|
+
foreignKeyConstraint,
|
|
63
70
|
dotNotCreateIndex
|
|
64
71
|
});
|
|
65
72
|
|
|
@@ -7,7 +7,7 @@ import QueryCompiler from "../../compiler/QueryCompiler.js";
|
|
|
7
7
|
import EntityType from "../../entity-query/EntityType.js";
|
|
8
8
|
import Migrations from "../../migrations/Migrations.js";
|
|
9
9
|
import ChangeEntry from "../../model/changes/ChangeEntry.js";
|
|
10
|
-
import { BinaryExpression, Constant, DeleteStatement, ExistsExpression, Expression, Identifier, InsertStatement, NotExits, ReturnUpdated, SelectStatement, TableLiteral, UnionAllStatement, UpdateStatement, ValuesStatement } from "../../query/ast/Expressions.js";
|
|
10
|
+
import { BinaryExpression, Constant, DeleteStatement, ExistsExpression, Expression, Identifier, InsertStatement, NotExits, ReturnUpdated, SelectStatement, TableLiteral, UnionAllStatement, UpdateStatement, UpsertStatement, ValuesStatement } from "../../query/ast/Expressions.js";
|
|
11
11
|
|
|
12
12
|
export interface IRecord {
|
|
13
13
|
[key: string]: string | boolean | number | Date | Uint8Array | Blob;
|
|
@@ -148,6 +148,75 @@ export abstract class BaseDriver {
|
|
|
148
148
|
/** Must dispose ObjectPools */
|
|
149
149
|
abstract dispose();
|
|
150
150
|
|
|
151
|
+
createUpsertExpression(type: EntityType, entity: any, mode: "update" | "upsert" | "insert"): Expression {
|
|
152
|
+
const table = type.fullyQualifiedName as TableLiteral;
|
|
153
|
+
|
|
154
|
+
if (mode === "insert") {
|
|
155
|
+
const fields = [];
|
|
156
|
+
const values = [];
|
|
157
|
+
for (const iterator of type.columns) {
|
|
158
|
+
const value = entity[iterator.name];
|
|
159
|
+
if (value === void 0) {
|
|
160
|
+
continue;
|
|
161
|
+
}
|
|
162
|
+
fields.push(Expression.identifier(iterator.columnName));
|
|
163
|
+
values.push(Expression.constant(value));
|
|
164
|
+
}
|
|
165
|
+
return InsertStatement.create({
|
|
166
|
+
table,
|
|
167
|
+
values: ValuesStatement.create({
|
|
168
|
+
fields,
|
|
169
|
+
values: [values]
|
|
170
|
+
})
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
const insert = [] as BinaryExpression[];
|
|
176
|
+
const update = [] as BinaryExpression[];
|
|
177
|
+
const keys = [] as BinaryExpression[];
|
|
178
|
+
for (const iterator of type.columns) {
|
|
179
|
+
const value = entity[iterator.name];
|
|
180
|
+
const assign = Expression.assign(
|
|
181
|
+
Expression.identifier(iterator.columnName),
|
|
182
|
+
Expression.constant(value)
|
|
183
|
+
);
|
|
184
|
+
if (iterator.key) {
|
|
185
|
+
keys.push(assign);
|
|
186
|
+
insert.push(assign);
|
|
187
|
+
continue;
|
|
188
|
+
}
|
|
189
|
+
if (value === undefined) {
|
|
190
|
+
continue;
|
|
191
|
+
}
|
|
192
|
+
insert.push(assign);
|
|
193
|
+
if (value === undefined) {
|
|
194
|
+
continue;
|
|
195
|
+
}
|
|
196
|
+
update.push(assign);
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
|
|
200
|
+
if (mode === "update") {
|
|
201
|
+
let where = null;
|
|
202
|
+
for (const iterator of keys) {
|
|
203
|
+
where = where ? Expression.logicalAnd(where, iterator) : iterator;
|
|
204
|
+
}
|
|
205
|
+
return UpdateStatement.create({
|
|
206
|
+
table,
|
|
207
|
+
set: update,
|
|
208
|
+
where
|
|
209
|
+
});
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
return UpsertStatement.create({
|
|
213
|
+
table,
|
|
214
|
+
insert,
|
|
215
|
+
update,
|
|
216
|
+
keys
|
|
217
|
+
});
|
|
218
|
+
}
|
|
219
|
+
|
|
151
220
|
createInsertExpression(type: EntityType, entity: any): InsertStatement {
|
|
152
221
|
const returnFields = [] as Identifier[];
|
|
153
222
|
const fields = [] as Identifier[];
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import ExpressionToSql from "../../query/ast/ExpressionToSql.js";
|
|
2
|
-
import { BooleanLiteral, InsertStatement, NumberLiteral, OrderByExpression, ReturnUpdated, SelectStatement, ValuesStatement } from "../../query/ast/Expressions.js";
|
|
3
|
-
import { ITextQuery, prepare } from "../../query/ast/IStringTransformer.js";
|
|
2
|
+
import { BooleanLiteral, InsertStatement, NumberLiteral, OrderByExpression, ReturnUpdated, SelectStatement, UpsertStatement, ValuesStatement } from "../../query/ast/Expressions.js";
|
|
3
|
+
import { ITextQuery, prepare, prepareJoin } from "../../query/ast/IStringTransformer.js";
|
|
4
4
|
|
|
5
5
|
export default class ExpressionToSqlServer extends ExpressionToSql {
|
|
6
6
|
|
|
@@ -23,7 +23,7 @@ export default class ExpressionToSqlServer extends ExpressionToSql {
|
|
|
23
23
|
}
|
|
24
24
|
|
|
25
25
|
visitInsertStatement(e: InsertStatement): ITextQuery {
|
|
26
|
-
const returnValues = this.visit(e.returnValues);
|
|
26
|
+
const returnValues = e.returnValues ? this.visit(e.returnValues) : [];
|
|
27
27
|
if (e.values instanceof ValuesStatement) {
|
|
28
28
|
|
|
29
29
|
const rows = [];
|
|
@@ -45,6 +45,48 @@ export default class ExpressionToSqlServer extends ExpressionToSql {
|
|
|
45
45
|
|
|
46
46
|
}
|
|
47
47
|
|
|
48
|
+
visitUpsertStatement(e: UpsertStatement): ITextQuery {
|
|
49
|
+
const table = this.visit(e.table);
|
|
50
|
+
|
|
51
|
+
const insertColumns = [];
|
|
52
|
+
const insertValues = [];
|
|
53
|
+
const updateSet = [];
|
|
54
|
+
|
|
55
|
+
const compare = [];
|
|
56
|
+
|
|
57
|
+
for (const { left, right } of e.insert) {
|
|
58
|
+
const c = this.visit(left);
|
|
59
|
+
const v = this.visit(right);
|
|
60
|
+
insertColumns.push(c);
|
|
61
|
+
insertValues.push(v);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
for (const { left, right } of e.update) {
|
|
65
|
+
const c = this.visit(left);
|
|
66
|
+
const v = this.visit(right);
|
|
67
|
+
updateSet.push(prepare `${c} = ${v}`);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
for (const { left, right } of e.keys) {
|
|
71
|
+
const c = this.visit(left);
|
|
72
|
+
const v = this.visit(right);
|
|
73
|
+
compare.push(prepare `${c} = ${v}`);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
return prepare `INSERT INTO ${table} (${prepareJoin(insertColumns)})
|
|
78
|
+
SELECT * FROM (VALUES (${prepareJoin(insertValues)})) as A(${prepareJoin(insertColumns)})
|
|
79
|
+
WHERE NOT EXISTS (SELECT 1 FROM ${table} WHERE ${prepareJoin(compare, " AND ")});
|
|
80
|
+
IF @@ROWCOUNT=0
|
|
81
|
+
BEGIN
|
|
82
|
+
UPDATE ${table}
|
|
83
|
+
SET
|
|
84
|
+
${prepareJoin(updateSet)}
|
|
85
|
+
WHERE ${prepareJoin(compare, " AND ")}
|
|
86
|
+
END;`;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
|
|
48
90
|
visitSelectStatement(e: SelectStatement): ITextQuery {
|
|
49
91
|
|
|
50
92
|
this.prepareStatement(e);
|
|
@@ -148,6 +148,15 @@ export default class EntityType {
|
|
|
148
148
|
relatedType.relations.push(inverseRelation);
|
|
149
149
|
inverseRelation.relatedRelation = relation;
|
|
150
150
|
relation.relatedRelation = inverseRelation;
|
|
151
|
+
|
|
152
|
+
let { foreignKeyConstraint } = relation;
|
|
153
|
+
if(foreignKeyConstraint) {
|
|
154
|
+
foreignKeyConstraint = { ... foreignKeyConstraint};
|
|
155
|
+
relation.foreignKeyConstraint = foreignKeyConstraint;
|
|
156
|
+
foreignKeyConstraint.name ||= `FK_${this.name}_${fkColumn.name}_${this.typeClass.name}_${relatedType.keys[0].name}`;
|
|
157
|
+
foreignKeyConstraint.column = fkColumn;
|
|
158
|
+
foreignKeyConstraint.refColumns = relatedType.keys;
|
|
159
|
+
}
|
|
151
160
|
return relation;
|
|
152
161
|
}
|
|
153
162
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { modelSymbol } from "../common/symbols/symbols.js";
|
|
2
2
|
import type QueryCompiler from "../compiler/QueryCompiler.js";
|
|
3
|
-
import {
|
|
4
|
-
import
|
|
3
|
+
import type { IForeignKeyConstraint } from "../decorators/IForeignKeyConstraint.js";
|
|
4
|
+
import type { IIndex } from "../decorators/IIndex.js";
|
|
5
5
|
import type EntityType from "../entity-query/EntityType.js";
|
|
6
6
|
import type EntityContext from "../model/EntityContext.js";
|
|
7
7
|
import type EntityQuery from "../model/EntityQuery.js";
|
|
@@ -37,6 +37,27 @@ export default abstract class Migrations {
|
|
|
37
37
|
for (const index of type.indexes) {
|
|
38
38
|
await this.migrateIndexInternal(context, index, type);
|
|
39
39
|
}
|
|
40
|
+
|
|
41
|
+
for (const { isInverseRelation , foreignKeyConstraint, relatedTypeClass } of type.relations) {
|
|
42
|
+
if (isInverseRelation) {
|
|
43
|
+
continue;
|
|
44
|
+
}
|
|
45
|
+
if (!foreignKeyConstraint) {
|
|
46
|
+
continue;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const relatedEntity = model.register(relatedTypeClass)[modelSymbol] as EntityType;
|
|
50
|
+
|
|
51
|
+
foreignKeyConstraint.type = type;
|
|
52
|
+
foreignKeyConstraint.column = type.getProperty(foreignKeyConstraint.column.name).field;
|
|
53
|
+
const refColumns = foreignKeyConstraint.refColumns;
|
|
54
|
+
foreignKeyConstraint.refColumns = [];
|
|
55
|
+
for (const iterator of refColumns) {
|
|
56
|
+
foreignKeyConstraint.refColumns.push(relatedEntity.getProperty(iterator.name).field);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
await this.migrateForeignKey(context, foreignKeyConstraint);
|
|
60
|
+
}
|
|
40
61
|
}
|
|
41
62
|
|
|
42
63
|
}
|
|
@@ -69,5 +90,7 @@ export default abstract class Migrations {
|
|
|
69
90
|
|
|
70
91
|
abstract migrateTable(context: EntityContext, type: EntityType): Promise<any>;
|
|
71
92
|
|
|
93
|
+
abstract migrateForeignKey(context: EntityContext, constraint: IForeignKeyConstraint);
|
|
94
|
+
|
|
72
95
|
|
|
73
96
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { IColumn } from "../../decorators/IColumn.js";
|
|
2
|
+
import { IForeignKeyConstraint } from "../../decorators/IForeignKeyConstraint.js";
|
|
2
3
|
import { IIndex } from "../../decorators/IIndex.js";
|
|
3
4
|
import { BaseConnection, BaseDriver } from "../../drivers/base/BaseDriver.js";
|
|
4
5
|
import EntityType from "../../entity-query/EntityType.js";
|
|
@@ -129,5 +130,54 @@ export default class PostgresAutomaticMigrations extends PostgresMigrations {
|
|
|
129
130
|
await driver.executeQuery(query);
|
|
130
131
|
}
|
|
131
132
|
|
|
133
|
+
async migrateForeignKey(context: EntityContext, constraint: IForeignKeyConstraint) {
|
|
134
|
+
const { type } = constraint;
|
|
135
|
+
const name = type.schema
|
|
136
|
+
? type.schema + "." + type.name
|
|
137
|
+
: type.name;
|
|
138
|
+
|
|
139
|
+
let text = `select constraint_name
|
|
140
|
+
from information_schema.constraint_column_usage
|
|
141
|
+
where table_name = $1 and constraint_name = $2 `;
|
|
142
|
+
|
|
143
|
+
const values = [type.name, constraint.name];
|
|
144
|
+
|
|
145
|
+
if(type.schema) {
|
|
146
|
+
text += " and schema_name = $3";
|
|
147
|
+
values.push(type.schema);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
const driver = context.connection;
|
|
151
|
+
|
|
152
|
+
const r = await driver.executeQuery({ text, values });
|
|
153
|
+
if (r.rows?.length) {
|
|
154
|
+
return;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
text = `ALTER TABLE ${name} ADD CONSTRAINT ${constraint.name}
|
|
158
|
+
foreign key (${constraint.column.columnName})
|
|
159
|
+
references ${constraint.refColumns[0].entityType.name}(
|
|
160
|
+
${constraint.refColumns.map((x) => x.columnName).join(",")}
|
|
161
|
+
) `;
|
|
162
|
+
|
|
163
|
+
switch(constraint.cascade) {
|
|
164
|
+
case "delete":
|
|
165
|
+
text += " ON DELETE CASCADE";
|
|
166
|
+
break;
|
|
167
|
+
case "set-null":
|
|
168
|
+
text += " ON DELETE SET NULL";
|
|
169
|
+
break;
|
|
170
|
+
case "set-default":
|
|
171
|
+
text += " ON DELETE SET DEFAULT";
|
|
172
|
+
break;
|
|
173
|
+
case "restrict":
|
|
174
|
+
text += " ON DELETE RESTRICT";
|
|
175
|
+
break;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
await driver.executeQuery(text);
|
|
179
|
+
|
|
180
|
+
}
|
|
181
|
+
|
|
132
182
|
|
|
133
183
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { IColumn } from "../../decorators/IColumn.js";
|
|
2
|
+
import { IForeignKeyConstraint } from "../../decorators/IForeignKeyConstraint.js";
|
|
2
3
|
import { IIndex } from "../../decorators/IIndex.js";
|
|
3
4
|
import { BaseConnection, BaseDriver } from "../../drivers/base/BaseDriver.js";
|
|
4
5
|
import { SqlServerLiteral } from "../../drivers/sql-server/SqlServerLiteral.js";
|
|
@@ -136,5 +137,51 @@ export default class SqlServerAutomaticMigrations extends SqlServerMigrations {
|
|
|
136
137
|
await driver.executeQuery(query);
|
|
137
138
|
}
|
|
138
139
|
|
|
140
|
+
async migrateForeignKey(context: EntityContext, constraint: IForeignKeyConstraint) {
|
|
141
|
+
const { type } = constraint;
|
|
142
|
+
const name = type.schema
|
|
143
|
+
? type.schema + "." + type.name
|
|
144
|
+
: type.name;
|
|
145
|
+
|
|
146
|
+
let text = `SELECT COUNT(*)
|
|
147
|
+
FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS
|
|
148
|
+
WHERE TABLE_NAME='${type.name}'
|
|
149
|
+
AND CONSTRAINT_NAME='${constraint.name}'
|
|
150
|
+
AND CONSTRAINT_TYPE='FOREIGN KEY'`;
|
|
151
|
+
|
|
152
|
+
if(type.schema) {
|
|
153
|
+
text += ` and schema_name = ${type.schema}`;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
const driver = context.connection;
|
|
157
|
+
|
|
158
|
+
const r = await driver.executeQuery(text);
|
|
159
|
+
if (r.rows?.length) {
|
|
160
|
+
return;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
text = `ALTER TABLE ${name} ADD CONSTRAINT ${constraint.name}
|
|
164
|
+
foreign key (${constraint.column.columnName})
|
|
165
|
+
references ${constraint.refColumns[0].entityType.name}(
|
|
166
|
+
${constraint.refColumns.map((x) => x.columnName).join(",")}
|
|
167
|
+
) `;
|
|
168
|
+
|
|
169
|
+
switch(constraint.cascade) {
|
|
170
|
+
case "delete":
|
|
171
|
+
text += " ON DELETE CASCADE";
|
|
172
|
+
break;
|
|
173
|
+
case "set-null":
|
|
174
|
+
text += " ON DELETE SET NULL";
|
|
175
|
+
break;
|
|
176
|
+
case "set-default":
|
|
177
|
+
text += " ON DELETE SET DEFAULT";
|
|
178
|
+
break;
|
|
179
|
+
case "restrict":
|
|
180
|
+
text += " ON DELETE RESTRICT";
|
|
181
|
+
break;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
await driver.executeQuery(text);
|
|
185
|
+
}
|
|
139
186
|
|
|
140
187
|
}
|
|
@@ -1,12 +1,16 @@
|
|
|
1
1
|
import type EntityContext from "./EntityContext.js";
|
|
2
2
|
import type EntityType from "../entity-query/EntityType.js";
|
|
3
3
|
import type { IEntityQuery, IFilterExpression } from "./IFilterWithParameter.js";
|
|
4
|
-
import { Expression } from "../query/ast/Expressions.js";
|
|
5
4
|
import EntityQuery from "./EntityQuery.js";
|
|
6
5
|
import { contextSymbol, modelSymbol } from "../common/symbols/symbols.js";
|
|
6
|
+
import { Expression } from "../query/ast/Expressions.js";
|
|
7
7
|
|
|
8
8
|
export class EntitySource<T = any> {
|
|
9
9
|
|
|
10
|
+
public statements = {
|
|
11
|
+
|
|
12
|
+
};
|
|
13
|
+
|
|
10
14
|
get [modelSymbol]() {
|
|
11
15
|
return this.model;
|
|
12
16
|
}
|
|
@@ -17,6 +21,7 @@ export class EntitySource<T = any> {
|
|
|
17
21
|
|
|
18
22
|
private filter: Expression;
|
|
19
23
|
|
|
24
|
+
|
|
20
25
|
constructor(
|
|
21
26
|
private readonly model: EntityType,
|
|
22
27
|
private readonly context: EntityContext
|
|
@@ -24,6 +29,13 @@ export class EntitySource<T = any> {
|
|
|
24
29
|
|
|
25
30
|
}
|
|
26
31
|
|
|
32
|
+
public async saveDirect(item: Partial<T>, mode: "update" | "upsert" | "insert") {
|
|
33
|
+
const { driver } = this.context;
|
|
34
|
+
const expression = driver.createUpsertExpression(this.model, item, mode);
|
|
35
|
+
const { text, values } = driver.compiler.compileExpression(null, expression);
|
|
36
|
+
await this.context.connection.executeQuery({ text, values });
|
|
37
|
+
}
|
|
38
|
+
|
|
27
39
|
public add(item: Partial<T>): T {
|
|
28
40
|
const p = Object.getPrototypeOf(item).constructor;
|
|
29
41
|
if (!p || p === Object) {
|
|
@@ -3,7 +3,7 @@ import EntityType, { IEntityProperty } from "../../entity-query/EntityType.js";
|
|
|
3
3
|
import EntityQuery from "../../model/EntityQuery.js";
|
|
4
4
|
import { FilteredExpression, filteredSymbol } from "../../model/events/FilteredExpression.js";
|
|
5
5
|
import { NotSupportedError } from "../parser/NotSupportedError.js";
|
|
6
|
-
import { ArrowFunctionExpression, BigIntLiteral, BinaryExpression, BooleanLiteral, CallExpression, CoalesceExpression, ConditionalExpression, Constant, DeleteStatement, ExistsExpression, Expression, ExpressionAs, ExpressionType, Identifier, InsertStatement, JoinExpression, MemberExpression, NewObjectExpression, NotExits, NullExpression, NumberLiteral, OrderByExpression, ParameterExpression, ReturnUpdated, SelectStatement, StringLiteral, TableLiteral, TemplateLiteral, UnionAllStatement, UpdateStatement, ValuesStatement } from "./Expressions.js";
|
|
6
|
+
import { ArrowFunctionExpression, BigIntLiteral, BinaryExpression, BooleanLiteral, CallExpression, CoalesceExpression, ConditionalExpression, Constant, DeleteStatement, ExistsExpression, Expression, ExpressionAs, ExpressionType, Identifier, InsertStatement, JoinExpression, MemberExpression, UpsertStatement, NewObjectExpression, NotExits, NullExpression, NumberLiteral, OrderByExpression, ParameterExpression, ReturnUpdated, SelectStatement, StringLiteral, TableLiteral, TemplateLiteral, UnionAllStatement, UpdateStatement, ValuesStatement } from "./Expressions.js";
|
|
7
7
|
import { ITextQuery, QueryParameter, prepare, prepareJoin } from "./IStringTransformer.js";
|
|
8
8
|
import ParameterScope from "./ParameterScope.js";
|
|
9
9
|
import Visitor from "./Visitor.js";
|
|
@@ -457,7 +457,7 @@ export default class ExpressionToSql extends Visitor<ITextQuery> {
|
|
|
457
457
|
}
|
|
458
458
|
|
|
459
459
|
visitInsertStatement(e: InsertStatement): ITextQuery {
|
|
460
|
-
const returnValues = this.visit(e.returnValues);
|
|
460
|
+
const returnValues = e.returnValues ? this.visit(e.returnValues) : [];
|
|
461
461
|
if (e.values instanceof ValuesStatement) {
|
|
462
462
|
|
|
463
463
|
const rows = [];
|
|
@@ -490,6 +490,41 @@ export default class ExpressionToSql extends Visitor<ITextQuery> {
|
|
|
490
490
|
return prepare `UPDATE ${table} SET ${set} WHERE ${where}`;
|
|
491
491
|
}
|
|
492
492
|
|
|
493
|
+
visitUpsertStatement(e: UpsertStatement): ITextQuery {
|
|
494
|
+
const table = this.visit(e.table);
|
|
495
|
+
|
|
496
|
+
const insertColumns = [];
|
|
497
|
+
const insertValues = [];
|
|
498
|
+
const updateSet = [];
|
|
499
|
+
|
|
500
|
+
const compare = [];
|
|
501
|
+
|
|
502
|
+
for (const { left, right } of e.insert) {
|
|
503
|
+
const c = this.visit(left);
|
|
504
|
+
const v = this.visit(right);
|
|
505
|
+
insertColumns.push(c);
|
|
506
|
+
insertValues.push(v);
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
for (const { left, right } of e.update) {
|
|
510
|
+
const c = this.visit(left);
|
|
511
|
+
const v = this.visit(right);
|
|
512
|
+
updateSet.push(prepare `${c} = ${v}`);
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
for (const { left, right } of e.keys) {
|
|
516
|
+
const c = this.visit(left);
|
|
517
|
+
compare.push(c);
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
|
|
521
|
+
return prepare `INSERT INTO ${table} (${prepareJoin(insertColumns)})
|
|
522
|
+
VALUES (${prepareJoin(insertValues)})
|
|
523
|
+
ON CONFLICT(${prepareJoin(compare)})
|
|
524
|
+
DO UPDATE SET
|
|
525
|
+
${prepareJoin(updateSet)} `;
|
|
526
|
+
}
|
|
527
|
+
|
|
493
528
|
visitNewObjectExpression(e: NewObjectExpression): ITextQuery {
|
|
494
529
|
return prepare `FROM (${this.visitArray(e.properties)})`;
|
|
495
530
|
}
|
|
@@ -392,6 +392,14 @@ export class UpdateStatement extends Expression {
|
|
|
392
392
|
|
|
393
393
|
}
|
|
394
394
|
|
|
395
|
+
export class UpsertStatement extends Expression {
|
|
396
|
+
readonly type = "UpsertStatement";
|
|
397
|
+
table: TableLiteral | Identifier;
|
|
398
|
+
insert: BinaryExpression[];
|
|
399
|
+
update: BinaryExpression[];
|
|
400
|
+
keys: BinaryExpression[];
|
|
401
|
+
}
|
|
402
|
+
|
|
395
403
|
export class UnionAllStatement extends Expression {
|
|
396
404
|
readonly type = "UnionAllStatement";
|
|
397
405
|
queries: Expression[];
|
|
@@ -399,7 +407,7 @@ export class UnionAllStatement extends Expression {
|
|
|
399
407
|
|
|
400
408
|
export class DeleteStatement extends Expression {
|
|
401
409
|
readonly type = "DeleteStatement";
|
|
402
|
-
table: TableLiteral | Identifier
|
|
410
|
+
table: TableLiteral | Identifier;
|
|
403
411
|
where: Expression;
|
|
404
412
|
}
|
|
405
413
|
|
|
@@ -414,6 +422,7 @@ export type ExpressionType =
|
|
|
414
422
|
InsertStatement|
|
|
415
423
|
UpdateStatement|
|
|
416
424
|
DeleteStatement|
|
|
425
|
+
UpsertStatement|
|
|
417
426
|
ReturnUpdated|
|
|
418
427
|
OrderByExpression|
|
|
419
428
|
JoinExpression|
|
package/src/query/ast/Visitor.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
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, NotExits, NullExpression, NumberLiteral, OrderByExpression, ParameterExpression, ReturnUpdated, SelectStatement, StringLiteral, TableLiteral, TemplateElement, TemplateLiteral, UnionAllStatement, UpdateStatement, ValuesStatement } from "./Expressions.js";
|
|
2
|
+
import { ArrayExpression, ArrowFunctionExpression, BigIntLiteral, BinaryExpression, BooleanLiteral, CallExpression, CoalesceExpression, ConditionalExpression, Constant, DeleteStatement, ExistsExpression, Expression, ExpressionAs, ExpressionType, Identifier, InsertStatement, JoinExpression, MemberExpression, UpsertStatement, NewObjectExpression, NotExits, NullExpression, NumberLiteral, OrderByExpression, ParameterExpression, ReturnUpdated, SelectStatement, StringLiteral, TableLiteral, TemplateElement, TemplateLiteral, UnionAllStatement, UpdateStatement, ValuesStatement } from "./Expressions.js";
|
|
3
3
|
|
|
4
4
|
|
|
5
5
|
export default abstract class Visitor<T = any> {
|
|
@@ -71,10 +71,15 @@ export default abstract class Visitor<T = any> {
|
|
|
71
71
|
return this.visitNotExists(e);
|
|
72
72
|
case "UnionAllStatement":
|
|
73
73
|
return this.visitUnionAllStatement(e);
|
|
74
|
+
case "UpsertStatement":
|
|
75
|
+
return this.visitUpsertStatement(e);
|
|
74
76
|
}
|
|
75
77
|
const c: never = e;
|
|
76
78
|
throw new Error(`${e1.type} Not implemented`);
|
|
77
79
|
}
|
|
80
|
+
visitUpsertStatement(e: UpsertStatement): T {
|
|
81
|
+
throw new Error("Method not implemented.");
|
|
82
|
+
}
|
|
78
83
|
visitUnionAllStatement(e: UnionAllStatement): T {
|
|
79
84
|
throw new NotSupportedError("Union All");
|
|
80
85
|
}
|
|
@@ -297,7 +297,10 @@ export class OrderItem {
|
|
|
297
297
|
@RelateTo({
|
|
298
298
|
type: () => Order,
|
|
299
299
|
property: (orderItem) => orderItem.order,
|
|
300
|
-
inverseProperty: (order) => order.orderItems
|
|
300
|
+
inverseProperty: (order) => order.orderItems,
|
|
301
|
+
foreignKeyConstraint: {
|
|
302
|
+
cascade: "delete"
|
|
303
|
+
}
|
|
301
304
|
})
|
|
302
305
|
public orderID: number;
|
|
303
306
|
|
|
@@ -10,6 +10,8 @@ import DateTime from "../types/DateTime.js";
|
|
|
10
10
|
import WorkflowClock from "./WorkflowClock.js";
|
|
11
11
|
import RawQuery from "../compiler/RawQuery.js";
|
|
12
12
|
|
|
13
|
+
const loadedFromDb = Symbol("loadedFromDB");
|
|
14
|
+
|
|
13
15
|
@Table("Workflows")
|
|
14
16
|
@Index({
|
|
15
17
|
name: "IX_Workflows_Group",
|
|
@@ -126,6 +128,7 @@ export default class WorkflowStorage {
|
|
|
126
128
|
error: r.error,
|
|
127
129
|
lastID: r.lastID,
|
|
128
130
|
taskGroup: r.taskGroup,
|
|
131
|
+
[loadedFromDb]: true,
|
|
129
132
|
};
|
|
130
133
|
}
|
|
131
134
|
return null;
|
|
@@ -148,7 +151,7 @@ export default class WorkflowStorage {
|
|
|
148
151
|
output: r.output,
|
|
149
152
|
error: r.error,
|
|
150
153
|
lastID: r.lastID,
|
|
151
|
-
|
|
154
|
+
[loadedFromDb]: true
|
|
152
155
|
};
|
|
153
156
|
}
|
|
154
157
|
return null;
|
|
@@ -187,22 +190,30 @@ export default class WorkflowStorage {
|
|
|
187
190
|
const db = new WorkflowContext(this.driver);
|
|
188
191
|
const connection = db.connection;
|
|
189
192
|
await connection.runInTransaction(async () => {
|
|
190
|
-
let w = await db.workflows.where(state, (p) => (x) => x.id === p.id).first();
|
|
191
|
-
if (!w) {
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
for (const key in state) {
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
193
|
+
// let w = await db.workflows.where(state, (p) => (x) => x.id === p.id).first();
|
|
194
|
+
// if (!w) {
|
|
195
|
+
// w = db.workflows.add(state);
|
|
196
|
+
// w.taskGroup ||= "default";
|
|
197
|
+
// }
|
|
198
|
+
|
|
199
|
+
// for (const key in state) {
|
|
200
|
+
// if (Object.prototype.hasOwnProperty.call(state, key)) {
|
|
201
|
+
// const element = state[key];
|
|
202
|
+
// w[key] = element;
|
|
203
|
+
// }
|
|
204
|
+
// }
|
|
205
|
+
|
|
206
|
+
// w.state ||= "queued";
|
|
207
|
+
// w.updated ??= DateTime.utcNow;
|
|
208
|
+
state.state ||= "queued";
|
|
209
|
+
state.updated ??= DateTime.utcNow;
|
|
210
|
+
state.taskGroup ||= "default";
|
|
211
|
+
// await db.saveChanges();
|
|
212
|
+
if(state[loadedFromDb]) {
|
|
213
|
+
await db.workflows.saveDirect(state, "update");
|
|
214
|
+
} else {
|
|
215
|
+
await db.workflows.saveDirect(state, "upsert");
|
|
201
216
|
}
|
|
202
|
-
|
|
203
|
-
w.state ||= "queued";
|
|
204
|
-
w.updated ??= DateTime.utcNow;
|
|
205
|
-
await db.saveChanges();
|
|
206
217
|
});
|
|
207
218
|
}
|
|
208
219
|
|