relq 1.0.5 → 1.0.6
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 +252 -12
- package/dist/cjs/cli/commands/commit.cjs +12 -1
- package/dist/cjs/cli/commands/export.cjs +25 -19
- package/dist/cjs/cli/commands/import.cjs +219 -100
- package/dist/cjs/cli/commands/init.cjs +86 -14
- package/dist/cjs/cli/commands/pull.cjs +104 -23
- package/dist/cjs/cli/commands/push.cjs +38 -3
- package/dist/cjs/cli/index.cjs +9 -1
- package/dist/cjs/cli/utils/ast/codegen/builder.cjs +297 -0
- package/dist/cjs/cli/utils/ast/codegen/constraints.cjs +185 -0
- package/dist/cjs/cli/utils/ast/codegen/defaults.cjs +311 -0
- package/dist/cjs/cli/utils/ast/codegen/index.cjs +24 -0
- package/dist/cjs/cli/utils/ast/codegen/type-map.cjs +116 -0
- package/dist/cjs/cli/utils/ast/codegen/utils.cjs +69 -0
- package/dist/cjs/cli/utils/ast/index.cjs +19 -0
- package/dist/cjs/cli/utils/ast/transformer/helpers.cjs +154 -0
- package/dist/cjs/cli/utils/ast/transformer/index.cjs +25 -0
- package/dist/cjs/cli/utils/ast/types.cjs +2 -0
- package/dist/cjs/cli/utils/ast-codegen.cjs +949 -0
- package/dist/cjs/cli/utils/ast-transformer.cjs +916 -0
- package/dist/cjs/cli/utils/change-tracker.cjs +50 -1
- package/dist/cjs/cli/utils/cli-utils.cjs +151 -0
- package/dist/cjs/cli/utils/fast-introspect.cjs +149 -23
- package/dist/cjs/cli/utils/pg-parser.cjs +1 -0
- package/dist/cjs/cli/utils/repo-manager.cjs +121 -4
- package/dist/cjs/cli/utils/schema-comparator.cjs +98 -14
- package/dist/cjs/cli/utils/schema-introspect.cjs +56 -19
- package/dist/cjs/cli/utils/snapshot-manager.cjs +0 -1
- package/dist/cjs/cli/utils/sql-generator.cjs +353 -64
- package/dist/cjs/cli/utils/type-generator.cjs +114 -15
- package/dist/cjs/core/relq-client.cjs +22 -6
- package/dist/cjs/schema-definition/column-types.cjs +149 -13
- package/dist/cjs/schema-definition/defaults.cjs +72 -0
- package/dist/cjs/schema-definition/index.cjs +15 -1
- package/dist/cjs/schema-definition/introspection.cjs +7 -3
- package/dist/cjs/schema-definition/pg-relations.cjs +169 -0
- package/dist/cjs/schema-definition/pg-view.cjs +30 -0
- package/dist/cjs/schema-definition/table-definition.cjs +110 -4
- package/dist/cjs/types/config-types.cjs +13 -4
- package/dist/cjs/utils/aws-dsql.cjs +177 -0
- package/dist/config.d.ts +146 -1
- package/dist/esm/cli/commands/add.js +250 -13
- package/dist/esm/cli/commands/commit.js +12 -1
- package/dist/esm/cli/commands/export.js +25 -19
- package/dist/esm/cli/commands/import.js +221 -102
- package/dist/esm/cli/commands/init.js +86 -14
- package/dist/esm/cli/commands/pull.js +106 -25
- package/dist/esm/cli/commands/push.js +39 -4
- package/dist/esm/cli/index.js +9 -1
- package/dist/esm/cli/utils/ast/codegen/builder.js +291 -0
- package/dist/esm/cli/utils/ast/codegen/constraints.js +176 -0
- package/dist/esm/cli/utils/ast/codegen/defaults.js +305 -0
- package/dist/esm/cli/utils/ast/codegen/index.js +6 -0
- package/dist/esm/cli/utils/ast/codegen/type-map.js +111 -0
- package/dist/esm/cli/utils/ast/codegen/utils.js +60 -0
- package/dist/esm/cli/utils/ast/index.js +3 -0
- package/dist/esm/cli/utils/ast/transformer/helpers.js +141 -0
- package/dist/esm/cli/utils/ast/transformer/index.js +2 -0
- package/dist/esm/cli/utils/ast/types.js +1 -0
- package/dist/esm/cli/utils/ast-codegen.js +945 -0
- package/dist/esm/cli/utils/ast-transformer.js +907 -0
- package/dist/esm/cli/utils/change-tracker.js +50 -1
- package/dist/esm/cli/utils/cli-utils.js +147 -0
- package/dist/esm/cli/utils/fast-introspect.js +149 -23
- package/dist/esm/cli/utils/pg-parser.js +1 -0
- package/dist/esm/cli/utils/repo-manager.js +114 -4
- package/dist/esm/cli/utils/schema-comparator.js +98 -14
- package/dist/esm/cli/utils/schema-introspect.js +56 -19
- package/dist/esm/cli/utils/snapshot-manager.js +0 -1
- package/dist/esm/cli/utils/sql-generator.js +353 -64
- package/dist/esm/cli/utils/type-generator.js +114 -15
- package/dist/esm/core/relq-client.js +23 -7
- package/dist/esm/schema-definition/column-types.js +146 -12
- package/dist/esm/schema-definition/defaults.js +69 -0
- package/dist/esm/schema-definition/index.js +3 -0
- package/dist/esm/schema-definition/introspection.js +7 -3
- package/dist/esm/schema-definition/pg-relations.js +161 -0
- package/dist/esm/schema-definition/pg-view.js +24 -0
- package/dist/esm/schema-definition/table-definition.js +110 -4
- package/dist/esm/types/config-types.js +12 -4
- package/dist/esm/utils/aws-dsql.js +139 -0
- package/dist/index.d.ts +159 -1
- package/dist/schema-builder.d.ts +1314 -32
- package/package.json +1 -1
|
@@ -41,6 +41,12 @@ exports.getFetchHead = getFetchHead;
|
|
|
41
41
|
exports.setFetchHead = setFetchHead;
|
|
42
42
|
exports.generateHash = generateHash;
|
|
43
43
|
exports.shortHash = shortHash;
|
|
44
|
+
exports.getConnectionId = getConnectionId;
|
|
45
|
+
exports.getConnectionLabel = getConnectionLabel;
|
|
46
|
+
exports.getRemoteRef = getRemoteRef;
|
|
47
|
+
exports.isCommitSyncedWith = isCommitSyncedWith;
|
|
48
|
+
exports.markCommitAsPushed = markCommitAsPushed;
|
|
49
|
+
exports.markCommitAsPulled = markCommitAsPulled;
|
|
44
50
|
exports.saveCommit = saveCommit;
|
|
45
51
|
exports.loadCommit = loadCommit;
|
|
46
52
|
exports.getAllCommits = getAllCommits;
|
|
@@ -65,6 +71,7 @@ exports.stageChanges = stageChanges;
|
|
|
65
71
|
exports.unstageChanges = unstageChanges;
|
|
66
72
|
exports.clearWorkingState = clearWorkingState;
|
|
67
73
|
exports.getStagedChanges = getStagedChanges;
|
|
74
|
+
exports.cleanupStagedChanges = cleanupStagedChanges;
|
|
68
75
|
exports.getUnstagedChanges = getUnstagedChanges;
|
|
69
76
|
exports.hasUncommittedChanges = hasUncommittedChanges;
|
|
70
77
|
exports.ensureRemoteTable = ensureRemoteTable;
|
|
@@ -147,6 +154,81 @@ function generateHash(schema) {
|
|
|
147
154
|
function shortHash(hash) {
|
|
148
155
|
return hash.substring(0, 7);
|
|
149
156
|
}
|
|
157
|
+
function getConnectionId(connection) {
|
|
158
|
+
const parts = [
|
|
159
|
+
connection.host || 'localhost',
|
|
160
|
+
String(connection.port || 5432),
|
|
161
|
+
connection.database || 'postgres',
|
|
162
|
+
connection.user || 'postgres',
|
|
163
|
+
];
|
|
164
|
+
return crypto.createHash('sha256').update(parts.join('/')).digest('hex').substring(0, 12);
|
|
165
|
+
}
|
|
166
|
+
function getConnectionLabel(connection) {
|
|
167
|
+
const user = connection.user || 'postgres';
|
|
168
|
+
const host = connection.host || 'localhost';
|
|
169
|
+
const db = connection.database || 'postgres';
|
|
170
|
+
return `${user}@${host}/${db}`;
|
|
171
|
+
}
|
|
172
|
+
function getRemoteRef(connection) {
|
|
173
|
+
return {
|
|
174
|
+
id: getConnectionId(connection),
|
|
175
|
+
label: getConnectionLabel(connection),
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
function isCommitSyncedWith(commit, connection) {
|
|
179
|
+
const connectionId = getConnectionId(connection);
|
|
180
|
+
if (commit.remotes?.pushed?.some((r) => r.id === connectionId)) {
|
|
181
|
+
return { synced: true, type: 'pushed' };
|
|
182
|
+
}
|
|
183
|
+
if (commit.remotes?.pulled?.some((r) => r.id === connectionId)) {
|
|
184
|
+
return { synced: true, type: 'pulled' };
|
|
185
|
+
}
|
|
186
|
+
if (commit.message?.startsWith('pull: sync from ')) {
|
|
187
|
+
const pullTarget = commit.message.replace('pull: sync from ', '');
|
|
188
|
+
const currentLabel = getConnectionLabel(connection);
|
|
189
|
+
const legacyLabel = `${connection.user}@${connection.host}:${connection.port || 5432}/${connection.database}`;
|
|
190
|
+
if (pullTarget === currentLabel || pullTarget === legacyLabel) {
|
|
191
|
+
return { synced: true, type: 'pulled' };
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
return { synced: false, type: null };
|
|
195
|
+
}
|
|
196
|
+
function markCommitAsPushed(commitHash, connection, projectRoot = process.cwd()) {
|
|
197
|
+
const commitPath = path.join(projectRoot, RELQ_DIR, COMMITS_DIR, `${commitHash}.json`);
|
|
198
|
+
if (!fs.existsSync(commitPath)) {
|
|
199
|
+
return;
|
|
200
|
+
}
|
|
201
|
+
const commit = JSON.parse(fs.readFileSync(commitPath, 'utf-8'));
|
|
202
|
+
const remoteRef = getRemoteRef(connection);
|
|
203
|
+
if (!commit.remotes) {
|
|
204
|
+
commit.remotes = {};
|
|
205
|
+
}
|
|
206
|
+
if (!commit.remotes.pushed) {
|
|
207
|
+
commit.remotes.pushed = [];
|
|
208
|
+
}
|
|
209
|
+
if (!commit.remotes.pushed.some((r) => r.id === remoteRef.id)) {
|
|
210
|
+
commit.remotes.pushed.push(remoteRef);
|
|
211
|
+
}
|
|
212
|
+
fs.writeFileSync(commitPath, JSON.stringify(commit, null, 2), 'utf-8');
|
|
213
|
+
}
|
|
214
|
+
function markCommitAsPulled(commitHash, connection, projectRoot = process.cwd()) {
|
|
215
|
+
const commitPath = path.join(projectRoot, RELQ_DIR, COMMITS_DIR, `${commitHash}.json`);
|
|
216
|
+
if (!fs.existsSync(commitPath)) {
|
|
217
|
+
return;
|
|
218
|
+
}
|
|
219
|
+
const commit = JSON.parse(fs.readFileSync(commitPath, 'utf-8'));
|
|
220
|
+
const remoteRef = getRemoteRef(connection);
|
|
221
|
+
if (!commit.remotes) {
|
|
222
|
+
commit.remotes = {};
|
|
223
|
+
}
|
|
224
|
+
if (!commit.remotes.pulled) {
|
|
225
|
+
commit.remotes.pulled = [];
|
|
226
|
+
}
|
|
227
|
+
if (!commit.remotes.pulled.some((r) => r.id === remoteRef.id)) {
|
|
228
|
+
commit.remotes.pulled.push(remoteRef);
|
|
229
|
+
}
|
|
230
|
+
fs.writeFileSync(commitPath, JSON.stringify(commit, null, 2), 'utf-8');
|
|
231
|
+
}
|
|
150
232
|
function saveCommit(commit, projectRoot = process.cwd()) {
|
|
151
233
|
const commitsPath = path.join(projectRoot, RELQ_DIR, COMMITS_DIR);
|
|
152
234
|
const commitPath = path.join(commitsPath, `${commit.hash}.json`);
|
|
@@ -349,7 +431,9 @@ function stageChanges(patterns, projectRoot = process.cwd()) {
|
|
|
349
431
|
remaining.push(change);
|
|
350
432
|
}
|
|
351
433
|
}
|
|
352
|
-
|
|
434
|
+
const newKeys = new Set(staged.map(c => `${c.objectType}:${c.objectName}:${c.parentName || ''}`));
|
|
435
|
+
const keptStaged = state.staged.filter(c => !newKeys.has(`${c.objectType}:${c.objectName}:${c.parentName || ''}`));
|
|
436
|
+
state.staged = [...keptStaged, ...staged];
|
|
353
437
|
state.unstaged = remaining;
|
|
354
438
|
state.timestamp = new Date().toISOString();
|
|
355
439
|
saveWorkingState(state, projectRoot);
|
|
@@ -390,6 +474,18 @@ function getStagedChanges(projectRoot = process.cwd()) {
|
|
|
390
474
|
const state = loadWorkingState(projectRoot);
|
|
391
475
|
return state?.staged || [];
|
|
392
476
|
}
|
|
477
|
+
function cleanupStagedChanges(validChanges, projectRoot = process.cwd()) {
|
|
478
|
+
const state = getOrCreateWorkingState(projectRoot);
|
|
479
|
+
const validKeys = new Set(validChanges.map(c => `${c.objectType}:${c.objectName}:${c.parentName || ''}`));
|
|
480
|
+
const originalCount = state.staged.length;
|
|
481
|
+
state.staged = state.staged.filter(c => validKeys.has(`${c.objectType}:${c.objectName}:${c.parentName || ''}`));
|
|
482
|
+
const removedCount = originalCount - state.staged.length;
|
|
483
|
+
if (removedCount > 0) {
|
|
484
|
+
state.timestamp = new Date().toISOString();
|
|
485
|
+
saveWorkingState(state, projectRoot);
|
|
486
|
+
}
|
|
487
|
+
return removedCount;
|
|
488
|
+
}
|
|
393
489
|
function getUnstagedChanges(projectRoot = process.cwd()) {
|
|
394
490
|
const state = loadWorkingState(projectRoot);
|
|
395
491
|
return state?.unstaged || [];
|
|
@@ -468,7 +564,7 @@ async function getRemoteHead(connection) {
|
|
|
468
564
|
const commits = await fetchRemoteCommits(connection, 1);
|
|
469
565
|
return commits.length > 0 ? commits[0].hash : null;
|
|
470
566
|
}
|
|
471
|
-
async function pushCommit(connection, commit) {
|
|
567
|
+
async function pushCommit(connection, commit, projectRoot = process.cwd()) {
|
|
472
568
|
const { Pool } = await Promise.resolve().then(() => __importStar(require("../../addon/pg/index.cjs")));
|
|
473
569
|
const pool = new Pool({
|
|
474
570
|
host: connection.host,
|
|
@@ -480,6 +576,27 @@ async function pushCommit(connection, commit) {
|
|
|
480
576
|
ssl: connection.ssl,
|
|
481
577
|
});
|
|
482
578
|
try {
|
|
579
|
+
let schemaSnapshot = null;
|
|
580
|
+
if (commit.schema) {
|
|
581
|
+
schemaSnapshot = commit.schema;
|
|
582
|
+
}
|
|
583
|
+
else {
|
|
584
|
+
schemaSnapshot = loadSnapshot(projectRoot);
|
|
585
|
+
}
|
|
586
|
+
if (!schemaSnapshot) {
|
|
587
|
+
throw new Error(`Cannot push commit ${commit.hash}: No schema snapshot available. Run 'relq pull' first.`);
|
|
588
|
+
}
|
|
589
|
+
const stats = commit.stats || {
|
|
590
|
+
tables: 0,
|
|
591
|
+
columns: 0,
|
|
592
|
+
indexes: 0,
|
|
593
|
+
enums: 0,
|
|
594
|
+
domains: 0,
|
|
595
|
+
compositeTypes: 0,
|
|
596
|
+
sequences: 0,
|
|
597
|
+
functions: 0,
|
|
598
|
+
triggers: 0,
|
|
599
|
+
};
|
|
483
600
|
await pool.query(`
|
|
484
601
|
INSERT INTO _relq_commits (hash, parent_hash, author, message, schema_snapshot, stats)
|
|
485
602
|
VALUES ($1, $2, $3, $4, $5, $6)
|
|
@@ -489,8 +606,8 @@ async function pushCommit(connection, commit) {
|
|
|
489
606
|
commit.parentHash,
|
|
490
607
|
commit.author,
|
|
491
608
|
commit.message,
|
|
492
|
-
JSON.stringify(
|
|
493
|
-
JSON.stringify(
|
|
609
|
+
JSON.stringify(schemaSnapshot),
|
|
610
|
+
JSON.stringify(stats),
|
|
494
611
|
]);
|
|
495
612
|
}
|
|
496
613
|
finally {
|
|
@@ -198,6 +198,7 @@ function compareTables(before, after) {
|
|
|
198
198
|
changes.push(...compareConstraints(beforeTable.constraints || [], table.constraints || [], name));
|
|
199
199
|
changes.push(...compareTableComments(beforeTable, table, name));
|
|
200
200
|
changes.push(...compareColumnComments(beforeTable.columns, table.columns, name));
|
|
201
|
+
changes.push(...compareIndexComments(beforeTable.indexes || [], table.indexes || [], name));
|
|
201
202
|
changes.push(...comparePartitions(beforeTable, table, name));
|
|
202
203
|
}
|
|
203
204
|
}
|
|
@@ -231,21 +232,46 @@ function tableToChangeData(table) {
|
|
|
231
232
|
}
|
|
232
233
|
function compareColumns(before, after, tableName) {
|
|
233
234
|
const changes = [];
|
|
234
|
-
const
|
|
235
|
-
const
|
|
236
|
-
|
|
237
|
-
|
|
235
|
+
const beforeByName = new Map(before.map(c => [c.name, c]));
|
|
236
|
+
const afterByName = new Map(after.map(c => [c.name, c]));
|
|
237
|
+
const beforeByTrackingId = new Map(before.filter(c => c.trackingId).map(c => [c.trackingId, c]));
|
|
238
|
+
const afterByTrackingId = new Map(after.filter(c => c.trackingId).map(c => [c.trackingId, c]));
|
|
239
|
+
const processedBefore = new Set();
|
|
240
|
+
const processedAfter = new Set();
|
|
241
|
+
for (const [trackingId, afterCol] of afterByTrackingId) {
|
|
242
|
+
const beforeCol = beforeByTrackingId.get(trackingId);
|
|
243
|
+
if (beforeCol && beforeCol.name !== afterCol.name) {
|
|
244
|
+
changes.push((0, change_tracker_1.createChange)('RENAME', 'COLUMN', afterCol.name, columnToChangeData(beforeCol), columnToChangeData(afterCol), tableName));
|
|
245
|
+
processedBefore.add(beforeCol.name);
|
|
246
|
+
processedAfter.add(afterCol.name);
|
|
247
|
+
}
|
|
248
|
+
else if (beforeCol && beforeCol.name === afterCol.name) {
|
|
249
|
+
if (hasColumnChanged(beforeCol, afterCol)) {
|
|
250
|
+
changes.push((0, change_tracker_1.createChange)('ALTER', 'COLUMN', afterCol.name, columnToChangeData(beforeCol), columnToChangeData(afterCol), tableName));
|
|
251
|
+
}
|
|
252
|
+
processedBefore.add(beforeCol.name);
|
|
253
|
+
processedAfter.add(afterCol.name);
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
for (const [name, col] of afterByName) {
|
|
257
|
+
if (processedAfter.has(name))
|
|
258
|
+
continue;
|
|
259
|
+
if (!beforeByName.has(name)) {
|
|
238
260
|
changes.push((0, change_tracker_1.createChange)('CREATE', 'COLUMN', name, null, columnToChangeData(col), tableName));
|
|
239
261
|
}
|
|
240
262
|
else {
|
|
241
|
-
const beforeCol =
|
|
242
|
-
if (hasColumnChanged(beforeCol, col)) {
|
|
263
|
+
const beforeCol = beforeByName.get(name);
|
|
264
|
+
if (!processedBefore.has(name) && hasColumnChanged(beforeCol, col)) {
|
|
243
265
|
changes.push((0, change_tracker_1.createChange)('ALTER', 'COLUMN', name, columnToChangeData(beforeCol), columnToChangeData(col), tableName));
|
|
244
266
|
}
|
|
267
|
+
processedBefore.add(name);
|
|
245
268
|
}
|
|
269
|
+
processedAfter.add(name);
|
|
246
270
|
}
|
|
247
|
-
for (const [name, col] of
|
|
248
|
-
if (
|
|
271
|
+
for (const [name, col] of beforeByName) {
|
|
272
|
+
if (processedBefore.has(name))
|
|
273
|
+
continue;
|
|
274
|
+
if (!afterByName.has(name)) {
|
|
249
275
|
changes.push((0, change_tracker_1.createChange)('DROP', 'COLUMN', name, columnToChangeData(col), null, tableName));
|
|
250
276
|
}
|
|
251
277
|
}
|
|
@@ -262,6 +288,7 @@ function columnToChangeData(col) {
|
|
|
262
288
|
maxLength: col.maxLength,
|
|
263
289
|
precision: col.precision,
|
|
264
290
|
scale: col.scale,
|
|
291
|
+
trackingId: col.trackingId,
|
|
265
292
|
};
|
|
266
293
|
}
|
|
267
294
|
function normalizeDataType(type) {
|
|
@@ -328,10 +355,28 @@ function hasColumnChanged(before, after) {
|
|
|
328
355
|
}
|
|
329
356
|
function compareIndexes(before, after, tableName) {
|
|
330
357
|
const changes = [];
|
|
331
|
-
const
|
|
332
|
-
const
|
|
333
|
-
|
|
334
|
-
|
|
358
|
+
const beforeByName = new Map(before.map(i => [i.name, i]));
|
|
359
|
+
const afterByName = new Map(after.map(i => [i.name, i]));
|
|
360
|
+
const beforeByTrackingId = new Map(before.filter(i => i.trackingId).map(i => [i.trackingId, i]));
|
|
361
|
+
const afterByTrackingId = new Map(after.filter(i => i.trackingId).map(i => [i.trackingId, i]));
|
|
362
|
+
const processedBefore = new Set();
|
|
363
|
+
const processedAfter = new Set();
|
|
364
|
+
for (const [trackingId, afterIdx] of afterByTrackingId) {
|
|
365
|
+
const beforeIdx = beforeByTrackingId.get(trackingId);
|
|
366
|
+
if (beforeIdx && beforeIdx.name !== afterIdx.name && !beforeIdx.isPrimary && !afterIdx.isPrimary) {
|
|
367
|
+
changes.push((0, change_tracker_1.createChange)('RENAME', 'INDEX', afterIdx.name, { name: beforeIdx.name, tableName, columns: beforeIdx.columns }, { name: afterIdx.name, tableName, columns: afterIdx.columns, isUnique: afterIdx.isUnique, type: afterIdx.type }));
|
|
368
|
+
processedBefore.add(beforeIdx.name);
|
|
369
|
+
processedAfter.add(afterIdx.name);
|
|
370
|
+
}
|
|
371
|
+
else if (beforeIdx && beforeIdx.name === afterIdx.name) {
|
|
372
|
+
processedBefore.add(beforeIdx.name);
|
|
373
|
+
processedAfter.add(afterIdx.name);
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
for (const [name, idx] of afterByName) {
|
|
377
|
+
if (processedAfter.has(name) || idx.isPrimary)
|
|
378
|
+
continue;
|
|
379
|
+
if (!beforeByName.has(name)) {
|
|
335
380
|
changes.push((0, change_tracker_1.createChange)('CREATE', 'INDEX', name, null, {
|
|
336
381
|
name: idx.name,
|
|
337
382
|
tableName,
|
|
@@ -340,9 +385,12 @@ function compareIndexes(before, after, tableName) {
|
|
|
340
385
|
type: idx.type,
|
|
341
386
|
}));
|
|
342
387
|
}
|
|
388
|
+
processedAfter.add(name);
|
|
343
389
|
}
|
|
344
|
-
for (const [name, idx] of
|
|
345
|
-
if (
|
|
390
|
+
for (const [name, idx] of beforeByName) {
|
|
391
|
+
if (processedBefore.has(name) || idx.isPrimary)
|
|
392
|
+
continue;
|
|
393
|
+
if (!afterByName.has(name)) {
|
|
346
394
|
changes.push((0, change_tracker_1.createChange)('DROP', 'INDEX', name, {
|
|
347
395
|
name: idx.name,
|
|
348
396
|
tableName,
|
|
@@ -508,6 +556,42 @@ function compareColumnComments(before, after, tableName) {
|
|
|
508
556
|
}
|
|
509
557
|
return changes;
|
|
510
558
|
}
|
|
559
|
+
function compareIndexComments(before, after, tableName) {
|
|
560
|
+
const changes = [];
|
|
561
|
+
const beforeMap = new Map(before.map(i => [i.name, i]));
|
|
562
|
+
const afterMap = new Map(after.map(i => [i.name, i]));
|
|
563
|
+
for (const [name, afterIdx] of afterMap) {
|
|
564
|
+
const beforeIdx = beforeMap.get(name);
|
|
565
|
+
const afterComment = afterIdx.comment || null;
|
|
566
|
+
const beforeComment = beforeIdx ? beforeIdx.comment || null : null;
|
|
567
|
+
if (afterComment && !beforeComment) {
|
|
568
|
+
changes.push((0, change_tracker_1.createChange)('CREATE', 'INDEX_COMMENT', name, null, {
|
|
569
|
+
tableName,
|
|
570
|
+
indexName: name,
|
|
571
|
+
comment: afterComment,
|
|
572
|
+
}, tableName));
|
|
573
|
+
}
|
|
574
|
+
else if (!afterComment && beforeComment) {
|
|
575
|
+
changes.push((0, change_tracker_1.createChange)('DROP', 'INDEX_COMMENT', name, {
|
|
576
|
+
tableName,
|
|
577
|
+
indexName: name,
|
|
578
|
+
comment: beforeComment,
|
|
579
|
+
}, null, tableName));
|
|
580
|
+
}
|
|
581
|
+
else if (afterComment && beforeComment && afterComment !== beforeComment) {
|
|
582
|
+
changes.push((0, change_tracker_1.createChange)('ALTER', 'INDEX_COMMENT', name, {
|
|
583
|
+
tableName,
|
|
584
|
+
indexName: name,
|
|
585
|
+
comment: beforeComment,
|
|
586
|
+
}, {
|
|
587
|
+
tableName,
|
|
588
|
+
indexName: name,
|
|
589
|
+
comment: afterComment,
|
|
590
|
+
}, tableName));
|
|
591
|
+
}
|
|
592
|
+
}
|
|
593
|
+
return changes;
|
|
594
|
+
}
|
|
511
595
|
function comparePartitions(before, after, tableName) {
|
|
512
596
|
const changes = [];
|
|
513
597
|
const beforePartitioned = before.isPartitioned;
|
|
@@ -35,6 +35,22 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
35
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
36
|
exports.introspectDatabase = introspectDatabase;
|
|
37
37
|
exports.tableHasData = tableHasData;
|
|
38
|
+
function mapInternalToFriendlyType(internalType) {
|
|
39
|
+
const typeMap = {
|
|
40
|
+
'int2': 'smallint',
|
|
41
|
+
'int4': 'integer',
|
|
42
|
+
'int8': 'bigint',
|
|
43
|
+
'float4': 'real',
|
|
44
|
+
'float8': 'double precision',
|
|
45
|
+
'bool': 'boolean',
|
|
46
|
+
'timestamptz': 'timestamp with time zone',
|
|
47
|
+
'timetz': 'time with time zone',
|
|
48
|
+
'varchar': 'character varying',
|
|
49
|
+
'bpchar': 'character',
|
|
50
|
+
'varbit': 'bit varying',
|
|
51
|
+
};
|
|
52
|
+
return typeMap[internalType] || internalType;
|
|
53
|
+
}
|
|
38
54
|
async function introspectDatabase(connection, onProgress, options) {
|
|
39
55
|
const { includeFunctions = false, includeTriggers = false } = options || {};
|
|
40
56
|
const { Pool } = await Promise.resolve().then(() => __importStar(require("../../addon/pg/index.cjs")));
|
|
@@ -54,7 +70,8 @@ async function introspectDatabase(connection, onProgress, options) {
|
|
|
54
70
|
SELECT
|
|
55
71
|
t.table_name,
|
|
56
72
|
t.table_schema,
|
|
57
|
-
(SELECT reltuples::bigint FROM pg_class WHERE relname = t.table_name) as row_count
|
|
73
|
+
(SELECT reltuples::bigint FROM pg_class WHERE relname = t.table_name) as row_count,
|
|
74
|
+
obj_description((quote_ident(t.table_schema) || '.' || quote_ident(t.table_name))::regclass, 'pg_class') as table_comment
|
|
58
75
|
FROM information_schema.tables t
|
|
59
76
|
WHERE t.table_schema = 'public'
|
|
60
77
|
AND t.table_type = 'BASE TABLE'
|
|
@@ -111,11 +128,17 @@ async function introspectDatabase(connection, onProgress, options) {
|
|
|
111
128
|
WHERE tc.table_name = $1 AND tc.constraint_type = 'PRIMARY KEY'
|
|
112
129
|
) pk ON pk.column_name = c.column_name
|
|
113
130
|
LEFT JOIN (
|
|
131
|
+
-- Only mark as unique if column is the ONLY column in the unique constraint
|
|
132
|
+
-- (not part of a composite unique constraint)
|
|
114
133
|
SELECT kcu.column_name, true as is_unique
|
|
115
134
|
FROM information_schema.table_constraints tc
|
|
116
135
|
JOIN information_schema.key_column_usage kcu
|
|
117
136
|
ON tc.constraint_name = kcu.constraint_name
|
|
118
137
|
WHERE tc.table_name = $1 AND tc.constraint_type = 'UNIQUE'
|
|
138
|
+
AND (
|
|
139
|
+
SELECT COUNT(*) FROM information_schema.key_column_usage kcu2
|
|
140
|
+
WHERE kcu2.constraint_name = tc.constraint_name
|
|
141
|
+
) = 1
|
|
119
142
|
) uq ON uq.column_name = c.column_name
|
|
120
143
|
LEFT JOIN (
|
|
121
144
|
SELECT
|
|
@@ -132,29 +155,41 @@ async function introspectDatabase(connection, onProgress, options) {
|
|
|
132
155
|
WHERE c.table_name = $1 AND c.table_schema = $2
|
|
133
156
|
ORDER BY c.ordinal_position;
|
|
134
157
|
`, [tableName, tableSchema]);
|
|
135
|
-
const columns = columnsResult.rows.map(col =>
|
|
136
|
-
|
|
137
|
-
dataType
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
158
|
+
const columns = columnsResult.rows.map(col => {
|
|
159
|
+
let dataType = col.data_type;
|
|
160
|
+
if (dataType === 'ARRAY' && col.udt_name) {
|
|
161
|
+
const baseType = col.udt_name.startsWith('_') ? col.udt_name.slice(1) : col.udt_name;
|
|
162
|
+
const friendlyBase = mapInternalToFriendlyType(baseType);
|
|
163
|
+
dataType = `${friendlyBase}[]`;
|
|
164
|
+
}
|
|
165
|
+
else if (dataType === 'USER-DEFINED' && col.udt_name) {
|
|
166
|
+
dataType = col.udt_name;
|
|
167
|
+
}
|
|
168
|
+
return {
|
|
169
|
+
name: col.column_name,
|
|
170
|
+
dataType,
|
|
171
|
+
isNullable: col.is_nullable === 'YES',
|
|
172
|
+
defaultValue: col.column_default,
|
|
173
|
+
isPrimaryKey: col.is_primary_key,
|
|
174
|
+
isUnique: col.is_unique,
|
|
175
|
+
maxLength: col.character_maximum_length,
|
|
176
|
+
precision: col.numeric_precision,
|
|
177
|
+
scale: col.numeric_scale,
|
|
178
|
+
references: col.foreign_table ? {
|
|
179
|
+
table: col.foreign_table,
|
|
180
|
+
column: col.foreign_column,
|
|
181
|
+
} : null,
|
|
182
|
+
comment: col.column_comment || undefined,
|
|
183
|
+
};
|
|
184
|
+
});
|
|
151
185
|
const indexesResult = await pool.query(`
|
|
152
186
|
SELECT
|
|
153
187
|
i.relname as index_name,
|
|
154
188
|
array_agg(a.attname ORDER BY k.n) as columns,
|
|
155
189
|
ix.indisunique as is_unique,
|
|
156
190
|
ix.indisprimary as is_primary,
|
|
157
|
-
am.amname as index_type
|
|
191
|
+
am.amname as index_type,
|
|
192
|
+
obj_description(i.oid, 'pg_class') as index_comment
|
|
158
193
|
FROM pg_index ix
|
|
159
194
|
JOIN pg_class t ON t.oid = ix.indrelid
|
|
160
195
|
JOIN pg_class i ON i.oid = ix.indexrelid
|
|
@@ -162,7 +197,7 @@ async function introspectDatabase(connection, onProgress, options) {
|
|
|
162
197
|
JOIN LATERAL unnest(ix.indkey) WITH ORDINALITY AS k(attnum, n) ON true
|
|
163
198
|
JOIN pg_attribute a ON a.attrelid = t.oid AND a.attnum = k.attnum
|
|
164
199
|
WHERE t.relname = $1
|
|
165
|
-
GROUP BY i.relname, ix.indisunique, ix.indisprimary, am.amname
|
|
200
|
+
GROUP BY i.relname, i.oid, ix.indisunique, ix.indisprimary, am.amname
|
|
166
201
|
ORDER BY i.relname;
|
|
167
202
|
`, [tableName]);
|
|
168
203
|
const indexes = indexesResult.rows.map(idx => ({
|
|
@@ -171,6 +206,7 @@ async function introspectDatabase(connection, onProgress, options) {
|
|
|
171
206
|
isUnique: idx.is_unique,
|
|
172
207
|
isPrimary: idx.is_primary,
|
|
173
208
|
type: idx.index_type,
|
|
209
|
+
comment: idx.index_comment || null,
|
|
174
210
|
}));
|
|
175
211
|
const constraintsResult = await pool.query(`
|
|
176
212
|
SELECT
|
|
@@ -238,6 +274,7 @@ async function introspectDatabase(connection, onProgress, options) {
|
|
|
238
274
|
isPartitioned: partitionInfo.is_partitioned || false,
|
|
239
275
|
partitionType: partitionInfo.partition_type,
|
|
240
276
|
partitionKey,
|
|
277
|
+
comment: row.table_comment || null,
|
|
241
278
|
});
|
|
242
279
|
}
|
|
243
280
|
onProgress?.('fetching_extensions');
|