relq 1.0.2 → 1.0.4
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/dist/cjs/cli/commands/add.cjs +403 -27
- package/dist/cjs/cli/commands/branch.cjs +13 -23
- package/dist/cjs/cli/commands/checkout.cjs +16 -29
- package/dist/cjs/cli/commands/cherry-pick.cjs +3 -4
- package/dist/cjs/cli/commands/commit.cjs +21 -29
- package/dist/cjs/cli/commands/diff.cjs +28 -32
- package/dist/cjs/cli/commands/export.cjs +7 -7
- package/dist/cjs/cli/commands/fetch.cjs +15 -21
- package/dist/cjs/cli/commands/generate.cjs +28 -54
- package/dist/cjs/cli/commands/history.cjs +19 -40
- package/dist/cjs/cli/commands/import.cjs +34 -41
- package/dist/cjs/cli/commands/init.cjs +69 -59
- package/dist/cjs/cli/commands/introspect.cjs +4 -8
- package/dist/cjs/cli/commands/log.cjs +26 -32
- package/dist/cjs/cli/commands/merge.cjs +24 -41
- package/dist/cjs/cli/commands/migrate.cjs +12 -25
- package/dist/cjs/cli/commands/pull.cjs +216 -106
- package/dist/cjs/cli/commands/push.cjs +35 -75
- package/dist/cjs/cli/commands/remote.cjs +2 -1
- package/dist/cjs/cli/commands/reset.cjs +22 -43
- package/dist/cjs/cli/commands/resolve.cjs +12 -14
- package/dist/cjs/cli/commands/rollback.cjs +16 -38
- package/dist/cjs/cli/commands/stash.cjs +5 -7
- package/dist/cjs/cli/commands/status.cjs +5 -10
- package/dist/cjs/cli/commands/sync.cjs +30 -50
- package/dist/cjs/cli/commands/tag.cjs +3 -4
- package/dist/cjs/cli/index.cjs +72 -9
- package/dist/cjs/cli/utils/change-tracker.cjs +107 -3
- package/dist/cjs/cli/utils/cli-utils.cjs +217 -0
- package/dist/cjs/cli/utils/config-loader.cjs +34 -8
- package/dist/cjs/cli/utils/fast-introspect.cjs +109 -3
- package/dist/cjs/cli/utils/git-utils.cjs +42 -161
- package/dist/cjs/cli/utils/pool-manager.cjs +156 -0
- package/dist/cjs/cli/utils/project-root.cjs +56 -5
- package/dist/cjs/cli/utils/relqignore.cjs +1 -0
- package/dist/cjs/cli/utils/repo-manager.cjs +47 -0
- package/dist/cjs/cli/utils/schema-comparator.cjs +301 -11
- package/dist/cjs/cli/utils/schema-diff.cjs +202 -1
- package/dist/cjs/cli/utils/schema-hash.cjs +2 -1
- package/dist/cjs/cli/utils/schema-introspect.cjs +7 -3
- package/dist/cjs/cli/utils/snapshot-manager.cjs +1 -0
- package/dist/cjs/cli/utils/spinner.cjs +14 -106
- package/dist/cjs/cli/utils/sql-generator.cjs +10 -2
- package/dist/cjs/cli/utils/type-generator.cjs +28 -16
- package/dist/config.d.ts +16 -6
- package/dist/esm/cli/commands/add.js +372 -29
- package/dist/esm/cli/commands/branch.js +14 -24
- package/dist/esm/cli/commands/checkout.js +16 -29
- package/dist/esm/cli/commands/cherry-pick.js +3 -4
- package/dist/esm/cli/commands/commit.js +22 -30
- package/dist/esm/cli/commands/diff.js +6 -10
- package/dist/esm/cli/commands/export.js +8 -8
- package/dist/esm/cli/commands/fetch.js +14 -20
- package/dist/esm/cli/commands/generate.js +28 -54
- package/dist/esm/cli/commands/history.js +11 -32
- package/dist/esm/cli/commands/import.js +35 -42
- package/dist/esm/cli/commands/init.js +65 -55
- package/dist/esm/cli/commands/introspect.js +4 -8
- package/dist/esm/cli/commands/log.js +6 -12
- package/dist/esm/cli/commands/merge.js +20 -37
- package/dist/esm/cli/commands/migrate.js +12 -25
- package/dist/esm/cli/commands/pull.js +204 -94
- package/dist/esm/cli/commands/push.js +21 -61
- package/dist/esm/cli/commands/remote.js +2 -1
- package/dist/esm/cli/commands/reset.js +16 -37
- package/dist/esm/cli/commands/resolve.js +13 -15
- package/dist/esm/cli/commands/rollback.js +16 -38
- package/dist/esm/cli/commands/stash.js +6 -8
- package/dist/esm/cli/commands/status.js +6 -11
- package/dist/esm/cli/commands/sync.js +30 -50
- package/dist/esm/cli/commands/tag.js +3 -4
- package/dist/esm/cli/index.js +72 -9
- package/dist/esm/cli/utils/change-tracker.js +107 -3
- package/dist/esm/cli/utils/cli-utils.js +169 -0
- package/dist/esm/cli/utils/config-loader.js +34 -8
- package/dist/esm/cli/utils/fast-introspect.js +109 -3
- package/dist/esm/cli/utils/git-utils.js +2 -124
- package/dist/esm/cli/utils/pool-manager.js +114 -0
- package/dist/esm/cli/utils/project-root.js +55 -5
- package/dist/esm/cli/utils/relqignore.js +1 -0
- package/dist/esm/cli/utils/repo-manager.js +42 -0
- package/dist/esm/cli/utils/schema-comparator.js +301 -11
- package/dist/esm/cli/utils/schema-diff.js +202 -1
- package/dist/esm/cli/utils/schema-hash.js +2 -1
- package/dist/esm/cli/utils/schema-introspect.js +7 -3
- package/dist/esm/cli/utils/snapshot-manager.js +1 -0
- package/dist/esm/cli/utils/spinner.js +1 -101
- package/dist/esm/cli/utils/sql-generator.js +10 -2
- package/dist/esm/cli/utils/type-generator.js +28 -16
- package/dist/index.d.ts +25 -8
- package/dist/schema-builder.d.ts +18 -7
- package/package.json +1 -1
|
@@ -1,14 +1,18 @@
|
|
|
1
1
|
import { createChange } from "./change-tracker.js";
|
|
2
|
+
function arraysEqual(a, b) {
|
|
3
|
+
if (a.length !== b.length)
|
|
4
|
+
return false;
|
|
5
|
+
return a.every((val, idx) => val === b[idx]);
|
|
6
|
+
}
|
|
2
7
|
export function compareSchemas(before, after) {
|
|
3
8
|
const changes = [];
|
|
4
9
|
changes.push(...compareExtensions(before.extensions || [], after.extensions || []));
|
|
5
10
|
changes.push(...compareEnums(before.enums || [], after.enums || []));
|
|
6
11
|
changes.push(...compareDomains(before.domains || [], after.domains || []));
|
|
7
12
|
changes.push(...compareCompositeTypes(before.compositeTypes || [], after.compositeTypes || []));
|
|
13
|
+
changes.push(...compareSequences(before.sequences || [], after.sequences || []));
|
|
8
14
|
changes.push(...compareTables(before.tables, after.tables));
|
|
9
|
-
|
|
10
|
-
const afterViews = after.views || [];
|
|
11
|
-
changes.push(...compareViews(beforeViews, afterViews));
|
|
15
|
+
changes.push(...compareViews(before.views || [], after.views || []));
|
|
12
16
|
changes.push(...compareFunctions(before.functions || [], after.functions || []));
|
|
13
17
|
changes.push(...compareTriggers(before.triggers || [], after.triggers || []));
|
|
14
18
|
return changes;
|
|
@@ -98,6 +102,38 @@ function compareCompositeTypes(before, after) {
|
|
|
98
102
|
}
|
|
99
103
|
return changes;
|
|
100
104
|
}
|
|
105
|
+
function compareSequences(before, after) {
|
|
106
|
+
const changes = [];
|
|
107
|
+
const beforeMap = new Map(before.map(s => [s.name, s]));
|
|
108
|
+
const afterMap = new Map(after.map(s => [s.name, s]));
|
|
109
|
+
for (const [name, seq] of afterMap) {
|
|
110
|
+
if (!beforeMap.has(name)) {
|
|
111
|
+
changes.push(createChange('CREATE', 'SEQUENCE', name, null, seq));
|
|
112
|
+
}
|
|
113
|
+
else {
|
|
114
|
+
const beforeSeq = beforeMap.get(name);
|
|
115
|
+
if (hasSequenceChanged(beforeSeq, seq)) {
|
|
116
|
+
changes.push(createChange('ALTER', 'SEQUENCE', name, beforeSeq, seq));
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
for (const [name, seq] of beforeMap) {
|
|
121
|
+
if (!afterMap.has(name)) {
|
|
122
|
+
changes.push(createChange('DROP', 'SEQUENCE', name, seq, null));
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
return changes;
|
|
126
|
+
}
|
|
127
|
+
function hasSequenceChanged(before, after) {
|
|
128
|
+
return (before.dataType !== after.dataType ||
|
|
129
|
+
before.start !== after.start ||
|
|
130
|
+
before.increment !== after.increment ||
|
|
131
|
+
before.minValue !== after.minValue ||
|
|
132
|
+
before.maxValue !== after.maxValue ||
|
|
133
|
+
before.cache !== after.cache ||
|
|
134
|
+
before.cycle !== after.cycle ||
|
|
135
|
+
before.ownedBy !== after.ownedBy);
|
|
136
|
+
}
|
|
101
137
|
function compareTables(before, after) {
|
|
102
138
|
const changes = [];
|
|
103
139
|
const beforeMap = new Map(before.map(t => [t.name, t]));
|
|
@@ -105,12 +141,57 @@ function compareTables(before, after) {
|
|
|
105
141
|
for (const [name, table] of afterMap) {
|
|
106
142
|
if (!beforeMap.has(name)) {
|
|
107
143
|
changes.push(createChange('CREATE', 'TABLE', name, null, tableToChangeData(table)));
|
|
144
|
+
const tableIndexes = table.indexes || [];
|
|
145
|
+
for (const idx of tableIndexes) {
|
|
146
|
+
if (!idx.isPrimary) {
|
|
147
|
+
changes.push(createChange('CREATE', 'INDEX', idx.name, null, {
|
|
148
|
+
name: idx.name,
|
|
149
|
+
tableName: name,
|
|
150
|
+
columns: Array.isArray(idx.columns) ? idx.columns : [idx.columns],
|
|
151
|
+
isUnique: idx.isUnique,
|
|
152
|
+
type: idx.type,
|
|
153
|
+
}));
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
if (table.isPartitioned && table.partitionType && table.partitionKey) {
|
|
157
|
+
changes.push(createChange('CREATE', 'PARTITION', name, null, {
|
|
158
|
+
tableName: name,
|
|
159
|
+
type: table.partitionType,
|
|
160
|
+
key: table.partitionKey,
|
|
161
|
+
}));
|
|
162
|
+
const childPartitions = table.childPartitions || [];
|
|
163
|
+
for (const cp of childPartitions) {
|
|
164
|
+
changes.push(createChange('CREATE', 'PARTITION_CHILD', cp.name, null, {
|
|
165
|
+
name: cp.name,
|
|
166
|
+
parentTable: name,
|
|
167
|
+
bound: cp.partitionBound,
|
|
168
|
+
}, name));
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
if (table.comment) {
|
|
172
|
+
changes.push(createChange('CREATE', 'TABLE_COMMENT', name, null, {
|
|
173
|
+
tableName: name,
|
|
174
|
+
comment: table.comment,
|
|
175
|
+
}, name));
|
|
176
|
+
}
|
|
177
|
+
for (const col of table.columns) {
|
|
178
|
+
if (col.comment) {
|
|
179
|
+
changes.push(createChange('CREATE', 'COLUMN_COMMENT', col.name, null, {
|
|
180
|
+
tableName: name,
|
|
181
|
+
columnName: col.name,
|
|
182
|
+
comment: col.comment,
|
|
183
|
+
}, name));
|
|
184
|
+
}
|
|
185
|
+
}
|
|
108
186
|
}
|
|
109
187
|
else {
|
|
110
188
|
const beforeTable = beforeMap.get(name);
|
|
111
189
|
changes.push(...compareColumns(beforeTable.columns, table.columns, name));
|
|
112
190
|
changes.push(...compareIndexes(beforeTable.indexes || [], table.indexes || [], name));
|
|
113
191
|
changes.push(...compareConstraints(beforeTable.constraints || [], table.constraints || [], name));
|
|
192
|
+
changes.push(...compareTableComments(beforeTable, table, name));
|
|
193
|
+
changes.push(...compareColumnComments(beforeTable.columns, table.columns, name));
|
|
194
|
+
changes.push(...comparePartitions(beforeTable, table, name));
|
|
114
195
|
}
|
|
115
196
|
}
|
|
116
197
|
for (const [name, table] of beforeMap) {
|
|
@@ -176,10 +257,65 @@ function columnToChangeData(col) {
|
|
|
176
257
|
scale: col.scale,
|
|
177
258
|
};
|
|
178
259
|
}
|
|
260
|
+
function normalizeDataType(type) {
|
|
261
|
+
if (!type)
|
|
262
|
+
return '';
|
|
263
|
+
let normalized = type.toLowerCase().trim();
|
|
264
|
+
const typeMap = {
|
|
265
|
+
'int4': 'integer',
|
|
266
|
+
'int8': 'bigint',
|
|
267
|
+
'int2': 'smallint',
|
|
268
|
+
'float4': 'real',
|
|
269
|
+
'float8': 'double precision',
|
|
270
|
+
'bool': 'boolean',
|
|
271
|
+
'timestamptz': 'timestamp',
|
|
272
|
+
'timetz': 'time',
|
|
273
|
+
'_text': 'text[]',
|
|
274
|
+
'_int4': 'integer[]',
|
|
275
|
+
'_int8': 'bigint[]',
|
|
276
|
+
'_varchar': 'varchar[]',
|
|
277
|
+
'_uuid': 'uuid[]',
|
|
278
|
+
'_bool': 'boolean[]',
|
|
279
|
+
'_jsonb': 'jsonb[]',
|
|
280
|
+
'character varying': 'varchar',
|
|
281
|
+
'character': 'char',
|
|
282
|
+
'double precision': 'doubleprecision',
|
|
283
|
+
};
|
|
284
|
+
if (normalized.startsWith('_') && !typeMap[normalized]) {
|
|
285
|
+
return normalized.substring(1) + '[]';
|
|
286
|
+
}
|
|
287
|
+
return typeMap[normalized] || normalized;
|
|
288
|
+
}
|
|
289
|
+
function normalizeDefault(value) {
|
|
290
|
+
if (value == null)
|
|
291
|
+
return null;
|
|
292
|
+
let normalized = value.trim().toLowerCase();
|
|
293
|
+
normalized = normalized.replace(/array\[\]::([\w\[\]]+)/gi, "'empty_array'");
|
|
294
|
+
normalized = normalized.replace(/::\w+(?:\s+\w+)?(?:\[\])?/g, '');
|
|
295
|
+
normalized = normalized.replace(/^array\[\]$/i, "'empty_array'");
|
|
296
|
+
normalized = normalized.replace(/now\s*\(\s*\)/g, 'now()');
|
|
297
|
+
normalized = normalized.replace(/gen_random_uuid\s*\(\s*\)/g, 'gen_random_uuid()');
|
|
298
|
+
normalized = normalized.replace(/current_date/g, 'current_date');
|
|
299
|
+
normalized = normalized.replace(/current_timestamp/g, 'current_timestamp');
|
|
300
|
+
normalized = normalized.replace(/'\[\]'/g, "'empty_array'");
|
|
301
|
+
normalized = normalized.replace(/'\{\}'/g, "'empty_array'");
|
|
302
|
+
normalized = normalized.replace(/:\s+/g, ':');
|
|
303
|
+
normalized = normalized.replace(/,\s+/g, ',');
|
|
304
|
+
normalized = normalized.replace(/\[\s+/g, '[');
|
|
305
|
+
normalized = normalized.replace(/\s+\]/g, ']');
|
|
306
|
+
normalized = normalized.replace(/\{\s+/g, '{');
|
|
307
|
+
normalized = normalized.replace(/\s+\}/g, '}');
|
|
308
|
+
if (normalized === 'true' || normalized === "'t'" || normalized === 't')
|
|
309
|
+
normalized = 'true';
|
|
310
|
+
if (normalized === 'false' || normalized === "'f'" || normalized === 'f')
|
|
311
|
+
normalized = 'false';
|
|
312
|
+
normalized = normalized.replace(/^'([^']*)'$/, '$1');
|
|
313
|
+
return normalized || null;
|
|
314
|
+
}
|
|
179
315
|
function hasColumnChanged(before, after) {
|
|
180
|
-
return (before.dataType
|
|
316
|
+
return (normalizeDataType(before.dataType) !== normalizeDataType(after.dataType) ||
|
|
181
317
|
before.isNullable !== after.isNullable ||
|
|
182
|
-
before.defaultValue !== after.defaultValue ||
|
|
318
|
+
normalizeDefault(before.defaultValue) !== normalizeDefault(after.defaultValue) ||
|
|
183
319
|
before.isPrimaryKey !== after.isPrimaryKey ||
|
|
184
320
|
before.isUnique !== after.isUnique);
|
|
185
321
|
}
|
|
@@ -211,10 +347,14 @@ function compareIndexes(before, after, tableName) {
|
|
|
211
347
|
}
|
|
212
348
|
function compareConstraints(before, after, tableName) {
|
|
213
349
|
const changes = [];
|
|
214
|
-
const
|
|
215
|
-
const
|
|
216
|
-
|
|
217
|
-
|
|
350
|
+
const beforeChecks = before.filter(c => c.type?.toUpperCase() === 'CHECK');
|
|
351
|
+
const afterChecks = after.filter(c => c.type?.toUpperCase() === 'CHECK');
|
|
352
|
+
const beforeOther = before.filter(c => c.type?.toUpperCase() !== 'CHECK');
|
|
353
|
+
const afterOther = after.filter(c => c.type?.toUpperCase() !== 'CHECK');
|
|
354
|
+
const beforeOtherMap = new Map(beforeOther.map(c => [c.name, c]));
|
|
355
|
+
const afterOtherMap = new Map(afterOther.map(c => [c.name, c]));
|
|
356
|
+
for (const [name, con] of afterOtherMap) {
|
|
357
|
+
if (!beforeOtherMap.has(name)) {
|
|
218
358
|
const objectType = constraintTypeToObjectType(con.type);
|
|
219
359
|
changes.push(createChange('CREATE', objectType, name, null, {
|
|
220
360
|
name: con.name,
|
|
@@ -222,8 +362,8 @@ function compareConstraints(before, after, tableName) {
|
|
|
222
362
|
}, tableName));
|
|
223
363
|
}
|
|
224
364
|
}
|
|
225
|
-
for (const [name, con] of
|
|
226
|
-
if (!
|
|
365
|
+
for (const [name, con] of beforeOtherMap) {
|
|
366
|
+
if (!afterOtherMap.has(name)) {
|
|
227
367
|
const objectType = constraintTypeToObjectType(con.type);
|
|
228
368
|
changes.push(createChange('DROP', objectType, name, {
|
|
229
369
|
name: con.name,
|
|
@@ -231,6 +371,59 @@ function compareConstraints(before, after, tableName) {
|
|
|
231
371
|
}, null, tableName));
|
|
232
372
|
}
|
|
233
373
|
}
|
|
374
|
+
const extractColumnFromCheckDef = (definition) => {
|
|
375
|
+
const enumMatch = definition.match(/\((\w+)\)::text\s*=\s*ANY/i);
|
|
376
|
+
if (enumMatch)
|
|
377
|
+
return enumMatch[1].toLowerCase();
|
|
378
|
+
const compMatch = definition.match(/\(\(?(\w+)\s*(?:>=?|<=?|<>|!=|=)/i);
|
|
379
|
+
if (compMatch)
|
|
380
|
+
return compMatch[1].toLowerCase();
|
|
381
|
+
return null;
|
|
382
|
+
};
|
|
383
|
+
const extractColumnFromCheckName = (name, tableName) => {
|
|
384
|
+
const lower = name.toLowerCase();
|
|
385
|
+
const tablePrefix = tableName.toLowerCase() + '_';
|
|
386
|
+
let colPart = lower.startsWith(tablePrefix) ? lower.slice(tablePrefix.length) : lower;
|
|
387
|
+
if (colPart.startsWith('check_'))
|
|
388
|
+
colPart = colPart.slice(6);
|
|
389
|
+
if (colPart.endsWith('_check'))
|
|
390
|
+
colPart = colPart.slice(0, -6);
|
|
391
|
+
return colPart;
|
|
392
|
+
};
|
|
393
|
+
const beforeChecksByCol = new Map();
|
|
394
|
+
const afterChecksByCol = new Map();
|
|
395
|
+
for (const c of beforeChecks) {
|
|
396
|
+
const col = extractColumnFromCheckDef(c.definition) || extractColumnFromCheckName(c.name, tableName);
|
|
397
|
+
beforeChecksByCol.set(col, c);
|
|
398
|
+
}
|
|
399
|
+
for (const c of afterChecks) {
|
|
400
|
+
const col = extractColumnFromCheckDef(c.definition) || extractColumnFromCheckName(c.name, tableName);
|
|
401
|
+
afterChecksByCol.set(col, c);
|
|
402
|
+
}
|
|
403
|
+
const matchedBefore = new Set();
|
|
404
|
+
const matchedAfter = new Set();
|
|
405
|
+
for (const [col, afterCon] of afterChecksByCol) {
|
|
406
|
+
if (beforeChecksByCol.has(col)) {
|
|
407
|
+
matchedBefore.add(beforeChecksByCol.get(col).name);
|
|
408
|
+
matchedAfter.add(afterCon.name);
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
for (const c of afterChecks) {
|
|
412
|
+
if (!matchedAfter.has(c.name)) {
|
|
413
|
+
changes.push(createChange('CREATE', 'CHECK', c.name, null, {
|
|
414
|
+
name: c.name,
|
|
415
|
+
definition: c.definition,
|
|
416
|
+
}, tableName));
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
for (const c of beforeChecks) {
|
|
420
|
+
if (!matchedBefore.has(c.name)) {
|
|
421
|
+
changes.push(createChange('DROP', 'CHECK', c.name, {
|
|
422
|
+
name: c.name,
|
|
423
|
+
definition: c.definition,
|
|
424
|
+
}, null, tableName));
|
|
425
|
+
}
|
|
426
|
+
}
|
|
234
427
|
return changes;
|
|
235
428
|
}
|
|
236
429
|
function constraintTypeToObjectType(type) {
|
|
@@ -245,6 +438,103 @@ function constraintTypeToObjectType(type) {
|
|
|
245
438
|
return 'EXCLUSION';
|
|
246
439
|
return 'CONSTRAINT';
|
|
247
440
|
}
|
|
441
|
+
function compareTableComments(before, after, tableName) {
|
|
442
|
+
const changes = [];
|
|
443
|
+
const beforeComment = before.comment;
|
|
444
|
+
const afterComment = after.comment;
|
|
445
|
+
if (afterComment && !beforeComment) {
|
|
446
|
+
changes.push(createChange('CREATE', 'TABLE_COMMENT', tableName, null, {
|
|
447
|
+
tableName,
|
|
448
|
+
comment: afterComment,
|
|
449
|
+
}, tableName));
|
|
450
|
+
}
|
|
451
|
+
else if (!afterComment && beforeComment) {
|
|
452
|
+
changes.push(createChange('DROP', 'TABLE_COMMENT', tableName, {
|
|
453
|
+
tableName,
|
|
454
|
+
comment: beforeComment,
|
|
455
|
+
}, null, tableName));
|
|
456
|
+
}
|
|
457
|
+
else if (afterComment && beforeComment && afterComment !== beforeComment) {
|
|
458
|
+
changes.push(createChange('ALTER', 'TABLE_COMMENT', tableName, {
|
|
459
|
+
tableName,
|
|
460
|
+
comment: beforeComment,
|
|
461
|
+
}, {
|
|
462
|
+
tableName,
|
|
463
|
+
comment: afterComment,
|
|
464
|
+
}, tableName));
|
|
465
|
+
}
|
|
466
|
+
return changes;
|
|
467
|
+
}
|
|
468
|
+
function compareColumnComments(before, after, tableName) {
|
|
469
|
+
const changes = [];
|
|
470
|
+
const beforeMap = new Map(before.map(c => [c.name, c]));
|
|
471
|
+
const afterMap = new Map(after.map(c => [c.name, c]));
|
|
472
|
+
for (const [name, col] of afterMap) {
|
|
473
|
+
const afterComment = col.comment;
|
|
474
|
+
const beforeCol = beforeMap.get(name);
|
|
475
|
+
const beforeComment = beforeCol ? beforeCol.comment : undefined;
|
|
476
|
+
if (afterComment && !beforeComment) {
|
|
477
|
+
changes.push(createChange('CREATE', 'COLUMN_COMMENT', name, null, {
|
|
478
|
+
tableName,
|
|
479
|
+
columnName: name,
|
|
480
|
+
comment: afterComment,
|
|
481
|
+
}, tableName));
|
|
482
|
+
}
|
|
483
|
+
else if (!afterComment && beforeComment) {
|
|
484
|
+
changes.push(createChange('DROP', 'COLUMN_COMMENT', name, {
|
|
485
|
+
tableName,
|
|
486
|
+
columnName: name,
|
|
487
|
+
comment: beforeComment,
|
|
488
|
+
}, null, tableName));
|
|
489
|
+
}
|
|
490
|
+
else if (afterComment && beforeComment && afterComment !== beforeComment) {
|
|
491
|
+
changes.push(createChange('ALTER', 'COLUMN_COMMENT', name, {
|
|
492
|
+
tableName,
|
|
493
|
+
columnName: name,
|
|
494
|
+
comment: beforeComment,
|
|
495
|
+
}, {
|
|
496
|
+
tableName,
|
|
497
|
+
columnName: name,
|
|
498
|
+
comment: afterComment,
|
|
499
|
+
}, tableName));
|
|
500
|
+
}
|
|
501
|
+
}
|
|
502
|
+
return changes;
|
|
503
|
+
}
|
|
504
|
+
function comparePartitions(before, after, tableName) {
|
|
505
|
+
const changes = [];
|
|
506
|
+
const beforePartitioned = before.isPartitioned;
|
|
507
|
+
const afterPartitioned = after.isPartitioned;
|
|
508
|
+
if (afterPartitioned && !beforePartitioned) {
|
|
509
|
+
changes.push(createChange('CREATE', 'PARTITION', tableName, null, {
|
|
510
|
+
tableName,
|
|
511
|
+
type: after.partitionType,
|
|
512
|
+
key: after.partitionKey,
|
|
513
|
+
}));
|
|
514
|
+
}
|
|
515
|
+
else if (!afterPartitioned && beforePartitioned) {
|
|
516
|
+
changes.push(createChange('DROP', 'PARTITION', tableName, {
|
|
517
|
+
tableName,
|
|
518
|
+
type: before.partitionType,
|
|
519
|
+
key: before.partitionKey,
|
|
520
|
+
}, null));
|
|
521
|
+
}
|
|
522
|
+
else if (afterPartitioned && beforePartitioned) {
|
|
523
|
+
const keysMatch = arraysEqual(before.partitionKey || [], after.partitionKey || []);
|
|
524
|
+
if (before.partitionType !== after.partitionType || !keysMatch) {
|
|
525
|
+
changes.push(createChange('ALTER', 'PARTITION', tableName, {
|
|
526
|
+
tableName,
|
|
527
|
+
type: before.partitionType,
|
|
528
|
+
key: before.partitionKey,
|
|
529
|
+
}, {
|
|
530
|
+
tableName,
|
|
531
|
+
type: after.partitionType,
|
|
532
|
+
key: after.partitionKey,
|
|
533
|
+
}));
|
|
534
|
+
}
|
|
535
|
+
}
|
|
536
|
+
return changes;
|
|
537
|
+
}
|
|
248
538
|
function compareViews(before, after) {
|
|
249
539
|
const changes = [];
|
|
250
540
|
const beforeMap = new Map(before.map(v => [v.name, v]));
|
|
@@ -1,4 +1,203 @@
|
|
|
1
1
|
import { colors } from "./spinner.js";
|
|
2
|
+
const TYPE_ALIASES = {
|
|
3
|
+
'int': 'integer',
|
|
4
|
+
'int2': 'smallint',
|
|
5
|
+
'int4': 'integer',
|
|
6
|
+
'int8': 'bigint',
|
|
7
|
+
'integer': 'integer',
|
|
8
|
+
'smallint': 'smallint',
|
|
9
|
+
'bigint': 'bigint',
|
|
10
|
+
'serial': 'serial',
|
|
11
|
+
'serial2': 'smallserial',
|
|
12
|
+
'serial4': 'serial',
|
|
13
|
+
'serial8': 'bigserial',
|
|
14
|
+
'smallserial': 'smallserial',
|
|
15
|
+
'bigserial': 'bigserial',
|
|
16
|
+
'float': 'double precision',
|
|
17
|
+
'float4': 'real',
|
|
18
|
+
'float8': 'double precision',
|
|
19
|
+
'real': 'real',
|
|
20
|
+
'double precision': 'double precision',
|
|
21
|
+
'decimal': 'numeric',
|
|
22
|
+
'numeric': 'numeric',
|
|
23
|
+
'bool': 'boolean',
|
|
24
|
+
'boolean': 'boolean',
|
|
25
|
+
'char': 'character',
|
|
26
|
+
'character': 'character',
|
|
27
|
+
'bpchar': 'character',
|
|
28
|
+
'varchar': 'character varying',
|
|
29
|
+
'character varying': 'character varying',
|
|
30
|
+
'text': 'text',
|
|
31
|
+
'name': 'name',
|
|
32
|
+
'citext': 'citext',
|
|
33
|
+
'timestamp': 'timestamp without time zone',
|
|
34
|
+
'timestamp without time zone': 'timestamp without time zone',
|
|
35
|
+
'timestamptz': 'timestamp with time zone',
|
|
36
|
+
'timestamp with time zone': 'timestamp with time zone',
|
|
37
|
+
'date': 'date',
|
|
38
|
+
'time': 'time without time zone',
|
|
39
|
+
'time without time zone': 'time without time zone',
|
|
40
|
+
'timetz': 'time with time zone',
|
|
41
|
+
'time with time zone': 'time with time zone',
|
|
42
|
+
'interval': 'interval',
|
|
43
|
+
'abstime': 'abstime',
|
|
44
|
+
'reltime': 'reltime',
|
|
45
|
+
'tinterval': 'tinterval',
|
|
46
|
+
'bytea': 'bytea',
|
|
47
|
+
'bit': 'bit',
|
|
48
|
+
'varbit': 'bit varying',
|
|
49
|
+
'bit varying': 'bit varying',
|
|
50
|
+
'uuid': 'uuid',
|
|
51
|
+
'json': 'json',
|
|
52
|
+
'jsonb': 'jsonb',
|
|
53
|
+
'jsonpath': 'jsonpath',
|
|
54
|
+
'xml': 'xml',
|
|
55
|
+
'money': 'money',
|
|
56
|
+
'inet': 'inet',
|
|
57
|
+
'cidr': 'cidr',
|
|
58
|
+
'macaddr': 'macaddr',
|
|
59
|
+
'macaddr8': 'macaddr8',
|
|
60
|
+
'point': 'point',
|
|
61
|
+
'line': 'line',
|
|
62
|
+
'lseg': 'lseg',
|
|
63
|
+
'box': 'box',
|
|
64
|
+
'path': 'path',
|
|
65
|
+
'polygon': 'polygon',
|
|
66
|
+
'circle': 'circle',
|
|
67
|
+
'int4range': 'int4range',
|
|
68
|
+
'int8range': 'int8range',
|
|
69
|
+
'numrange': 'numrange',
|
|
70
|
+
'tsrange': 'tsrange',
|
|
71
|
+
'tstzrange': 'tstzrange',
|
|
72
|
+
'daterange': 'daterange',
|
|
73
|
+
'int4multirange': 'int4multirange',
|
|
74
|
+
'int8multirange': 'int8multirange',
|
|
75
|
+
'nummultirange': 'nummultirange',
|
|
76
|
+
'tsmultirange': 'tsmultirange',
|
|
77
|
+
'tstzmultirange': 'tstzmultirange',
|
|
78
|
+
'datemultirange': 'datemultirange',
|
|
79
|
+
'tsvector': 'tsvector',
|
|
80
|
+
'tsquery': 'tsquery',
|
|
81
|
+
'gtsvector': 'gtsvector',
|
|
82
|
+
'oid': 'oid',
|
|
83
|
+
'regclass': 'regclass',
|
|
84
|
+
'regcollation': 'regcollation',
|
|
85
|
+
'regconfig': 'regconfig',
|
|
86
|
+
'regdictionary': 'regdictionary',
|
|
87
|
+
'regnamespace': 'regnamespace',
|
|
88
|
+
'regoper': 'regoper',
|
|
89
|
+
'regoperator': 'regoperator',
|
|
90
|
+
'regproc': 'regproc',
|
|
91
|
+
'regprocedure': 'regprocedure',
|
|
92
|
+
'regrole': 'regrole',
|
|
93
|
+
'regtype': 'regtype',
|
|
94
|
+
'xid': 'xid',
|
|
95
|
+
'xid8': 'xid8',
|
|
96
|
+
'cid': 'cid',
|
|
97
|
+
'tid': 'tid',
|
|
98
|
+
'aclitem': 'aclitem',
|
|
99
|
+
'smgr': 'smgr',
|
|
100
|
+
'unknown': 'unknown',
|
|
101
|
+
'internal': 'internal',
|
|
102
|
+
'opaque': 'opaque',
|
|
103
|
+
'anyelement': 'anyelement',
|
|
104
|
+
'anyarray': 'anyarray',
|
|
105
|
+
'anynonarray': 'anynonarray',
|
|
106
|
+
'anyenum': 'anyenum',
|
|
107
|
+
'anyrange': 'anyrange',
|
|
108
|
+
'anymultirange': 'anymultirange',
|
|
109
|
+
'anycompatible': 'anycompatible',
|
|
110
|
+
'anycompatiblearray': 'anycompatiblearray',
|
|
111
|
+
'anycompatiblenonarray': 'anycompatiblenonarray',
|
|
112
|
+
'anycompatiblerange': 'anycompatiblerange',
|
|
113
|
+
'anycompatiblemultirange': 'anycompatiblemultirange',
|
|
114
|
+
'cstring': 'cstring',
|
|
115
|
+
'record': 'record',
|
|
116
|
+
'trigger': 'trigger',
|
|
117
|
+
'event_trigger': 'event_trigger',
|
|
118
|
+
'pg_lsn': 'pg_lsn',
|
|
119
|
+
'pg_snapshot': 'pg_snapshot',
|
|
120
|
+
'txid_snapshot': 'txid_snapshot',
|
|
121
|
+
'fdw_handler': 'fdw_handler',
|
|
122
|
+
'index_am_handler': 'index_am_handler',
|
|
123
|
+
'tsm_handler': 'tsm_handler',
|
|
124
|
+
'table_am_handler': 'table_am_handler',
|
|
125
|
+
'language_handler': 'language_handler',
|
|
126
|
+
'void': 'void',
|
|
127
|
+
'refcursor': 'refcursor',
|
|
128
|
+
'_int2': 'smallint[]',
|
|
129
|
+
'_int4': 'integer[]',
|
|
130
|
+
'_int8': 'bigint[]',
|
|
131
|
+
'_float4': 'real[]',
|
|
132
|
+
'_float8': 'double precision[]',
|
|
133
|
+
'_numeric': 'numeric[]',
|
|
134
|
+
'_bool': 'boolean[]',
|
|
135
|
+
'_text': 'text[]',
|
|
136
|
+
'_varchar': 'character varying[]',
|
|
137
|
+
'_bpchar': 'character[]',
|
|
138
|
+
'_char': 'character[]',
|
|
139
|
+
'_name': 'name[]',
|
|
140
|
+
'_bytea': 'bytea[]',
|
|
141
|
+
'_bit': 'bit[]',
|
|
142
|
+
'_varbit': 'bit varying[]',
|
|
143
|
+
'_uuid': 'uuid[]',
|
|
144
|
+
'_json': 'json[]',
|
|
145
|
+
'_jsonb': 'jsonb[]',
|
|
146
|
+
'_xml': 'xml[]',
|
|
147
|
+
'_money': 'money[]',
|
|
148
|
+
'_timestamp': 'timestamp without time zone[]',
|
|
149
|
+
'_timestamptz': 'timestamp with time zone[]',
|
|
150
|
+
'_date': 'date[]',
|
|
151
|
+
'_time': 'time without time zone[]',
|
|
152
|
+
'_timetz': 'time with time zone[]',
|
|
153
|
+
'_interval': 'interval[]',
|
|
154
|
+
'_inet': 'inet[]',
|
|
155
|
+
'_cidr': 'cidr[]',
|
|
156
|
+
'_macaddr': 'macaddr[]',
|
|
157
|
+
'_macaddr8': 'macaddr8[]',
|
|
158
|
+
'_point': 'point[]',
|
|
159
|
+
'_line': 'line[]',
|
|
160
|
+
'_lseg': 'lseg[]',
|
|
161
|
+
'_box': 'box[]',
|
|
162
|
+
'_path': 'path[]',
|
|
163
|
+
'_polygon': 'polygon[]',
|
|
164
|
+
'_circle': 'circle[]',
|
|
165
|
+
'_int4range': 'int4range[]',
|
|
166
|
+
'_int8range': 'int8range[]',
|
|
167
|
+
'_numrange': 'numrange[]',
|
|
168
|
+
'_tsrange': 'tsrange[]',
|
|
169
|
+
'_tstzrange': 'tstzrange[]',
|
|
170
|
+
'_daterange': 'daterange[]',
|
|
171
|
+
'_tsvector': 'tsvector[]',
|
|
172
|
+
'_tsquery': 'tsquery[]',
|
|
173
|
+
'_oid': 'oid[]',
|
|
174
|
+
'_regclass': 'regclass[]',
|
|
175
|
+
'_regtype': 'regtype[]',
|
|
176
|
+
'_regproc': 'regproc[]',
|
|
177
|
+
'_xid': 'xid[]',
|
|
178
|
+
'_cid': 'cid[]',
|
|
179
|
+
'_tid': 'tid[]',
|
|
180
|
+
'_aclitem': 'aclitem[]',
|
|
181
|
+
'_cstring': 'cstring[]',
|
|
182
|
+
'_record': 'record[]',
|
|
183
|
+
'_pg_lsn': 'pg_lsn[]',
|
|
184
|
+
'_txid_snapshot': 'txid_snapshot[]',
|
|
185
|
+
'_refcursor': 'refcursor[]',
|
|
186
|
+
'_citext': 'citext[]',
|
|
187
|
+
};
|
|
188
|
+
function normalizeType(type) {
|
|
189
|
+
if (!type)
|
|
190
|
+
return type;
|
|
191
|
+
let normalized = type.toLowerCase().trim();
|
|
192
|
+
if (TYPE_ALIASES[normalized]) {
|
|
193
|
+
return TYPE_ALIASES[normalized];
|
|
194
|
+
}
|
|
195
|
+
normalized = normalized.replace(' without time zone', '');
|
|
196
|
+
if (TYPE_ALIASES[normalized]) {
|
|
197
|
+
return TYPE_ALIASES[normalized];
|
|
198
|
+
}
|
|
199
|
+
return normalized;
|
|
200
|
+
}
|
|
2
201
|
export function diffSchemas(local, remote) {
|
|
3
202
|
const tables = diffTables(local.tables, remote.tables);
|
|
4
203
|
const extensions = diffExtensions(local.extensions, remote.extensions);
|
|
@@ -75,7 +274,9 @@ function diffColumns(local, remote) {
|
|
|
75
274
|
}
|
|
76
275
|
function compareColumns(local, remote) {
|
|
77
276
|
const changes = [];
|
|
78
|
-
|
|
277
|
+
const localType = normalizeType(local.type);
|
|
278
|
+
const remoteType = normalizeType(remote.type);
|
|
279
|
+
if (localType !== remoteType) {
|
|
79
280
|
changes.push({ field: 'type', from: local.type, to: remote.type });
|
|
80
281
|
}
|
|
81
282
|
if (local.nullable !== remote.nullable) {
|
|
@@ -23,8 +23,9 @@ function normalizeTable(table) {
|
|
|
23
23
|
columns,
|
|
24
24
|
indexes,
|
|
25
25
|
constraints,
|
|
26
|
+
isPartitioned: table.isPartitioned,
|
|
26
27
|
partitionType: table.partitionType,
|
|
27
|
-
partitionKey: table.partitionKey
|
|
28
|
+
partitionKey: table.partitionKey ? [...table.partitionKey] : undefined,
|
|
28
29
|
};
|
|
29
30
|
}
|
|
30
31
|
function normalizeColumn(col) {
|
|
@@ -31,7 +31,7 @@ export async function introspectDatabase(connection, onProgress, options) {
|
|
|
31
31
|
WHERE n.nspname = 'public' AND c.relispartition = true;
|
|
32
32
|
`);
|
|
33
33
|
const partitionTableNames = new Set(partitionNamesResult.rows.map((r) => r.name));
|
|
34
|
-
const nonPartitionTables = tablesResult.rows.filter((r) => !partitionTableNames.has(r.table_name) && !r.table_name.startsWith('_relq'));
|
|
34
|
+
const nonPartitionTables = tablesResult.rows.filter((r) => !partitionTableNames.has(r.table_name) && !r.table_name.startsWith('_relq') && !r.table_name.startsWith('_kuery'));
|
|
35
35
|
const totalTables = nonPartitionTables.length;
|
|
36
36
|
let tableIndex = 0;
|
|
37
37
|
for (let i = 0; i < tablesResult.rows.length; i++) {
|
|
@@ -39,9 +39,12 @@ export async function introspectDatabase(connection, onProgress, options) {
|
|
|
39
39
|
const tableName = row.table_name;
|
|
40
40
|
const tableSchema = row.table_schema;
|
|
41
41
|
const rowCount = parseInt(row.row_count) || 0;
|
|
42
|
+
const isInternal = tableName.startsWith('_relq') || tableName.startsWith('_kuery');
|
|
43
|
+
if (isInternal) {
|
|
44
|
+
continue;
|
|
45
|
+
}
|
|
42
46
|
const isPartition = partitionTableNames.has(tableName);
|
|
43
|
-
|
|
44
|
-
if (!isPartition && !isInternal) {
|
|
47
|
+
if (!isPartition) {
|
|
45
48
|
tableIndex++;
|
|
46
49
|
onProgress?.('parsing_table', `${tableName} (${tableIndex}/${totalTables})`);
|
|
47
50
|
}
|
|
@@ -371,6 +374,7 @@ export async function introspectDatabase(connection, onProgress, options) {
|
|
|
371
374
|
domains: [],
|
|
372
375
|
compositeTypes: [],
|
|
373
376
|
sequences: [],
|
|
377
|
+
collations: [],
|
|
374
378
|
functions,
|
|
375
379
|
triggers,
|
|
376
380
|
policies,
|