frg-data-diff 2.0.0 → 2.1.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 +45 -4
- package/dist/cli/generator.js +156 -5
- package/dist/cli/pg-views.d.ts +2 -0
- package/dist/cli/pg-views.js +195 -0
- package/dist/cli/root.js +6 -0
- package/dist/config/config-schema.d.ts +32 -0
- package/dist/config/config-schema.js +4 -0
- package/dist/config/load-config.d.ts +1 -0
- package/dist/config/load-config.js +78 -1
- package/dist/config/resolve-options.d.ts +8 -2
- package/dist/config/resolve-options.js +28 -0
- package/dist/config/write-config.d.ts +2 -1
- package/dist/config/write-config.js +4 -0
- package/dist/db/pg-views.d.ts +24 -0
- package/dist/db/pg-views.js +137 -0
- package/dist/diff/pg-views-diff.d.ts +11 -0
- package/dist/diff/pg-views-diff.js +133 -0
- package/dist/schema-diff/generate-schema-diff.js +1 -1
- package/dist/schema-diff/schema-diff-schema.d.ts +3 -3
- package/dist/schema-diff/schema-diff-schema.js +1 -1
- package/dist/shared/generator-wizard.js +9 -0
- package/frg-triggers-diff.sql +4 -0
- package/frg-views-diff.sql +4 -0
- package/package.json +1 -1
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.generatePgViewsDiffSql = generatePgViewsDiffSql;
|
|
4
|
+
const pg_views_1 = require("../db/pg-views");
|
|
5
|
+
const identifiers_1 = require("../shared/identifiers");
|
|
6
|
+
async function generatePgViewsDiffSql(sourcePool, destPool, options) {
|
|
7
|
+
reportProgress(options.onProgress, "PostgreSQL views: resolving view patterns");
|
|
8
|
+
const { views } = await (0, pg_views_1.resolvePgViewPatterns)(sourcePool, destPool, options.schema, options.views, options.excludeViews);
|
|
9
|
+
reportProgress(options.onProgress, `PostgreSQL views: comparing ${views.length} view(s)`);
|
|
10
|
+
const sourceClient = await sourcePool.connect();
|
|
11
|
+
const destClient = await destPool.connect();
|
|
12
|
+
let sql = `-- PostgreSQL Views Diff\n`;
|
|
13
|
+
sql += `-- Generated automatically\n\n`;
|
|
14
|
+
try {
|
|
15
|
+
reportProgress(options.onProgress, "PostgreSQL views: fetching view definitions from source");
|
|
16
|
+
const sourceViews = await (0, pg_views_1.fetchPgViews)(sourceClient, options.schema, views, {
|
|
17
|
+
onViewStart: (viewName, viewPosition, totalViews) => {
|
|
18
|
+
reportProgress(options.onProgress, `[pg-views source ${viewPosition}/${totalViews}] ${options.schema}.${viewName}: fetching definition`);
|
|
19
|
+
},
|
|
20
|
+
});
|
|
21
|
+
reportProgress(options.onProgress, "PostgreSQL views: fetching view definitions from destination");
|
|
22
|
+
const destViews = await (0, pg_views_1.fetchPgViews)(destClient, options.schema, views, {
|
|
23
|
+
onViewStart: (viewName, viewPosition, totalViews) => {
|
|
24
|
+
reportProgress(options.onProgress, `[pg-views dest ${viewPosition}/${totalViews}] ${options.schema}.${viewName}: fetching definition`);
|
|
25
|
+
},
|
|
26
|
+
});
|
|
27
|
+
const sourceViewsMap = new Map(sourceViews.map((view) => [view.viewName, view]));
|
|
28
|
+
const destViewsMap = new Map(destViews.map((view) => [view.viewName, view]));
|
|
29
|
+
const work = buildPgViewDiffWork(views, sourceViewsMap, destViewsMap, options);
|
|
30
|
+
if (work.viewsToDrop.length === 0 && work.viewsToCreate.length === 0) {
|
|
31
|
+
reportProgress(options.onProgress, "PostgreSQL views: no differences found");
|
|
32
|
+
sql += `-- No differences found.\n`;
|
|
33
|
+
return sql;
|
|
34
|
+
}
|
|
35
|
+
reportProgress(options.onProgress, "PostgreSQL views: generating SQL script");
|
|
36
|
+
if (work.viewsToDrop.length > 0) {
|
|
37
|
+
sql += `-- Drop removed or incompatible views\n`;
|
|
38
|
+
for (const view of work.viewsToDrop) {
|
|
39
|
+
sql += `${buildDropViewStatement(view)}\n`;
|
|
40
|
+
}
|
|
41
|
+
sql += `\n`;
|
|
42
|
+
}
|
|
43
|
+
if (work.viewsToCreate.length > 0) {
|
|
44
|
+
sql += `-- Create or replace source views\n`;
|
|
45
|
+
for (const view of work.viewsToCreate) {
|
|
46
|
+
sql += `${buildCreateViewStatement(view)}\n\n`;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
finally {
|
|
51
|
+
sourceClient.release();
|
|
52
|
+
destClient.release();
|
|
53
|
+
}
|
|
54
|
+
return sql;
|
|
55
|
+
}
|
|
56
|
+
function buildPgViewDiffWork(views, sourceViewsMap, destViewsMap, options) {
|
|
57
|
+
const viewsToDrop = [];
|
|
58
|
+
const viewsToCreate = [];
|
|
59
|
+
for (const [viewIndex, viewName] of views.entries()) {
|
|
60
|
+
const progressLabel = formatPgViewsProgressLabel(viewIndex + 1, views.length, options.schema, viewName);
|
|
61
|
+
const sourceView = sourceViewsMap.get(viewName);
|
|
62
|
+
const destView = destViewsMap.get(viewName);
|
|
63
|
+
reportProgress(options.onProgress, `${progressLabel}: comparing view`);
|
|
64
|
+
if (sourceView && !destView) {
|
|
65
|
+
viewsToCreate.push(sourceView);
|
|
66
|
+
reportProgress(options.onProgress, `${progressLabel}: view to create`);
|
|
67
|
+
continue;
|
|
68
|
+
}
|
|
69
|
+
if (!sourceView && destView) {
|
|
70
|
+
viewsToDrop.push(destView);
|
|
71
|
+
reportProgress(options.onProgress, `${progressLabel}: view to drop`);
|
|
72
|
+
continue;
|
|
73
|
+
}
|
|
74
|
+
if (!sourceView || !destView) {
|
|
75
|
+
reportProgress(options.onProgress, `${progressLabel}: no view metadata`);
|
|
76
|
+
continue;
|
|
77
|
+
}
|
|
78
|
+
if (pgViewsAreEqual(sourceView, destView)) {
|
|
79
|
+
reportProgress(options.onProgress, `${progressLabel}: no changes`);
|
|
80
|
+
continue;
|
|
81
|
+
}
|
|
82
|
+
if (requiresDropBeforeCreate(sourceView, destView)) {
|
|
83
|
+
viewsToDrop.push(destView);
|
|
84
|
+
}
|
|
85
|
+
viewsToCreate.push(sourceView);
|
|
86
|
+
reportProgress(options.onProgress, `${progressLabel}: view definition changed`);
|
|
87
|
+
}
|
|
88
|
+
return { viewsToDrop, viewsToCreate };
|
|
89
|
+
}
|
|
90
|
+
function pgViewsAreEqual(sourceView, destView) {
|
|
91
|
+
if (sourceView.viewType !== destView.viewType) {
|
|
92
|
+
return false;
|
|
93
|
+
}
|
|
94
|
+
return (normalizeViewDefinition(sourceView.viewDefinition) ===
|
|
95
|
+
normalizeViewDefinition(destView.viewDefinition));
|
|
96
|
+
}
|
|
97
|
+
function requiresDropBeforeCreate(sourceView, destView) {
|
|
98
|
+
if (sourceView.viewType !== destView.viewType) {
|
|
99
|
+
return true;
|
|
100
|
+
}
|
|
101
|
+
return sourceView.viewType === "materialized_view";
|
|
102
|
+
}
|
|
103
|
+
function buildDropViewStatement(view) {
|
|
104
|
+
return `DROP ${buildViewObjectKeyword(view)} IF EXISTS ${quoteQualifiedView(view)};`;
|
|
105
|
+
}
|
|
106
|
+
function buildCreateViewStatement(view) {
|
|
107
|
+
const definition = normalizeViewDefinition(view.viewDefinition);
|
|
108
|
+
if (view.viewType === "materialized_view") {
|
|
109
|
+
return `CREATE MATERIALIZED VIEW ${quoteQualifiedView(view)} AS\n${definition};`;
|
|
110
|
+
}
|
|
111
|
+
return `CREATE OR REPLACE VIEW ${quoteQualifiedView(view)} AS\n${definition};`;
|
|
112
|
+
}
|
|
113
|
+
function buildViewObjectKeyword(view) {
|
|
114
|
+
if (view.viewType === "materialized_view") {
|
|
115
|
+
return "MATERIALIZED VIEW";
|
|
116
|
+
}
|
|
117
|
+
return "VIEW";
|
|
118
|
+
}
|
|
119
|
+
function quoteQualifiedView(view) {
|
|
120
|
+
return `${(0, identifiers_1.quoteIdentifier)(view.viewSchema)}.${(0, identifiers_1.quoteIdentifier)(view.viewName)}`;
|
|
121
|
+
}
|
|
122
|
+
function normalizeViewDefinition(definition) {
|
|
123
|
+
return definition.trim().replace(/;+$/, "").trim();
|
|
124
|
+
}
|
|
125
|
+
function formatPgViewsProgressLabel(viewPosition, totalViews, schema, viewName) {
|
|
126
|
+
return `[pg-views ${viewPosition}/${totalViews}] ${schema}.${viewName}`;
|
|
127
|
+
}
|
|
128
|
+
function reportProgress(callback, message) {
|
|
129
|
+
if (callback) {
|
|
130
|
+
callback(message);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
//# sourceMappingURL=pg-views-diff.js.map
|
|
@@ -68,7 +68,7 @@ async function generateSchemaDiff(sourcePool, destPool, options) {
|
|
|
68
68
|
destClient.release();
|
|
69
69
|
}
|
|
70
70
|
return {
|
|
71
|
-
format: "
|
|
71
|
+
format: "frg-schema-diff-json/v1",
|
|
72
72
|
generatedAt: new Date().toISOString(),
|
|
73
73
|
source: { schema: options.schema },
|
|
74
74
|
dest: { schema: options.schema },
|
|
@@ -518,7 +518,7 @@ declare const schemaTableDiffSchema: z.ZodObject<{
|
|
|
518
518
|
} | null;
|
|
519
519
|
}>;
|
|
520
520
|
export declare const schemaDiffJsonSchema: z.ZodObject<{
|
|
521
|
-
format: z.ZodLiteral<"
|
|
521
|
+
format: z.ZodLiteral<"frg-schema-diff-json/v1">;
|
|
522
522
|
generatedAt: z.ZodString;
|
|
523
523
|
source: z.ZodObject<{
|
|
524
524
|
schema: z.ZodString;
|
|
@@ -911,7 +911,7 @@ export declare const schemaDiffJsonSchema: z.ZodObject<{
|
|
|
911
911
|
tables: string[];
|
|
912
912
|
excludedTables: string[];
|
|
913
913
|
};
|
|
914
|
-
format: "
|
|
914
|
+
format: "frg-schema-diff-json/v1";
|
|
915
915
|
generatedAt: string;
|
|
916
916
|
source: {
|
|
917
917
|
schema: string;
|
|
@@ -996,7 +996,7 @@ export declare const schemaDiffJsonSchema: z.ZodObject<{
|
|
|
996
996
|
tables: string[];
|
|
997
997
|
excludedTables: string[];
|
|
998
998
|
};
|
|
999
|
-
format: "
|
|
999
|
+
format: "frg-schema-diff-json/v1";
|
|
1000
1000
|
generatedAt: string;
|
|
1001
1001
|
source: {
|
|
1002
1002
|
schema: string;
|
|
@@ -48,7 +48,7 @@ const schemaDiffSummarySchema = zod_1.z.object({
|
|
|
48
48
|
primaryKeysToChange: zod_1.z.number().int(),
|
|
49
49
|
});
|
|
50
50
|
exports.schemaDiffJsonSchema = zod_1.z.object({
|
|
51
|
-
format: zod_1.z.literal("
|
|
51
|
+
format: zod_1.z.literal("frg-schema-diff-json/v1"),
|
|
52
52
|
generatedAt: zod_1.z.string(),
|
|
53
53
|
source: zod_1.z.object({ schema: zod_1.z.string() }),
|
|
54
54
|
dest: zod_1.z.object({ schema: zod_1.z.string() }),
|
|
@@ -83,6 +83,11 @@ async function promptForGeneratorOptions(defaults, promptFn, dependencies = {})
|
|
|
83
83
|
const pgTriggersOutput = await promptConfigString("PostgreSQL triggers output file", defaults.pgTriggersOutput, ask, env, envrcPath, {}, onEnvrcWrite);
|
|
84
84
|
const generatePgTriggers = await promptBoolean("Generate a PostgreSQL triggers and functions diff? (SQL script)", defaults.generatePgTriggers ?? true, ask, env, envrcPath, onEnvrcWrite);
|
|
85
85
|
console.log("");
|
|
86
|
+
const pgViews = await promptOptionalList('PostgreSQL views to compare (comma-separated, "*" supported; blank for all)', defaults.pgViews, ask, env, envrcPath, onEnvrcWrite);
|
|
87
|
+
const pgViewsExclude = await promptOptionalList('PostgreSQL views to exclude (comma-separated, "*" supported)', defaults.pgViewsExclude, ask, env, envrcPath, onEnvrcWrite);
|
|
88
|
+
const pgViewsOutput = await promptConfigString("PostgreSQL views output file", defaults.pgViewsOutput, ask, env, envrcPath, {}, onEnvrcWrite);
|
|
89
|
+
const generatePgViews = await promptBoolean("Generate a PostgreSQL views diff? (SQL script)", defaults.generatePgViews ?? true, ask, env, envrcPath, onEnvrcWrite);
|
|
90
|
+
console.log("");
|
|
86
91
|
const pretty = await promptBoolean("Pretty-print JSON", defaults.pretty, ask, env, envrcPath, onEnvrcWrite);
|
|
87
92
|
return {
|
|
88
93
|
sourcePgHost,
|
|
@@ -104,15 +109,19 @@ async function promptForGeneratorOptions(defaults, promptFn, dependencies = {})
|
|
|
104
109
|
schemaDiffExcludeTables,
|
|
105
110
|
pgTriggersTables,
|
|
106
111
|
pgTriggersExcludeTables,
|
|
112
|
+
pgViews,
|
|
113
|
+
pgViewsExclude,
|
|
107
114
|
ignoreColumns,
|
|
108
115
|
includeDeletes,
|
|
109
116
|
skipMissingPk,
|
|
110
117
|
output,
|
|
111
118
|
schemaDiffOutput,
|
|
112
119
|
pgTriggersOutput,
|
|
120
|
+
pgViewsOutput,
|
|
113
121
|
pretty,
|
|
114
122
|
generateSql,
|
|
115
123
|
generatePgTriggers,
|
|
124
|
+
generatePgViews,
|
|
116
125
|
};
|
|
117
126
|
}
|
|
118
127
|
finally {
|
package/package.json
CHANGED