@entity-access/entity-access 1.0.490 → 1.0.492

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 (58) hide show
  1. package/dist/common/CIMap.d.ts +16 -0
  2. package/dist/common/CIMap.d.ts.map +1 -0
  3. package/dist/common/CIMap.js +43 -0
  4. package/dist/common/CIMap.js.map +1 -0
  5. package/dist/common/IColumnSchema.d.ts +9 -0
  6. package/dist/common/IColumnSchema.d.ts.map +1 -1
  7. package/dist/drivers/base/BaseDriver.d.ts +0 -2
  8. package/dist/drivers/base/BaseDriver.d.ts.map +1 -1
  9. package/dist/drivers/base/BaseDriver.js.map +1 -1
  10. package/dist/drivers/base/ExistingSchema.d.ts +14 -0
  11. package/dist/drivers/base/ExistingSchema.d.ts.map +1 -0
  12. package/dist/drivers/base/ExistingSchema.js +34 -0
  13. package/dist/drivers/base/ExistingSchema.js.map +1 -0
  14. package/dist/drivers/postgres/PostgreSqlDriver.d.ts.map +1 -1
  15. package/dist/drivers/postgres/PostgreSqlDriver.js +0 -31
  16. package/dist/drivers/postgres/PostgreSqlDriver.js.map +1 -1
  17. package/dist/drivers/sql-server/SqlServerDriver.d.ts +0 -2
  18. package/dist/drivers/sql-server/SqlServerDriver.d.ts.map +1 -1
  19. package/dist/drivers/sql-server/SqlServerDriver.js +0 -41
  20. package/dist/drivers/sql-server/SqlServerDriver.js.map +1 -1
  21. package/dist/migrations/Migrations.d.ts +9 -2
  22. package/dist/migrations/Migrations.d.ts.map +1 -1
  23. package/dist/migrations/Migrations.js +54 -6
  24. package/dist/migrations/Migrations.js.map +1 -1
  25. package/dist/migrations/postgres/PostgresAutomaticMigrations.d.ts +2 -4
  26. package/dist/migrations/postgres/PostgresAutomaticMigrations.d.ts.map +1 -1
  27. package/dist/migrations/postgres/PostgresAutomaticMigrations.js +15 -39
  28. package/dist/migrations/postgres/PostgresAutomaticMigrations.js.map +1 -1
  29. package/dist/migrations/postgres/PostgresMigrations.d.ts +3 -0
  30. package/dist/migrations/postgres/PostgresMigrations.d.ts.map +1 -1
  31. package/dist/migrations/postgres/PostgresMigrations.js +68 -0
  32. package/dist/migrations/postgres/PostgresMigrations.js.map +1 -1
  33. package/dist/migrations/sql-server/SqlServerAutomaticMigrations.d.ts +2 -4
  34. package/dist/migrations/sql-server/SqlServerAutomaticMigrations.d.ts.map +1 -1
  35. package/dist/migrations/sql-server/SqlServerAutomaticMigrations.js +22 -45
  36. package/dist/migrations/sql-server/SqlServerAutomaticMigrations.js.map +1 -1
  37. package/dist/migrations/sql-server/SqlServerMigrations.d.ts +5 -0
  38. package/dist/migrations/sql-server/SqlServerMigrations.d.ts.map +1 -1
  39. package/dist/migrations/sql-server/SqlServerMigrations.js +77 -0
  40. package/dist/migrations/sql-server/SqlServerMigrations.js.map +1 -1
  41. package/dist/tsconfig.tsbuildinfo +1 -1
  42. package/package.json +1 -1
  43. package/src/common/CIMap.ts +50 -0
  44. package/src/common/IColumnSchema.ts +12 -0
  45. package/src/drivers/base/BaseDriver.ts +1 -3
  46. package/src/drivers/base/ExistingSchema.ts +60 -0
  47. package/src/drivers/postgres/PostgreSqlDriver.ts +1 -32
  48. package/src/drivers/sql-server/SqlServerDriver.ts +1 -42
  49. package/src/migrations/Migrations.ts +86 -14
  50. package/src/migrations/postgres/PostgresAutomaticMigrations.ts +22 -54
  51. package/src/migrations/postgres/PostgresMigrations.ts +78 -0
  52. package/src/migrations/sql-server/SqlServerAutomaticMigrations.ts +27 -58
  53. package/src/migrations/sql-server/SqlServerMigrations.ts +86 -0
  54. package/dist/migrations/ExistingSchema.d.ts +0 -9
  55. package/dist/migrations/ExistingSchema.d.ts.map +0 -1
  56. package/dist/migrations/ExistingSchema.js +0 -31
  57. package/dist/migrations/ExistingSchema.js.map +0 -1
  58. package/src/migrations/ExistingSchema.ts +0 -39
@@ -0,0 +1,50 @@
1
+ export default class CIMap<V> implements Map<string, V> {
2
+
3
+ [Symbol.toStringTag] = "CIMap";
4
+
5
+ get size() {
6
+ return this.map.size;
7
+ }
8
+
9
+ private map = new Map<string,V>();
10
+
11
+
12
+ clear() {
13
+ this.map.clear();
14
+ }
15
+ delete(key: string): boolean {
16
+ key = key?.toLowerCase();
17
+ return this.map.delete(key);
18
+ }
19
+ forEach(callbackfn: (value: V, key: string, map: Map<string, V>) => void, thisArg?: any): void {
20
+ return this.map.forEach(callbackfn);
21
+ }
22
+ get(key: string): V {
23
+ key = key?.toLowerCase();
24
+ return this.map.get(key);
25
+ }
26
+ has(key: string): boolean {
27
+ key = key?.toLowerCase();
28
+ return this.map.has(key);
29
+ }
30
+ set(key: string, value: V): this {
31
+ key = key?.toLowerCase();
32
+ this.map.set(key, value);
33
+ return this;
34
+ }
35
+
36
+ entries() {
37
+ return this.map.entries();
38
+ }
39
+ keys() {
40
+ return this.map.keys();
41
+ }
42
+ values() {
43
+ return this.map.values();
44
+ }
45
+ [Symbol.iterator]() {
46
+ return this.map[Symbol.iterator]();
47
+ }
48
+
49
+
50
+ }
@@ -8,4 +8,16 @@ export default interface IColumnSchema {
8
8
  computed?: any;
9
9
  ownerName?: string;
10
10
  ownerType?: string;
11
+ }
12
+
13
+ export interface IIndexSchema {
14
+ name: string;
15
+ }
16
+
17
+ export interface IConstraintSchema {
18
+ name: string;
19
+ }
20
+
21
+ export interface IForeignKeyConstraintSchema {
22
+ name: string;
11
23
  }
@@ -12,6 +12,7 @@ import ChangeEntry, { IChange } from "../../model/changes/ChangeEntry.js";
12
12
  import type EntityContext from "../../model/EntityContext.js";
13
13
  import { BinaryExpression, Constant, DeleteStatement, Expression, Identifier, InsertStatement, ReturnUpdated, SelectStatement, TableLiteral, UpdateStatement, UpsertStatement, ValuesStatement } from "../../query/ast/Expressions.js";
14
14
  import { Query } from "../../query/Query.js";
15
+ import type ExistingSchema from "./ExistingSchema.js";
15
16
 
16
17
  export interface IRecord {
17
18
  [key: string]: string | boolean | number | Date | Uint8Array | Blob;
@@ -210,9 +211,6 @@ export abstract class BaseConnection {
210
211
  return tx;
211
212
  }
212
213
 
213
- abstract getColumnSchema(schema: string): Promise<IColumnSchema[]>;
214
-
215
-
216
214
  protected abstract createDbTransaction(): Promise<EntityTransaction>;
217
215
 
218
216
  }
@@ -0,0 +1,60 @@
1
+ import CIMap from "../../common/CIMap.js";
2
+ import IColumnSchema, { IConstraintSchema, IForeignKeyConstraintSchema, IIndexSchema } from "../../common/IColumnSchema.js";
3
+ import { BaseConnection } from "./BaseDriver.js";
4
+
5
+ export default class ExistingSchema {
6
+
7
+ public readonly tables: Map<string, Map<string, IColumnSchema>>;
8
+ public readonly indexes: Map<string, IIndexSchema>;
9
+ public readonly constraints: Map<string, IConstraintSchema>;
10
+ public readonly foreignKeys: Map<string, IForeignKeyConstraintSchema>;
11
+
12
+ constructor(
13
+ caseInsensitive = false,
14
+ {
15
+ columns,
16
+ indexes,
17
+ constraints,
18
+ foreignKeys
19
+ }: {
20
+ columns: IColumnSchema[],
21
+ indexes?: IIndexSchema[],
22
+ constraints?: IConstraintSchema[],
23
+ foreignKeys?: IForeignKeyConstraintSchema[]
24
+ }
25
+ ) {
26
+
27
+ this.tables = caseInsensitive
28
+ ? new CIMap<CIMap<IColumnSchema>>()
29
+ : new Map<string,Map<string, IColumnSchema>>();
30
+
31
+ for (const c of columns) {
32
+ const table = c.ownerName;
33
+ let list = this.tables.get(table);
34
+ if (!list) {
35
+ list = caseInsensitive ? new CIMap<IColumnSchema>() : new Map<string, IColumnSchema>();
36
+ this.tables.set(table, list);
37
+ }
38
+ list.set(c.name, c);
39
+ }
40
+
41
+ this.indexes = new CIMap<IIndexSchema>();
42
+
43
+ for (const index of indexes) {
44
+ this.indexes.set(index.name, index);
45
+ }
46
+
47
+ this.constraints = new CIMap<IConstraintSchema>();
48
+
49
+ for (const constraint of constraints) {
50
+ this.constraints.set(constraint.name, constraint);
51
+ }
52
+
53
+ this.foreignKeys = new CIMap<IForeignKeyConstraintSchema>();
54
+
55
+ for (const fk of foreignKeys) {
56
+ this.foreignKeys.set(fk.name, fk);
57
+ }
58
+ }
59
+
60
+ }
@@ -11,6 +11,7 @@ import DateTime from "../../types/DateTime.js";
11
11
  import { BaseConnection, BaseDriver, EntityTransaction, IDbConnectionString, IDbReader, IQuery, toQuery } from "../base/BaseDriver.js";
12
12
  import pg from "pg";
13
13
  import Cursor from "pg-cursor";
14
+ import ExistingSchema from "../base/ExistingSchema.js";
14
15
  export interface IPgSqlConnectionString extends IDbConnectionString {
15
16
 
16
17
  user?: string, // default process.env.PGUSER || process.env.USER
@@ -220,38 +221,6 @@ class PostgreSqlConnection extends BaseConnection {
220
221
  return new PostgresAutomaticMigrations(context);
221
222
  }
222
223
 
223
- async getColumnSchema(schema: string): Promise<IColumnSchema[]> {
224
- const text = `
225
- select
226
- column_name as "columnName",
227
- case data_type
228
- when 'bigint' then 'BigInt'
229
- when 'boolean' then 'Boolean'
230
- when 'timestamp' then 'DateTime'
231
- when 'timestamp with time zone' then 'DateTime'
232
- when 'timestamp without time zone' then 'DateTime'
233
- when 'integer' then 'Int'
234
- when 'real' then 'Double'
235
- when 'numeric' then 'Decimal'
236
- else 'Char' end as "dataType",
237
- case
238
- when is_nullable = 'YES' then true
239
- else false end as "nullable",
240
- character_maximum_length as "length",
241
- case
242
- when is_identity = 'YES' then 'identity'
243
- else null end as "identity",
244
- case
245
- when is_generated = 'YES' then '() => 1'
246
- else null end as "computed",
247
- table_name as "ownerName",
248
- 'table' as "ownerType"
249
- from information_schema.columns
250
- where table_schema = $1`;
251
- const r = await this.executeQuery({ text, values: [ schema ]});
252
- return r.rows;
253
- }
254
-
255
224
  public async executeReader(command: IQuery, signal?: AbortSignal): Promise<IDbReader> {
256
225
  return new DbReader(command, this, signal);
257
226
  }
@@ -11,6 +11,7 @@ import EntityType from "../../entity-query/EntityType.js";
11
11
  import DateTime from "../../types/DateTime.js";
12
12
  import IColumnSchema from "../../common/IColumnSchema.js";
13
13
  import type EntityContext from "../../model/EntityContext.js";
14
+ import ExistingSchema from "../base/ExistingSchema.js";
14
15
 
15
16
  export type ISqlServerConnectionString = IDbConnectionString & sql.config;
16
17
 
@@ -129,48 +130,6 @@ export class SqlServerConnection extends BaseConnection {
129
130
  super(driver);
130
131
  }
131
132
 
132
- async getColumnSchema(schema: string): Promise<IColumnSchema[]> {
133
- const text = `
134
- SELECT
135
- COLUMN_NAME as [name],
136
- CASE DATA_TYPE
137
- WHEN 'bit' THEN 'Boolean'
138
- WHEN 'int' Then 'Int'
139
- WHEN 'bigint' THEN 'BigInt'
140
- WHEN 'date' then 'DateTime'
141
- WHEN 'datetime' then 'DateTime'
142
- WHEN 'datetime2' then 'DateTime'
143
- WHEN 'real' then 'Float'
144
- WHEN 'double' then 'Double'
145
- WHEN 'decimal' then 'Decimal'
146
- WHEN 'identity' then 'UUID'
147
- WHEN 'varbinary' then 'ByteArray'
148
- WHEN 'geometry' then 'Geometry'
149
- ELSE 'Char'
150
- END as [dataType],
151
- CASE WHEN IS_NULLABLE = 'YES' THEN 1 ELSE 0 END as [nullable],
152
- CHARACTER_MAXIMUM_LENGTH as [length],
153
- CASE
154
- WHEN COLUMN_DEFAULT = 'getutcdate()' then '() => Sql.date.now()'
155
- WHEN COLUMN_DEFAULT = '(getutcdate())' then '() => Sql.date.now()'
156
- WHEN COLUMN_DEFAULT = '(newid())' then '() => Sql.crypto.randomUUID()'
157
- WHEN (COLUMN_DEFAULT = '(0)' OR COLUMN_DEFAULT = '((0))')
158
- AND DATA_TYPE = 'bit' THEN '() => false'
159
- WHEN (COLUMN_DEFAULT = '(1)' OR COLUMN_DEFAULT = '((1))')
160
- AND DATA_TYPE = 'bit' THEN '() => true'
161
- WHEN COLUMN_DEFAULT is NULL THEN ''
162
- ELSE '() => ' + COLUMN_DEFAULT
163
- END as [default],
164
- ColumnProperty(OBJECT_ID(TABLE_SCHEMA+'.'+TABLE_NAME),COLUMN_NAME,'IsComputed') as [computed],
165
- TABLE_NAME as [ownerName],
166
- 'table' as [ownerType]
167
- FROM INFORMATION_SCHEMA.COLUMNS
168
- WHERE TABLE_SCHEMA = $1
169
- `;
170
- const r = await this.executeQuery({ text, values: [schema] });
171
- return r.rows;
172
- }
173
-
174
133
  public async executeReader(command: IQuery, signal?: AbortSignal): Promise<IDbReader> {
175
134
  command = toQuery(command);
176
135
  let rq = await this.newRequest(signal);
@@ -1,3 +1,4 @@
1
+ import CIMap from "../common/CIMap.js";
1
2
  import Logger, { ConsoleLogger } from "../common/Logger.js";
2
3
  import { modelSymbol } from "../common/symbols/symbols.js";
3
4
  import type QueryCompiler from "../compiler/QueryCompiler.js";
@@ -6,18 +7,20 @@ import { IColumn } from "../decorators/IColumn.js";
6
7
  import type { IForeignKeyConstraint } from "../decorators/IForeignKeyConstraint.js";
7
8
  import type { IIndex } from "../decorators/IIndex.js";
8
9
  import type { BaseConnection, IQuery, IQueryResult } from "../drivers/base/BaseDriver.js";
10
+ import ExistingSchema from "../drivers/base/ExistingSchema.js";
9
11
  import type EntityType from "../entity-query/EntityType.js";
10
12
  import type EntityContext from "../model/EntityContext.js";
11
13
  import type EntityQuery from "../model/EntityQuery.js";
12
- import ExistingSchema from "./ExistingSchema.js";
13
14
 
14
15
  export default abstract class Migrations {
15
16
 
16
17
  logger: Logger;
17
18
 
19
+ protected schemaCache = new Map<string, ExistingSchema>();
20
+
18
21
  constructor(
19
22
  private context: EntityContext,
20
- private connection: BaseConnection = context.connection,
23
+ protected connection: BaseConnection = context.connection,
21
24
  protected compiler: QueryCompiler = context.driver.compiler
22
25
  ) {
23
26
 
@@ -76,12 +79,17 @@ export default abstract class Migrations {
76
79
  }
77
80
  }
78
81
 
82
+ const schema = await this.getSchema(type);
83
+
79
84
  await this.migrateTable(context, type);
80
85
 
81
86
  // create constraints
82
87
  for (const iterator of type.checkConstraints) {
88
+ if (schema.constraints.has(iterator.name)) {
89
+ continue;
90
+ }
83
91
  const source = context.query(type.typeClass) as EntityQuery<any>;
84
- const { target , textQuery } = this.compiler.compileToSql(source, `(p) => ${iterator.filter}` as any);
92
+ const { textQuery } = this.compiler.compileToSql(source, `(p) => ${iterator.filter}` as any);
85
93
  const r = new RegExp(source.selectStatement.sourceParameter.name + "\\.", "ig");
86
94
  iterator.filter = textQuery.join("").replace(r, "") as any;
87
95
  await this.migrateCheckConstraint(context, iterator, type);
@@ -91,20 +99,21 @@ export default abstract class Migrations {
91
99
  await this.migrateIndexInternal(context, index, type);
92
100
  }
93
101
 
94
- for (const { isInverseRelation , foreignKeyConstraint, relatedTypeClass } of type.relations) {
102
+ if (createIndexForForeignKeys) {
103
+ postMigration.push(() =>
104
+ this.createIndexForForeignKeys(context, type, type.nonKeys.filter((x) =>
105
+ x.fkRelation
106
+ && (!x.key || type.keys.indexOf(x) !== 0)
107
+ && !x.fkRelation?.doNotCreateIndex))
108
+ );
109
+ }
95
110
 
96
- if (createIndexForForeignKeys) {
97
- postMigration.push(() =>
98
- this.createIndexForForeignKeys(context, type, type.nonKeys.filter((x) =>
99
- x.fkRelation
100
- && (!x.key || type.keys.indexOf(x) !== 0)
101
- && !x.fkRelation?.doNotCreateIndex))
102
- );
103
- }
111
+ for (const { isInverseRelation , foreignKeyConstraint, relatedTypeClass } of type.relations) {
104
112
 
105
113
  if (isInverseRelation) {
106
114
  continue;
107
115
  }
116
+
108
117
  if (!foreignKeyConstraint) {
109
118
  continue;
110
119
  }
@@ -120,6 +129,9 @@ export default abstract class Migrations {
120
129
  // foreignKeyConstraint.refColumns.push(relatedEntity.getProperty(iterator.name).field);
121
130
  // }
122
131
 
132
+ if(schema.foreignKeys.has(foreignKeyConstraint.name)) {
133
+ continue;
134
+ }
123
135
  postMigration.push(() => this.migrateForeignKey(context, foreignKeyConstraint));
124
136
  }
125
137
  }
@@ -173,6 +185,13 @@ export default abstract class Migrations {
173
185
 
174
186
  index = { ... index };
175
187
 
188
+ const schema = await this.getSchema(type);
189
+
190
+ if (schema.indexes.has(index.name)) {
191
+ return;
192
+ }
193
+
194
+
176
195
  for (const column of index.columns) {
177
196
  const c = type.getProperty(column.name);
178
197
  if (c.field) {
@@ -192,14 +211,67 @@ export default abstract class Migrations {
192
211
 
193
212
  }
194
213
 
195
- abstract migrateIndex(context: EntityContext, index: IIndex, type: EntityType);
214
+ async migrateTable(context: EntityContext, type: EntityType) {
196
215
 
197
- abstract migrateTable(context: EntityContext, type: EntityType): Promise<any>;
216
+ const schema = await this.getSchema(type);
217
+
218
+ // create table if not exists...
219
+ const nonKeyColumns = type.nonKeys;
220
+ const keys = type.keys;
221
+
222
+ if (!schema.tables.has(type.name)) {
223
+ await this.createTable(type, keys);
224
+ }
225
+
226
+ await this.createColumns(type, nonKeyColumns);
227
+
228
+ }
229
+
230
+ async createColumns(type: EntityType, nonKeyColumns: IColumn[]) {
231
+ const name = type.schema
232
+ ? type.schema + "." + type.name
233
+ : type.name;
234
+
235
+ if (nonKeyColumns.length > 1) {
236
+ nonKeyColumns.sort((a, b) => (a.order ?? 0) - (b.order ?? 0));
237
+ }
238
+
239
+ const schema = await this.getSchema(type);
240
+
241
+ const table = schema.tables.get(type.name);
242
+
243
+ for (const iterator of nonKeyColumns) {
244
+ if (table?.has(iterator.columnName)) {
245
+ continue;
246
+ }
247
+ await this.createColumn(type, iterator);
248
+ }
249
+
250
+ }
251
+
252
+ abstract createTable(type: EntityType, keys: IColumn[]);
253
+
254
+ abstract createColumn(type: EntityType, column: IColumn);
255
+
256
+ abstract migrateIndex(context: EntityContext, index: IIndex, type: EntityType);
198
257
 
199
258
  abstract migrateForeignKey(context: EntityContext, constraint: IForeignKeyConstraint);
200
259
 
201
260
  abstract migrateCheckConstraint(context: EntityContext, checkConstraint: ICheckConstraint, type: EntityType);
202
261
 
262
+ public async getSchema(type: EntityType): Promise<ExistingSchema> {
263
+ const schema = type.schema || "__ default + __ schema ";
264
+ let s = this.schemaCache.get(schema);
265
+ if (s) {
266
+ return s;
267
+ }
268
+ s = await this.getExistingSchema(type);
269
+ this.schemaCache.set(schema, s);
270
+ return s;
271
+ }
272
+
273
+ protected abstract getExistingSchema(type: EntityType): Promise<ExistingSchema>;
274
+
203
275
  protected executeQuery(command: IQuery, signal?: AbortSignal): Promise<IQueryResult> {
204
276
  const text = typeof command === "string" ? command : command.text;
205
277
  this.logger?.log(text);
@@ -4,9 +4,9 @@ import { IColumn } from "../../decorators/IColumn.js";
4
4
  import { IForeignKeyConstraint } from "../../decorators/IForeignKeyConstraint.js";
5
5
  import { IIndex } from "../../decorators/IIndex.js";
6
6
  import { BaseConnection, BaseDriver } from "../../drivers/base/BaseDriver.js";
7
+ import ExistingSchema from "../../drivers/base/ExistingSchema.js";
7
8
  import EntityType from "../../entity-query/EntityType.js";
8
9
  import type EntityContext from "../../model/EntityContext.js";
9
- import ExistingSchema from "../ExistingSchema.js";
10
10
  import PostgresMigrations from "./PostgresMigrations.js";
11
11
 
12
12
  export default class PostgresAutomaticMigrations extends PostgresMigrations {
@@ -20,21 +20,6 @@ export default class PostgresAutomaticMigrations extends PostgresMigrations {
20
20
  )`);
21
21
  }
22
22
 
23
- async migrateTable(context: EntityContext, type: EntityType) {
24
-
25
-
26
- // create table if not exists...
27
- const nonKeyColumns = type.nonKeys;
28
- const keys = type.keys;
29
-
30
- const driver = context.connection;
31
-
32
- await this.createTable(driver, type, keys);
33
-
34
- await this.createColumns(driver, type, nonKeyColumns);
35
-
36
- }
37
-
38
23
  async createIndexForForeignKeys(context: EntityContext, type: EntityType, fkColumns: IColumn[]) {
39
24
  for (const iterator of fkColumns) {
40
25
  const filter = iterator.nullable
@@ -45,56 +30,39 @@ export default class PostgresAutomaticMigrations extends PostgresMigrations {
45
30
  columns: [{ name: iterator.columnName, descending: iterator.indexOrder !== "ascending"}],
46
31
  filter
47
32
  };
48
- await this.migrateIndex(context, indexDef, type);
33
+ await this.migrateIndexInternal(context, indexDef, type);
49
34
  }
50
35
  }
51
36
 
52
- async createColumns(driver: BaseConnection, type: EntityType, nonKeyColumns: IColumn[]) {
37
+ async createColumn(type: EntityType, iterator: IColumn) {
53
38
 
54
39
  const name = type.schema
55
- ? type.schema + "." + type.name
56
- : type.name;
57
-
58
- if (nonKeyColumns.length > 1) {
59
- nonKeyColumns.sort((a, b) => (a.order ?? 0) - (b.order ?? 0));
60
- }
40
+ ? type.schema + "." + type.name
41
+ : type.name;
61
42
 
62
- const columns = await ExistingSchema.getSchema(driver, type.schema || "public", type.name, true);
43
+ const { quotedColumnName } = iterator;
63
44
 
64
- const columnSet = new Set(columns.map((x) => x.name));
65
45
 
66
- for (const iterator of nonKeyColumns) {
67
- const { columnName } = iterator;
68
- if (columnSet.has(columnName)) {
69
- continue;
70
- }
71
- let def = `ALTER TABLE ${name} ADD COLUMN IF NOT EXISTS ${columnName} `;
72
- def += this.getColumnDefinition(iterator);
46
+ let def = `ALTER TABLE ${name} ADD COLUMN IF NOT EXISTS ${quotedColumnName} `;
47
+ def += this.getColumnDefinition(iterator);
73
48
 
74
- if (iterator.nullable !== true) {
75
- def += " NOT NULL ";
76
- }
77
-
78
- if (iterator.generated === "computed") {
79
- def += ` GENERATED ALWAYS AS (${iterator.computed}) ${iterator.stored ? "STORED" : ""} \r\n\t`;
80
- }
81
-
82
- if (typeof iterator.default === "string") {
83
- def += " DEFAULT " + iterator.default;
84
- }
49
+ if (iterator.nullable !== true) {
50
+ def += " NOT NULL ";
51
+ }
85
52
 
86
- await this.executeQuery(def + ";");
53
+ if (iterator.generated === "computed") {
54
+ def += ` GENERATED ALWAYS AS (${iterator.computed}) ${iterator.stored ? "STORED" : ""} \r\n\t`;
87
55
  }
88
56
 
89
- }
57
+ if (typeof iterator.default === "string") {
58
+ def += " DEFAULT " + iterator.default;
59
+ }
90
60
 
91
- async createTable(driver: BaseConnection, type: EntityType, keys: IColumn[]) {
61
+ await this.executeQuery(def + ";");
92
62
 
93
- const columns = await ExistingSchema.getSchema(driver, type.schema || "public", type.name, true);
63
+ }
94
64
 
95
- if (columns.length) {
96
- return;
97
- }
65
+ async createTable(type: EntityType, keys: IColumn[]) {
98
66
 
99
67
  const name = type.schema
100
68
  ? type.schema + "." + type.name
@@ -132,10 +100,10 @@ export default class PostgresAutomaticMigrations extends PostgresMigrations {
132
100
  }
133
101
 
134
102
  async migrateIndex(context: EntityContext, index: IIndex, type: EntityType) {
135
- const driver = context.connection;
103
+
136
104
  const name = type.schema
137
- ? type.schema + "." + type.name
138
- : type.name;
105
+ ? type.schema + "." + type.name
106
+ : type.name;
139
107
  const indexName = index.name;
140
108
  const columns = [];
141
109
  for (const column of index.columns) {
@@ -1,8 +1,86 @@
1
1
  import { IColumn } from "../../decorators/IColumn.js";
2
+ import ExistingSchema from "../../drivers/base/ExistingSchema.js";
3
+ import EntityType from "../../entity-query/EntityType.js";
2
4
  import Migrations from "../Migrations.js";
3
5
 
4
6
  export default abstract class PostgresMigrations extends Migrations {
5
7
 
8
+ async getExistingSchema(type: EntityType) {
9
+
10
+ const schema = type.schema || "public";
11
+ const values = [schema];
12
+
13
+ let text = `
14
+ select
15
+ column_name as "name",
16
+ case data_type
17
+ when 'bigint' then 'BigInt'
18
+ when 'boolean' then 'Boolean'
19
+ when 'timestamp' then 'DateTime'
20
+ when 'timestamp with time zone' then 'DateTime'
21
+ when 'timestamp without time zone' then 'DateTime'
22
+ when 'integer' then 'Int'
23
+ when 'real' then 'Double'
24
+ when 'numeric' then 'Decimal'
25
+ else 'Char' end as "dataType",
26
+ case
27
+ when is_nullable = 'YES' then true
28
+ else false end as "nullable",
29
+ character_maximum_length as "length",
30
+ case
31
+ when is_identity = 'YES' then 'identity'
32
+ else null end as "identity",
33
+ case
34
+ when is_generated = 'YES' then '() => 1'
35
+ else null end as "computed",
36
+ table_name as "ownerName",
37
+ 'table' as "ownerType"
38
+ from information_schema.columns
39
+ where table_schema = $1`;
40
+
41
+ let r = await this.executeQuery({ text, values});
42
+ const columns = r.rows;
43
+
44
+ text = `
45
+ SELECT
46
+ tc.constraint_name as "name"
47
+ FROM information_schema.table_constraints AS tc
48
+ JOIN information_schema.key_column_usage AS kcu
49
+ ON tc.constraint_name = kcu.constraint_name
50
+ AND tc.table_schema = kcu.table_schema
51
+ JOIN information_schema.constraint_column_usage AS ccu
52
+ ON ccu.constraint_name = tc.constraint_name
53
+ WHERE
54
+ tc.constraint_type = 'FOREIGN KEY'
55
+ AND tc.table_schema = $1
56
+ `;
57
+ r = await this.executeQuery({ text, values});
58
+ const foreignKeys = r.rows;
59
+
60
+ text = `
61
+ SELECT indexname as "name" FROM pg_indexes where schemaName=$1`;
62
+ r = await this.executeQuery({ text, values });
63
+ const indexes = r.rows;
64
+
65
+ text = `
66
+ SELECT
67
+ tc.constraint_name as "name"
68
+ FROM information_schema.table_constraints AS tc
69
+ JOIN information_schema.key_column_usage AS kcu
70
+ ON tc.constraint_name = kcu.constraint_name
71
+ AND tc.table_schema = kcu.table_schema
72
+ JOIN information_schema.constraint_column_usage AS ccu
73
+ ON ccu.constraint_name = tc.constraint_name
74
+ WHERE
75
+ tc.constraint_type <> 'FOREIGN KEY'
76
+ AND tc.table_schema = $1`;
77
+
78
+ r = await this.executeQuery({ text, values });
79
+ const constraints = r.rows;
80
+
81
+ return new ExistingSchema(false, { columns, foreignKeys, indexes, constraints });
82
+ }
83
+
6
84
  protected getColumnDefinition(iterator: IColumn) {
7
85
  if (iterator.dataType === "Decimal") {
8
86
  if (iterator.precision && iterator.scale) {