@javalabs/prisma-client 1.0.26 → 1.0.29
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/CODEOWNERS +1 -1
- package/README.md +269 -269
- package/migration-config.json +63 -63
- package/migration-config.json.bk +95 -95
- package/migrations/add_reserved_amount.sql +7 -7
- package/package.json +44 -44
- package/prisma/migrations/add_uuid_to_transactions.sql +13 -13
- package/prisma/schema.prisma +609 -607
- package/src/index.ts +23 -23
- package/src/prisma-factory.service.ts +40 -40
- package/src/prisma.module.ts +9 -9
- package/src/prisma.service.ts +16 -16
- package/src/scripts/add-uuid-to-table.ts +138 -138
- package/src/scripts/create-tenant-schemas.ts +145 -145
- package/src/scripts/data-migration/batch-migrator.ts +248 -248
- package/src/scripts/data-migration/data-transformer.ts +426 -426
- package/src/scripts/data-migration/db-connector.ts +120 -120
- package/src/scripts/data-migration/dependency-resolver.ts +174 -174
- package/src/scripts/data-migration/entity-discovery.ts +196 -196
- package/src/scripts/data-migration/foreign-key-manager.ts +277 -277
- package/src/scripts/data-migration/migration-config.json +63 -63
- package/src/scripts/data-migration/migration-tool.ts +509 -509
- package/src/scripts/data-migration/schema-utils.ts +248 -248
- package/src/scripts/data-migration/tenant-migrator.ts +201 -201
- package/src/scripts/data-migration/typecast-manager.ts +193 -193
- package/src/scripts/data-migration/types.ts +113 -113
- package/src/scripts/database-initializer.ts +49 -49
- package/src/scripts/drop-database.ts +104 -104
- package/src/scripts/dump-source-db.sh +61 -61
- package/src/scripts/encrypt-user-passwords.ts +36 -36
- package/src/scripts/error-handler.ts +117 -117
- package/src/scripts/fix-data-types.ts +241 -241
- package/src/scripts/fix-enum-values.ts +357 -357
- package/src/scripts/fix-schema-discrepancies.ts +317 -317
- package/src/scripts/fix-table-indexes.ts +601 -601
- package/src/scripts/migrate-schema-structure.ts +90 -90
- package/src/scripts/migrate-uuid.ts +76 -76
- package/src/scripts/post-migration-validator.ts +526 -526
- package/src/scripts/pre-migration-validator.ts +610 -610
- package/src/scripts/reset-database.ts +263 -263
- package/src/scripts/retry-failed-migrations.ts +416 -416
- package/src/scripts/run-migration.ts +707 -707
- package/src/scripts/schema-sync.ts +128 -128
- package/src/scripts/sequence-sync-cli.ts +416 -416
- package/src/scripts/sequence-synchronizer.ts +127 -127
- package/src/scripts/sync-enum-types.ts +170 -170
- package/src/scripts/sync-enum-values.ts +563 -563
- package/src/scripts/truncate-database.ts +123 -123
- package/src/scripts/verify-migration-setup.ts +135 -135
- package/tsconfig.json +17 -17
- package/dist/index.d.ts +0 -7
- package/dist/index.js +0 -34
- package/dist/index.js.map +0 -1
- package/dist/prisma-factory.service.d.ts +0 -9
- package/dist/prisma-factory.service.js +0 -47
- package/dist/prisma-factory.service.js.map +0 -1
- package/dist/prisma.module.d.ts +0 -2
- package/dist/prisma.module.js +0 -23
- package/dist/prisma.module.js.map +0 -1
- package/dist/prisma.service.d.ts +0 -6
- package/dist/prisma.service.js +0 -27
- package/dist/prisma.service.js.map +0 -1
- package/dist/scripts/add-uuid-to-table.d.ts +0 -8
- package/dist/scripts/add-uuid-to-table.js +0 -98
- package/dist/scripts/add-uuid-to-table.js.map +0 -1
- package/dist/scripts/create-tenant-schemas.d.ts +0 -1
- package/dist/scripts/create-tenant-schemas.js +0 -117
- package/dist/scripts/create-tenant-schemas.js.map +0 -1
- package/dist/scripts/data-migration/batch-migrator.d.ts +0 -20
- package/dist/scripts/data-migration/batch-migrator.js +0 -134
- package/dist/scripts/data-migration/batch-migrator.js.map +0 -1
- package/dist/scripts/data-migration/data-transformer.d.ts +0 -26
- package/dist/scripts/data-migration/data-transformer.js +0 -278
- package/dist/scripts/data-migration/data-transformer.js.map +0 -1
- package/dist/scripts/data-migration/db-connector.d.ts +0 -12
- package/dist/scripts/data-migration/db-connector.js +0 -94
- package/dist/scripts/data-migration/db-connector.js.map +0 -1
- package/dist/scripts/data-migration/dependency-resolver.d.ts +0 -18
- package/dist/scripts/data-migration/dependency-resolver.js +0 -132
- package/dist/scripts/data-migration/dependency-resolver.js.map +0 -1
- package/dist/scripts/data-migration/entity-discovery.d.ts +0 -11
- package/dist/scripts/data-migration/entity-discovery.js +0 -152
- package/dist/scripts/data-migration/entity-discovery.js.map +0 -1
- package/dist/scripts/data-migration/foreign-key-manager.d.ts +0 -18
- package/dist/scripts/data-migration/foreign-key-manager.js +0 -160
- package/dist/scripts/data-migration/foreign-key-manager.js.map +0 -1
- package/dist/scripts/data-migration/migration-tool.d.ts +0 -48
- package/dist/scripts/data-migration/migration-tool.js +0 -290
- package/dist/scripts/data-migration/migration-tool.js.map +0 -1
- package/dist/scripts/data-migration/schema-utils.d.ts +0 -18
- package/dist/scripts/data-migration/schema-utils.js +0 -207
- package/dist/scripts/data-migration/schema-utils.js.map +0 -1
- package/dist/scripts/data-migration/tenant-migrator.d.ts +0 -15
- package/dist/scripts/data-migration/tenant-migrator.js +0 -117
- package/dist/scripts/data-migration/tenant-migrator.js.map +0 -1
- package/dist/scripts/data-migration/typecast-manager.d.ts +0 -9
- package/dist/scripts/data-migration/typecast-manager.js +0 -179
- package/dist/scripts/data-migration/typecast-manager.js.map +0 -1
- package/dist/scripts/data-migration/types.d.ts +0 -100
- package/dist/scripts/data-migration/types.js +0 -3
- package/dist/scripts/data-migration/types.js.map +0 -1
- package/dist/scripts/database-initializer.d.ts +0 -5
- package/dist/scripts/database-initializer.js +0 -45
- package/dist/scripts/database-initializer.js.map +0 -1
- package/dist/scripts/drop-database.d.ts +0 -10
- package/dist/scripts/drop-database.js +0 -81
- package/dist/scripts/drop-database.js.map +0 -1
- package/dist/scripts/encrypt-user-passwords.d.ts +0 -1
- package/dist/scripts/encrypt-user-passwords.js +0 -33
- package/dist/scripts/encrypt-user-passwords.js.map +0 -1
- package/dist/scripts/error-handler.d.ts +0 -12
- package/dist/scripts/error-handler.js +0 -82
- package/dist/scripts/error-handler.js.map +0 -1
- package/dist/scripts/fix-data-types.d.ts +0 -10
- package/dist/scripts/fix-data-types.js +0 -185
- package/dist/scripts/fix-data-types.js.map +0 -1
- package/dist/scripts/fix-enum-values.d.ts +0 -17
- package/dist/scripts/fix-enum-values.js +0 -234
- package/dist/scripts/fix-enum-values.js.map +0 -1
- package/dist/scripts/fix-schema-discrepancies.d.ts +0 -21
- package/dist/scripts/fix-schema-discrepancies.js +0 -240
- package/dist/scripts/fix-schema-discrepancies.js.map +0 -1
- package/dist/scripts/fix-table-indexes.d.ts +0 -26
- package/dist/scripts/fix-table-indexes.js +0 -460
- package/dist/scripts/fix-table-indexes.js.map +0 -1
- package/dist/scripts/migrate-schema-structure.d.ts +0 -1
- package/dist/scripts/migrate-schema-structure.js +0 -76
- package/dist/scripts/migrate-schema-structure.js.map +0 -1
- package/dist/scripts/migrate-uuid.d.ts +0 -2
- package/dist/scripts/migrate-uuid.js +0 -57
- package/dist/scripts/migrate-uuid.js.map +0 -1
- package/dist/scripts/post-migration-validator.d.ts +0 -34
- package/dist/scripts/post-migration-validator.js +0 -363
- package/dist/scripts/post-migration-validator.js.map +0 -1
- package/dist/scripts/pre-migration-validator.d.ts +0 -25
- package/dist/scripts/pre-migration-validator.js +0 -491
- package/dist/scripts/pre-migration-validator.js.map +0 -1
- package/dist/scripts/reset-database.d.ts +0 -17
- package/dist/scripts/reset-database.js +0 -202
- package/dist/scripts/reset-database.js.map +0 -1
- package/dist/scripts/retry-failed-migrations.d.ts +0 -14
- package/dist/scripts/retry-failed-migrations.js +0 -301
- package/dist/scripts/retry-failed-migrations.js.map +0 -1
- package/dist/scripts/run-migration.d.ts +0 -1
- package/dist/scripts/run-migration.js +0 -512
- package/dist/scripts/run-migration.js.map +0 -1
- package/dist/scripts/schema-sync.d.ts +0 -1
- package/dist/scripts/schema-sync.js +0 -85
- package/dist/scripts/schema-sync.js.map +0 -1
- package/dist/scripts/sequence-sync-cli.d.ts +0 -2
- package/dist/scripts/sequence-sync-cli.js +0 -287
- package/dist/scripts/sequence-sync-cli.js.map +0 -1
- package/dist/scripts/sequence-synchronizer.d.ts +0 -8
- package/dist/scripts/sequence-synchronizer.js +0 -88
- package/dist/scripts/sequence-synchronizer.js.map +0 -1
- package/dist/scripts/sync-enum-types.d.ts +0 -13
- package/dist/scripts/sync-enum-types.js +0 -139
- package/dist/scripts/sync-enum-types.js.map +0 -1
- package/dist/scripts/sync-enum-values.d.ts +0 -20
- package/dist/scripts/sync-enum-values.js +0 -336
- package/dist/scripts/sync-enum-values.js.map +0 -1
- package/dist/scripts/truncate-database.d.ts +0 -10
- package/dist/scripts/truncate-database.js +0 -100
- package/dist/scripts/truncate-database.js.map +0 -1
- package/dist/scripts/verify-migration-setup.d.ts +0 -11
- package/dist/scripts/verify-migration-setup.js +0 -120
- package/dist/scripts/verify-migration-setup.js.map +0 -1
- package/dist/tsconfig.tsbuildinfo +0 -1
- package/prisma/migrations/add_athena_match_fields.sql +0 -18
- package/prisma/migrations/add_bank_receipt_number.sql +0 -9
|
@@ -1,248 +1,248 @@
|
|
|
1
|
-
import { Logger } from "@nestjs/common";
|
|
2
|
-
import { SchemaUtils } from "./schema-utils";
|
|
3
|
-
import { DataTransformer } from "./data-transformer";
|
|
4
|
-
import { TypecastManager } from "./typecast-manager";
|
|
5
|
-
import {
|
|
6
|
-
ColumnSchema,
|
|
7
|
-
DatabaseConnections,
|
|
8
|
-
MigrationOptions,
|
|
9
|
-
TableConfig,
|
|
10
|
-
} from "./types";
|
|
11
|
-
|
|
12
|
-
const BATCH_SIZE = 100;
|
|
13
|
-
const MAX_RETRIES = 3;
|
|
14
|
-
const RETRY_BASE_DELAY = 1000;
|
|
15
|
-
|
|
16
|
-
export class BatchMigrator {
|
|
17
|
-
private readonly logger = new Logger("BatchMigrator");
|
|
18
|
-
private readonly typecastManager: TypecastManager;
|
|
19
|
-
|
|
20
|
-
constructor(
|
|
21
|
-
private readonly schemaUtils: SchemaUtils,
|
|
22
|
-
private readonly dataTransformer: DataTransformer,
|
|
23
|
-
private readonly connections: DatabaseConnections,
|
|
24
|
-
private readonly options: MigrationOptions,
|
|
25
|
-
private readonly providerId?: string | null
|
|
26
|
-
) {
|
|
27
|
-
this.typecastManager = new TypecastManager();
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
async validateSchema(
|
|
31
|
-
sourceColumns: ColumnSchema[],
|
|
32
|
-
targetColumns: ColumnSchema[]
|
|
33
|
-
): Promise<void> {
|
|
34
|
-
for (const targetColumn of targetColumns) {
|
|
35
|
-
const sourceColumn = sourceColumns.find(
|
|
36
|
-
(col) => col.column_name === targetColumn.column_name
|
|
37
|
-
);
|
|
38
|
-
|
|
39
|
-
if (!sourceColumn) {
|
|
40
|
-
if (targetColumn.is_nullable === "NO") {
|
|
41
|
-
throw new Error(
|
|
42
|
-
`Required column ${targetColumn.column_name} not found in source schema`
|
|
43
|
-
);
|
|
44
|
-
}
|
|
45
|
-
this.logger.warn(
|
|
46
|
-
`Column ${targetColumn.column_name} not found in source schema but is nullable`
|
|
47
|
-
);
|
|
48
|
-
continue;
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
if (
|
|
52
|
-
!this.typecastManager.areTypesCompatible(
|
|
53
|
-
sourceColumn.data_type,
|
|
54
|
-
targetColumn.data_type
|
|
55
|
-
)
|
|
56
|
-
) {
|
|
57
|
-
throw new Error(
|
|
58
|
-
`Incompatible data types for column ${targetColumn.column_name}: ` +
|
|
59
|
-
`source ${sourceColumn.data_type} -> target ${targetColumn.data_type}`
|
|
60
|
-
);
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
async migrateEntityDataInBatches(
|
|
66
|
-
sourceSchema: string,
|
|
67
|
-
targetSchema: string,
|
|
68
|
-
tableConfig: TableConfig,
|
|
69
|
-
tenantId: string
|
|
70
|
-
): Promise<void> {
|
|
71
|
-
const sourceColumns = await this.schemaUtils.getTableColumns(
|
|
72
|
-
sourceSchema,
|
|
73
|
-
tableConfig.sourceTable
|
|
74
|
-
);
|
|
75
|
-
const targetColumns = await this.schemaUtils.getTableColumns(
|
|
76
|
-
targetSchema,
|
|
77
|
-
tableConfig.targetTable
|
|
78
|
-
);
|
|
79
|
-
|
|
80
|
-
await this.validateSchema(sourceColumns, targetColumns);
|
|
81
|
-
|
|
82
|
-
let offset = 0;
|
|
83
|
-
let hasMoreRecords = true;
|
|
84
|
-
let retryCount = 0;
|
|
85
|
-
|
|
86
|
-
while (hasMoreRecords) {
|
|
87
|
-
try {
|
|
88
|
-
const records = await this.fetchBatch(
|
|
89
|
-
sourceSchema,
|
|
90
|
-
tableConfig,
|
|
91
|
-
offset,
|
|
92
|
-
BATCH_SIZE
|
|
93
|
-
);
|
|
94
|
-
|
|
95
|
-
if (records.length === 0) {
|
|
96
|
-
hasMoreRecords = false;
|
|
97
|
-
continue;
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
await this.processBatchWithTransaction(
|
|
101
|
-
records,
|
|
102
|
-
targetSchema,
|
|
103
|
-
tableConfig,
|
|
104
|
-
sourceColumns,
|
|
105
|
-
targetColumns,
|
|
106
|
-
tenantId
|
|
107
|
-
);
|
|
108
|
-
|
|
109
|
-
offset += BATCH_SIZE;
|
|
110
|
-
retryCount = 0; // Reset retry count on success
|
|
111
|
-
} catch (error) {
|
|
112
|
-
if (retryCount < MAX_RETRIES) {
|
|
113
|
-
retryCount++;
|
|
114
|
-
const delay = RETRY_BASE_DELAY * Math.pow(2, retryCount - 1);
|
|
115
|
-
this.logger.warn(
|
|
116
|
-
`Error processing batch, retrying in ${delay}ms (attempt ${retryCount}/${MAX_RETRIES}): ${error.message}`
|
|
117
|
-
);
|
|
118
|
-
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
119
|
-
} else {
|
|
120
|
-
throw new Error(
|
|
121
|
-
`Failed to process batch after ${MAX_RETRIES} retries: ${error.message}`
|
|
122
|
-
);
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
private async fetchBatch(
|
|
129
|
-
sourceSchema: string,
|
|
130
|
-
tableConfig: TableConfig,
|
|
131
|
-
offset: number,
|
|
132
|
-
limit: number
|
|
133
|
-
): Promise<any[]> {
|
|
134
|
-
const query = `
|
|
135
|
-
SELECT *
|
|
136
|
-
FROM "${sourceSchema}"."${tableConfig.sourceTable}"
|
|
137
|
-
${this.buildWhereClause(tableConfig)}
|
|
138
|
-
ORDER BY "${tableConfig.idField}"
|
|
139
|
-
LIMIT ${limit}
|
|
140
|
-
OFFSET ${offset}
|
|
141
|
-
`;
|
|
142
|
-
|
|
143
|
-
const result = await this.connections.sourcePool.query(query);
|
|
144
|
-
return result.rows;
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
private buildWhereClause(tableConfig: TableConfig): string {
|
|
148
|
-
const conditions = [];
|
|
149
|
-
|
|
150
|
-
if (this.providerId) {
|
|
151
|
-
conditions.push(`"${tableConfig.providerLink}" = '${this.providerId}'`);
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
if (tableConfig.filterColumn) {
|
|
155
|
-
conditions.push(`"${tableConfig.filterColumn}" IS NOT NULL`);
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
return conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
private async processBatchWithTransaction(
|
|
162
|
-
records: any[],
|
|
163
|
-
targetSchema: string,
|
|
164
|
-
tableConfig: TableConfig,
|
|
165
|
-
sourceColumns: ColumnSchema[],
|
|
166
|
-
targetColumns: ColumnSchema[],
|
|
167
|
-
tenantId: string
|
|
168
|
-
): Promise<void> {
|
|
169
|
-
const client = await this.connections.targetPool.connect();
|
|
170
|
-
try {
|
|
171
|
-
await client.query("BEGIN");
|
|
172
|
-
|
|
173
|
-
for (const record of records) {
|
|
174
|
-
const transformedRecord = await this.transformRecord(
|
|
175
|
-
record,
|
|
176
|
-
sourceColumns,
|
|
177
|
-
targetColumns,
|
|
178
|
-
tenantId
|
|
179
|
-
);
|
|
180
|
-
|
|
181
|
-
await this.insertRecord(
|
|
182
|
-
client,
|
|
183
|
-
targetSchema,
|
|
184
|
-
tableConfig.targetTable,
|
|
185
|
-
transformedRecord
|
|
186
|
-
);
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
await client.query("COMMIT");
|
|
190
|
-
} catch (error) {
|
|
191
|
-
await client.query("ROLLBACK");
|
|
192
|
-
throw error;
|
|
193
|
-
} finally {
|
|
194
|
-
client.release();
|
|
195
|
-
}
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
private async transformRecord(
|
|
199
|
-
record: any,
|
|
200
|
-
sourceColumns: ColumnSchema[],
|
|
201
|
-
targetColumns: ColumnSchema[],
|
|
202
|
-
tenantId: string
|
|
203
|
-
): Promise<any> {
|
|
204
|
-
const transformedRecord: any = {};
|
|
205
|
-
|
|
206
|
-
for (const targetColumn of targetColumns) {
|
|
207
|
-
const sourceColumn = sourceColumns.find(
|
|
208
|
-
(col) => col.column_name === targetColumn.column_name
|
|
209
|
-
);
|
|
210
|
-
|
|
211
|
-
if (!sourceColumn) {
|
|
212
|
-
transformedRecord[targetColumn.column_name] = null;
|
|
213
|
-
continue;
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
const value = record[targetColumn.column_name];
|
|
217
|
-
transformedRecord[targetColumn.column_name] =
|
|
218
|
-
await this.dataTransformer.transformColumnValue(
|
|
219
|
-
value,
|
|
220
|
-
targetColumn.column_name,
|
|
221
|
-
{ ...targetColumn, source_type: sourceColumn.data_type },
|
|
222
|
-
tenantId
|
|
223
|
-
);
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
return transformedRecord;
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
private async insertRecord(
|
|
230
|
-
client: any,
|
|
231
|
-
schema: string,
|
|
232
|
-
table: string,
|
|
233
|
-
record: any
|
|
234
|
-
): Promise<void> {
|
|
235
|
-
const columns = Object.keys(record);
|
|
236
|
-
const values = Object.values(record);
|
|
237
|
-
const placeholders = values.map((_, i) => `$${i + 1}`).join(", ");
|
|
238
|
-
|
|
239
|
-
const query = `
|
|
240
|
-
INSERT INTO "${schema}"."${table}"
|
|
241
|
-
(${columns.map((col) => `"${col}"`).join(", ")})
|
|
242
|
-
VALUES (${placeholders})
|
|
243
|
-
ON CONFLICT DO NOTHING
|
|
244
|
-
`;
|
|
245
|
-
|
|
246
|
-
await client.query(query, values);
|
|
247
|
-
}
|
|
248
|
-
}
|
|
1
|
+
import { Logger } from "@nestjs/common";
|
|
2
|
+
import { SchemaUtils } from "./schema-utils";
|
|
3
|
+
import { DataTransformer } from "./data-transformer";
|
|
4
|
+
import { TypecastManager } from "./typecast-manager";
|
|
5
|
+
import {
|
|
6
|
+
ColumnSchema,
|
|
7
|
+
DatabaseConnections,
|
|
8
|
+
MigrationOptions,
|
|
9
|
+
TableConfig,
|
|
10
|
+
} from "./types";
|
|
11
|
+
|
|
12
|
+
const BATCH_SIZE = 100;
|
|
13
|
+
const MAX_RETRIES = 3;
|
|
14
|
+
const RETRY_BASE_DELAY = 1000;
|
|
15
|
+
|
|
16
|
+
export class BatchMigrator {
|
|
17
|
+
private readonly logger = new Logger("BatchMigrator");
|
|
18
|
+
private readonly typecastManager: TypecastManager;
|
|
19
|
+
|
|
20
|
+
constructor(
|
|
21
|
+
private readonly schemaUtils: SchemaUtils,
|
|
22
|
+
private readonly dataTransformer: DataTransformer,
|
|
23
|
+
private readonly connections: DatabaseConnections,
|
|
24
|
+
private readonly options: MigrationOptions,
|
|
25
|
+
private readonly providerId?: string | null
|
|
26
|
+
) {
|
|
27
|
+
this.typecastManager = new TypecastManager();
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
async validateSchema(
|
|
31
|
+
sourceColumns: ColumnSchema[],
|
|
32
|
+
targetColumns: ColumnSchema[]
|
|
33
|
+
): Promise<void> {
|
|
34
|
+
for (const targetColumn of targetColumns) {
|
|
35
|
+
const sourceColumn = sourceColumns.find(
|
|
36
|
+
(col) => col.column_name === targetColumn.column_name
|
|
37
|
+
);
|
|
38
|
+
|
|
39
|
+
if (!sourceColumn) {
|
|
40
|
+
if (targetColumn.is_nullable === "NO") {
|
|
41
|
+
throw new Error(
|
|
42
|
+
`Required column ${targetColumn.column_name} not found in source schema`
|
|
43
|
+
);
|
|
44
|
+
}
|
|
45
|
+
this.logger.warn(
|
|
46
|
+
`Column ${targetColumn.column_name} not found in source schema but is nullable`
|
|
47
|
+
);
|
|
48
|
+
continue;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
if (
|
|
52
|
+
!this.typecastManager.areTypesCompatible(
|
|
53
|
+
sourceColumn.data_type,
|
|
54
|
+
targetColumn.data_type
|
|
55
|
+
)
|
|
56
|
+
) {
|
|
57
|
+
throw new Error(
|
|
58
|
+
`Incompatible data types for column ${targetColumn.column_name}: ` +
|
|
59
|
+
`source ${sourceColumn.data_type} -> target ${targetColumn.data_type}`
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
async migrateEntityDataInBatches(
|
|
66
|
+
sourceSchema: string,
|
|
67
|
+
targetSchema: string,
|
|
68
|
+
tableConfig: TableConfig,
|
|
69
|
+
tenantId: string
|
|
70
|
+
): Promise<void> {
|
|
71
|
+
const sourceColumns = await this.schemaUtils.getTableColumns(
|
|
72
|
+
sourceSchema,
|
|
73
|
+
tableConfig.sourceTable
|
|
74
|
+
);
|
|
75
|
+
const targetColumns = await this.schemaUtils.getTableColumns(
|
|
76
|
+
targetSchema,
|
|
77
|
+
tableConfig.targetTable
|
|
78
|
+
);
|
|
79
|
+
|
|
80
|
+
await this.validateSchema(sourceColumns, targetColumns);
|
|
81
|
+
|
|
82
|
+
let offset = 0;
|
|
83
|
+
let hasMoreRecords = true;
|
|
84
|
+
let retryCount = 0;
|
|
85
|
+
|
|
86
|
+
while (hasMoreRecords) {
|
|
87
|
+
try {
|
|
88
|
+
const records = await this.fetchBatch(
|
|
89
|
+
sourceSchema,
|
|
90
|
+
tableConfig,
|
|
91
|
+
offset,
|
|
92
|
+
BATCH_SIZE
|
|
93
|
+
);
|
|
94
|
+
|
|
95
|
+
if (records.length === 0) {
|
|
96
|
+
hasMoreRecords = false;
|
|
97
|
+
continue;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
await this.processBatchWithTransaction(
|
|
101
|
+
records,
|
|
102
|
+
targetSchema,
|
|
103
|
+
tableConfig,
|
|
104
|
+
sourceColumns,
|
|
105
|
+
targetColumns,
|
|
106
|
+
tenantId
|
|
107
|
+
);
|
|
108
|
+
|
|
109
|
+
offset += BATCH_SIZE;
|
|
110
|
+
retryCount = 0; // Reset retry count on success
|
|
111
|
+
} catch (error) {
|
|
112
|
+
if (retryCount < MAX_RETRIES) {
|
|
113
|
+
retryCount++;
|
|
114
|
+
const delay = RETRY_BASE_DELAY * Math.pow(2, retryCount - 1);
|
|
115
|
+
this.logger.warn(
|
|
116
|
+
`Error processing batch, retrying in ${delay}ms (attempt ${retryCount}/${MAX_RETRIES}): ${error.message}`
|
|
117
|
+
);
|
|
118
|
+
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
119
|
+
} else {
|
|
120
|
+
throw new Error(
|
|
121
|
+
`Failed to process batch after ${MAX_RETRIES} retries: ${error.message}`
|
|
122
|
+
);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
private async fetchBatch(
|
|
129
|
+
sourceSchema: string,
|
|
130
|
+
tableConfig: TableConfig,
|
|
131
|
+
offset: number,
|
|
132
|
+
limit: number
|
|
133
|
+
): Promise<any[]> {
|
|
134
|
+
const query = `
|
|
135
|
+
SELECT *
|
|
136
|
+
FROM "${sourceSchema}"."${tableConfig.sourceTable}"
|
|
137
|
+
${this.buildWhereClause(tableConfig)}
|
|
138
|
+
ORDER BY "${tableConfig.idField}"
|
|
139
|
+
LIMIT ${limit}
|
|
140
|
+
OFFSET ${offset}
|
|
141
|
+
`;
|
|
142
|
+
|
|
143
|
+
const result = await this.connections.sourcePool.query(query);
|
|
144
|
+
return result.rows;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
private buildWhereClause(tableConfig: TableConfig): string {
|
|
148
|
+
const conditions = [];
|
|
149
|
+
|
|
150
|
+
if (this.providerId) {
|
|
151
|
+
conditions.push(`"${tableConfig.providerLink}" = '${this.providerId}'`);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
if (tableConfig.filterColumn) {
|
|
155
|
+
conditions.push(`"${tableConfig.filterColumn}" IS NOT NULL`);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
return conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
private async processBatchWithTransaction(
|
|
162
|
+
records: any[],
|
|
163
|
+
targetSchema: string,
|
|
164
|
+
tableConfig: TableConfig,
|
|
165
|
+
sourceColumns: ColumnSchema[],
|
|
166
|
+
targetColumns: ColumnSchema[],
|
|
167
|
+
tenantId: string
|
|
168
|
+
): Promise<void> {
|
|
169
|
+
const client = await this.connections.targetPool.connect();
|
|
170
|
+
try {
|
|
171
|
+
await client.query("BEGIN");
|
|
172
|
+
|
|
173
|
+
for (const record of records) {
|
|
174
|
+
const transformedRecord = await this.transformRecord(
|
|
175
|
+
record,
|
|
176
|
+
sourceColumns,
|
|
177
|
+
targetColumns,
|
|
178
|
+
tenantId
|
|
179
|
+
);
|
|
180
|
+
|
|
181
|
+
await this.insertRecord(
|
|
182
|
+
client,
|
|
183
|
+
targetSchema,
|
|
184
|
+
tableConfig.targetTable,
|
|
185
|
+
transformedRecord
|
|
186
|
+
);
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
await client.query("COMMIT");
|
|
190
|
+
} catch (error) {
|
|
191
|
+
await client.query("ROLLBACK");
|
|
192
|
+
throw error;
|
|
193
|
+
} finally {
|
|
194
|
+
client.release();
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
private async transformRecord(
|
|
199
|
+
record: any,
|
|
200
|
+
sourceColumns: ColumnSchema[],
|
|
201
|
+
targetColumns: ColumnSchema[],
|
|
202
|
+
tenantId: string
|
|
203
|
+
): Promise<any> {
|
|
204
|
+
const transformedRecord: any = {};
|
|
205
|
+
|
|
206
|
+
for (const targetColumn of targetColumns) {
|
|
207
|
+
const sourceColumn = sourceColumns.find(
|
|
208
|
+
(col) => col.column_name === targetColumn.column_name
|
|
209
|
+
);
|
|
210
|
+
|
|
211
|
+
if (!sourceColumn) {
|
|
212
|
+
transformedRecord[targetColumn.column_name] = null;
|
|
213
|
+
continue;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
const value = record[targetColumn.column_name];
|
|
217
|
+
transformedRecord[targetColumn.column_name] =
|
|
218
|
+
await this.dataTransformer.transformColumnValue(
|
|
219
|
+
value,
|
|
220
|
+
targetColumn.column_name,
|
|
221
|
+
{ ...targetColumn, source_type: sourceColumn.data_type },
|
|
222
|
+
tenantId
|
|
223
|
+
);
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
return transformedRecord;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
private async insertRecord(
|
|
230
|
+
client: any,
|
|
231
|
+
schema: string,
|
|
232
|
+
table: string,
|
|
233
|
+
record: any
|
|
234
|
+
): Promise<void> {
|
|
235
|
+
const columns = Object.keys(record);
|
|
236
|
+
const values = Object.values(record);
|
|
237
|
+
const placeholders = values.map((_, i) => `$${i + 1}`).join(", ");
|
|
238
|
+
|
|
239
|
+
const query = `
|
|
240
|
+
INSERT INTO "${schema}"."${table}"
|
|
241
|
+
(${columns.map((col) => `"${col}"`).join(", ")})
|
|
242
|
+
VALUES (${placeholders})
|
|
243
|
+
ON CONFLICT DO NOTHING
|
|
244
|
+
`;
|
|
245
|
+
|
|
246
|
+
await client.query(query, values);
|
|
247
|
+
}
|
|
248
|
+
}
|