frg-data-diff 2.0.0
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/README.md +628 -0
- package/dist/apply/apply-diff.d.ts +24 -0
- package/dist/apply/apply-diff.js +205 -0
- package/dist/apply/conflict.d.ts +20 -0
- package/dist/apply/conflict.js +14 -0
- package/dist/apply/plan.d.ts +13 -0
- package/dist/apply/plan.js +37 -0
- package/dist/cli/apply.d.ts +8 -0
- package/dist/cli/apply.js +270 -0
- package/dist/cli/generator.d.ts +9 -0
- package/dist/cli/generator.js +804 -0
- package/dist/cli/pg-triggers.d.ts +2 -0
- package/dist/cli/pg-triggers.js +185 -0
- package/dist/cli/root.d.ts +8 -0
- package/dist/cli/root.js +231 -0
- package/dist/cli/sql.d.ts +9 -0
- package/dist/cli/sql.js +158 -0
- package/dist/config/config-schema.d.ts +380 -0
- package/dist/config/config-schema.js +95 -0
- package/dist/config/load-config.d.ts +12 -0
- package/dist/config/load-config.js +87 -0
- package/dist/config/resolve-options.d.ts +95 -0
- package/dist/config/resolve-options.js +183 -0
- package/dist/config/write-config.d.ts +18 -0
- package/dist/config/write-config.js +103 -0
- package/dist/db/connection.d.ts +28 -0
- package/dist/db/connection.js +34 -0
- package/dist/db/metadata.d.ts +70 -0
- package/dist/db/metadata.js +238 -0
- package/dist/db/pg-triggers.d.ts +27 -0
- package/dist/db/pg-triggers.js +59 -0
- package/dist/db/sql.d.ts +72 -0
- package/dist/db/sql.js +250 -0
- package/dist/diff/diff-schema.d.ts +380 -0
- package/dist/diff/diff-schema.js +67 -0
- package/dist/diff/generate-diff.d.ts +20 -0
- package/dist/diff/generate-diff.js +224 -0
- package/dist/diff/pg-triggers-diff.d.ts +11 -0
- package/dist/diff/pg-triggers-diff.js +161 -0
- package/dist/diff/serialize-value.d.ts +35 -0
- package/dist/diff/serialize-value.js +242 -0
- package/dist/diff/write-diff-yaml.d.ts +3 -0
- package/dist/diff/write-diff-yaml.js +48 -0
- package/dist/schema-diff/generate-schema-diff.d.ts +12 -0
- package/dist/schema-diff/generate-schema-diff.js +355 -0
- package/dist/schema-diff/generate-schema-sql.d.ts +20 -0
- package/dist/schema-diff/generate-schema-sql.js +187 -0
- package/dist/schema-diff/schema-diff-schema.d.ts +1088 -0
- package/dist/schema-diff/schema-diff-schema.js +70 -0
- package/dist/shared/env-values.d.ts +11 -0
- package/dist/shared/env-values.js +100 -0
- package/dist/shared/generator-wizard.d.ts +10 -0
- package/dist/shared/generator-wizard.js +337 -0
- package/dist/shared/identifiers.d.ts +24 -0
- package/dist/shared/identifiers.js +45 -0
- package/dist/shared/prompts.d.ts +26 -0
- package/dist/shared/prompts.js +104 -0
- package/dist/shared/summary.d.ts +33 -0
- package/dist/shared/summary.js +41 -0
- package/dist/sql/generate-sql.d.ts +19 -0
- package/dist/sql/generate-sql.js +223 -0
- package/package.json +39 -0
|
@@ -0,0 +1,355 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.generateSchemaDiff = generateSchemaDiff;
|
|
4
|
+
const metadata_1 = require("../db/metadata");
|
|
5
|
+
const identifiers_1 = require("../shared/identifiers");
|
|
6
|
+
async function generateSchemaDiff(sourcePool, destPool, options) {
|
|
7
|
+
const sourceClient = await sourcePool.connect();
|
|
8
|
+
const destClient = await destPool.connect();
|
|
9
|
+
const tableDiffs = [];
|
|
10
|
+
const summary = {
|
|
11
|
+
tablesCompared: 0,
|
|
12
|
+
tablesToCreate: 0,
|
|
13
|
+
tablesToDrop: 0,
|
|
14
|
+
columnsToAdd: 0,
|
|
15
|
+
columnsToAlter: 0,
|
|
16
|
+
columnsToDrop: 0,
|
|
17
|
+
primaryKeysToChange: 0,
|
|
18
|
+
};
|
|
19
|
+
try {
|
|
20
|
+
const [sourceTables, destTables] = await Promise.all([
|
|
21
|
+
(0, metadata_1.listTables)(sourceClient, options.schema),
|
|
22
|
+
(0, metadata_1.listTables)(destClient, options.schema),
|
|
23
|
+
]);
|
|
24
|
+
const sourceTableSet = new Set(sourceTables);
|
|
25
|
+
const destTableSet = new Set(destTables);
|
|
26
|
+
for (const [tableIndex, table] of options.tables.entries()) {
|
|
27
|
+
const progressLabel = formatSchemaProgressLabel(tableIndex + 1, options.tables.length, options.schema, table);
|
|
28
|
+
if (options.excludeTables.includes(table)) {
|
|
29
|
+
reportVerboseProgress(options, `${progressLabel}: skipping excluded schema diff table`);
|
|
30
|
+
continue;
|
|
31
|
+
}
|
|
32
|
+
(0, identifiers_1.validateIdentifier)(table, "schema diff table name");
|
|
33
|
+
summary.tablesCompared++;
|
|
34
|
+
reportProgress(options.onProgress, `${progressLabel}: loading table metadata`);
|
|
35
|
+
const sourceExists = sourceTableSet.has(table);
|
|
36
|
+
const destExists = destTableSet.has(table);
|
|
37
|
+
reportVerboseProgress(options, `${progressLabel}: source exists ${sourceExists}, destination exists ${destExists}`);
|
|
38
|
+
const [sourceMeta, destMeta] = await Promise.all([
|
|
39
|
+
sourceExists
|
|
40
|
+
? (0, metadata_1.fetchTableMetadata)(sourceClient, options.schema, table)
|
|
41
|
+
: Promise.resolve(undefined),
|
|
42
|
+
destExists
|
|
43
|
+
? (0, metadata_1.fetchTableMetadata)(destClient, options.schema, table)
|
|
44
|
+
: Promise.resolve(undefined),
|
|
45
|
+
]);
|
|
46
|
+
const tableDiff = buildSchemaTableDiff(options.schema, table, sourceMeta, destMeta);
|
|
47
|
+
reportProgress(options.onProgress, `${progressLabel}: ${summarizeSchemaTableDiff(tableDiff)}`);
|
|
48
|
+
if (!tableDiff) {
|
|
49
|
+
continue;
|
|
50
|
+
}
|
|
51
|
+
if (tableDiff.createTable) {
|
|
52
|
+
summary.tablesToCreate++;
|
|
53
|
+
}
|
|
54
|
+
if (tableDiff.dropTable) {
|
|
55
|
+
summary.tablesToDrop++;
|
|
56
|
+
}
|
|
57
|
+
summary.columnsToAdd += tableDiff.addColumns.length;
|
|
58
|
+
summary.columnsToAlter += tableDiff.alterColumns.length;
|
|
59
|
+
summary.columnsToDrop += tableDiff.dropColumns.length;
|
|
60
|
+
if (tableDiff.primaryKeyChange) {
|
|
61
|
+
summary.primaryKeysToChange++;
|
|
62
|
+
}
|
|
63
|
+
tableDiffs.push(tableDiff);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
finally {
|
|
67
|
+
sourceClient.release();
|
|
68
|
+
destClient.release();
|
|
69
|
+
}
|
|
70
|
+
return {
|
|
71
|
+
format: "postgres-schema-diff-json/v1",
|
|
72
|
+
generatedAt: new Date().toISOString(),
|
|
73
|
+
source: { schema: options.schema },
|
|
74
|
+
dest: { schema: options.schema },
|
|
75
|
+
options: {
|
|
76
|
+
tables: options.tables,
|
|
77
|
+
excludedTables: options.excludeTables,
|
|
78
|
+
},
|
|
79
|
+
tables: tableDiffs,
|
|
80
|
+
summary,
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
function buildSchemaTableDiff(schema, table, sourceMeta, destMeta) {
|
|
84
|
+
if (sourceMeta && !destMeta) {
|
|
85
|
+
return {
|
|
86
|
+
table,
|
|
87
|
+
schema,
|
|
88
|
+
sourceExists: true,
|
|
89
|
+
destExists: false,
|
|
90
|
+
createTable: {
|
|
91
|
+
columns: sourceMeta.columns.map(buildSchemaColumnDefinition),
|
|
92
|
+
primaryKey: [...sourceMeta.primaryKey],
|
|
93
|
+
},
|
|
94
|
+
dropTable: false,
|
|
95
|
+
addColumns: [],
|
|
96
|
+
alterColumns: [],
|
|
97
|
+
dropColumns: [],
|
|
98
|
+
primaryKeyChange: null,
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
if (!sourceMeta && destMeta) {
|
|
102
|
+
return {
|
|
103
|
+
table,
|
|
104
|
+
schema,
|
|
105
|
+
sourceExists: false,
|
|
106
|
+
destExists: true,
|
|
107
|
+
createTable: null,
|
|
108
|
+
dropTable: true,
|
|
109
|
+
addColumns: [],
|
|
110
|
+
alterColumns: [],
|
|
111
|
+
dropColumns: [],
|
|
112
|
+
primaryKeyChange: null,
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
if (!sourceMeta || !destMeta) {
|
|
116
|
+
return null;
|
|
117
|
+
}
|
|
118
|
+
const sourceColumns = new Map(sourceMeta.columns.map((column) => [column.columnName, column]));
|
|
119
|
+
const destColumns = new Map(destMeta.columns.map((column) => [column.columnName, column]));
|
|
120
|
+
const addColumns = sourceMeta.columns
|
|
121
|
+
.filter((column) => !destColumns.has(column.columnName))
|
|
122
|
+
.map(buildSchemaColumnDefinition);
|
|
123
|
+
const dropColumns = destMeta.columns
|
|
124
|
+
.filter((column) => !sourceColumns.has(column.columnName))
|
|
125
|
+
.map(buildSchemaColumnDefinition);
|
|
126
|
+
const alterColumns = [];
|
|
127
|
+
for (const sourceColumn of sourceMeta.columns) {
|
|
128
|
+
const destColumn = destColumns.get(sourceColumn.columnName);
|
|
129
|
+
if (!destColumn) {
|
|
130
|
+
continue;
|
|
131
|
+
}
|
|
132
|
+
const from = buildSchemaColumnDefinition(destColumn);
|
|
133
|
+
const to = buildSchemaColumnDefinition(sourceColumn);
|
|
134
|
+
if (!schemaColumnsAreEqual(from, to)) {
|
|
135
|
+
alterColumns.push({
|
|
136
|
+
column: sourceColumn.columnName,
|
|
137
|
+
from,
|
|
138
|
+
to,
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
const primaryKeyChange = buildPrimaryKeyChange(sourceMeta, destMeta);
|
|
143
|
+
const hasChanges = addColumns.length > 0 ||
|
|
144
|
+
alterColumns.length > 0 ||
|
|
145
|
+
dropColumns.length > 0 ||
|
|
146
|
+
primaryKeyChange !== null;
|
|
147
|
+
if (!hasChanges) {
|
|
148
|
+
return null;
|
|
149
|
+
}
|
|
150
|
+
return {
|
|
151
|
+
table,
|
|
152
|
+
schema,
|
|
153
|
+
sourceExists: true,
|
|
154
|
+
destExists: true,
|
|
155
|
+
createTable: null,
|
|
156
|
+
dropTable: false,
|
|
157
|
+
addColumns,
|
|
158
|
+
alterColumns,
|
|
159
|
+
dropColumns,
|
|
160
|
+
primaryKeyChange,
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
function buildPrimaryKeyChange(sourceMeta, destMeta) {
|
|
164
|
+
if (arraysEqual(sourceMeta.primaryKey, destMeta.primaryKey)) {
|
|
165
|
+
return null;
|
|
166
|
+
}
|
|
167
|
+
(0, identifiers_1.validateIdentifiers)(sourceMeta.primaryKey, "source primary key column");
|
|
168
|
+
(0, identifiers_1.validateIdentifiers)(destMeta.primaryKey, "destination primary key column");
|
|
169
|
+
return {
|
|
170
|
+
from: [...destMeta.primaryKey],
|
|
171
|
+
to: [...sourceMeta.primaryKey],
|
|
172
|
+
dropConstraintName: destMeta.primaryKeyConstraintName,
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
function buildSchemaColumnDefinition(column) {
|
|
176
|
+
return {
|
|
177
|
+
name: column.columnName,
|
|
178
|
+
type: buildColumnTypeSql(column),
|
|
179
|
+
nullable: column.isNullable,
|
|
180
|
+
default: normalizeColumnDefault(column),
|
|
181
|
+
isGenerated: column.isGenerated,
|
|
182
|
+
generationExpression: column.generationExpression,
|
|
183
|
+
ordinalPosition: column.ordinalPosition,
|
|
184
|
+
};
|
|
185
|
+
}
|
|
186
|
+
function normalizeColumnDefault(column) {
|
|
187
|
+
if (column.isGenerated) {
|
|
188
|
+
return null;
|
|
189
|
+
}
|
|
190
|
+
return column.columnDefault;
|
|
191
|
+
}
|
|
192
|
+
function formatSchemaProgressLabel(tablePosition, totalTables, schema, table) {
|
|
193
|
+
return `[schema ${tablePosition}/${totalTables}] ${schema}.${table}`;
|
|
194
|
+
}
|
|
195
|
+
function summarizeSchemaTableDiff(tableDiff) {
|
|
196
|
+
if (!tableDiff) {
|
|
197
|
+
return "no schema changes";
|
|
198
|
+
}
|
|
199
|
+
const changeDescriptions = [];
|
|
200
|
+
if (tableDiff.createTable) {
|
|
201
|
+
changeDescriptions.push("create table");
|
|
202
|
+
}
|
|
203
|
+
if (tableDiff.dropTable) {
|
|
204
|
+
changeDescriptions.push("drop table");
|
|
205
|
+
}
|
|
206
|
+
if (tableDiff.addColumns.length > 0) {
|
|
207
|
+
changeDescriptions.push(`${tableDiff.addColumns.length} column(s) to add`);
|
|
208
|
+
}
|
|
209
|
+
if (tableDiff.alterColumns.length > 0) {
|
|
210
|
+
changeDescriptions.push(`${tableDiff.alterColumns.length} column(s) to alter`);
|
|
211
|
+
}
|
|
212
|
+
if (tableDiff.dropColumns.length > 0) {
|
|
213
|
+
changeDescriptions.push(`${tableDiff.dropColumns.length} column(s) to drop`);
|
|
214
|
+
}
|
|
215
|
+
if (tableDiff.primaryKeyChange) {
|
|
216
|
+
changeDescriptions.push("primary key change");
|
|
217
|
+
}
|
|
218
|
+
return changeDescriptions.join(", ");
|
|
219
|
+
}
|
|
220
|
+
function reportProgress(callback, message) {
|
|
221
|
+
if (callback) {
|
|
222
|
+
callback(message);
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
function reportVerboseProgress(options, message) {
|
|
226
|
+
if (!options.verbose) {
|
|
227
|
+
return;
|
|
228
|
+
}
|
|
229
|
+
if (options.onVerboseProgress) {
|
|
230
|
+
options.onVerboseProgress(message);
|
|
231
|
+
return;
|
|
232
|
+
}
|
|
233
|
+
console.log(message);
|
|
234
|
+
}
|
|
235
|
+
function buildColumnTypeSql(column) {
|
|
236
|
+
if (column.domainName) {
|
|
237
|
+
return qualifyTypeName(column.domainSchema, column.domainName);
|
|
238
|
+
}
|
|
239
|
+
if (column.dataType === "ARRAY") {
|
|
240
|
+
const elementType = buildArrayElementTypeSql(column);
|
|
241
|
+
return `${elementType}[]`;
|
|
242
|
+
}
|
|
243
|
+
if (column.dataType === "character varying") {
|
|
244
|
+
return column.characterMaximumLength
|
|
245
|
+
? `character varying(${column.characterMaximumLength})`
|
|
246
|
+
: "character varying";
|
|
247
|
+
}
|
|
248
|
+
if (column.dataType === "character") {
|
|
249
|
+
return column.characterMaximumLength
|
|
250
|
+
? `character(${column.characterMaximumLength})`
|
|
251
|
+
: "character";
|
|
252
|
+
}
|
|
253
|
+
if (column.dataType === "numeric") {
|
|
254
|
+
if (column.numericPrecision !== null && column.numericScale !== null) {
|
|
255
|
+
return `numeric(${column.numericPrecision}, ${column.numericScale})`;
|
|
256
|
+
}
|
|
257
|
+
if (column.numericPrecision !== null) {
|
|
258
|
+
return `numeric(${column.numericPrecision})`;
|
|
259
|
+
}
|
|
260
|
+
return "numeric";
|
|
261
|
+
}
|
|
262
|
+
if (column.dataType === "time without time zone") {
|
|
263
|
+
return withOptionalPrecision("time", column.datetimePrecision);
|
|
264
|
+
}
|
|
265
|
+
if (column.dataType === "time with time zone") {
|
|
266
|
+
return withOptionalPrecision("time with time zone", column.datetimePrecision);
|
|
267
|
+
}
|
|
268
|
+
if (column.dataType === "timestamp without time zone") {
|
|
269
|
+
return withOptionalPrecision("timestamp", column.datetimePrecision);
|
|
270
|
+
}
|
|
271
|
+
if (column.dataType === "timestamp with time zone") {
|
|
272
|
+
return withOptionalPrecision("timestamp with time zone", column.datetimePrecision);
|
|
273
|
+
}
|
|
274
|
+
if (column.dataType === "interval") {
|
|
275
|
+
let typeSql = "interval";
|
|
276
|
+
if (column.intervalType) {
|
|
277
|
+
typeSql += ` ${column.intervalType}`;
|
|
278
|
+
}
|
|
279
|
+
if (column.intervalPrecision !== null) {
|
|
280
|
+
typeSql += `(${column.intervalPrecision})`;
|
|
281
|
+
}
|
|
282
|
+
return typeSql;
|
|
283
|
+
}
|
|
284
|
+
if (column.dataType === "USER-DEFINED") {
|
|
285
|
+
return qualifyTypeName(column.udtSchema, column.udtName);
|
|
286
|
+
}
|
|
287
|
+
return column.dataType;
|
|
288
|
+
}
|
|
289
|
+
function buildArrayElementTypeSql(column) {
|
|
290
|
+
const elementTypeName = column.udtName.startsWith("_")
|
|
291
|
+
? column.udtName.slice(1)
|
|
292
|
+
: column.udtName;
|
|
293
|
+
if (column.udtSchema !== "pg_catalog") {
|
|
294
|
+
return qualifyTypeName(column.udtSchema, elementTypeName);
|
|
295
|
+
}
|
|
296
|
+
return mapPgCatalogTypeName(elementTypeName);
|
|
297
|
+
}
|
|
298
|
+
function qualifyTypeName(schema, typeName) {
|
|
299
|
+
(0, identifiers_1.validateIdentifier)(typeName, "type name");
|
|
300
|
+
if (!schema || schema === "pg_catalog") {
|
|
301
|
+
return (0, identifiers_1.quoteIdentifier)(typeName);
|
|
302
|
+
}
|
|
303
|
+
(0, identifiers_1.validateIdentifier)(schema, "type schema name");
|
|
304
|
+
return `${(0, identifiers_1.quoteIdentifier)(schema)}.${(0, identifiers_1.quoteIdentifier)(typeName)}`;
|
|
305
|
+
}
|
|
306
|
+
function withOptionalPrecision(typeName, precision) {
|
|
307
|
+
if (precision === null) {
|
|
308
|
+
return typeName;
|
|
309
|
+
}
|
|
310
|
+
return `${typeName}(${precision})`;
|
|
311
|
+
}
|
|
312
|
+
function mapPgCatalogTypeName(typeName) {
|
|
313
|
+
switch (typeName) {
|
|
314
|
+
case "int2":
|
|
315
|
+
return "smallint";
|
|
316
|
+
case "int4":
|
|
317
|
+
return "integer";
|
|
318
|
+
case "int8":
|
|
319
|
+
return "bigint";
|
|
320
|
+
case "float4":
|
|
321
|
+
return "real";
|
|
322
|
+
case "float8":
|
|
323
|
+
return "double precision";
|
|
324
|
+
case "bool":
|
|
325
|
+
return "boolean";
|
|
326
|
+
case "varchar":
|
|
327
|
+
return "character varying";
|
|
328
|
+
case "bpchar":
|
|
329
|
+
return "character";
|
|
330
|
+
case "timestamptz":
|
|
331
|
+
return "timestamp with time zone";
|
|
332
|
+
case "timestamp":
|
|
333
|
+
return "timestamp";
|
|
334
|
+
case "timetz":
|
|
335
|
+
return "time with time zone";
|
|
336
|
+
case "time":
|
|
337
|
+
return "time";
|
|
338
|
+
default:
|
|
339
|
+
return (0, identifiers_1.quoteIdentifier)(typeName);
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
function schemaColumnsAreEqual(left, right) {
|
|
343
|
+
return (left.type === right.type &&
|
|
344
|
+
left.nullable === right.nullable &&
|
|
345
|
+
left.default === right.default &&
|
|
346
|
+
left.isGenerated === right.isGenerated &&
|
|
347
|
+
left.generationExpression === right.generationExpression);
|
|
348
|
+
}
|
|
349
|
+
function arraysEqual(left, right) {
|
|
350
|
+
if (left.length !== right.length) {
|
|
351
|
+
return false;
|
|
352
|
+
}
|
|
353
|
+
return left.every((value, index) => value === right[index]);
|
|
354
|
+
}
|
|
355
|
+
//# sourceMappingURL=generate-schema-diff.js.map
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { type SchemaDiffJson } from "./schema-diff-schema";
|
|
2
|
+
export interface GenerateSchemaSqlOptions {
|
|
3
|
+
transaction: boolean;
|
|
4
|
+
includeDrops: boolean;
|
|
5
|
+
onProgress?: (message: string) => void;
|
|
6
|
+
}
|
|
7
|
+
export interface GeneratedSchemaSqlResult {
|
|
8
|
+
sql: string;
|
|
9
|
+
summary: {
|
|
10
|
+
tablesToCreate: number;
|
|
11
|
+
tablesToDrop: number;
|
|
12
|
+
columnsToAdd: number;
|
|
13
|
+
columnsToAlter: number;
|
|
14
|
+
columnsToDrop: number;
|
|
15
|
+
primaryKeysToChange: number;
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
export declare function buildSchemaSqlOutputPath(inputPath: string): string;
|
|
19
|
+
export declare function generateSchemaSqlScript(diff: SchemaDiffJson, options: GenerateSchemaSqlOptions): GeneratedSchemaSqlResult;
|
|
20
|
+
//# sourceMappingURL=generate-schema-sql.d.ts.map
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.buildSchemaSqlOutputPath = buildSchemaSqlOutputPath;
|
|
4
|
+
exports.generateSchemaSqlScript = generateSchemaSqlScript;
|
|
5
|
+
const identifiers_1 = require("../shared/identifiers");
|
|
6
|
+
function buildSchemaSqlOutputPath(inputPath) {
|
|
7
|
+
return inputPath.endsWith(".json")
|
|
8
|
+
? inputPath.replace(/\.json$/i, ".sql")
|
|
9
|
+
: `${inputPath}.sql`;
|
|
10
|
+
}
|
|
11
|
+
function generateSchemaSqlScript(diff, options) {
|
|
12
|
+
const lines = [];
|
|
13
|
+
const summary = {
|
|
14
|
+
tablesToCreate: 0,
|
|
15
|
+
tablesToDrop: 0,
|
|
16
|
+
columnsToAdd: 0,
|
|
17
|
+
columnsToAlter: 0,
|
|
18
|
+
columnsToDrop: 0,
|
|
19
|
+
primaryKeysToChange: 0,
|
|
20
|
+
};
|
|
21
|
+
lines.push("-- Generated by frg-data-diff");
|
|
22
|
+
lines.push("-- Review this schema SQL carefully before executing it.");
|
|
23
|
+
lines.push(`-- Schema diff format: ${diff.format}`);
|
|
24
|
+
lines.push(`-- Generated at: ${diff.generatedAt}`);
|
|
25
|
+
lines.push(`-- Source schema: ${diff.source.schema}`);
|
|
26
|
+
lines.push(`-- Destination schema: ${diff.dest.schema}`);
|
|
27
|
+
if (options.transaction) {
|
|
28
|
+
lines.push("");
|
|
29
|
+
lines.push("BEGIN;");
|
|
30
|
+
}
|
|
31
|
+
reportProgress(options.onProgress, `Generating schema SQL for ${diff.tables.length} table diff(s)...`);
|
|
32
|
+
for (const [tableIndex, tableDiff] of diff.tables.entries()) {
|
|
33
|
+
const progressLabel = `[schema-sql ${tableIndex + 1}/${diff.tables.length}] ${tableDiff.schema}.${tableDiff.table}`;
|
|
34
|
+
reportProgress(options.onProgress, `${progressLabel}: ${summarizeSchemaSqlWork(tableDiff, options)}`);
|
|
35
|
+
const statements = buildTableStatements(tableDiff, options, summary);
|
|
36
|
+
if (statements.length === 0) {
|
|
37
|
+
reportProgress(options.onProgress, `${progressLabel}: no SQL statements generated`);
|
|
38
|
+
continue;
|
|
39
|
+
}
|
|
40
|
+
reportProgress(options.onProgress, `${progressLabel}: generated ${statements.length} SQL statement(s)`);
|
|
41
|
+
lines.push("");
|
|
42
|
+
lines.push(`-- ${tableDiff.schema}.${tableDiff.table}`);
|
|
43
|
+
lines.push(...statements);
|
|
44
|
+
}
|
|
45
|
+
if (options.transaction) {
|
|
46
|
+
lines.push("");
|
|
47
|
+
lines.push("COMMIT;");
|
|
48
|
+
}
|
|
49
|
+
lines.push("");
|
|
50
|
+
return {
|
|
51
|
+
sql: lines.join("\n"),
|
|
52
|
+
summary,
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
function buildTableStatements(tableDiff, options, summary) {
|
|
56
|
+
(0, identifiers_1.validateIdentifier)(tableDiff.schema, "schema name");
|
|
57
|
+
(0, identifiers_1.validateIdentifier)(tableDiff.table, "table name");
|
|
58
|
+
const qualifiedTable = (0, identifiers_1.quoteQualifiedTable)(tableDiff.schema, tableDiff.table);
|
|
59
|
+
const statements = [];
|
|
60
|
+
if (tableDiff.createTable) {
|
|
61
|
+
statements.push(buildCreateTableStatement(tableDiff.schema, tableDiff.table, tableDiff.createTable.columns, tableDiff.createTable.primaryKey));
|
|
62
|
+
summary.tablesToCreate++;
|
|
63
|
+
return statements;
|
|
64
|
+
}
|
|
65
|
+
if (tableDiff.dropTable) {
|
|
66
|
+
if (options.includeDrops) {
|
|
67
|
+
statements.push(`DROP TABLE IF EXISTS ${qualifiedTable};`);
|
|
68
|
+
summary.tablesToDrop++;
|
|
69
|
+
}
|
|
70
|
+
return statements;
|
|
71
|
+
}
|
|
72
|
+
if (tableDiff.primaryKeyChange &&
|
|
73
|
+
tableDiff.primaryKeyChange.from.length > 0) {
|
|
74
|
+
const constraintName = tableDiff.primaryKeyChange.dropConstraintName ??
|
|
75
|
+
`${tableDiff.table}_pkey`;
|
|
76
|
+
(0, identifiers_1.validateIdentifier)(constraintName, "primary key constraint name");
|
|
77
|
+
statements.push(`ALTER TABLE ${qualifiedTable} DROP CONSTRAINT IF EXISTS ${(0, identifiers_1.quoteIdentifier)(constraintName)};`);
|
|
78
|
+
summary.primaryKeysToChange++;
|
|
79
|
+
}
|
|
80
|
+
for (const column of tableDiff.addColumns) {
|
|
81
|
+
statements.push(`ALTER TABLE ${qualifiedTable} ADD COLUMN ${buildColumnDefinitionSql(column)};`);
|
|
82
|
+
summary.columnsToAdd++;
|
|
83
|
+
}
|
|
84
|
+
for (const columnChange of tableDiff.alterColumns) {
|
|
85
|
+
statements.push(...buildAlterColumnStatements(qualifiedTable, columnChange));
|
|
86
|
+
summary.columnsToAlter++;
|
|
87
|
+
}
|
|
88
|
+
if (options.includeDrops) {
|
|
89
|
+
for (const column of tableDiff.dropColumns) {
|
|
90
|
+
(0, identifiers_1.validateIdentifier)(column.name, "drop column name");
|
|
91
|
+
statements.push(`ALTER TABLE ${qualifiedTable} DROP COLUMN ${(0, identifiers_1.quoteIdentifier)(column.name)};`);
|
|
92
|
+
summary.columnsToDrop++;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
if (tableDiff.primaryKeyChange && tableDiff.primaryKeyChange.to.length > 0) {
|
|
96
|
+
(0, identifiers_1.validateIdentifiers)(tableDiff.primaryKeyChange.to, "primary key column");
|
|
97
|
+
statements.push(`ALTER TABLE ${qualifiedTable} ADD PRIMARY KEY (${tableDiff.primaryKeyChange.to.map(identifiers_1.quoteIdentifier).join(", ")});`);
|
|
98
|
+
if (tableDiff.primaryKeyChange.from.length === 0) {
|
|
99
|
+
summary.primaryKeysToChange++;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
return statements;
|
|
103
|
+
}
|
|
104
|
+
function buildCreateTableStatement(schema, table, columns, primaryKey) {
|
|
105
|
+
(0, identifiers_1.validateIdentifier)(schema, "schema name");
|
|
106
|
+
(0, identifiers_1.validateIdentifier)(table, "table name");
|
|
107
|
+
(0, identifiers_1.validateIdentifiers)(columns.map((column) => column.name), "create column");
|
|
108
|
+
(0, identifiers_1.validateIdentifiers)(primaryKey, "primary key column");
|
|
109
|
+
const lines = columns.map((column) => ` ${buildColumnDefinitionSql(column)}`);
|
|
110
|
+
if (primaryKey.length > 0) {
|
|
111
|
+
lines.push(` PRIMARY KEY (${primaryKey.map(identifiers_1.quoteIdentifier).join(", ")})`);
|
|
112
|
+
}
|
|
113
|
+
return `CREATE TABLE ${(0, identifiers_1.quoteQualifiedTable)(schema, table)} (\n${lines.join(",\n")}\n);`;
|
|
114
|
+
}
|
|
115
|
+
function buildColumnDefinitionSql(column) {
|
|
116
|
+
(0, identifiers_1.validateIdentifier)(column.name, "column name");
|
|
117
|
+
let definition = `${(0, identifiers_1.quoteIdentifier)(column.name)} ${column.type}`;
|
|
118
|
+
if (column.isGenerated && column.generationExpression) {
|
|
119
|
+
definition += ` GENERATED ALWAYS AS (${column.generationExpression}) STORED`;
|
|
120
|
+
return definition;
|
|
121
|
+
}
|
|
122
|
+
if (column.default !== null) {
|
|
123
|
+
definition += ` DEFAULT ${column.default}`;
|
|
124
|
+
}
|
|
125
|
+
if (!column.nullable) {
|
|
126
|
+
definition += " NOT NULL";
|
|
127
|
+
}
|
|
128
|
+
return definition;
|
|
129
|
+
}
|
|
130
|
+
function buildAlterColumnStatements(qualifiedTable, columnChange) {
|
|
131
|
+
const columnName = (0, identifiers_1.quoteIdentifier)(columnChange.column);
|
|
132
|
+
const statements = [];
|
|
133
|
+
if (columnChange.from.isGenerated !== columnChange.to.isGenerated ||
|
|
134
|
+
columnChange.from.generationExpression !==
|
|
135
|
+
columnChange.to.generationExpression) {
|
|
136
|
+
statements.push(`ALTER TABLE ${qualifiedTable} DROP COLUMN ${columnName};`);
|
|
137
|
+
statements.push(`ALTER TABLE ${qualifiedTable} ADD COLUMN ${buildColumnDefinitionSql(columnChange.to)};`);
|
|
138
|
+
return statements;
|
|
139
|
+
}
|
|
140
|
+
if (columnChange.from.type !== columnChange.to.type) {
|
|
141
|
+
statements.push(`ALTER TABLE ${qualifiedTable} ALTER COLUMN ${columnName} TYPE ${columnChange.to.type};`);
|
|
142
|
+
}
|
|
143
|
+
if (columnChange.from.default !== columnChange.to.default) {
|
|
144
|
+
if (columnChange.to.default === null) {
|
|
145
|
+
statements.push(`ALTER TABLE ${qualifiedTable} ALTER COLUMN ${columnName} DROP DEFAULT;`);
|
|
146
|
+
}
|
|
147
|
+
else {
|
|
148
|
+
statements.push(`ALTER TABLE ${qualifiedTable} ALTER COLUMN ${columnName} SET DEFAULT ${columnChange.to.default};`);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
if (columnChange.from.nullable !== columnChange.to.nullable) {
|
|
152
|
+
statements.push(columnChange.to.nullable
|
|
153
|
+
? `ALTER TABLE ${qualifiedTable} ALTER COLUMN ${columnName} DROP NOT NULL;`
|
|
154
|
+
: `ALTER TABLE ${qualifiedTable} ALTER COLUMN ${columnName} SET NOT NULL;`);
|
|
155
|
+
}
|
|
156
|
+
return statements;
|
|
157
|
+
}
|
|
158
|
+
function summarizeSchemaSqlWork(tableDiff, options) {
|
|
159
|
+
const changeDescriptions = [];
|
|
160
|
+
if (tableDiff.createTable) {
|
|
161
|
+
changeDescriptions.push("create table");
|
|
162
|
+
}
|
|
163
|
+
if (tableDiff.dropTable) {
|
|
164
|
+
changeDescriptions.push(options.includeDrops ? "drop table" : "drop table skipped");
|
|
165
|
+
}
|
|
166
|
+
if (tableDiff.addColumns.length > 0) {
|
|
167
|
+
changeDescriptions.push(`${tableDiff.addColumns.length} column(s) to add`);
|
|
168
|
+
}
|
|
169
|
+
if (tableDiff.alterColumns.length > 0) {
|
|
170
|
+
changeDescriptions.push(`${tableDiff.alterColumns.length} column(s) to alter`);
|
|
171
|
+
}
|
|
172
|
+
if (tableDiff.dropColumns.length > 0) {
|
|
173
|
+
changeDescriptions.push(options.includeDrops
|
|
174
|
+
? `${tableDiff.dropColumns.length} column(s) to drop`
|
|
175
|
+
: `${tableDiff.dropColumns.length} column drop(s) skipped`);
|
|
176
|
+
}
|
|
177
|
+
if (tableDiff.primaryKeyChange) {
|
|
178
|
+
changeDescriptions.push("primary key change");
|
|
179
|
+
}
|
|
180
|
+
return changeDescriptions.join(", ");
|
|
181
|
+
}
|
|
182
|
+
function reportProgress(callback, message) {
|
|
183
|
+
if (callback) {
|
|
184
|
+
callback(message);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
//# sourceMappingURL=generate-schema-sql.js.map
|