@javalabs/prisma-client 1.0.27 → 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.
Files changed (50) hide show
  1. package/.github/CODEOWNERS +1 -1
  2. package/README.md +269 -269
  3. package/migration-config.json +63 -63
  4. package/migration-config.json.bk +95 -95
  5. package/migrations/add_reserved_amount.sql +7 -7
  6. package/package.json +44 -44
  7. package/prisma/migrations/add_uuid_to_transactions.sql +13 -13
  8. package/prisma/schema.prisma +609 -601
  9. package/src/index.ts +23 -23
  10. package/src/prisma-factory.service.ts +40 -40
  11. package/src/prisma.module.ts +9 -9
  12. package/src/prisma.service.ts +16 -16
  13. package/src/scripts/add-uuid-to-table.ts +138 -138
  14. package/src/scripts/create-tenant-schemas.ts +145 -145
  15. package/src/scripts/data-migration/batch-migrator.ts +248 -248
  16. package/src/scripts/data-migration/data-transformer.ts +426 -426
  17. package/src/scripts/data-migration/db-connector.ts +120 -120
  18. package/src/scripts/data-migration/dependency-resolver.ts +174 -174
  19. package/src/scripts/data-migration/entity-discovery.ts +196 -196
  20. package/src/scripts/data-migration/foreign-key-manager.ts +277 -277
  21. package/src/scripts/data-migration/migration-config.json +63 -63
  22. package/src/scripts/data-migration/migration-tool.ts +509 -509
  23. package/src/scripts/data-migration/schema-utils.ts +248 -248
  24. package/src/scripts/data-migration/tenant-migrator.ts +201 -201
  25. package/src/scripts/data-migration/typecast-manager.ts +193 -193
  26. package/src/scripts/data-migration/types.ts +113 -113
  27. package/src/scripts/database-initializer.ts +49 -49
  28. package/src/scripts/drop-database.ts +104 -104
  29. package/src/scripts/dump-source-db.sh +61 -61
  30. package/src/scripts/encrypt-user-passwords.ts +36 -36
  31. package/src/scripts/error-handler.ts +117 -117
  32. package/src/scripts/fix-data-types.ts +241 -241
  33. package/src/scripts/fix-enum-values.ts +357 -357
  34. package/src/scripts/fix-schema-discrepancies.ts +317 -317
  35. package/src/scripts/fix-table-indexes.ts +601 -601
  36. package/src/scripts/migrate-schema-structure.ts +90 -90
  37. package/src/scripts/migrate-uuid.ts +76 -76
  38. package/src/scripts/post-migration-validator.ts +526 -526
  39. package/src/scripts/pre-migration-validator.ts +610 -610
  40. package/src/scripts/reset-database.ts +263 -263
  41. package/src/scripts/retry-failed-migrations.ts +416 -416
  42. package/src/scripts/run-migration.ts +707 -707
  43. package/src/scripts/schema-sync.ts +128 -128
  44. package/src/scripts/sequence-sync-cli.ts +416 -416
  45. package/src/scripts/sequence-synchronizer.ts +127 -127
  46. package/src/scripts/sync-enum-types.ts +170 -170
  47. package/src/scripts/sync-enum-values.ts +563 -563
  48. package/src/scripts/truncate-database.ts +123 -123
  49. package/src/scripts/verify-migration-setup.ts +135 -135
  50. package/tsconfig.json +17 -17
@@ -1,357 +1,357 @@
1
- import * as pg from "pg";
2
- import * as dotenv from "dotenv";
3
- import { Logger } from "@nestjs/common";
4
- import * as fs from "fs";
5
- import * as path from "path";
6
-
7
- dotenv.config();
8
-
9
- export class EnumFixer {
10
- private readonly logger = new Logger("EnumFixer");
11
- private readonly pool: pg.Pool;
12
- private enumValuesCache: Record<string, string[]> = {};
13
- // Fix the type definition to match how it's actually used
14
- private mappingsCache: Record<string, string | null> = {};
15
- private readonly mappingsFilePath: string;
16
-
17
- constructor(
18
- private readonly databaseUrl: string = process.env.DATABASE_URL,
19
- private readonly saveMapping: boolean = true
20
- ) {
21
- this.pool = new pg.Pool({
22
- connectionString: this.databaseUrl,
23
- });
24
-
25
- // Crear directorio para mappings si no existe
26
- const mappingsDir = path.join(process.cwd(), "migration-logs");
27
- if (!fs.existsSync(mappingsDir)) {
28
- fs.mkdirSync(mappingsDir, { recursive: true });
29
- }
30
-
31
- // Archivo para guardar los mappings
32
- const timestamp = new Date()
33
- .toISOString()
34
- .replace(/:/g, "-")
35
- .replace(/\..+/, "");
36
- this.mappingsFilePath = path.join(
37
- mappingsDir,
38
- `enum-mappings-${timestamp}.json`
39
- );
40
-
41
- // Inicializar archivo de mappings
42
- if (this.saveMapping) {
43
- this.saveMappings();
44
- }
45
- }
46
-
47
- async fixEnumValues() {
48
- try {
49
- this.logger.log("Starting enum values fixing process");
50
-
51
- // Obtener todos los schemas excepto los del sistema
52
- const schemas = await this.getSchemas();
53
- this.logger.log(`Found ${schemas.length} schemas to process`);
54
-
55
- // Para cada schema, procesar sus campos enum
56
- for (const schema of schemas) {
57
- await this.fixEnumValuesForSchema(schema);
58
- }
59
-
60
- // Guardar los mappings finales
61
- if (this.saveMapping) {
62
- this.saveMappings();
63
- this.logger.log(`Enum mappings saved to: ${this.mappingsFilePath}`);
64
- }
65
-
66
- this.logger.log("Enum values fixing completed successfully");
67
- } catch (error) {
68
- this.logger.error(
69
- `Error during enum values fixing: ${error.message}`,
70
- error.stack
71
- );
72
- } finally {
73
- await this.cleanup();
74
- }
75
- }
76
-
77
- private async getSchemas(): Promise<string[]> {
78
- const result = await this.pool.query(`
79
- SELECT schema_name
80
- FROM information_schema.schemata
81
- WHERE schema_name NOT IN ('information_schema', 'pg_catalog', 'pg_toast')
82
- AND schema_name NOT LIKE 'pg_%'
83
- `);
84
-
85
- return result.rows.map((row) => row.schema_name);
86
- }
87
-
88
- private async fixEnumValuesForSchema(schema: string) {
89
- this.logger.log(`Processing enum fields for schema: ${schema}`);
90
-
91
- try {
92
- // Obtener todas las columnas enum en este schema
93
- const enumColumnsQuery = `
94
- SELECT
95
- c.table_name,
96
- c.column_name,
97
- c.udt_name
98
- FROM
99
- information_schema.columns c
100
- WHERE
101
- c.table_schema = $1
102
- AND c.data_type = 'USER-DEFINED'
103
- AND c.udt_name LIKE 'enum_%'
104
- `;
105
-
106
- const enumColumnsResult = await this.pool.query(enumColumnsQuery, [
107
- schema,
108
- ]);
109
-
110
- if (enumColumnsResult.rows.length === 0) {
111
- this.logger.log(`No enum fields found in schema: ${schema}`);
112
- return;
113
- }
114
-
115
- this.logger.log(
116
- `Found ${enumColumnsResult.rows.length} enum fields in schema: ${schema}`
117
- );
118
-
119
- // Procesar cada columna enum
120
- for (const row of enumColumnsResult.rows) {
121
- const tableName = row.table_name;
122
- const columnName = row.column_name;
123
- const enumTypeName = row.udt_name;
124
-
125
- await this.fixEnumColumn(schema, tableName, columnName, enumTypeName);
126
- }
127
-
128
- this.logger.log(`Completed processing enum fields for schema: ${schema}`);
129
- } catch (error) {
130
- this.logger.error(
131
- `Error processing enum fields for schema ${schema}: ${error.message}`,
132
- error.stack
133
- );
134
- }
135
- }
136
-
137
- private async fixEnumColumn(
138
- schema: string,
139
- tableName: string,
140
- columnName: string,
141
- enumTypeName: string
142
- ) {
143
- this.logger.log(
144
- `Fixing enum column ${columnName} (${enumTypeName}) in table ${schema}.${tableName}`
145
- );
146
-
147
- try {
148
- // Obtener los valores válidos para el enum
149
- let validEnumValues: string[];
150
- if (this.enumValuesCache[enumTypeName]) {
151
- validEnumValues = this.enumValuesCache[enumTypeName];
152
- } else {
153
- validEnumValues = await this.getEnumValues(enumTypeName);
154
- this.enumValuesCache[enumTypeName] = validEnumValues;
155
- }
156
-
157
- if (validEnumValues.length === 0) {
158
- this.logger.warn(
159
- `No enum values found for type ${enumTypeName}. Skipping.`
160
- );
161
- return;
162
- }
163
-
164
- this.logger.log(
165
- `Valid values for ${enumTypeName}: ${validEnumValues.join(", ")}`
166
- );
167
-
168
- // Obtener valores actuales en la columna
169
- const currentValuesQuery = `
170
- SELECT DISTINCT "${columnName}"
171
- FROM "${schema}"."${tableName}"
172
- WHERE "${columnName}" IS NOT NULL
173
- `;
174
-
175
- const currentValuesResult = await this.pool.query(currentValuesQuery);
176
- const currentValues = currentValuesResult.rows.map((r) => r[columnName]);
177
-
178
- // Encontrar valores inválidos
179
- const invalidValues = currentValues.filter(
180
- (val) => !validEnumValues.includes(val)
181
- );
182
-
183
- if (invalidValues.length === 0) {
184
- this.logger.log(
185
- `No invalid values found for enum column ${columnName} in table ${schema}.${tableName}`
186
- );
187
- return;
188
- }
189
-
190
- this.logger.log(
191
- `Found ${
192
- invalidValues.length
193
- } invalid values for enum ${enumTypeName} in ${schema}.${tableName}: ${invalidValues.join(
194
- ", "
195
- )}`
196
- );
197
-
198
- // Para cada valor inválido, intentar mapearlo a uno válido o establecerlo a NULL
199
- for (const invalidValue of invalidValues) {
200
- // Intentar encontrar un mapping existente
201
- const mappingKey = `${enumTypeName}:${invalidValue}`;
202
- let mappedValue: string | null = null;
203
-
204
- if (this.mappingsCache[mappingKey]) {
205
- mappedValue = this.mappingsCache[mappingKey];
206
- this.logger.log(
207
- `Using cached mapping for ${invalidValue} -> ${mappedValue}`
208
- );
209
- } else {
210
- // Intentar encontrar una coincidencia sin distinción de mayúsculas/minúsculas
211
- const matchingValidValue = validEnumValues.find(
212
- (v) => v.toLowerCase() === invalidValue.toLowerCase()
213
- );
214
-
215
- if (matchingValidValue) {
216
- mappedValue = matchingValidValue;
217
- this.logger.log(
218
- `Found case-insensitive match for ${invalidValue} -> ${mappedValue}`
219
- );
220
- } else {
221
- // Intentar encontrar una coincidencia parcial
222
- const similarValues = validEnumValues.filter(
223
- (v) =>
224
- v.toLowerCase().includes(invalidValue.toLowerCase()) ||
225
- invalidValue.toLowerCase().includes(v.toLowerCase())
226
- );
227
-
228
- if (similarValues.length === 1) {
229
- mappedValue = similarValues[0];
230
- this.logger.log(
231
- `Found partial match for ${invalidValue} -> ${mappedValue}`
232
- );
233
- } else if (similarValues.length > 1) {
234
- // Si hay múltiples coincidencias, usar la más cercana por longitud
235
- mappedValue = similarValues.reduce((prev, curr) =>
236
- Math.abs(curr.length - invalidValue.length) <
237
- Math.abs(prev.length - invalidValue.length)
238
- ? curr
239
- : prev
240
- );
241
- this.logger.log(
242
- `Found multiple partial matches for ${invalidValue}, using closest: ${mappedValue}`
243
- );
244
- } else {
245
- // Si no hay coincidencias, usar NULL
246
- mappedValue = null;
247
- this.logger.log(
248
- `No match found for ${invalidValue}, will set to NULL`
249
- );
250
- }
251
- }
252
-
253
- // Guardar el mapping para uso futuro
254
- this.mappingsCache[mappingKey] = mappedValue;
255
- if (this.saveMapping) {
256
- this.saveMappings();
257
- }
258
- }
259
-
260
- // Actualizar los registros con el valor mapeado usando CAST explícito
261
- const updateQuery =
262
- mappedValue !== null
263
- ? `
264
- UPDATE "${schema}"."${tableName}"
265
- SET "${columnName}" = CAST($1 AS "${enumTypeName}")
266
- WHERE "${columnName}" = $2
267
- `
268
- : `
269
- UPDATE "${schema}"."${tableName}"
270
- SET "${columnName}" = NULL
271
- WHERE "${columnName}" = $1
272
- `;
273
-
274
- const updateParams =
275
- mappedValue !== null ? [mappedValue, invalidValue] : [invalidValue];
276
-
277
- const updateResult = await this.pool.query(updateQuery, updateParams);
278
-
279
- this.logger.log(
280
- `Updated ${
281
- updateResult.rowCount
282
- } rows in ${schema}.${tableName} with ${invalidValue} -> ${
283
- mappedValue || "NULL"
284
- } with explicit CAST`
285
- );
286
- }
287
- } catch (error) {
288
- this.logger.error(
289
- `Error fixing enum column ${columnName} in table ${schema}.${tableName}: ${error.message}`,
290
- error.stack
291
- );
292
- }
293
- }
294
-
295
- private async getEnumValues(enumTypeName: string): Promise<string[]> {
296
- try {
297
- const enumQuery = `
298
- SELECT e.enumlabel
299
- FROM pg_enum e
300
- JOIN pg_type t ON e.enumtypid = t.oid
301
- WHERE t.typname = $1
302
- ORDER BY e.enumsortorder
303
- `;
304
-
305
- const result = await this.pool.query(enumQuery, [enumTypeName]);
306
- return result.rows.map((row) => row.enumlabel);
307
- } catch (error) {
308
- this.logger.error(
309
- `Error getting enum values for ${enumTypeName}: ${error.message}`
310
- );
311
- return [];
312
- }
313
- }
314
-
315
- private saveMappings(): void {
316
- try {
317
- fs.writeFileSync(
318
- this.mappingsFilePath,
319
- JSON.stringify(this.mappingsCache, null, 2),
320
- "utf8"
321
- );
322
- } catch (error) {
323
- this.logger.error(`Error saving enum mappings: ${error.message}`);
324
- }
325
- }
326
-
327
- private async cleanup() {
328
- this.logger.log("Cleaning up database connections");
329
- await this.pool.end();
330
- }
331
- }
332
-
333
- // Script para ejecutar desde línea de comandos
334
- if (require.main === module) {
335
- const run = async () => {
336
- try {
337
- // Obtener la URL de la base de datos desde los argumentos o .env
338
- const databaseUrl = process.argv[2] || process.env.DATABASE_URL;
339
-
340
- if (!databaseUrl) {
341
- console.error(
342
- "Error: No database URL provided. Please provide a database URL as an argument or set DATABASE_URL environment variable."
343
- );
344
- process.exit(1);
345
- }
346
-
347
- const enumFixer = new EnumFixer(databaseUrl);
348
- await enumFixer.fixEnumValues();
349
- process.exit(0);
350
- } catch (error) {
351
- console.error("Error:", error.message);
352
- process.exit(1);
353
- }
354
- };
355
-
356
- run();
357
- }
1
+ import * as pg from "pg";
2
+ import * as dotenv from "dotenv";
3
+ import { Logger } from "@nestjs/common";
4
+ import * as fs from "fs";
5
+ import * as path from "path";
6
+
7
+ dotenv.config();
8
+
9
+ export class EnumFixer {
10
+ private readonly logger = new Logger("EnumFixer");
11
+ private readonly pool: pg.Pool;
12
+ private enumValuesCache: Record<string, string[]> = {};
13
+ // Fix the type definition to match how it's actually used
14
+ private mappingsCache: Record<string, string | null> = {};
15
+ private readonly mappingsFilePath: string;
16
+
17
+ constructor(
18
+ private readonly databaseUrl: string = process.env.DATABASE_URL,
19
+ private readonly saveMapping: boolean = true
20
+ ) {
21
+ this.pool = new pg.Pool({
22
+ connectionString: this.databaseUrl,
23
+ });
24
+
25
+ // Crear directorio para mappings si no existe
26
+ const mappingsDir = path.join(process.cwd(), "migration-logs");
27
+ if (!fs.existsSync(mappingsDir)) {
28
+ fs.mkdirSync(mappingsDir, { recursive: true });
29
+ }
30
+
31
+ // Archivo para guardar los mappings
32
+ const timestamp = new Date()
33
+ .toISOString()
34
+ .replace(/:/g, "-")
35
+ .replace(/\..+/, "");
36
+ this.mappingsFilePath = path.join(
37
+ mappingsDir,
38
+ `enum-mappings-${timestamp}.json`
39
+ );
40
+
41
+ // Inicializar archivo de mappings
42
+ if (this.saveMapping) {
43
+ this.saveMappings();
44
+ }
45
+ }
46
+
47
+ async fixEnumValues() {
48
+ try {
49
+ this.logger.log("Starting enum values fixing process");
50
+
51
+ // Obtener todos los schemas excepto los del sistema
52
+ const schemas = await this.getSchemas();
53
+ this.logger.log(`Found ${schemas.length} schemas to process`);
54
+
55
+ // Para cada schema, procesar sus campos enum
56
+ for (const schema of schemas) {
57
+ await this.fixEnumValuesForSchema(schema);
58
+ }
59
+
60
+ // Guardar los mappings finales
61
+ if (this.saveMapping) {
62
+ this.saveMappings();
63
+ this.logger.log(`Enum mappings saved to: ${this.mappingsFilePath}`);
64
+ }
65
+
66
+ this.logger.log("Enum values fixing completed successfully");
67
+ } catch (error) {
68
+ this.logger.error(
69
+ `Error during enum values fixing: ${error.message}`,
70
+ error.stack
71
+ );
72
+ } finally {
73
+ await this.cleanup();
74
+ }
75
+ }
76
+
77
+ private async getSchemas(): Promise<string[]> {
78
+ const result = await this.pool.query(`
79
+ SELECT schema_name
80
+ FROM information_schema.schemata
81
+ WHERE schema_name NOT IN ('information_schema', 'pg_catalog', 'pg_toast')
82
+ AND schema_name NOT LIKE 'pg_%'
83
+ `);
84
+
85
+ return result.rows.map((row) => row.schema_name);
86
+ }
87
+
88
+ private async fixEnumValuesForSchema(schema: string) {
89
+ this.logger.log(`Processing enum fields for schema: ${schema}`);
90
+
91
+ try {
92
+ // Obtener todas las columnas enum en este schema
93
+ const enumColumnsQuery = `
94
+ SELECT
95
+ c.table_name,
96
+ c.column_name,
97
+ c.udt_name
98
+ FROM
99
+ information_schema.columns c
100
+ WHERE
101
+ c.table_schema = $1
102
+ AND c.data_type = 'USER-DEFINED'
103
+ AND c.udt_name LIKE 'enum_%'
104
+ `;
105
+
106
+ const enumColumnsResult = await this.pool.query(enumColumnsQuery, [
107
+ schema,
108
+ ]);
109
+
110
+ if (enumColumnsResult.rows.length === 0) {
111
+ this.logger.log(`No enum fields found in schema: ${schema}`);
112
+ return;
113
+ }
114
+
115
+ this.logger.log(
116
+ `Found ${enumColumnsResult.rows.length} enum fields in schema: ${schema}`
117
+ );
118
+
119
+ // Procesar cada columna enum
120
+ for (const row of enumColumnsResult.rows) {
121
+ const tableName = row.table_name;
122
+ const columnName = row.column_name;
123
+ const enumTypeName = row.udt_name;
124
+
125
+ await this.fixEnumColumn(schema, tableName, columnName, enumTypeName);
126
+ }
127
+
128
+ this.logger.log(`Completed processing enum fields for schema: ${schema}`);
129
+ } catch (error) {
130
+ this.logger.error(
131
+ `Error processing enum fields for schema ${schema}: ${error.message}`,
132
+ error.stack
133
+ );
134
+ }
135
+ }
136
+
137
+ private async fixEnumColumn(
138
+ schema: string,
139
+ tableName: string,
140
+ columnName: string,
141
+ enumTypeName: string
142
+ ) {
143
+ this.logger.log(
144
+ `Fixing enum column ${columnName} (${enumTypeName}) in table ${schema}.${tableName}`
145
+ );
146
+
147
+ try {
148
+ // Obtener los valores válidos para el enum
149
+ let validEnumValues: string[];
150
+ if (this.enumValuesCache[enumTypeName]) {
151
+ validEnumValues = this.enumValuesCache[enumTypeName];
152
+ } else {
153
+ validEnumValues = await this.getEnumValues(enumTypeName);
154
+ this.enumValuesCache[enumTypeName] = validEnumValues;
155
+ }
156
+
157
+ if (validEnumValues.length === 0) {
158
+ this.logger.warn(
159
+ `No enum values found for type ${enumTypeName}. Skipping.`
160
+ );
161
+ return;
162
+ }
163
+
164
+ this.logger.log(
165
+ `Valid values for ${enumTypeName}: ${validEnumValues.join(", ")}`
166
+ );
167
+
168
+ // Obtener valores actuales en la columna
169
+ const currentValuesQuery = `
170
+ SELECT DISTINCT "${columnName}"
171
+ FROM "${schema}"."${tableName}"
172
+ WHERE "${columnName}" IS NOT NULL
173
+ `;
174
+
175
+ const currentValuesResult = await this.pool.query(currentValuesQuery);
176
+ const currentValues = currentValuesResult.rows.map((r) => r[columnName]);
177
+
178
+ // Encontrar valores inválidos
179
+ const invalidValues = currentValues.filter(
180
+ (val) => !validEnumValues.includes(val)
181
+ );
182
+
183
+ if (invalidValues.length === 0) {
184
+ this.logger.log(
185
+ `No invalid values found for enum column ${columnName} in table ${schema}.${tableName}`
186
+ );
187
+ return;
188
+ }
189
+
190
+ this.logger.log(
191
+ `Found ${
192
+ invalidValues.length
193
+ } invalid values for enum ${enumTypeName} in ${schema}.${tableName}: ${invalidValues.join(
194
+ ", "
195
+ )}`
196
+ );
197
+
198
+ // Para cada valor inválido, intentar mapearlo a uno válido o establecerlo a NULL
199
+ for (const invalidValue of invalidValues) {
200
+ // Intentar encontrar un mapping existente
201
+ const mappingKey = `${enumTypeName}:${invalidValue}`;
202
+ let mappedValue: string | null = null;
203
+
204
+ if (this.mappingsCache[mappingKey]) {
205
+ mappedValue = this.mappingsCache[mappingKey];
206
+ this.logger.log(
207
+ `Using cached mapping for ${invalidValue} -> ${mappedValue}`
208
+ );
209
+ } else {
210
+ // Intentar encontrar una coincidencia sin distinción de mayúsculas/minúsculas
211
+ const matchingValidValue = validEnumValues.find(
212
+ (v) => v.toLowerCase() === invalidValue.toLowerCase()
213
+ );
214
+
215
+ if (matchingValidValue) {
216
+ mappedValue = matchingValidValue;
217
+ this.logger.log(
218
+ `Found case-insensitive match for ${invalidValue} -> ${mappedValue}`
219
+ );
220
+ } else {
221
+ // Intentar encontrar una coincidencia parcial
222
+ const similarValues = validEnumValues.filter(
223
+ (v) =>
224
+ v.toLowerCase().includes(invalidValue.toLowerCase()) ||
225
+ invalidValue.toLowerCase().includes(v.toLowerCase())
226
+ );
227
+
228
+ if (similarValues.length === 1) {
229
+ mappedValue = similarValues[0];
230
+ this.logger.log(
231
+ `Found partial match for ${invalidValue} -> ${mappedValue}`
232
+ );
233
+ } else if (similarValues.length > 1) {
234
+ // Si hay múltiples coincidencias, usar la más cercana por longitud
235
+ mappedValue = similarValues.reduce((prev, curr) =>
236
+ Math.abs(curr.length - invalidValue.length) <
237
+ Math.abs(prev.length - invalidValue.length)
238
+ ? curr
239
+ : prev
240
+ );
241
+ this.logger.log(
242
+ `Found multiple partial matches for ${invalidValue}, using closest: ${mappedValue}`
243
+ );
244
+ } else {
245
+ // Si no hay coincidencias, usar NULL
246
+ mappedValue = null;
247
+ this.logger.log(
248
+ `No match found for ${invalidValue}, will set to NULL`
249
+ );
250
+ }
251
+ }
252
+
253
+ // Guardar el mapping para uso futuro
254
+ this.mappingsCache[mappingKey] = mappedValue;
255
+ if (this.saveMapping) {
256
+ this.saveMappings();
257
+ }
258
+ }
259
+
260
+ // Actualizar los registros con el valor mapeado usando CAST explícito
261
+ const updateQuery =
262
+ mappedValue !== null
263
+ ? `
264
+ UPDATE "${schema}"."${tableName}"
265
+ SET "${columnName}" = CAST($1 AS "${enumTypeName}")
266
+ WHERE "${columnName}" = $2
267
+ `
268
+ : `
269
+ UPDATE "${schema}"."${tableName}"
270
+ SET "${columnName}" = NULL
271
+ WHERE "${columnName}" = $1
272
+ `;
273
+
274
+ const updateParams =
275
+ mappedValue !== null ? [mappedValue, invalidValue] : [invalidValue];
276
+
277
+ const updateResult = await this.pool.query(updateQuery, updateParams);
278
+
279
+ this.logger.log(
280
+ `Updated ${
281
+ updateResult.rowCount
282
+ } rows in ${schema}.${tableName} with ${invalidValue} -> ${
283
+ mappedValue || "NULL"
284
+ } with explicit CAST`
285
+ );
286
+ }
287
+ } catch (error) {
288
+ this.logger.error(
289
+ `Error fixing enum column ${columnName} in table ${schema}.${tableName}: ${error.message}`,
290
+ error.stack
291
+ );
292
+ }
293
+ }
294
+
295
+ private async getEnumValues(enumTypeName: string): Promise<string[]> {
296
+ try {
297
+ const enumQuery = `
298
+ SELECT e.enumlabel
299
+ FROM pg_enum e
300
+ JOIN pg_type t ON e.enumtypid = t.oid
301
+ WHERE t.typname = $1
302
+ ORDER BY e.enumsortorder
303
+ `;
304
+
305
+ const result = await this.pool.query(enumQuery, [enumTypeName]);
306
+ return result.rows.map((row) => row.enumlabel);
307
+ } catch (error) {
308
+ this.logger.error(
309
+ `Error getting enum values for ${enumTypeName}: ${error.message}`
310
+ );
311
+ return [];
312
+ }
313
+ }
314
+
315
+ private saveMappings(): void {
316
+ try {
317
+ fs.writeFileSync(
318
+ this.mappingsFilePath,
319
+ JSON.stringify(this.mappingsCache, null, 2),
320
+ "utf8"
321
+ );
322
+ } catch (error) {
323
+ this.logger.error(`Error saving enum mappings: ${error.message}`);
324
+ }
325
+ }
326
+
327
+ private async cleanup() {
328
+ this.logger.log("Cleaning up database connections");
329
+ await this.pool.end();
330
+ }
331
+ }
332
+
333
+ // Script para ejecutar desde línea de comandos
334
+ if (require.main === module) {
335
+ const run = async () => {
336
+ try {
337
+ // Obtener la URL de la base de datos desde los argumentos o .env
338
+ const databaseUrl = process.argv[2] || process.env.DATABASE_URL;
339
+
340
+ if (!databaseUrl) {
341
+ console.error(
342
+ "Error: No database URL provided. Please provide a database URL as an argument or set DATABASE_URL environment variable."
343
+ );
344
+ process.exit(1);
345
+ }
346
+
347
+ const enumFixer = new EnumFixer(databaseUrl);
348
+ await enumFixer.fixEnumValues();
349
+ process.exit(0);
350
+ } catch (error) {
351
+ console.error("Error:", error.message);
352
+ process.exit(1);
353
+ }
354
+ };
355
+
356
+ run();
357
+ }