@javalabs/prisma-client 1.0.18 → 1.0.20
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 -0
- package/README.md +269 -269
- package/dist/index.d.ts +1 -1
- package/dist/prisma.service.d.ts +1 -1
- package/dist/scripts/add-uuid-to-table.js +32 -32
- package/dist/scripts/data-migration/batch-migrator.js +12 -12
- package/dist/scripts/data-migration/data-transformer.js +14 -14
- package/dist/scripts/data-migration/dependency-resolver.js +23 -23
- package/dist/scripts/data-migration/entity-discovery.js +68 -68
- package/dist/scripts/data-migration/foreign-key-manager.js +23 -23
- package/dist/scripts/data-migration/migration-tool.js +5 -5
- package/dist/scripts/data-migration/schema-utils.js +74 -74
- package/dist/scripts/data-migration/typecast-manager.js +4 -4
- package/dist/scripts/database-initializer.js +5 -5
- package/dist/scripts/drop-database.js +5 -5
- package/dist/scripts/fix-data-types.js +53 -53
- package/dist/scripts/fix-enum-values.js +34 -34
- package/dist/scripts/fix-schema-discrepancies.js +40 -40
- package/dist/scripts/fix-table-indexes.js +81 -81
- package/dist/scripts/migrate-schema-structure.js +4 -4
- package/dist/scripts/migrate-uuid.js +19 -19
- package/dist/scripts/post-migration-validator.js +49 -49
- package/dist/scripts/pre-migration-validator.js +107 -107
- package/dist/scripts/reset-database.js +21 -21
- package/dist/scripts/retry-failed-migrations.js +28 -28
- package/dist/scripts/run-migration.js +5 -5
- package/dist/scripts/schema-sync.js +18 -18
- package/dist/scripts/sequence-sync-cli.js +55 -55
- package/dist/scripts/sequence-synchronizer.js +20 -20
- package/dist/scripts/sync-enum-types.js +30 -30
- package/dist/scripts/sync-enum-values.js +52 -52
- package/dist/scripts/truncate-database.js +10 -10
- package/dist/scripts/verify-migration-setup.js +10 -10
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/migration-config.json +63 -63
- package/migration-config.json.bk +95 -95
- package/migrations/add_reserved_amount.sql +8 -0
- package/package.json +44 -44
- package/prisma/migrations/add_uuid_to_transactions.sql +13 -13
- package/prisma/schema.prisma +601 -554
- 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/scripts/data-migration/dependency-manager.d.ts +0 -9
- package/dist/scripts/data-migration/dependency-manager.js +0 -86
- package/dist/scripts/data-migration/dependency-manager.js.map +0 -1
- package/dist/scripts/data-migration/migration-config.json +0 -63
- package/dist/scripts/data-migration/migration-phases.d.ts +0 -5
- package/dist/scripts/data-migration/migration-phases.js +0 -55
- package/dist/scripts/data-migration/migration-phases.js.map +0 -1
- package/dist/scripts/data-migration/multi-source-migrator.d.ts +0 -17
- package/dist/scripts/data-migration/multi-source-migrator.js +0 -130
- package/dist/scripts/data-migration/multi-source-migrator.js.map +0 -1
- package/dist/scripts/data-migration/phase-generator.d.ts +0 -15
- package/dist/scripts/data-migration/phase-generator.js +0 -187
- package/dist/scripts/data-migration/phase-generator.js.map +0 -1
- package/dist/scripts/data-migration.d.ts +0 -22
- package/dist/scripts/data-migration.js +0 -593
- package/dist/scripts/data-migration.js.map +0 -1
- package/dist/scripts/multi-db-migration.d.ts +0 -1
- package/dist/scripts/multi-db-migration.js +0 -55
- package/dist/scripts/multi-db-migration.js.map +0 -1
|
@@ -1,127 +1,127 @@
|
|
|
1
|
-
import { Logger } from "@nestjs/common";
|
|
2
|
-
import { Pool } from "pg";
|
|
3
|
-
|
|
4
|
-
export class SequenceSynchronizer {
|
|
5
|
-
private readonly logger = new Logger("SequenceSynchronizer");
|
|
6
|
-
private readonly pool: Pool;
|
|
7
|
-
|
|
8
|
-
constructor(databaseUrl: string) {
|
|
9
|
-
this.pool = new Pool({
|
|
10
|
-
connectionString: databaseUrl,
|
|
11
|
-
});
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
async synchronizeSequences(): Promise<void> {
|
|
15
|
-
try {
|
|
16
|
-
this.logger.log("Iniciando sincronización de secuencias");
|
|
17
|
-
|
|
18
|
-
// Obtener todas las tablas en la base de datos
|
|
19
|
-
const tables = await this.getTables();
|
|
20
|
-
this.logger.log(`Encontradas ${tables.length} tablas para procesar`);
|
|
21
|
-
|
|
22
|
-
// Procesar cada tabla
|
|
23
|
-
for (const table of tables) {
|
|
24
|
-
await this.synchronizeTableSequences(table);
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
this.logger.log("Sincronización de secuencias completada con éxito");
|
|
28
|
-
} catch (error) {
|
|
29
|
-
this.logger.error(
|
|
30
|
-
`Error durante la sincronización de secuencias: ${error.message}`,
|
|
31
|
-
error.stack
|
|
32
|
-
);
|
|
33
|
-
throw error;
|
|
34
|
-
} finally {
|
|
35
|
-
await this.pool.end();
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
private async getTables(): Promise<string[]> {
|
|
40
|
-
const query = `
|
|
41
|
-
SELECT table_name
|
|
42
|
-
FROM information_schema.tables
|
|
43
|
-
WHERE table_schema = 'public'
|
|
44
|
-
AND table_type = 'BASE TABLE'
|
|
45
|
-
`;
|
|
46
|
-
|
|
47
|
-
const result = await this.pool.query(query);
|
|
48
|
-
return result.rows.map((row) => row.table_name);
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
private async synchronizeTableSequences(tableName: string): Promise<void> {
|
|
52
|
-
try {
|
|
53
|
-
// Verificar si la tabla tiene una columna id
|
|
54
|
-
const columns = await this.pool.query(
|
|
55
|
-
`
|
|
56
|
-
SELECT column_name
|
|
57
|
-
FROM information_schema.columns
|
|
58
|
-
WHERE table_name = $1
|
|
59
|
-
AND column_name = 'id'
|
|
60
|
-
`,
|
|
61
|
-
[tableName]
|
|
62
|
-
);
|
|
63
|
-
|
|
64
|
-
if (columns.rows.length === 0) {
|
|
65
|
-
this.logger.debug(
|
|
66
|
-
`La tabla ${tableName} no tiene columna id, omitiendo`
|
|
67
|
-
);
|
|
68
|
-
return;
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
// Verificar si existe una secuencia para esta tabla
|
|
72
|
-
const sequenceName = `${tableName}_id_seq`;
|
|
73
|
-
const sequences = await this.pool.query(
|
|
74
|
-
`
|
|
75
|
-
SELECT sequence_name
|
|
76
|
-
FROM information_schema.sequences
|
|
77
|
-
WHERE sequence_name = $1
|
|
78
|
-
`,
|
|
79
|
-
[sequenceName]
|
|
80
|
-
);
|
|
81
|
-
|
|
82
|
-
if (sequences.rows.length === 0) {
|
|
83
|
-
this.logger.debug(
|
|
84
|
-
`No se encontró secuencia para la tabla ${tableName}, omitiendo`
|
|
85
|
-
);
|
|
86
|
-
return;
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
// Obtener el máximo ID actual de la tabla
|
|
90
|
-
const maxResult = await this.pool.query(`
|
|
91
|
-
SELECT COALESCE(MAX(id), 0) as max_id FROM "${tableName}"
|
|
92
|
-
`);
|
|
93
|
-
const maxId = maxResult.rows[0].max_id;
|
|
94
|
-
|
|
95
|
-
// Obtener el valor actual de la secuencia
|
|
96
|
-
const currValResult = await this.pool.query(`
|
|
97
|
-
SELECT last_value FROM "${sequenceName}"
|
|
98
|
-
`);
|
|
99
|
-
const currVal = currValResult.rows[0].last_value;
|
|
100
|
-
|
|
101
|
-
this.logger.log(
|
|
102
|
-
`Tabla ${tableName}: ID Máximo = ${maxId}, Valor Actual de Secuencia = ${currVal}`
|
|
103
|
-
);
|
|
104
|
-
|
|
105
|
-
// Actualizar la secuencia solo si es necesario
|
|
106
|
-
if (currVal <= maxId) {
|
|
107
|
-
await this.pool.query(
|
|
108
|
-
`
|
|
109
|
-
SELECT setval($1, $2, true)
|
|
110
|
-
`,
|
|
111
|
-
[sequenceName, maxId]
|
|
112
|
-
);
|
|
113
|
-
this.logger.log(
|
|
114
|
-
`Actualizada secuencia para tabla ${tableName} a ${maxId}`
|
|
115
|
-
);
|
|
116
|
-
} else {
|
|
117
|
-
this.logger.log(
|
|
118
|
-
`La secuencia para la tabla ${tableName} ya está adelantada al ID máximo, no se requiere actualización`
|
|
119
|
-
);
|
|
120
|
-
}
|
|
121
|
-
} catch (error) {
|
|
122
|
-
this.logger.error(
|
|
123
|
-
`Error sincronizando secuencias para tabla ${tableName}: ${error.message}`
|
|
124
|
-
);
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
}
|
|
1
|
+
import { Logger } from "@nestjs/common";
|
|
2
|
+
import { Pool } from "pg";
|
|
3
|
+
|
|
4
|
+
export class SequenceSynchronizer {
|
|
5
|
+
private readonly logger = new Logger("SequenceSynchronizer");
|
|
6
|
+
private readonly pool: Pool;
|
|
7
|
+
|
|
8
|
+
constructor(databaseUrl: string) {
|
|
9
|
+
this.pool = new Pool({
|
|
10
|
+
connectionString: databaseUrl,
|
|
11
|
+
});
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
async synchronizeSequences(): Promise<void> {
|
|
15
|
+
try {
|
|
16
|
+
this.logger.log("Iniciando sincronización de secuencias");
|
|
17
|
+
|
|
18
|
+
// Obtener todas las tablas en la base de datos
|
|
19
|
+
const tables = await this.getTables();
|
|
20
|
+
this.logger.log(`Encontradas ${tables.length} tablas para procesar`);
|
|
21
|
+
|
|
22
|
+
// Procesar cada tabla
|
|
23
|
+
for (const table of tables) {
|
|
24
|
+
await this.synchronizeTableSequences(table);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
this.logger.log("Sincronización de secuencias completada con éxito");
|
|
28
|
+
} catch (error) {
|
|
29
|
+
this.logger.error(
|
|
30
|
+
`Error durante la sincronización de secuencias: ${error.message}`,
|
|
31
|
+
error.stack
|
|
32
|
+
);
|
|
33
|
+
throw error;
|
|
34
|
+
} finally {
|
|
35
|
+
await this.pool.end();
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
private async getTables(): Promise<string[]> {
|
|
40
|
+
const query = `
|
|
41
|
+
SELECT table_name
|
|
42
|
+
FROM information_schema.tables
|
|
43
|
+
WHERE table_schema = 'public'
|
|
44
|
+
AND table_type = 'BASE TABLE'
|
|
45
|
+
`;
|
|
46
|
+
|
|
47
|
+
const result = await this.pool.query(query);
|
|
48
|
+
return result.rows.map((row) => row.table_name);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
private async synchronizeTableSequences(tableName: string): Promise<void> {
|
|
52
|
+
try {
|
|
53
|
+
// Verificar si la tabla tiene una columna id
|
|
54
|
+
const columns = await this.pool.query(
|
|
55
|
+
`
|
|
56
|
+
SELECT column_name
|
|
57
|
+
FROM information_schema.columns
|
|
58
|
+
WHERE table_name = $1
|
|
59
|
+
AND column_name = 'id'
|
|
60
|
+
`,
|
|
61
|
+
[tableName]
|
|
62
|
+
);
|
|
63
|
+
|
|
64
|
+
if (columns.rows.length === 0) {
|
|
65
|
+
this.logger.debug(
|
|
66
|
+
`La tabla ${tableName} no tiene columna id, omitiendo`
|
|
67
|
+
);
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// Verificar si existe una secuencia para esta tabla
|
|
72
|
+
const sequenceName = `${tableName}_id_seq`;
|
|
73
|
+
const sequences = await this.pool.query(
|
|
74
|
+
`
|
|
75
|
+
SELECT sequence_name
|
|
76
|
+
FROM information_schema.sequences
|
|
77
|
+
WHERE sequence_name = $1
|
|
78
|
+
`,
|
|
79
|
+
[sequenceName]
|
|
80
|
+
);
|
|
81
|
+
|
|
82
|
+
if (sequences.rows.length === 0) {
|
|
83
|
+
this.logger.debug(
|
|
84
|
+
`No se encontró secuencia para la tabla ${tableName}, omitiendo`
|
|
85
|
+
);
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// Obtener el máximo ID actual de la tabla
|
|
90
|
+
const maxResult = await this.pool.query(`
|
|
91
|
+
SELECT COALESCE(MAX(id), 0) as max_id FROM "${tableName}"
|
|
92
|
+
`);
|
|
93
|
+
const maxId = maxResult.rows[0].max_id;
|
|
94
|
+
|
|
95
|
+
// Obtener el valor actual de la secuencia
|
|
96
|
+
const currValResult = await this.pool.query(`
|
|
97
|
+
SELECT last_value FROM "${sequenceName}"
|
|
98
|
+
`);
|
|
99
|
+
const currVal = currValResult.rows[0].last_value;
|
|
100
|
+
|
|
101
|
+
this.logger.log(
|
|
102
|
+
`Tabla ${tableName}: ID Máximo = ${maxId}, Valor Actual de Secuencia = ${currVal}`
|
|
103
|
+
);
|
|
104
|
+
|
|
105
|
+
// Actualizar la secuencia solo si es necesario
|
|
106
|
+
if (currVal <= maxId) {
|
|
107
|
+
await this.pool.query(
|
|
108
|
+
`
|
|
109
|
+
SELECT setval($1, $2, true)
|
|
110
|
+
`,
|
|
111
|
+
[sequenceName, maxId]
|
|
112
|
+
);
|
|
113
|
+
this.logger.log(
|
|
114
|
+
`Actualizada secuencia para tabla ${tableName} a ${maxId}`
|
|
115
|
+
);
|
|
116
|
+
} else {
|
|
117
|
+
this.logger.log(
|
|
118
|
+
`La secuencia para la tabla ${tableName} ya está adelantada al ID máximo, no se requiere actualización`
|
|
119
|
+
);
|
|
120
|
+
}
|
|
121
|
+
} catch (error) {
|
|
122
|
+
this.logger.error(
|
|
123
|
+
`Error sincronizando secuencias para tabla ${tableName}: ${error.message}`
|
|
124
|
+
);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
}
|
|
@@ -1,171 +1,171 @@
|
|
|
1
|
-
import * as pg from 'pg';
|
|
2
|
-
import * as dotenv from 'dotenv';
|
|
3
|
-
import { Logger } from '@nestjs/common';
|
|
4
|
-
|
|
5
|
-
dotenv.config();
|
|
6
|
-
|
|
7
|
-
export class EnumSynchronizer {
|
|
8
|
-
private readonly logger = new Logger('EnumSynchronizer');
|
|
9
|
-
private readonly sourcePool: pg.Pool;
|
|
10
|
-
private readonly targetPool: pg.Pool;
|
|
11
|
-
|
|
12
|
-
constructor(
|
|
13
|
-
private readonly sourceUrl: string = process.env.SOURCE_DATABASE_URL,
|
|
14
|
-
private readonly targetUrl: string = process.env.DATABASE_URL
|
|
15
|
-
) {
|
|
16
|
-
this.sourcePool = new pg.Pool({
|
|
17
|
-
connectionString: this.sourceUrl,
|
|
18
|
-
});
|
|
19
|
-
|
|
20
|
-
this.targetPool = new pg.Pool({
|
|
21
|
-
connectionString: this.targetUrl,
|
|
22
|
-
});
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
async synchronizeEnums() {
|
|
26
|
-
try {
|
|
27
|
-
this.logger.log('Starting enum synchronization');
|
|
28
|
-
|
|
29
|
-
// Obtener todos los tipos enum en la base de datos de origen
|
|
30
|
-
const sourceEnumsResult = await this.sourcePool.query(`
|
|
31
|
-
SELECT t.typname AS enum_name
|
|
32
|
-
FROM pg_type t
|
|
33
|
-
JOIN pg_catalog.pg_namespace n ON n.oid = t.typnamespace
|
|
34
|
-
WHERE t.typtype = 'e'
|
|
35
|
-
AND n.nspname = 'public'
|
|
36
|
-
`);
|
|
37
|
-
|
|
38
|
-
this.logger.log(`Found ${sourceEnumsResult.rows.length} enum types in source database`);
|
|
39
|
-
|
|
40
|
-
// Para cada tipo enum, sincronizar sus valores
|
|
41
|
-
for (const enumRow of sourceEnumsResult.rows) {
|
|
42
|
-
const enumName = enumRow.enum_name;
|
|
43
|
-
await this.synchronizeEnumType(enumName);
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
this.logger.log('Enum synchronization completed successfully');
|
|
47
|
-
} catch (error) {
|
|
48
|
-
this.logger.error(`Error during enum synchronization: ${error.message}`, error.stack);
|
|
49
|
-
} finally {
|
|
50
|
-
await this.cleanup();
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
private async synchronizeEnumType(enumName: string) {
|
|
55
|
-
this.logger.log(`Synchronizing enum type: ${enumName}`);
|
|
56
|
-
|
|
57
|
-
try {
|
|
58
|
-
// Obtener valores del enum en la base de datos de origen
|
|
59
|
-
const sourceEnumValuesResult = await this.sourcePool.query(`
|
|
60
|
-
SELECT e.enumlabel
|
|
61
|
-
FROM pg_enum e
|
|
62
|
-
JOIN pg_type t ON e.enumtypid = t.oid
|
|
63
|
-
WHERE t.typname = $1
|
|
64
|
-
ORDER BY e.enumsortorder
|
|
65
|
-
`, [enumName]);
|
|
66
|
-
|
|
67
|
-
const sourceEnumValues = sourceEnumValuesResult.rows.map(row => row.enumlabel);
|
|
68
|
-
this.logger.log(`Source enum ${enumName} has values: ${sourceEnumValues.join(', ')}`);
|
|
69
|
-
|
|
70
|
-
// Verificar si el enum existe en la base de datos de destino
|
|
71
|
-
const targetEnumExistsResult = await this.targetPool.query(`
|
|
72
|
-
SELECT 1
|
|
73
|
-
FROM pg_type t
|
|
74
|
-
JOIN pg_catalog.pg_namespace n ON n.oid = t.typnamespace
|
|
75
|
-
WHERE t.typtype = 'e'
|
|
76
|
-
AND t.typname = $1
|
|
77
|
-
AND n.nspname = 'public'
|
|
78
|
-
LIMIT 1
|
|
79
|
-
`, [enumName]);
|
|
80
|
-
|
|
81
|
-
if (targetEnumExistsResult.rows.length === 0) {
|
|
82
|
-
// Si el enum no existe en el destino, crearlo
|
|
83
|
-
await this.createEnumType(enumName, sourceEnumValues);
|
|
84
|
-
} else {
|
|
85
|
-
// Si el enum existe, actualizar sus valores
|
|
86
|
-
await this.updateEnumValues(enumName, sourceEnumValues);
|
|
87
|
-
}
|
|
88
|
-
} catch (error) {
|
|
89
|
-
this.logger.error(`Error synchronizing enum type ${enumName}: ${error.message}`);
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
private async createEnumType(enumName: string, values: string[]) {
|
|
94
|
-
this.logger.log(`Creating enum type ${enumName} in target database`);
|
|
95
|
-
|
|
96
|
-
try {
|
|
97
|
-
// Escapar los valores para la consulta SQL
|
|
98
|
-
const escapedValues = values.map(value => `'${value.replace(/'/g, "''")}'`).join(', ');
|
|
99
|
-
|
|
100
|
-
// Crear el tipo enum
|
|
101
|
-
await this.targetPool.query(`
|
|
102
|
-
CREATE TYPE ${enumName} AS ENUM (${escapedValues})
|
|
103
|
-
`);
|
|
104
|
-
|
|
105
|
-
this.logger.log(`Successfully created enum type ${enumName} with values: ${values.join(', ')}`);
|
|
106
|
-
} catch (error) {
|
|
107
|
-
this.logger.error(`Error creating enum type ${enumName}: ${error.message}`);
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
private async updateEnumValues(enumName: string, sourceValues: string[]) {
|
|
112
|
-
this.logger.log(`Updating enum type ${enumName} in target database`);
|
|
113
|
-
|
|
114
|
-
try {
|
|
115
|
-
// Obtener valores actuales del enum en la base de datos de destino
|
|
116
|
-
const targetEnumValuesResult = await this.targetPool.query(`
|
|
117
|
-
SELECT e.enumlabel
|
|
118
|
-
FROM pg_enum e
|
|
119
|
-
JOIN pg_type t ON e.enumtypid = t.oid
|
|
120
|
-
WHERE t.typname = $1
|
|
121
|
-
ORDER BY e.enumsortorder
|
|
122
|
-
`, [enumName]);
|
|
123
|
-
|
|
124
|
-
const targetEnumValues = targetEnumValuesResult.rows.map(row => row.enumlabel);
|
|
125
|
-
this.logger.log(`Target enum ${enumName} has values: ${targetEnumValues.join(', ')}`);
|
|
126
|
-
|
|
127
|
-
// Encontrar valores que están en el origen pero no en el destino
|
|
128
|
-
const missingValues = sourceValues.filter(value => !targetEnumValues.includes(value));
|
|
129
|
-
|
|
130
|
-
if (missingValues.length === 0) {
|
|
131
|
-
this.logger.log(`Enum type ${enumName} is already up to date`);
|
|
132
|
-
return;
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
this.logger.log(`Adding ${missingValues.length} new values to enum ${enumName}: ${missingValues.join(', ')}`);
|
|
136
|
-
|
|
137
|
-
// Agregar los valores faltantes al enum
|
|
138
|
-
for (const value of missingValues) {
|
|
139
|
-
await this.targetPool.query(`
|
|
140
|
-
ALTER TYPE ${enumName} ADD VALUE '${value.replace(/'/g, "''")}'
|
|
141
|
-
`);
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
this.logger.log(`Successfully updated enum type ${enumName}`);
|
|
145
|
-
} catch (error) {
|
|
146
|
-
this.logger.error(`Error updating enum type ${enumName}: ${error.message}`);
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
private async cleanup() {
|
|
151
|
-
this.logger.log('Cleaning up database connections');
|
|
152
|
-
await this.sourcePool.end();
|
|
153
|
-
await this.targetPool.end();
|
|
154
|
-
}
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
// Script para ejecutar desde línea de comandos
|
|
158
|
-
if (require.main === module) {
|
|
159
|
-
const run = async () => {
|
|
160
|
-
try {
|
|
161
|
-
const synchronizer = new EnumSynchronizer();
|
|
162
|
-
await synchronizer.synchronizeEnums();
|
|
163
|
-
process.exit(0);
|
|
164
|
-
} catch (error) {
|
|
165
|
-
console.error('Error:', error.message);
|
|
166
|
-
process.exit(1);
|
|
167
|
-
}
|
|
168
|
-
};
|
|
169
|
-
|
|
170
|
-
run();
|
|
1
|
+
import * as pg from 'pg';
|
|
2
|
+
import * as dotenv from 'dotenv';
|
|
3
|
+
import { Logger } from '@nestjs/common';
|
|
4
|
+
|
|
5
|
+
dotenv.config();
|
|
6
|
+
|
|
7
|
+
export class EnumSynchronizer {
|
|
8
|
+
private readonly logger = new Logger('EnumSynchronizer');
|
|
9
|
+
private readonly sourcePool: pg.Pool;
|
|
10
|
+
private readonly targetPool: pg.Pool;
|
|
11
|
+
|
|
12
|
+
constructor(
|
|
13
|
+
private readonly sourceUrl: string = process.env.SOURCE_DATABASE_URL,
|
|
14
|
+
private readonly targetUrl: string = process.env.DATABASE_URL
|
|
15
|
+
) {
|
|
16
|
+
this.sourcePool = new pg.Pool({
|
|
17
|
+
connectionString: this.sourceUrl,
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
this.targetPool = new pg.Pool({
|
|
21
|
+
connectionString: this.targetUrl,
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
async synchronizeEnums() {
|
|
26
|
+
try {
|
|
27
|
+
this.logger.log('Starting enum synchronization');
|
|
28
|
+
|
|
29
|
+
// Obtener todos los tipos enum en la base de datos de origen
|
|
30
|
+
const sourceEnumsResult = await this.sourcePool.query(`
|
|
31
|
+
SELECT t.typname AS enum_name
|
|
32
|
+
FROM pg_type t
|
|
33
|
+
JOIN pg_catalog.pg_namespace n ON n.oid = t.typnamespace
|
|
34
|
+
WHERE t.typtype = 'e'
|
|
35
|
+
AND n.nspname = 'public'
|
|
36
|
+
`);
|
|
37
|
+
|
|
38
|
+
this.logger.log(`Found ${sourceEnumsResult.rows.length} enum types in source database`);
|
|
39
|
+
|
|
40
|
+
// Para cada tipo enum, sincronizar sus valores
|
|
41
|
+
for (const enumRow of sourceEnumsResult.rows) {
|
|
42
|
+
const enumName = enumRow.enum_name;
|
|
43
|
+
await this.synchronizeEnumType(enumName);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
this.logger.log('Enum synchronization completed successfully');
|
|
47
|
+
} catch (error) {
|
|
48
|
+
this.logger.error(`Error during enum synchronization: ${error.message}`, error.stack);
|
|
49
|
+
} finally {
|
|
50
|
+
await this.cleanup();
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
private async synchronizeEnumType(enumName: string) {
|
|
55
|
+
this.logger.log(`Synchronizing enum type: ${enumName}`);
|
|
56
|
+
|
|
57
|
+
try {
|
|
58
|
+
// Obtener valores del enum en la base de datos de origen
|
|
59
|
+
const sourceEnumValuesResult = await this.sourcePool.query(`
|
|
60
|
+
SELECT e.enumlabel
|
|
61
|
+
FROM pg_enum e
|
|
62
|
+
JOIN pg_type t ON e.enumtypid = t.oid
|
|
63
|
+
WHERE t.typname = $1
|
|
64
|
+
ORDER BY e.enumsortorder
|
|
65
|
+
`, [enumName]);
|
|
66
|
+
|
|
67
|
+
const sourceEnumValues = sourceEnumValuesResult.rows.map(row => row.enumlabel);
|
|
68
|
+
this.logger.log(`Source enum ${enumName} has values: ${sourceEnumValues.join(', ')}`);
|
|
69
|
+
|
|
70
|
+
// Verificar si el enum existe en la base de datos de destino
|
|
71
|
+
const targetEnumExistsResult = await this.targetPool.query(`
|
|
72
|
+
SELECT 1
|
|
73
|
+
FROM pg_type t
|
|
74
|
+
JOIN pg_catalog.pg_namespace n ON n.oid = t.typnamespace
|
|
75
|
+
WHERE t.typtype = 'e'
|
|
76
|
+
AND t.typname = $1
|
|
77
|
+
AND n.nspname = 'public'
|
|
78
|
+
LIMIT 1
|
|
79
|
+
`, [enumName]);
|
|
80
|
+
|
|
81
|
+
if (targetEnumExistsResult.rows.length === 0) {
|
|
82
|
+
// Si el enum no existe en el destino, crearlo
|
|
83
|
+
await this.createEnumType(enumName, sourceEnumValues);
|
|
84
|
+
} else {
|
|
85
|
+
// Si el enum existe, actualizar sus valores
|
|
86
|
+
await this.updateEnumValues(enumName, sourceEnumValues);
|
|
87
|
+
}
|
|
88
|
+
} catch (error) {
|
|
89
|
+
this.logger.error(`Error synchronizing enum type ${enumName}: ${error.message}`);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
private async createEnumType(enumName: string, values: string[]) {
|
|
94
|
+
this.logger.log(`Creating enum type ${enumName} in target database`);
|
|
95
|
+
|
|
96
|
+
try {
|
|
97
|
+
// Escapar los valores para la consulta SQL
|
|
98
|
+
const escapedValues = values.map(value => `'${value.replace(/'/g, "''")}'`).join(', ');
|
|
99
|
+
|
|
100
|
+
// Crear el tipo enum
|
|
101
|
+
await this.targetPool.query(`
|
|
102
|
+
CREATE TYPE ${enumName} AS ENUM (${escapedValues})
|
|
103
|
+
`);
|
|
104
|
+
|
|
105
|
+
this.logger.log(`Successfully created enum type ${enumName} with values: ${values.join(', ')}`);
|
|
106
|
+
} catch (error) {
|
|
107
|
+
this.logger.error(`Error creating enum type ${enumName}: ${error.message}`);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
private async updateEnumValues(enumName: string, sourceValues: string[]) {
|
|
112
|
+
this.logger.log(`Updating enum type ${enumName} in target database`);
|
|
113
|
+
|
|
114
|
+
try {
|
|
115
|
+
// Obtener valores actuales del enum en la base de datos de destino
|
|
116
|
+
const targetEnumValuesResult = await this.targetPool.query(`
|
|
117
|
+
SELECT e.enumlabel
|
|
118
|
+
FROM pg_enum e
|
|
119
|
+
JOIN pg_type t ON e.enumtypid = t.oid
|
|
120
|
+
WHERE t.typname = $1
|
|
121
|
+
ORDER BY e.enumsortorder
|
|
122
|
+
`, [enumName]);
|
|
123
|
+
|
|
124
|
+
const targetEnumValues = targetEnumValuesResult.rows.map(row => row.enumlabel);
|
|
125
|
+
this.logger.log(`Target enum ${enumName} has values: ${targetEnumValues.join(', ')}`);
|
|
126
|
+
|
|
127
|
+
// Encontrar valores que están en el origen pero no en el destino
|
|
128
|
+
const missingValues = sourceValues.filter(value => !targetEnumValues.includes(value));
|
|
129
|
+
|
|
130
|
+
if (missingValues.length === 0) {
|
|
131
|
+
this.logger.log(`Enum type ${enumName} is already up to date`);
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
this.logger.log(`Adding ${missingValues.length} new values to enum ${enumName}: ${missingValues.join(', ')}`);
|
|
136
|
+
|
|
137
|
+
// Agregar los valores faltantes al enum
|
|
138
|
+
for (const value of missingValues) {
|
|
139
|
+
await this.targetPool.query(`
|
|
140
|
+
ALTER TYPE ${enumName} ADD VALUE '${value.replace(/'/g, "''")}'
|
|
141
|
+
`);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
this.logger.log(`Successfully updated enum type ${enumName}`);
|
|
145
|
+
} catch (error) {
|
|
146
|
+
this.logger.error(`Error updating enum type ${enumName}: ${error.message}`);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
private async cleanup() {
|
|
151
|
+
this.logger.log('Cleaning up database connections');
|
|
152
|
+
await this.sourcePool.end();
|
|
153
|
+
await this.targetPool.end();
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// Script para ejecutar desde línea de comandos
|
|
158
|
+
if (require.main === module) {
|
|
159
|
+
const run = async () => {
|
|
160
|
+
try {
|
|
161
|
+
const synchronizer = new EnumSynchronizer();
|
|
162
|
+
await synchronizer.synchronizeEnums();
|
|
163
|
+
process.exit(0);
|
|
164
|
+
} catch (error) {
|
|
165
|
+
console.error('Error:', error.message);
|
|
166
|
+
process.exit(1);
|
|
167
|
+
}
|
|
168
|
+
};
|
|
169
|
+
|
|
170
|
+
run();
|
|
171
171
|
}
|