@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.
- package/dist/common/CIMap.d.ts +16 -0
- package/dist/common/CIMap.d.ts.map +1 -0
- package/dist/common/CIMap.js +43 -0
- package/dist/common/CIMap.js.map +1 -0
- package/dist/common/IColumnSchema.d.ts +9 -0
- package/dist/common/IColumnSchema.d.ts.map +1 -1
- package/dist/drivers/base/BaseDriver.d.ts +0 -2
- package/dist/drivers/base/BaseDriver.d.ts.map +1 -1
- package/dist/drivers/base/BaseDriver.js.map +1 -1
- package/dist/drivers/base/ExistingSchema.d.ts +14 -0
- package/dist/drivers/base/ExistingSchema.d.ts.map +1 -0
- package/dist/drivers/base/ExistingSchema.js +34 -0
- package/dist/drivers/base/ExistingSchema.js.map +1 -0
- package/dist/drivers/postgres/PostgreSqlDriver.d.ts.map +1 -1
- package/dist/drivers/postgres/PostgreSqlDriver.js +0 -31
- package/dist/drivers/postgres/PostgreSqlDriver.js.map +1 -1
- package/dist/drivers/sql-server/SqlServerDriver.d.ts +0 -2
- package/dist/drivers/sql-server/SqlServerDriver.d.ts.map +1 -1
- package/dist/drivers/sql-server/SqlServerDriver.js +0 -41
- package/dist/drivers/sql-server/SqlServerDriver.js.map +1 -1
- package/dist/migrations/Migrations.d.ts +9 -2
- package/dist/migrations/Migrations.d.ts.map +1 -1
- package/dist/migrations/Migrations.js +54 -6
- package/dist/migrations/Migrations.js.map +1 -1
- package/dist/migrations/postgres/PostgresAutomaticMigrations.d.ts +2 -4
- package/dist/migrations/postgres/PostgresAutomaticMigrations.d.ts.map +1 -1
- package/dist/migrations/postgres/PostgresAutomaticMigrations.js +15 -39
- package/dist/migrations/postgres/PostgresAutomaticMigrations.js.map +1 -1
- package/dist/migrations/postgres/PostgresMigrations.d.ts +3 -0
- package/dist/migrations/postgres/PostgresMigrations.d.ts.map +1 -1
- package/dist/migrations/postgres/PostgresMigrations.js +68 -0
- package/dist/migrations/postgres/PostgresMigrations.js.map +1 -1
- package/dist/migrations/sql-server/SqlServerAutomaticMigrations.d.ts +2 -4
- package/dist/migrations/sql-server/SqlServerAutomaticMigrations.d.ts.map +1 -1
- package/dist/migrations/sql-server/SqlServerAutomaticMigrations.js +22 -45
- package/dist/migrations/sql-server/SqlServerAutomaticMigrations.js.map +1 -1
- package/dist/migrations/sql-server/SqlServerMigrations.d.ts +5 -0
- package/dist/migrations/sql-server/SqlServerMigrations.d.ts.map +1 -1
- package/dist/migrations/sql-server/SqlServerMigrations.js +77 -0
- package/dist/migrations/sql-server/SqlServerMigrations.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/src/common/CIMap.ts +50 -0
- package/src/common/IColumnSchema.ts +12 -0
- package/src/drivers/base/BaseDriver.ts +1 -3
- package/src/drivers/base/ExistingSchema.ts +60 -0
- package/src/drivers/postgres/PostgreSqlDriver.ts +1 -32
- package/src/drivers/sql-server/SqlServerDriver.ts +1 -42
- package/src/migrations/Migrations.ts +86 -14
- package/src/migrations/postgres/PostgresAutomaticMigrations.ts +22 -54
- package/src/migrations/postgres/PostgresMigrations.ts +78 -0
- package/src/migrations/sql-server/SqlServerAutomaticMigrations.ts +27 -58
- package/src/migrations/sql-server/SqlServerMigrations.ts +86 -0
- package/dist/migrations/ExistingSchema.d.ts +0 -9
- package/dist/migrations/ExistingSchema.d.ts.map +0 -1
- package/dist/migrations/ExistingSchema.js +0 -31
- package/dist/migrations/ExistingSchema.js.map +0 -1
- 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
|
-
|
|
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 {
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
214
|
+
async migrateTable(context: EntityContext, type: EntityType) {
|
|
196
215
|
|
|
197
|
-
|
|
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.
|
|
33
|
+
await this.migrateIndexInternal(context, indexDef, type);
|
|
49
34
|
}
|
|
50
35
|
}
|
|
51
36
|
|
|
52
|
-
async
|
|
37
|
+
async createColumn(type: EntityType, iterator: IColumn) {
|
|
53
38
|
|
|
54
39
|
const name = type.schema
|
|
55
|
-
|
|
56
|
-
|
|
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
|
|
43
|
+
const { quotedColumnName } = iterator;
|
|
63
44
|
|
|
64
|
-
const columnSet = new Set(columns.map((x) => x.name));
|
|
65
45
|
|
|
66
|
-
|
|
67
|
-
|
|
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
|
-
|
|
75
|
-
|
|
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
|
-
|
|
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
|
-
|
|
61
|
+
await this.executeQuery(def + ";");
|
|
92
62
|
|
|
93
|
-
|
|
63
|
+
}
|
|
94
64
|
|
|
95
|
-
|
|
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
|
-
|
|
103
|
+
|
|
136
104
|
const name = type.schema
|
|
137
|
-
|
|
138
|
-
|
|
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) {
|