@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.
Files changed (74) hide show
  1. package/dist/decorators/IColumn.d.ts +2 -0
  2. package/dist/decorators/IColumn.d.ts.map +1 -1
  3. package/dist/decorators/IForeignKeyConstraint.d.ts +12 -0
  4. package/dist/decorators/IForeignKeyConstraint.d.ts.map +1 -0
  5. package/dist/decorators/IForeignKeyConstraint.js +2 -0
  6. package/dist/decorators/IForeignKeyConstraint.js.map +1 -0
  7. package/dist/decorators/Relate.d.ts +5 -0
  8. package/dist/decorators/Relate.d.ts.map +1 -1
  9. package/dist/decorators/Relate.js +2 -1
  10. package/dist/decorators/Relate.js.map +1 -1
  11. package/dist/drivers/base/BaseDriver.d.ts +2 -1
  12. package/dist/drivers/base/BaseDriver.d.ts.map +1 -1
  13. package/dist/drivers/base/BaseDriver.js +60 -1
  14. package/dist/drivers/base/BaseDriver.js.map +1 -1
  15. package/dist/drivers/sql-server/ExpressionToSqlServer.d.ts +2 -1
  16. package/dist/drivers/sql-server/ExpressionToSqlServer.d.ts.map +1 -1
  17. package/dist/drivers/sql-server/ExpressionToSqlServer.js +35 -2
  18. package/dist/drivers/sql-server/ExpressionToSqlServer.js.map +1 -1
  19. package/dist/entity-query/EntityType.d.ts.map +1 -1
  20. package/dist/entity-query/EntityType.js +8 -0
  21. package/dist/entity-query/EntityType.js.map +1 -1
  22. package/dist/migrations/Migrations.d.ts +3 -1
  23. package/dist/migrations/Migrations.d.ts.map +1 -1
  24. package/dist/migrations/Migrations.js +17 -0
  25. package/dist/migrations/Migrations.js.map +1 -1
  26. package/dist/migrations/postgres/PostgresAutomaticMigrations.d.ts +2 -0
  27. package/dist/migrations/postgres/PostgresAutomaticMigrations.d.ts.map +1 -1
  28. package/dist/migrations/postgres/PostgresAutomaticMigrations.js +39 -0
  29. package/dist/migrations/postgres/PostgresAutomaticMigrations.js.map +1 -1
  30. package/dist/migrations/sql-server/SqlServerAutomaticMigrations.d.ts +2 -0
  31. package/dist/migrations/sql-server/SqlServerAutomaticMigrations.d.ts.map +1 -1
  32. package/dist/migrations/sql-server/SqlServerAutomaticMigrations.js +39 -0
  33. package/dist/migrations/sql-server/SqlServerAutomaticMigrations.js.map +1 -1
  34. package/dist/model/EntitySource.d.ts +2 -0
  35. package/dist/model/EntitySource.d.ts.map +1 -1
  36. package/dist/model/EntitySource.js +7 -0
  37. package/dist/model/EntitySource.js.map +1 -1
  38. package/dist/query/ast/ExpressionToSql.d.ts +2 -1
  39. package/dist/query/ast/ExpressionToSql.d.ts.map +1 -1
  40. package/dist/query/ast/ExpressionToSql.js +28 -1
  41. package/dist/query/ast/ExpressionToSql.js.map +1 -1
  42. package/dist/query/ast/Expressions.d.ts +8 -1
  43. package/dist/query/ast/Expressions.d.ts.map +1 -1
  44. package/dist/query/ast/Expressions.js +6 -0
  45. package/dist/query/ast/Expressions.js.map +1 -1
  46. package/dist/query/ast/Visitor.d.ts +2 -1
  47. package/dist/query/ast/Visitor.d.ts.map +1 -1
  48. package/dist/query/ast/Visitor.js +5 -0
  49. package/dist/query/ast/Visitor.js.map +1 -1
  50. package/dist/tests/model/ShoppingContext.d.ts.map +1 -1
  51. package/dist/tests/model/ShoppingContext.js +4 -1
  52. package/dist/tests/model/ShoppingContext.js.map +1 -1
  53. package/dist/tsconfig.tsbuildinfo +1 -1
  54. package/dist/workflows/WorkflowStorage.d.ts +4 -1
  55. package/dist/workflows/WorkflowStorage.d.ts.map +1 -1
  56. package/dist/workflows/WorkflowStorage.js +24 -13
  57. package/dist/workflows/WorkflowStorage.js.map +1 -1
  58. package/package.json +3 -3
  59. package/src/decorators/IColumn.ts +3 -0
  60. package/src/decorators/IForeignKeyConstraint.ts +19 -0
  61. package/src/decorators/Relate.ts +8 -1
  62. package/src/drivers/base/BaseDriver.ts +70 -1
  63. package/src/drivers/sql-server/ExpressionToSqlServer.ts +45 -3
  64. package/src/entity-query/EntityType.ts +9 -0
  65. package/src/migrations/Migrations.ts +25 -2
  66. package/src/migrations/postgres/PostgresAutomaticMigrations.ts +50 -0
  67. package/src/migrations/sql-server/SqlServerAutomaticMigrations.ts +47 -0
  68. package/src/model/EntitySource.ts +13 -1
  69. package/src/query/ast/ExpressionToSql.ts +37 -2
  70. package/src/query/ast/Expressions.ts +10 -1
  71. package/src/query/ast/Visitor.ts +6 -1
  72. package/src/tests/model/ShoppingContext.ts +4 -1
  73. package/src/workflows/WorkflowStorage.ts +27 -16
  74. package/tsconfig.json +1 -1
@@ -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 { IIndex } from "../decorators/IIndex.js";
4
- import SchemaRegistry from "../decorators/SchemaRegistry.js";
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|
@@ -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
- taskGroup: r.taskGroup
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
- w = db.workflows.add(state);
193
- w.taskGroup ||= "default";
194
- }
195
-
196
- for (const key in state) {
197
- if (Object.prototype.hasOwnProperty.call(state, key)) {
198
- const element = state[key];
199
- w[key] = element;
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
 
package/tsconfig.json CHANGED
@@ -13,7 +13,7 @@
13
13
  "emitDecoratorMetadata": true,
14
14
  "lib": [
15
15
  "ES2018",
16
- "esnext.disposable"
16
+ "ESNext.Disposable"
17
17
  ]
18
18
  },
19
19
  "include": [