@javalabs/prisma-client 1.0.27 → 1.0.30

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 (168) hide show
  1. package/.github/CODEOWNERS +1 -1
  2. package/README.md +269 -269
  3. package/dist/index.d.ts +7 -0
  4. package/dist/index.js +34 -0
  5. package/dist/index.js.map +1 -0
  6. package/dist/prisma-factory.service.d.ts +9 -0
  7. package/dist/prisma-factory.service.js +47 -0
  8. package/dist/prisma-factory.service.js.map +1 -0
  9. package/dist/prisma.module.d.ts +2 -0
  10. package/dist/prisma.module.js +23 -0
  11. package/dist/prisma.module.js.map +1 -0
  12. package/dist/prisma.service.d.ts +6 -0
  13. package/dist/prisma.service.js +27 -0
  14. package/dist/prisma.service.js.map +1 -0
  15. package/dist/scripts/add-uuid-to-table.d.ts +8 -0
  16. package/dist/scripts/add-uuid-to-table.js +98 -0
  17. package/dist/scripts/add-uuid-to-table.js.map +1 -0
  18. package/dist/scripts/create-tenant-schemas.d.ts +1 -0
  19. package/dist/scripts/create-tenant-schemas.js +117 -0
  20. package/dist/scripts/create-tenant-schemas.js.map +1 -0
  21. package/dist/scripts/data-migration/batch-migrator.d.ts +20 -0
  22. package/dist/scripts/data-migration/batch-migrator.js +134 -0
  23. package/dist/scripts/data-migration/batch-migrator.js.map +1 -0
  24. package/dist/scripts/data-migration/data-transformer.d.ts +26 -0
  25. package/dist/scripts/data-migration/data-transformer.js +278 -0
  26. package/dist/scripts/data-migration/data-transformer.js.map +1 -0
  27. package/dist/scripts/data-migration/db-connector.d.ts +12 -0
  28. package/dist/scripts/data-migration/db-connector.js +94 -0
  29. package/dist/scripts/data-migration/db-connector.js.map +1 -0
  30. package/dist/scripts/data-migration/dependency-resolver.d.ts +18 -0
  31. package/dist/scripts/data-migration/dependency-resolver.js +132 -0
  32. package/dist/scripts/data-migration/dependency-resolver.js.map +1 -0
  33. package/dist/scripts/data-migration/entity-discovery.d.ts +11 -0
  34. package/dist/scripts/data-migration/entity-discovery.js +152 -0
  35. package/dist/scripts/data-migration/entity-discovery.js.map +1 -0
  36. package/dist/scripts/data-migration/foreign-key-manager.d.ts +18 -0
  37. package/dist/scripts/data-migration/foreign-key-manager.js +160 -0
  38. package/dist/scripts/data-migration/foreign-key-manager.js.map +1 -0
  39. package/dist/scripts/data-migration/migration-tool.d.ts +48 -0
  40. package/dist/scripts/data-migration/migration-tool.js +290 -0
  41. package/dist/scripts/data-migration/migration-tool.js.map +1 -0
  42. package/dist/scripts/data-migration/schema-utils.d.ts +18 -0
  43. package/dist/scripts/data-migration/schema-utils.js +207 -0
  44. package/dist/scripts/data-migration/schema-utils.js.map +1 -0
  45. package/dist/scripts/data-migration/tenant-migrator.d.ts +15 -0
  46. package/dist/scripts/data-migration/tenant-migrator.js +117 -0
  47. package/dist/scripts/data-migration/tenant-migrator.js.map +1 -0
  48. package/dist/scripts/data-migration/typecast-manager.d.ts +9 -0
  49. package/dist/scripts/data-migration/typecast-manager.js +179 -0
  50. package/dist/scripts/data-migration/typecast-manager.js.map +1 -0
  51. package/dist/scripts/data-migration/types.d.ts +100 -0
  52. package/dist/scripts/data-migration/types.js +3 -0
  53. package/dist/scripts/data-migration/types.js.map +1 -0
  54. package/dist/scripts/database-initializer.d.ts +5 -0
  55. package/dist/scripts/database-initializer.js +45 -0
  56. package/dist/scripts/database-initializer.js.map +1 -0
  57. package/dist/scripts/drop-database.d.ts +10 -0
  58. package/dist/scripts/drop-database.js +81 -0
  59. package/dist/scripts/drop-database.js.map +1 -0
  60. package/dist/scripts/encrypt-user-passwords.d.ts +1 -0
  61. package/dist/scripts/encrypt-user-passwords.js +33 -0
  62. package/dist/scripts/encrypt-user-passwords.js.map +1 -0
  63. package/dist/scripts/error-handler.d.ts +12 -0
  64. package/dist/scripts/error-handler.js +82 -0
  65. package/dist/scripts/error-handler.js.map +1 -0
  66. package/dist/scripts/fix-data-types.d.ts +10 -0
  67. package/dist/scripts/fix-data-types.js +185 -0
  68. package/dist/scripts/fix-data-types.js.map +1 -0
  69. package/dist/scripts/fix-enum-values.d.ts +17 -0
  70. package/dist/scripts/fix-enum-values.js +234 -0
  71. package/dist/scripts/fix-enum-values.js.map +1 -0
  72. package/dist/scripts/fix-schema-discrepancies.d.ts +21 -0
  73. package/dist/scripts/fix-schema-discrepancies.js +240 -0
  74. package/dist/scripts/fix-schema-discrepancies.js.map +1 -0
  75. package/dist/scripts/fix-table-indexes.d.ts +26 -0
  76. package/dist/scripts/fix-table-indexes.js +460 -0
  77. package/dist/scripts/fix-table-indexes.js.map +1 -0
  78. package/dist/scripts/migrate-schema-structure.d.ts +1 -0
  79. package/dist/scripts/migrate-schema-structure.js +76 -0
  80. package/dist/scripts/migrate-schema-structure.js.map +1 -0
  81. package/dist/scripts/migrate-uuid.d.ts +2 -0
  82. package/dist/scripts/migrate-uuid.js +57 -0
  83. package/dist/scripts/migrate-uuid.js.map +1 -0
  84. package/dist/scripts/post-migration-validator.d.ts +34 -0
  85. package/dist/scripts/post-migration-validator.js +363 -0
  86. package/dist/scripts/post-migration-validator.js.map +1 -0
  87. package/dist/scripts/pre-migration-validator.d.ts +25 -0
  88. package/dist/scripts/pre-migration-validator.js +491 -0
  89. package/dist/scripts/pre-migration-validator.js.map +1 -0
  90. package/dist/scripts/reset-database.d.ts +17 -0
  91. package/dist/scripts/reset-database.js +202 -0
  92. package/dist/scripts/reset-database.js.map +1 -0
  93. package/dist/scripts/retry-failed-migrations.d.ts +14 -0
  94. package/dist/scripts/retry-failed-migrations.js +301 -0
  95. package/dist/scripts/retry-failed-migrations.js.map +1 -0
  96. package/dist/scripts/run-migration.d.ts +1 -0
  97. package/dist/scripts/run-migration.js +512 -0
  98. package/dist/scripts/run-migration.js.map +1 -0
  99. package/dist/scripts/schema-sync.d.ts +1 -0
  100. package/dist/scripts/schema-sync.js +85 -0
  101. package/dist/scripts/schema-sync.js.map +1 -0
  102. package/dist/scripts/sequence-sync-cli.d.ts +2 -0
  103. package/dist/scripts/sequence-sync-cli.js +287 -0
  104. package/dist/scripts/sequence-sync-cli.js.map +1 -0
  105. package/dist/scripts/sequence-synchronizer.d.ts +8 -0
  106. package/dist/scripts/sequence-synchronizer.js +88 -0
  107. package/dist/scripts/sequence-synchronizer.js.map +1 -0
  108. package/dist/scripts/sync-enum-types.d.ts +13 -0
  109. package/dist/scripts/sync-enum-types.js +139 -0
  110. package/dist/scripts/sync-enum-types.js.map +1 -0
  111. package/dist/scripts/sync-enum-values.d.ts +20 -0
  112. package/dist/scripts/sync-enum-values.js +336 -0
  113. package/dist/scripts/sync-enum-values.js.map +1 -0
  114. package/dist/scripts/truncate-database.d.ts +10 -0
  115. package/dist/scripts/truncate-database.js +100 -0
  116. package/dist/scripts/truncate-database.js.map +1 -0
  117. package/dist/scripts/verify-migration-setup.d.ts +11 -0
  118. package/dist/scripts/verify-migration-setup.js +120 -0
  119. package/dist/scripts/verify-migration-setup.js.map +1 -0
  120. package/dist/tsconfig.tsbuildinfo +1 -0
  121. package/migration-config.json +63 -63
  122. package/migration-config.json.bk +95 -95
  123. package/migrations/add_reserved_amount.sql +7 -7
  124. package/package.json +44 -44
  125. package/prisma/migrations/add_uuid_to_transactions.sql +13 -13
  126. package/prisma/schema.prisma +609 -601
  127. package/src/index.ts +23 -23
  128. package/src/prisma-factory.service.ts +40 -40
  129. package/src/prisma.module.ts +9 -9
  130. package/src/prisma.service.ts +16 -16
  131. package/src/scripts/add-uuid-to-table.ts +138 -138
  132. package/src/scripts/create-tenant-schemas.ts +145 -145
  133. package/src/scripts/data-migration/batch-migrator.ts +248 -248
  134. package/src/scripts/data-migration/data-transformer.ts +426 -426
  135. package/src/scripts/data-migration/db-connector.ts +120 -120
  136. package/src/scripts/data-migration/dependency-resolver.ts +174 -174
  137. package/src/scripts/data-migration/entity-discovery.ts +196 -196
  138. package/src/scripts/data-migration/foreign-key-manager.ts +277 -277
  139. package/src/scripts/data-migration/migration-config.json +63 -63
  140. package/src/scripts/data-migration/migration-tool.ts +509 -509
  141. package/src/scripts/data-migration/schema-utils.ts +248 -248
  142. package/src/scripts/data-migration/tenant-migrator.ts +201 -201
  143. package/src/scripts/data-migration/typecast-manager.ts +193 -193
  144. package/src/scripts/data-migration/types.ts +113 -113
  145. package/src/scripts/database-initializer.ts +49 -49
  146. package/src/scripts/drop-database.ts +104 -104
  147. package/src/scripts/dump-source-db.sh +61 -61
  148. package/src/scripts/encrypt-user-passwords.ts +36 -36
  149. package/src/scripts/error-handler.ts +117 -117
  150. package/src/scripts/fix-data-types.ts +241 -241
  151. package/src/scripts/fix-enum-values.ts +357 -357
  152. package/src/scripts/fix-schema-discrepancies.ts +317 -317
  153. package/src/scripts/fix-table-indexes.ts +601 -601
  154. package/src/scripts/migrate-schema-structure.ts +90 -90
  155. package/src/scripts/migrate-uuid.ts +76 -76
  156. package/src/scripts/post-migration-validator.ts +526 -526
  157. package/src/scripts/pre-migration-validator.ts +610 -610
  158. package/src/scripts/reset-database.ts +263 -263
  159. package/src/scripts/retry-failed-migrations.ts +416 -416
  160. package/src/scripts/run-migration.ts +707 -707
  161. package/src/scripts/schema-sync.ts +128 -128
  162. package/src/scripts/sequence-sync-cli.ts +416 -416
  163. package/src/scripts/sequence-synchronizer.ts +127 -127
  164. package/src/scripts/sync-enum-types.ts +170 -170
  165. package/src/scripts/sync-enum-values.ts +563 -563
  166. package/src/scripts/truncate-database.ts +123 -123
  167. package/src/scripts/verify-migration-setup.ts +135 -135
  168. package/tsconfig.json +17 -17
@@ -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
+ }