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
package/dist/cli/root.js
CHANGED
|
@@ -49,6 +49,7 @@ const commandFiles = {
|
|
|
49
49
|
apply: "apply.js",
|
|
50
50
|
sql: "sql.js",
|
|
51
51
|
"pg-triggers": "pg-triggers.js",
|
|
52
|
+
"pg-views": "pg-views.js",
|
|
52
53
|
};
|
|
53
54
|
const helpText = `frg-data-diff
|
|
54
55
|
|
|
@@ -62,6 +63,7 @@ Commands:
|
|
|
62
63
|
apply Read a JSON diff file and safely apply it to a destination PostgreSQL database.
|
|
63
64
|
sql Read a JSON diff file and write a plain SQL script for manual review and execution.
|
|
64
65
|
pg-triggers Compare PostgreSQL triggers and functions and write a SQL diff script.
|
|
66
|
+
pg-views Compare PostgreSQL view definitions and write a SQL diff script.
|
|
65
67
|
version Print the package version.
|
|
66
68
|
|
|
67
69
|
Basic usage:
|
|
@@ -71,12 +73,14 @@ Basic usage:
|
|
|
71
73
|
npx frg-data-diff apply --execute
|
|
72
74
|
npx frg-data-diff sql --yes
|
|
73
75
|
npx frg-data-diff pg-triggers
|
|
76
|
+
npx frg-data-diff pg-views
|
|
74
77
|
npx frg-data-diff version
|
|
75
78
|
|
|
76
79
|
Config file:
|
|
77
80
|
.frg-data-diff.config.json
|
|
78
81
|
|
|
79
82
|
Commands look for .frg-data-diff.config.json in the current directory.
|
|
83
|
+
The config file accepts // line comments and /* block comments */.
|
|
80
84
|
Create it by running a command with --config or with CLI args on first run.
|
|
81
85
|
|
|
82
86
|
How to generate a diff:
|
|
@@ -149,6 +153,8 @@ Config file example (.frg-data-diff.config.json):
|
|
|
149
153
|
"schemaDiffOutput": "frg-schema-diff.json",
|
|
150
154
|
"pgTriggersTables": ["my_table"],
|
|
151
155
|
"pgTriggersOutput": "frg-triggers-diff.sql",
|
|
156
|
+
"pgViews": ["*"],
|
|
157
|
+
"pgViewsOutput": "frg-views-diff.sql",
|
|
152
158
|
"includeDeletes": true,
|
|
153
159
|
"pretty": true
|
|
154
160
|
},
|
|
@@ -19,6 +19,8 @@ export declare const generatorConfigSchema: z.ZodObject<{
|
|
|
19
19
|
schemaDiffExcludeTables: z.ZodOptional<z.ZodNullable<z.ZodArray<z.ZodEffects<z.ZodString, string, string>, "many">>>;
|
|
20
20
|
pgTriggersTables: z.ZodOptional<z.ZodArray<z.ZodEffects<z.ZodString, string, string>, "many">>;
|
|
21
21
|
pgTriggersExcludeTables: z.ZodOptional<z.ZodNullable<z.ZodArray<z.ZodEffects<z.ZodString, string, string>, "many">>>;
|
|
22
|
+
pgViews: z.ZodOptional<z.ZodArray<z.ZodEffects<z.ZodString, string, string>, "many">>;
|
|
23
|
+
pgViewsExclude: z.ZodOptional<z.ZodNullable<z.ZodArray<z.ZodEffects<z.ZodString, string, string>, "many">>>;
|
|
22
24
|
ignoreColumns: z.ZodDefault<z.ZodNullable<z.ZodArray<z.ZodEffects<z.ZodString, string, string>, "many">>>;
|
|
23
25
|
tablesWhereDataFilters: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodEffects<z.ZodString, string, string>>>;
|
|
24
26
|
includeDeletes: z.ZodDefault<z.ZodUnion<[z.ZodBoolean, z.ZodString]>>;
|
|
@@ -26,9 +28,11 @@ export declare const generatorConfigSchema: z.ZodObject<{
|
|
|
26
28
|
output: z.ZodDefault<z.ZodEffects<z.ZodString, string, string>>;
|
|
27
29
|
schemaDiffOutput: z.ZodOptional<z.ZodEffects<z.ZodString, string, string>>;
|
|
28
30
|
pgTriggersOutput: z.ZodOptional<z.ZodEffects<z.ZodString, string, string>>;
|
|
31
|
+
pgViewsOutput: z.ZodOptional<z.ZodEffects<z.ZodString, string, string>>;
|
|
29
32
|
pretty: z.ZodDefault<z.ZodUnion<[z.ZodBoolean, z.ZodString]>>;
|
|
30
33
|
generateSql: z.ZodOptional<z.ZodUnion<[z.ZodBoolean, z.ZodString]>>;
|
|
31
34
|
generatePgTriggers: z.ZodOptional<z.ZodUnion<[z.ZodBoolean, z.ZodString]>>;
|
|
35
|
+
generatePgViews: z.ZodOptional<z.ZodUnion<[z.ZodBoolean, z.ZodString]>>;
|
|
32
36
|
}, "strip", z.ZodTypeAny, {
|
|
33
37
|
schema: string;
|
|
34
38
|
includeDeletes: string | boolean;
|
|
@@ -54,11 +58,15 @@ export declare const generatorConfigSchema: z.ZodObject<{
|
|
|
54
58
|
schemaDiffExcludeTables?: string[] | null | undefined;
|
|
55
59
|
pgTriggersTables?: string[] | undefined;
|
|
56
60
|
pgTriggersExcludeTables?: string[] | null | undefined;
|
|
61
|
+
pgViews?: string[] | undefined;
|
|
62
|
+
pgViewsExclude?: string[] | null | undefined;
|
|
57
63
|
tablesWhereDataFilters?: Record<string, string> | undefined;
|
|
58
64
|
schemaDiffOutput?: string | undefined;
|
|
59
65
|
pgTriggersOutput?: string | undefined;
|
|
66
|
+
pgViewsOutput?: string | undefined;
|
|
60
67
|
generateSql?: string | boolean | undefined;
|
|
61
68
|
generatePgTriggers?: string | boolean | undefined;
|
|
69
|
+
generatePgViews?: string | boolean | undefined;
|
|
62
70
|
}, {
|
|
63
71
|
tables: string[];
|
|
64
72
|
sourcePgHost: string;
|
|
@@ -80,15 +88,19 @@ export declare const generatorConfigSchema: z.ZodObject<{
|
|
|
80
88
|
schemaDiffExcludeTables?: string[] | null | undefined;
|
|
81
89
|
pgTriggersTables?: string[] | undefined;
|
|
82
90
|
pgTriggersExcludeTables?: string[] | null | undefined;
|
|
91
|
+
pgViews?: string[] | undefined;
|
|
92
|
+
pgViewsExclude?: string[] | null | undefined;
|
|
83
93
|
ignoreColumns?: string[] | null | undefined;
|
|
84
94
|
tablesWhereDataFilters?: Record<string, string> | undefined;
|
|
85
95
|
skipMissingPk?: string | boolean | undefined;
|
|
86
96
|
output?: string | undefined;
|
|
87
97
|
schemaDiffOutput?: string | undefined;
|
|
88
98
|
pgTriggersOutput?: string | undefined;
|
|
99
|
+
pgViewsOutput?: string | undefined;
|
|
89
100
|
pretty?: string | boolean | undefined;
|
|
90
101
|
generateSql?: string | boolean | undefined;
|
|
91
102
|
generatePgTriggers?: string | boolean | undefined;
|
|
103
|
+
generatePgViews?: string | boolean | undefined;
|
|
92
104
|
}>;
|
|
93
105
|
export declare const applyConfigSchema: z.ZodObject<{
|
|
94
106
|
destPgHost: z.ZodEffects<z.ZodString, string, string>;
|
|
@@ -158,6 +170,8 @@ export declare const configSchema: z.ZodObject<{
|
|
|
158
170
|
schemaDiffExcludeTables: z.ZodOptional<z.ZodNullable<z.ZodArray<z.ZodEffects<z.ZodString, string, string>, "many">>>;
|
|
159
171
|
pgTriggersTables: z.ZodOptional<z.ZodArray<z.ZodEffects<z.ZodString, string, string>, "many">>;
|
|
160
172
|
pgTriggersExcludeTables: z.ZodOptional<z.ZodNullable<z.ZodArray<z.ZodEffects<z.ZodString, string, string>, "many">>>;
|
|
173
|
+
pgViews: z.ZodOptional<z.ZodArray<z.ZodEffects<z.ZodString, string, string>, "many">>;
|
|
174
|
+
pgViewsExclude: z.ZodOptional<z.ZodNullable<z.ZodArray<z.ZodEffects<z.ZodString, string, string>, "many">>>;
|
|
161
175
|
ignoreColumns: z.ZodDefault<z.ZodNullable<z.ZodArray<z.ZodEffects<z.ZodString, string, string>, "many">>>;
|
|
162
176
|
tablesWhereDataFilters: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodEffects<z.ZodString, string, string>>>;
|
|
163
177
|
includeDeletes: z.ZodDefault<z.ZodUnion<[z.ZodBoolean, z.ZodString]>>;
|
|
@@ -165,9 +179,11 @@ export declare const configSchema: z.ZodObject<{
|
|
|
165
179
|
output: z.ZodDefault<z.ZodEffects<z.ZodString, string, string>>;
|
|
166
180
|
schemaDiffOutput: z.ZodOptional<z.ZodEffects<z.ZodString, string, string>>;
|
|
167
181
|
pgTriggersOutput: z.ZodOptional<z.ZodEffects<z.ZodString, string, string>>;
|
|
182
|
+
pgViewsOutput: z.ZodOptional<z.ZodEffects<z.ZodString, string, string>>;
|
|
168
183
|
pretty: z.ZodDefault<z.ZodUnion<[z.ZodBoolean, z.ZodString]>>;
|
|
169
184
|
generateSql: z.ZodOptional<z.ZodUnion<[z.ZodBoolean, z.ZodString]>>;
|
|
170
185
|
generatePgTriggers: z.ZodOptional<z.ZodUnion<[z.ZodBoolean, z.ZodString]>>;
|
|
186
|
+
generatePgViews: z.ZodOptional<z.ZodUnion<[z.ZodBoolean, z.ZodString]>>;
|
|
171
187
|
}, "strip", z.ZodTypeAny, {
|
|
172
188
|
schema: string;
|
|
173
189
|
includeDeletes: string | boolean;
|
|
@@ -193,11 +209,15 @@ export declare const configSchema: z.ZodObject<{
|
|
|
193
209
|
schemaDiffExcludeTables?: string[] | null | undefined;
|
|
194
210
|
pgTriggersTables?: string[] | undefined;
|
|
195
211
|
pgTriggersExcludeTables?: string[] | null | undefined;
|
|
212
|
+
pgViews?: string[] | undefined;
|
|
213
|
+
pgViewsExclude?: string[] | null | undefined;
|
|
196
214
|
tablesWhereDataFilters?: Record<string, string> | undefined;
|
|
197
215
|
schemaDiffOutput?: string | undefined;
|
|
198
216
|
pgTriggersOutput?: string | undefined;
|
|
217
|
+
pgViewsOutput?: string | undefined;
|
|
199
218
|
generateSql?: string | boolean | undefined;
|
|
200
219
|
generatePgTriggers?: string | boolean | undefined;
|
|
220
|
+
generatePgViews?: string | boolean | undefined;
|
|
201
221
|
}, {
|
|
202
222
|
tables: string[];
|
|
203
223
|
sourcePgHost: string;
|
|
@@ -219,15 +239,19 @@ export declare const configSchema: z.ZodObject<{
|
|
|
219
239
|
schemaDiffExcludeTables?: string[] | null | undefined;
|
|
220
240
|
pgTriggersTables?: string[] | undefined;
|
|
221
241
|
pgTriggersExcludeTables?: string[] | null | undefined;
|
|
242
|
+
pgViews?: string[] | undefined;
|
|
243
|
+
pgViewsExclude?: string[] | null | undefined;
|
|
222
244
|
ignoreColumns?: string[] | null | undefined;
|
|
223
245
|
tablesWhereDataFilters?: Record<string, string> | undefined;
|
|
224
246
|
skipMissingPk?: string | boolean | undefined;
|
|
225
247
|
output?: string | undefined;
|
|
226
248
|
schemaDiffOutput?: string | undefined;
|
|
227
249
|
pgTriggersOutput?: string | undefined;
|
|
250
|
+
pgViewsOutput?: string | undefined;
|
|
228
251
|
pretty?: string | boolean | undefined;
|
|
229
252
|
generateSql?: string | boolean | undefined;
|
|
230
253
|
generatePgTriggers?: string | boolean | undefined;
|
|
254
|
+
generatePgViews?: string | boolean | undefined;
|
|
231
255
|
}>;
|
|
232
256
|
apply: z.ZodObject<{
|
|
233
257
|
destPgHost: z.ZodEffects<z.ZodString, string, string>;
|
|
@@ -302,11 +326,15 @@ export declare const configSchema: z.ZodObject<{
|
|
|
302
326
|
schemaDiffExcludeTables?: string[] | null | undefined;
|
|
303
327
|
pgTriggersTables?: string[] | undefined;
|
|
304
328
|
pgTriggersExcludeTables?: string[] | null | undefined;
|
|
329
|
+
pgViews?: string[] | undefined;
|
|
330
|
+
pgViewsExclude?: string[] | null | undefined;
|
|
305
331
|
tablesWhereDataFilters?: Record<string, string> | undefined;
|
|
306
332
|
schemaDiffOutput?: string | undefined;
|
|
307
333
|
pgTriggersOutput?: string | undefined;
|
|
334
|
+
pgViewsOutput?: string | undefined;
|
|
308
335
|
generateSql?: string | boolean | undefined;
|
|
309
336
|
generatePgTriggers?: string | boolean | undefined;
|
|
337
|
+
generatePgViews?: string | boolean | undefined;
|
|
310
338
|
};
|
|
311
339
|
apply: {
|
|
312
340
|
destPgHost: string;
|
|
@@ -347,15 +375,19 @@ export declare const configSchema: z.ZodObject<{
|
|
|
347
375
|
schemaDiffExcludeTables?: string[] | null | undefined;
|
|
348
376
|
pgTriggersTables?: string[] | undefined;
|
|
349
377
|
pgTriggersExcludeTables?: string[] | null | undefined;
|
|
378
|
+
pgViews?: string[] | undefined;
|
|
379
|
+
pgViewsExclude?: string[] | null | undefined;
|
|
350
380
|
ignoreColumns?: string[] | null | undefined;
|
|
351
381
|
tablesWhereDataFilters?: Record<string, string> | undefined;
|
|
352
382
|
skipMissingPk?: string | boolean | undefined;
|
|
353
383
|
output?: string | undefined;
|
|
354
384
|
schemaDiffOutput?: string | undefined;
|
|
355
385
|
pgTriggersOutput?: string | undefined;
|
|
386
|
+
pgViewsOutput?: string | undefined;
|
|
356
387
|
pretty?: string | boolean | undefined;
|
|
357
388
|
generateSql?: string | boolean | undefined;
|
|
358
389
|
generatePgTriggers?: string | boolean | undefined;
|
|
390
|
+
generatePgViews?: string | boolean | undefined;
|
|
359
391
|
};
|
|
360
392
|
apply: {
|
|
361
393
|
destPgHost: string;
|
|
@@ -46,6 +46,8 @@ exports.generatorConfigSchema = zod_1.z.object({
|
|
|
46
46
|
schemaDiffExcludeTables: zod_1.z.array(envCapableListItem).nullable().optional(),
|
|
47
47
|
pgTriggersTables: zod_1.z.array(envCapableListItem).optional(),
|
|
48
48
|
pgTriggersExcludeTables: zod_1.z.array(envCapableListItem).nullable().optional(),
|
|
49
|
+
pgViews: zod_1.z.array(envCapableListItem).optional(),
|
|
50
|
+
pgViewsExclude: zod_1.z.array(envCapableListItem).nullable().optional(),
|
|
49
51
|
ignoreColumns: zod_1.z.array(envCapableListItem).nullable().default([]),
|
|
50
52
|
tablesWhereDataFilters: tableWhereDataFilters.optional(),
|
|
51
53
|
includeDeletes: envCapableBoolean.default(true),
|
|
@@ -53,9 +55,11 @@ exports.generatorConfigSchema = zod_1.z.object({
|
|
|
53
55
|
output: envCapableString.default("frg-data-diff.json"),
|
|
54
56
|
schemaDiffOutput: envCapableString.optional(),
|
|
55
57
|
pgTriggersOutput: envCapableString.optional(),
|
|
58
|
+
pgViewsOutput: envCapableString.optional(),
|
|
56
59
|
pretty: envCapableBoolean.default(true),
|
|
57
60
|
generateSql: envCapableBoolean.optional(),
|
|
58
61
|
generatePgTriggers: envCapableBoolean.optional(),
|
|
62
|
+
generatePgViews: envCapableBoolean.optional(),
|
|
59
63
|
});
|
|
60
64
|
exports.applyConfigSchema = zod_1.z.object({
|
|
61
65
|
destPgHost: envCapableString,
|
|
@@ -9,4 +9,5 @@ export declare function findConfigFile(cwd?: string): string | null;
|
|
|
9
9
|
* Exits with a clear error if validation fails.
|
|
10
10
|
*/
|
|
11
11
|
export declare function loadConfig(configPath: string): Config;
|
|
12
|
+
export declare function stripJsonComments(input: string): string;
|
|
12
13
|
//# sourceMappingURL=load-config.d.ts.map
|
|
@@ -36,6 +36,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
36
36
|
exports.DEFAULT_CONFIG_FILENAME = void 0;
|
|
37
37
|
exports.findConfigFile = findConfigFile;
|
|
38
38
|
exports.loadConfig = loadConfig;
|
|
39
|
+
exports.stripJsonComments = stripJsonComments;
|
|
39
40
|
const fs = __importStar(require("fs"));
|
|
40
41
|
const path = __importStar(require("path"));
|
|
41
42
|
const config_schema_1 = require("./config-schema");
|
|
@@ -67,7 +68,7 @@ function loadConfig(configPath) {
|
|
|
67
68
|
}
|
|
68
69
|
let parsed;
|
|
69
70
|
try {
|
|
70
|
-
parsed = JSON.parse(raw);
|
|
71
|
+
parsed = JSON.parse(stripJsonComments(raw));
|
|
71
72
|
}
|
|
72
73
|
catch (err) {
|
|
73
74
|
console.error(`Failed to parse config file as JSON: ${configPath}`);
|
|
@@ -84,4 +85,80 @@ function loadConfig(configPath) {
|
|
|
84
85
|
}
|
|
85
86
|
return result.data;
|
|
86
87
|
}
|
|
88
|
+
function stripJsonComments(input) {
|
|
89
|
+
let output = "";
|
|
90
|
+
let inString = false;
|
|
91
|
+
let escaped = false;
|
|
92
|
+
let inLineComment = false;
|
|
93
|
+
let inBlockComment = false;
|
|
94
|
+
for (let index = 0; index < input.length; index++) {
|
|
95
|
+
const char = input[index];
|
|
96
|
+
const next = input[index + 1];
|
|
97
|
+
if (inLineComment) {
|
|
98
|
+
if (isLineBreak(char)) {
|
|
99
|
+
inLineComment = false;
|
|
100
|
+
output += char;
|
|
101
|
+
}
|
|
102
|
+
else {
|
|
103
|
+
output += " ";
|
|
104
|
+
}
|
|
105
|
+
continue;
|
|
106
|
+
}
|
|
107
|
+
if (inBlockComment) {
|
|
108
|
+
if (char === "*" && next === "/") {
|
|
109
|
+
output += " ";
|
|
110
|
+
index++;
|
|
111
|
+
inBlockComment = false;
|
|
112
|
+
continue;
|
|
113
|
+
}
|
|
114
|
+
if (isLineBreak(char)) {
|
|
115
|
+
output += char;
|
|
116
|
+
}
|
|
117
|
+
else {
|
|
118
|
+
output += " ";
|
|
119
|
+
}
|
|
120
|
+
continue;
|
|
121
|
+
}
|
|
122
|
+
if (inString) {
|
|
123
|
+
output += char;
|
|
124
|
+
if (escaped) {
|
|
125
|
+
escaped = false;
|
|
126
|
+
continue;
|
|
127
|
+
}
|
|
128
|
+
if (char === "\\") {
|
|
129
|
+
escaped = true;
|
|
130
|
+
continue;
|
|
131
|
+
}
|
|
132
|
+
if (char === '"') {
|
|
133
|
+
inString = false;
|
|
134
|
+
}
|
|
135
|
+
continue;
|
|
136
|
+
}
|
|
137
|
+
if (char === '"') {
|
|
138
|
+
inString = true;
|
|
139
|
+
output += char;
|
|
140
|
+
continue;
|
|
141
|
+
}
|
|
142
|
+
if (char === "/" && next === "/") {
|
|
143
|
+
output += " ";
|
|
144
|
+
index++;
|
|
145
|
+
inLineComment = true;
|
|
146
|
+
continue;
|
|
147
|
+
}
|
|
148
|
+
if (char === "/" && next === "*") {
|
|
149
|
+
output += " ";
|
|
150
|
+
index++;
|
|
151
|
+
inBlockComment = true;
|
|
152
|
+
continue;
|
|
153
|
+
}
|
|
154
|
+
output += char;
|
|
155
|
+
}
|
|
156
|
+
return output;
|
|
157
|
+
}
|
|
158
|
+
function isLineBreak(char) {
|
|
159
|
+
if (char === "\n") {
|
|
160
|
+
return true;
|
|
161
|
+
}
|
|
162
|
+
return char === "\r";
|
|
163
|
+
}
|
|
87
164
|
//# sourceMappingURL=load-config.js.map
|
|
@@ -22,6 +22,8 @@ export interface ResolvedGeneratorOptions {
|
|
|
22
22
|
schemaDiffExcludeTables: string[];
|
|
23
23
|
pgTriggersTables: string[];
|
|
24
24
|
pgTriggersExcludeTables: string[];
|
|
25
|
+
pgViews: string[];
|
|
26
|
+
pgViewsExclude: string[];
|
|
25
27
|
ignoreColumns: string[];
|
|
26
28
|
tablesWhereDataFilters?: Record<string, string>;
|
|
27
29
|
includeDeletes: boolean | string;
|
|
@@ -29,12 +31,14 @@ export interface ResolvedGeneratorOptions {
|
|
|
29
31
|
output: string;
|
|
30
32
|
schemaDiffOutput: string;
|
|
31
33
|
pgTriggersOutput: string;
|
|
34
|
+
pgViewsOutput: string;
|
|
32
35
|
pretty: boolean | string;
|
|
33
36
|
generateSql?: boolean | string;
|
|
34
37
|
generatePgTriggers?: boolean | string;
|
|
38
|
+
generatePgViews?: boolean | string;
|
|
35
39
|
verbose: boolean;
|
|
36
40
|
}
|
|
37
|
-
export interface RuntimeGeneratorOptions extends Omit<ResolvedGeneratorOptions, "sourcePgSsl" | "destPgSsl" | "includeDeletes" | "skipMissingPk" | "pretty" | "generateSql" | "generatePgTriggers"> {
|
|
41
|
+
export interface RuntimeGeneratorOptions extends Omit<ResolvedGeneratorOptions, "sourcePgSsl" | "destPgSsl" | "includeDeletes" | "skipMissingPk" | "pretty" | "generateSql" | "generatePgTriggers" | "generatePgViews"> {
|
|
38
42
|
sourcePgSsl: boolean;
|
|
39
43
|
destPgSsl: boolean;
|
|
40
44
|
includeDeletes: boolean;
|
|
@@ -42,12 +46,14 @@ export interface RuntimeGeneratorOptions extends Omit<ResolvedGeneratorOptions,
|
|
|
42
46
|
pretty: boolean;
|
|
43
47
|
generateSql?: boolean;
|
|
44
48
|
generatePgTriggers?: boolean;
|
|
49
|
+
generatePgViews?: boolean;
|
|
45
50
|
}
|
|
46
51
|
export type OptionalListInput = string[] | null;
|
|
47
|
-
export type GeneratorOptionInput = Partial<Omit<ResolvedGeneratorOptions, "excludeTables" | "schemaDiffExcludeTables" | "pgTriggersExcludeTables" | "ignoreColumns"> & {
|
|
52
|
+
export type GeneratorOptionInput = Partial<Omit<ResolvedGeneratorOptions, "excludeTables" | "schemaDiffExcludeTables" | "pgTriggersExcludeTables" | "pgViewsExclude" | "ignoreColumns"> & {
|
|
48
53
|
excludeTables: OptionalListInput;
|
|
49
54
|
schemaDiffExcludeTables: OptionalListInput;
|
|
50
55
|
pgTriggersExcludeTables: OptionalListInput;
|
|
56
|
+
pgViewsExclude: OptionalListInput;
|
|
51
57
|
ignoreColumns: OptionalListInput;
|
|
52
58
|
}>;
|
|
53
59
|
/**
|
|
@@ -18,6 +18,8 @@ function resolveGeneratorOptions(config, cliArgs) {
|
|
|
18
18
|
schemaDiffExcludeTables: [],
|
|
19
19
|
pgTriggersTables: [],
|
|
20
20
|
pgTriggersExcludeTables: [],
|
|
21
|
+
pgViews: [],
|
|
22
|
+
pgViewsExclude: [],
|
|
21
23
|
ignoreColumns: [],
|
|
22
24
|
tablesWhereDataFilters: {},
|
|
23
25
|
includeDeletes: true,
|
|
@@ -25,6 +27,7 @@ function resolveGeneratorOptions(config, cliArgs) {
|
|
|
25
27
|
output: "frg-data-diff.json",
|
|
26
28
|
schemaDiffOutput: "frg-schema-diff.json",
|
|
27
29
|
pgTriggersOutput: "frg-triggers-diff.sql",
|
|
30
|
+
pgViewsOutput: "frg-views-diff.sql",
|
|
28
31
|
pretty: true,
|
|
29
32
|
verbose: false,
|
|
30
33
|
};
|
|
@@ -34,6 +37,8 @@ function resolveGeneratorOptions(config, cliArgs) {
|
|
|
34
37
|
const schemaDiffExcludeTables = resolveOptionalListOption(cliArgs.schemaDiffExcludeTables, config?.schemaDiffExcludeTables, excludeTables);
|
|
35
38
|
const pgTriggersTables = resolveInheritedTableListOption(cliArgs.pgTriggersTables, config?.pgTriggersTables, tables);
|
|
36
39
|
const pgTriggersExcludeTables = resolveOptionalListOption(cliArgs.pgTriggersExcludeTables, config?.pgTriggersExcludeTables, excludeTables);
|
|
40
|
+
const pgViews = resolveListOption(cliArgs.pgViews, config?.pgViews, defaults.pgViews);
|
|
41
|
+
const pgViewsExclude = resolveOptionalListOption(cliArgs.pgViewsExclude, config?.pgViewsExclude, defaults.pgViewsExclude);
|
|
37
42
|
const ignoreColumns = resolveOptionalListOption(cliArgs.ignoreColumns, config?.ignoreColumns, defaults.ignoreColumns);
|
|
38
43
|
return {
|
|
39
44
|
sourcePgHost: cliArgs.sourcePgHost ?? config?.sourcePgHost ?? "",
|
|
@@ -55,6 +60,8 @@ function resolveGeneratorOptions(config, cliArgs) {
|
|
|
55
60
|
schemaDiffExcludeTables,
|
|
56
61
|
pgTriggersTables,
|
|
57
62
|
pgTriggersExcludeTables,
|
|
63
|
+
pgViews,
|
|
64
|
+
pgViewsExclude,
|
|
58
65
|
ignoreColumns,
|
|
59
66
|
tablesWhereDataFilters: cliArgs.tablesWhereDataFilters ??
|
|
60
67
|
config?.tablesWhereDataFilters ??
|
|
@@ -70,12 +77,25 @@ function resolveGeneratorOptions(config, cliArgs) {
|
|
|
70
77
|
pgTriggersOutput: cliArgs.pgTriggersOutput ??
|
|
71
78
|
config?.pgTriggersOutput ??
|
|
72
79
|
defaults.pgTriggersOutput,
|
|
80
|
+
pgViewsOutput: cliArgs.pgViewsOutput ??
|
|
81
|
+
config?.pgViewsOutput ??
|
|
82
|
+
defaults.pgViewsOutput,
|
|
73
83
|
pretty: cliArgs.pretty ?? config?.pretty ?? defaults.pretty,
|
|
74
84
|
generateSql: cliArgs.generateSql ?? config?.generateSql,
|
|
75
85
|
generatePgTriggers: cliArgs.generatePgTriggers ?? config?.generatePgTriggers,
|
|
86
|
+
generatePgViews: cliArgs.generatePgViews ?? config?.generatePgViews,
|
|
76
87
|
verbose: cliArgs.verbose ?? defaults.verbose,
|
|
77
88
|
};
|
|
78
89
|
}
|
|
90
|
+
function resolveListOption(cliValue, configValue, fallback) {
|
|
91
|
+
if (cliValue !== undefined) {
|
|
92
|
+
return cliValue;
|
|
93
|
+
}
|
|
94
|
+
if (configValue !== undefined) {
|
|
95
|
+
return configValue;
|
|
96
|
+
}
|
|
97
|
+
return fallback;
|
|
98
|
+
}
|
|
79
99
|
function resolveOptionalListOption(cliValue, configValue, fallback) {
|
|
80
100
|
if (cliValue !== undefined) {
|
|
81
101
|
return cliValue ?? [];
|
|
@@ -137,6 +157,10 @@ function resolveRuntimeGeneratorOptions(options) {
|
|
|
137
157
|
if (options.generatePgTriggers !== undefined) {
|
|
138
158
|
generatePgTriggers = (0, env_values_1.resolveBooleanValue)(options.generatePgTriggers, "generate pg triggers");
|
|
139
159
|
}
|
|
160
|
+
let generatePgViews;
|
|
161
|
+
if (options.generatePgViews !== undefined) {
|
|
162
|
+
generatePgViews = (0, env_values_1.resolveBooleanValue)(options.generatePgViews, "generate pg views");
|
|
163
|
+
}
|
|
140
164
|
return {
|
|
141
165
|
...options,
|
|
142
166
|
schema: (0, env_values_1.resolveStringValue)(options.schema, "schema"),
|
|
@@ -146,6 +170,8 @@ function resolveRuntimeGeneratorOptions(options) {
|
|
|
146
170
|
schemaDiffExcludeTables: (0, env_values_1.resolveListValues)(options.schemaDiffExcludeTables, "schema diff exclude tables"),
|
|
147
171
|
pgTriggersTables: (0, env_values_1.resolveListValues)(options.pgTriggersTables, "pg triggers tables"),
|
|
148
172
|
pgTriggersExcludeTables: (0, env_values_1.resolveListValues)(options.pgTriggersExcludeTables, "pg triggers exclude tables"),
|
|
173
|
+
pgViews: (0, env_values_1.resolveListValues)(options.pgViews, "pg views"),
|
|
174
|
+
pgViewsExclude: (0, env_values_1.resolveListValues)(options.pgViewsExclude, "pg views exclude"),
|
|
149
175
|
ignoreColumns: (0, env_values_1.resolveListValues)(options.ignoreColumns, "ignored columns"),
|
|
150
176
|
sourcePgSsl: (0, env_values_1.resolveBooleanValue)(options.sourcePgSsl, "source ssl"),
|
|
151
177
|
destPgSsl: (0, env_values_1.resolveBooleanValue)(options.destPgSsl, "destination ssl"),
|
|
@@ -154,9 +180,11 @@ function resolveRuntimeGeneratorOptions(options) {
|
|
|
154
180
|
output: (0, env_values_1.resolveStringValue)(options.output, "output file"),
|
|
155
181
|
schemaDiffOutput: (0, env_values_1.resolveStringValue)(options.schemaDiffOutput, "schema diff output file"),
|
|
156
182
|
pgTriggersOutput: (0, env_values_1.resolveStringValue)(options.pgTriggersOutput, "pg triggers output file"),
|
|
183
|
+
pgViewsOutput: (0, env_values_1.resolveStringValue)(options.pgViewsOutput, "pg views output file"),
|
|
157
184
|
pretty: (0, env_values_1.resolveBooleanValue)(options.pretty, "pretty-print json"),
|
|
158
185
|
generateSql,
|
|
159
186
|
generatePgTriggers,
|
|
187
|
+
generatePgViews,
|
|
160
188
|
};
|
|
161
189
|
}
|
|
162
190
|
function resolveRuntimeApplyOptions(options) {
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { type ResolvedGeneratorOptions, type ResolvedApplyOptions, type OptionalListInput } from "./resolve-options";
|
|
2
|
-
type WritableGeneratorOptions = Omit<ResolvedGeneratorOptions, "excludeTables" | "schemaDiffExcludeTables" | "pgTriggersExcludeTables" | "ignoreColumns"> & {
|
|
2
|
+
type WritableGeneratorOptions = Omit<ResolvedGeneratorOptions, "excludeTables" | "schemaDiffExcludeTables" | "pgTriggersExcludeTables" | "pgViewsExclude" | "ignoreColumns"> & {
|
|
3
3
|
excludeTables: OptionalListInput;
|
|
4
4
|
schemaDiffExcludeTables: OptionalListInput;
|
|
5
5
|
pgTriggersExcludeTables: OptionalListInput;
|
|
6
|
+
pgViewsExclude: OptionalListInput;
|
|
6
7
|
ignoreColumns: OptionalListInput;
|
|
7
8
|
};
|
|
8
9
|
/**
|
|
@@ -62,15 +62,19 @@ function buildConfigFile(generatorOptions, applyOptions) {
|
|
|
62
62
|
schemaDiffExcludeTables: generatorOptions.schemaDiffExcludeTables ?? [],
|
|
63
63
|
pgTriggersTables: generatorOptions.pgTriggersTables,
|
|
64
64
|
pgTriggersExcludeTables: generatorOptions.pgTriggersExcludeTables ?? [],
|
|
65
|
+
pgViews: generatorOptions.pgViews,
|
|
66
|
+
pgViewsExclude: generatorOptions.pgViewsExclude ?? [],
|
|
65
67
|
ignoreColumns: generatorOptions.ignoreColumns ?? [],
|
|
66
68
|
includeDeletes: generatorOptions.includeDeletes,
|
|
67
69
|
skipMissingPk: generatorOptions.skipMissingPk,
|
|
68
70
|
output: generatorOptions.output,
|
|
69
71
|
schemaDiffOutput: generatorOptions.schemaDiffOutput,
|
|
70
72
|
pgTriggersOutput: generatorOptions.pgTriggersOutput,
|
|
73
|
+
pgViewsOutput: generatorOptions.pgViewsOutput,
|
|
71
74
|
pretty: generatorOptions.pretty,
|
|
72
75
|
generateSql: generatorOptions.generateSql,
|
|
73
76
|
generatePgTriggers: generatorOptions.generatePgTriggers,
|
|
77
|
+
generatePgViews: generatorOptions.generatePgViews,
|
|
74
78
|
};
|
|
75
79
|
return {
|
|
76
80
|
format: "frg-data-diff-config/v1",
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { type Pool, type PoolClient } from "pg";
|
|
2
|
+
export type PgViewType = "view" | "materialized_view";
|
|
3
|
+
export interface PgViewInfo {
|
|
4
|
+
viewSchema: string;
|
|
5
|
+
viewName: string;
|
|
6
|
+
viewType: PgViewType;
|
|
7
|
+
viewDefinition: string;
|
|
8
|
+
}
|
|
9
|
+
export interface FetchPgViewsOptions {
|
|
10
|
+
onViewStart?: (viewName: string, viewPosition: number, totalViews: number) => void;
|
|
11
|
+
}
|
|
12
|
+
export interface ResolvedPgViewPatterns {
|
|
13
|
+
views: string[];
|
|
14
|
+
excludedViews: string[];
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Lists regular and materialized views in the given schema.
|
|
18
|
+
*/
|
|
19
|
+
export declare function listPgViews(client: PoolClient, schema: string): Promise<string[]>;
|
|
20
|
+
export declare function fetchPgView(client: PoolClient, schema: string, viewName: string): Promise<PgViewInfo | undefined>;
|
|
21
|
+
export declare function fetchPgViews(client: PoolClient, schema: string, views: string[], options?: FetchPgViewsOptions): Promise<PgViewInfo[]>;
|
|
22
|
+
export declare function resolvePgViewPatterns(sourcePool: Pool, destPool: Pool, schema: string, includePatterns: string[], excludePatterns: string[]): Promise<ResolvedPgViewPatterns>;
|
|
23
|
+
export declare function resolvePgViewPatternsFromViewLists(sourceViews: string[], destViews: string[], includePatterns: string[], excludePatterns: string[]): ResolvedPgViewPatterns;
|
|
24
|
+
//# sourceMappingURL=pg-views.d.ts.map
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.listPgViews = listPgViews;
|
|
4
|
+
exports.fetchPgView = fetchPgView;
|
|
5
|
+
exports.fetchPgViews = fetchPgViews;
|
|
6
|
+
exports.resolvePgViewPatterns = resolvePgViewPatterns;
|
|
7
|
+
exports.resolvePgViewPatternsFromViewLists = resolvePgViewPatternsFromViewLists;
|
|
8
|
+
/**
|
|
9
|
+
* Lists regular and materialized views in the given schema.
|
|
10
|
+
*/
|
|
11
|
+
async function listPgViews(client, schema) {
|
|
12
|
+
const result = await client.query(`
|
|
13
|
+
SELECT cls.relname AS view_name
|
|
14
|
+
FROM pg_class cls
|
|
15
|
+
JOIN pg_namespace ns ON ns.oid = cls.relnamespace
|
|
16
|
+
WHERE ns.nspname = $1
|
|
17
|
+
AND cls.relkind IN ('v', 'm')
|
|
18
|
+
ORDER BY cls.relname
|
|
19
|
+
`, [schema]);
|
|
20
|
+
return result.rows.map((row) => row.view_name);
|
|
21
|
+
}
|
|
22
|
+
async function fetchPgView(client, schema, viewName) {
|
|
23
|
+
const result = await client.query(`
|
|
24
|
+
SELECT
|
|
25
|
+
ns.nspname AS view_schema,
|
|
26
|
+
cls.relname AS view_name,
|
|
27
|
+
CASE cls.relkind
|
|
28
|
+
WHEN 'm' THEN 'materialized_view'
|
|
29
|
+
ELSE 'view'
|
|
30
|
+
END AS view_type,
|
|
31
|
+
pg_get_viewdef(cls.oid, true) AS view_definition
|
|
32
|
+
FROM pg_class cls
|
|
33
|
+
JOIN pg_namespace ns ON ns.oid = cls.relnamespace
|
|
34
|
+
WHERE ns.nspname = $1
|
|
35
|
+
AND cls.relname = $2
|
|
36
|
+
AND cls.relkind IN ('v', 'm')
|
|
37
|
+
`, [schema, viewName]);
|
|
38
|
+
const row = result.rows[0];
|
|
39
|
+
if (!row) {
|
|
40
|
+
return undefined;
|
|
41
|
+
}
|
|
42
|
+
return {
|
|
43
|
+
viewSchema: row.view_schema,
|
|
44
|
+
viewName: row.view_name,
|
|
45
|
+
viewType: row.view_type,
|
|
46
|
+
viewDefinition: row.view_definition,
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
async function fetchPgViews(client, schema, views, options = {}) {
|
|
50
|
+
const results = [];
|
|
51
|
+
for (const [viewIndex, viewName] of views.entries()) {
|
|
52
|
+
if (options.onViewStart) {
|
|
53
|
+
options.onViewStart(viewName, viewIndex + 1, views.length);
|
|
54
|
+
}
|
|
55
|
+
const view = await fetchPgView(client, schema, viewName);
|
|
56
|
+
if (view) {
|
|
57
|
+
results.push(view);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
return results;
|
|
61
|
+
}
|
|
62
|
+
async function resolvePgViewPatterns(sourcePool, destPool, schema, includePatterns, excludePatterns) {
|
|
63
|
+
const [sourceClient, destClient] = await Promise.all([
|
|
64
|
+
sourcePool.connect(),
|
|
65
|
+
destPool.connect(),
|
|
66
|
+
]);
|
|
67
|
+
try {
|
|
68
|
+
const [sourceViews, destViews] = await Promise.all([
|
|
69
|
+
listPgViews(sourceClient, schema),
|
|
70
|
+
listPgViews(destClient, schema),
|
|
71
|
+
]);
|
|
72
|
+
return resolvePgViewPatternsFromViewLists(sourceViews, destViews, includePatterns, excludePatterns);
|
|
73
|
+
}
|
|
74
|
+
finally {
|
|
75
|
+
sourceClient.release();
|
|
76
|
+
destClient.release();
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
function resolvePgViewPatternsFromViewLists(sourceViews, destViews, includePatterns, excludePatterns) {
|
|
80
|
+
const sourceSet = new Set(sourceViews);
|
|
81
|
+
const destSet = new Set(destViews);
|
|
82
|
+
const availableViews = Array.from(new Set([...sourceViews, ...destViews])).sort();
|
|
83
|
+
let requestedPatterns = includePatterns;
|
|
84
|
+
if (requestedPatterns.length === 0) {
|
|
85
|
+
requestedPatterns = ["*"];
|
|
86
|
+
}
|
|
87
|
+
const resolvedViews = [];
|
|
88
|
+
for (const pattern of requestedPatterns) {
|
|
89
|
+
const matches = resolvePattern(pattern, availableViews);
|
|
90
|
+
if (matches.length > 0) {
|
|
91
|
+
addUniqueMatches(resolvedViews, matches);
|
|
92
|
+
continue;
|
|
93
|
+
}
|
|
94
|
+
if (hasWildcard(pattern)) {
|
|
95
|
+
if (availableViews.length === 0 || pattern === "*") {
|
|
96
|
+
continue;
|
|
97
|
+
}
|
|
98
|
+
throw new Error(`View pattern "${pattern}" matched no views.`);
|
|
99
|
+
}
|
|
100
|
+
const missingFrom = [];
|
|
101
|
+
if (!sourceSet.has(pattern)) {
|
|
102
|
+
missingFrom.push("source");
|
|
103
|
+
}
|
|
104
|
+
if (!destSet.has(pattern)) {
|
|
105
|
+
missingFrom.push("destination");
|
|
106
|
+
}
|
|
107
|
+
throw new Error(`View "${pattern}" was not found in the ${missingFrom.join(" and ")} database.`);
|
|
108
|
+
}
|
|
109
|
+
const excludedViews = Array.from(new Set(excludePatterns.flatMap((pattern) => resolvePattern(pattern, resolvedViews))));
|
|
110
|
+
const views = resolvedViews.filter((view) => !excludedViews.includes(view));
|
|
111
|
+
return { views, excludedViews };
|
|
112
|
+
}
|
|
113
|
+
function addUniqueMatches(values, matches) {
|
|
114
|
+
for (const match of matches) {
|
|
115
|
+
if (!values.includes(match)) {
|
|
116
|
+
values.push(match);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
function resolvePattern(pattern, availableViews) {
|
|
121
|
+
if (!hasWildcard(pattern)) {
|
|
122
|
+
if (availableViews.includes(pattern)) {
|
|
123
|
+
return [pattern];
|
|
124
|
+
}
|
|
125
|
+
return [];
|
|
126
|
+
}
|
|
127
|
+
const matcher = buildWildcardRegex(pattern);
|
|
128
|
+
return availableViews.filter((view) => matcher.test(view));
|
|
129
|
+
}
|
|
130
|
+
function hasWildcard(pattern) {
|
|
131
|
+
return pattern.includes("*");
|
|
132
|
+
}
|
|
133
|
+
function buildWildcardRegex(pattern) {
|
|
134
|
+
const escaped = pattern.replace(/[|\\{}()[\]^$+?.]/g, "\\$&");
|
|
135
|
+
return new RegExp(`^${escaped.replace(/\*/g, ".*")}$`);
|
|
136
|
+
}
|
|
137
|
+
//# sourceMappingURL=pg-views.js.map
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { type Pool } from "pg";
|
|
2
|
+
export interface GeneratePgViewsDiffOptions {
|
|
3
|
+
schema: string;
|
|
4
|
+
views: string[];
|
|
5
|
+
excludeViews: string[];
|
|
6
|
+
verbose: boolean;
|
|
7
|
+
onProgress?: (message: string) => void;
|
|
8
|
+
onVerboseProgress?: (message: string) => void;
|
|
9
|
+
}
|
|
10
|
+
export declare function generatePgViewsDiffSql(sourcePool: Pool, destPool: Pool, options: GeneratePgViewsDiffOptions): Promise<string>;
|
|
11
|
+
//# sourceMappingURL=pg-views-diff.d.ts.map
|