relq 1.0.91 → 1.0.92
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.
|
@@ -345,16 +345,6 @@ function diffIndexes(local, remote) {
|
|
|
345
345
|
}
|
|
346
346
|
function diffConstraints(local, remote) {
|
|
347
347
|
const diffs = [];
|
|
348
|
-
const sig = (c) => {
|
|
349
|
-
const def = (c.definition || '').replace(/"/g, '').replace(/\s+/g, ' ').trim().toLowerCase();
|
|
350
|
-
return `${c.type}:${def}`;
|
|
351
|
-
};
|
|
352
|
-
const localSigs = new Map();
|
|
353
|
-
const remoteSigs = new Map();
|
|
354
|
-
for (const c of local)
|
|
355
|
-
localSigs.set(sig(c), c);
|
|
356
|
-
for (const c of remote)
|
|
357
|
-
remoteSigs.set(sig(c), c);
|
|
358
348
|
const toConstraintInfo = (c) => ({
|
|
359
349
|
name: c.name,
|
|
360
350
|
type: (c.type || 'CHECK'),
|
|
@@ -362,13 +352,111 @@ function diffConstraints(local, remote) {
|
|
|
362
352
|
definition: c.definition || '',
|
|
363
353
|
trackingId: c.trackingId,
|
|
364
354
|
});
|
|
365
|
-
|
|
366
|
-
|
|
355
|
+
const matchedLocal = new Set();
|
|
356
|
+
const matchedRemote = new Set();
|
|
357
|
+
for (let ri = 0; ri < remote.length; ri++) {
|
|
358
|
+
if (matchedRemote.has(ri))
|
|
359
|
+
continue;
|
|
360
|
+
const rc = remote[ri];
|
|
361
|
+
if (!rc.trackingId)
|
|
362
|
+
continue;
|
|
363
|
+
for (let li = 0; li < local.length; li++) {
|
|
364
|
+
if (matchedLocal.has(li))
|
|
365
|
+
continue;
|
|
366
|
+
const lc = local[li];
|
|
367
|
+
if (lc.trackingId && lc.trackingId === rc.trackingId) {
|
|
368
|
+
matchedLocal.add(li);
|
|
369
|
+
matchedRemote.add(ri);
|
|
370
|
+
break;
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
for (let ri = 0; ri < remote.length; ri++) {
|
|
375
|
+
if (matchedRemote.has(ri))
|
|
376
|
+
continue;
|
|
377
|
+
const rc = remote[ri];
|
|
378
|
+
if (!rc.name)
|
|
379
|
+
continue;
|
|
380
|
+
for (let li = 0; li < local.length; li++) {
|
|
381
|
+
if (matchedLocal.has(li))
|
|
382
|
+
continue;
|
|
383
|
+
const lc = local[li];
|
|
384
|
+
if (lc.name && lc.name === rc.name) {
|
|
385
|
+
matchedLocal.add(li);
|
|
386
|
+
matchedRemote.add(ri);
|
|
387
|
+
break;
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
const semanticSig = (c) => {
|
|
392
|
+
const def = c.definition || '';
|
|
393
|
+
const type = (c.type || '').toUpperCase();
|
|
394
|
+
if (type === 'PRIMARY KEY' || type === 'UNIQUE') {
|
|
395
|
+
const colMatch = def.match(/\(([^)]+)\)/);
|
|
396
|
+
if (colMatch) {
|
|
397
|
+
const cols = colMatch[1].replace(/"/g, '').split(',').map(s => s.trim().toLowerCase()).sort().join(',');
|
|
398
|
+
return `${type}:${cols}`;
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
if (type === 'FOREIGN KEY') {
|
|
402
|
+
const fkMatch = def.match(/FOREIGN\s+KEY\s*\(([^)]+)\)\s*REFERENCES\s+"?(\w+)"?/i);
|
|
403
|
+
if (fkMatch) {
|
|
404
|
+
const srcCols = fkMatch[1].replace(/"/g, '').split(',').map(s => s.trim().toLowerCase()).sort().join(',');
|
|
405
|
+
const targetTable = fkMatch[2].toLowerCase();
|
|
406
|
+
return `FK:${srcCols}→${targetTable}`;
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
return null;
|
|
410
|
+
};
|
|
411
|
+
for (let ri = 0; ri < remote.length; ri++) {
|
|
412
|
+
if (matchedRemote.has(ri))
|
|
413
|
+
continue;
|
|
414
|
+
const rSig = semanticSig(remote[ri]);
|
|
415
|
+
if (!rSig)
|
|
416
|
+
continue;
|
|
417
|
+
for (let li = 0; li < local.length; li++) {
|
|
418
|
+
if (matchedLocal.has(li))
|
|
419
|
+
continue;
|
|
420
|
+
const lSig = semanticSig(local[li]);
|
|
421
|
+
if (lSig && lSig === rSig) {
|
|
422
|
+
matchedLocal.add(li);
|
|
423
|
+
matchedRemote.add(ri);
|
|
424
|
+
break;
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
const unmatchedLocalChecks = local
|
|
429
|
+
.map((c, i) => ({ c, i }))
|
|
430
|
+
.filter(({ c, i }) => !matchedLocal.has(i) && (c.type || '').toUpperCase() === 'CHECK');
|
|
431
|
+
const unmatchedRemoteChecks = remote
|
|
432
|
+
.map((c, i) => ({ c, i }))
|
|
433
|
+
.filter(({ c, i }) => !matchedRemote.has(i) && (c.type || '').toUpperCase() === 'CHECK');
|
|
434
|
+
for (const { c: rc, i: ri } of unmatchedRemoteChecks) {
|
|
435
|
+
if (matchedRemote.has(ri))
|
|
436
|
+
continue;
|
|
437
|
+
if (!rc.name)
|
|
438
|
+
continue;
|
|
439
|
+
for (const { c: lc, i: li } of unmatchedLocalChecks) {
|
|
440
|
+
if (matchedLocal.has(li))
|
|
441
|
+
continue;
|
|
442
|
+
if (lc.name && (lc.name === rc.name ||
|
|
443
|
+
(lc.name.endsWith('_check') && rc.name.endsWith('_check') &&
|
|
444
|
+
lc.name.split('_').slice(-2).join('_') === rc.name.split('_').slice(-2).join('_')))) {
|
|
445
|
+
matchedLocal.add(li);
|
|
446
|
+
matchedRemote.add(ri);
|
|
447
|
+
break;
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
for (let ri = 0; ri < remote.length; ri++) {
|
|
452
|
+
if (!matchedRemote.has(ri)) {
|
|
453
|
+
const c = remote[ri];
|
|
367
454
|
diffs.push({ name: c.name || c.definition || 'unnamed', type: 'added', after: toConstraintInfo(c) });
|
|
368
455
|
}
|
|
369
456
|
}
|
|
370
|
-
for (
|
|
371
|
-
if (!
|
|
457
|
+
for (let li = 0; li < local.length; li++) {
|
|
458
|
+
if (!matchedLocal.has(li)) {
|
|
459
|
+
const c = local[li];
|
|
372
460
|
diffs.push({ name: c.name || c.definition || 'unnamed', type: 'removed', before: toConstraintInfo(c) });
|
|
373
461
|
}
|
|
374
462
|
}
|
|
@@ -542,32 +630,43 @@ function formatCategorizedSummary(diff) {
|
|
|
542
630
|
idxRemoved++;
|
|
543
631
|
}
|
|
544
632
|
for (const con of table.constraints || []) {
|
|
545
|
-
const name = (con.name || '').toLowerCase();
|
|
546
|
-
const isFk = name.includes('fkey') || name.includes('foreign');
|
|
547
|
-
const isUq = name.includes('unique') || name.includes('_key');
|
|
548
|
-
const isPk = name.includes('pkey') || name.includes('primary');
|
|
549
633
|
const before = con.before;
|
|
550
634
|
const after = con.after;
|
|
551
635
|
const conType = (before?.type || after?.type || '').toUpperCase();
|
|
552
|
-
|
|
636
|
+
let category = 'OTHER';
|
|
637
|
+
if (conType) {
|
|
638
|
+
category = conType;
|
|
639
|
+
}
|
|
640
|
+
else {
|
|
641
|
+
const name = (con.name || '').toLowerCase();
|
|
642
|
+
if (name.includes('pkey') || name.includes('primary'))
|
|
643
|
+
category = 'PRIMARY KEY';
|
|
644
|
+
else if (name.includes('fkey') || name.includes('foreign'))
|
|
645
|
+
category = 'FOREIGN KEY';
|
|
646
|
+
else if (name.includes('_check'))
|
|
647
|
+
category = 'CHECK';
|
|
648
|
+
else if (name.includes('_key') || name.includes('unique'))
|
|
649
|
+
category = 'UNIQUE';
|
|
650
|
+
}
|
|
651
|
+
if (category === 'FOREIGN KEY') {
|
|
553
652
|
if (con.type === 'added')
|
|
554
653
|
fkAdded++;
|
|
555
654
|
else if (con.type === 'removed')
|
|
556
655
|
fkRemoved++;
|
|
557
656
|
}
|
|
558
|
-
else if (
|
|
657
|
+
else if (category === 'UNIQUE') {
|
|
559
658
|
if (con.type === 'added')
|
|
560
659
|
uniqueAdded++;
|
|
561
660
|
else if (con.type === 'removed')
|
|
562
661
|
uniqueRemoved++;
|
|
563
662
|
}
|
|
564
|
-
else if (
|
|
663
|
+
else if (category === 'PRIMARY KEY') {
|
|
565
664
|
if (con.type === 'added')
|
|
566
665
|
pkAdded++;
|
|
567
666
|
else if (con.type === 'removed')
|
|
568
667
|
pkRemoved++;
|
|
569
668
|
}
|
|
570
|
-
else if (
|
|
669
|
+
else if (category === 'CHECK') {
|
|
571
670
|
if (con.type === 'added')
|
|
572
671
|
checkAdded++;
|
|
573
672
|
else if (con.type === 'removed')
|
|
@@ -419,6 +419,18 @@ function tableToAST(table) {
|
|
|
419
419
|
col.isUnique = true;
|
|
420
420
|
}
|
|
421
421
|
}
|
|
422
|
+
const hasPKConstraint = constraints.some(c => c.type === 'PRIMARY KEY');
|
|
423
|
+
if (!hasPKConstraint) {
|
|
424
|
+
const pkCols = columns.filter(c => c.isPrimaryKey).map(c => c.name);
|
|
425
|
+
if (pkCols.length > 0) {
|
|
426
|
+
constraints.push({
|
|
427
|
+
name: `${table.$name}_pkey`,
|
|
428
|
+
type: 'PRIMARY KEY',
|
|
429
|
+
columns: pkCols,
|
|
430
|
+
trackingId: `${table.$name}_pkey`,
|
|
431
|
+
});
|
|
432
|
+
}
|
|
433
|
+
}
|
|
422
434
|
return {
|
|
423
435
|
name: table.$name,
|
|
424
436
|
schema: table.$schema || 'public',
|
|
@@ -334,16 +334,6 @@ function diffIndexes(local, remote) {
|
|
|
334
334
|
}
|
|
335
335
|
function diffConstraints(local, remote) {
|
|
336
336
|
const diffs = [];
|
|
337
|
-
const sig = (c) => {
|
|
338
|
-
const def = (c.definition || '').replace(/"/g, '').replace(/\s+/g, ' ').trim().toLowerCase();
|
|
339
|
-
return `${c.type}:${def}`;
|
|
340
|
-
};
|
|
341
|
-
const localSigs = new Map();
|
|
342
|
-
const remoteSigs = new Map();
|
|
343
|
-
for (const c of local)
|
|
344
|
-
localSigs.set(sig(c), c);
|
|
345
|
-
for (const c of remote)
|
|
346
|
-
remoteSigs.set(sig(c), c);
|
|
347
337
|
const toConstraintInfo = (c) => ({
|
|
348
338
|
name: c.name,
|
|
349
339
|
type: (c.type || 'CHECK'),
|
|
@@ -351,13 +341,111 @@ function diffConstraints(local, remote) {
|
|
|
351
341
|
definition: c.definition || '',
|
|
352
342
|
trackingId: c.trackingId,
|
|
353
343
|
});
|
|
354
|
-
|
|
355
|
-
|
|
344
|
+
const matchedLocal = new Set();
|
|
345
|
+
const matchedRemote = new Set();
|
|
346
|
+
for (let ri = 0; ri < remote.length; ri++) {
|
|
347
|
+
if (matchedRemote.has(ri))
|
|
348
|
+
continue;
|
|
349
|
+
const rc = remote[ri];
|
|
350
|
+
if (!rc.trackingId)
|
|
351
|
+
continue;
|
|
352
|
+
for (let li = 0; li < local.length; li++) {
|
|
353
|
+
if (matchedLocal.has(li))
|
|
354
|
+
continue;
|
|
355
|
+
const lc = local[li];
|
|
356
|
+
if (lc.trackingId && lc.trackingId === rc.trackingId) {
|
|
357
|
+
matchedLocal.add(li);
|
|
358
|
+
matchedRemote.add(ri);
|
|
359
|
+
break;
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
for (let ri = 0; ri < remote.length; ri++) {
|
|
364
|
+
if (matchedRemote.has(ri))
|
|
365
|
+
continue;
|
|
366
|
+
const rc = remote[ri];
|
|
367
|
+
if (!rc.name)
|
|
368
|
+
continue;
|
|
369
|
+
for (let li = 0; li < local.length; li++) {
|
|
370
|
+
if (matchedLocal.has(li))
|
|
371
|
+
continue;
|
|
372
|
+
const lc = local[li];
|
|
373
|
+
if (lc.name && lc.name === rc.name) {
|
|
374
|
+
matchedLocal.add(li);
|
|
375
|
+
matchedRemote.add(ri);
|
|
376
|
+
break;
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
const semanticSig = (c) => {
|
|
381
|
+
const def = c.definition || '';
|
|
382
|
+
const type = (c.type || '').toUpperCase();
|
|
383
|
+
if (type === 'PRIMARY KEY' || type === 'UNIQUE') {
|
|
384
|
+
const colMatch = def.match(/\(([^)]+)\)/);
|
|
385
|
+
if (colMatch) {
|
|
386
|
+
const cols = colMatch[1].replace(/"/g, '').split(',').map(s => s.trim().toLowerCase()).sort().join(',');
|
|
387
|
+
return `${type}:${cols}`;
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
if (type === 'FOREIGN KEY') {
|
|
391
|
+
const fkMatch = def.match(/FOREIGN\s+KEY\s*\(([^)]+)\)\s*REFERENCES\s+"?(\w+)"?/i);
|
|
392
|
+
if (fkMatch) {
|
|
393
|
+
const srcCols = fkMatch[1].replace(/"/g, '').split(',').map(s => s.trim().toLowerCase()).sort().join(',');
|
|
394
|
+
const targetTable = fkMatch[2].toLowerCase();
|
|
395
|
+
return `FK:${srcCols}→${targetTable}`;
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
return null;
|
|
399
|
+
};
|
|
400
|
+
for (let ri = 0; ri < remote.length; ri++) {
|
|
401
|
+
if (matchedRemote.has(ri))
|
|
402
|
+
continue;
|
|
403
|
+
const rSig = semanticSig(remote[ri]);
|
|
404
|
+
if (!rSig)
|
|
405
|
+
continue;
|
|
406
|
+
for (let li = 0; li < local.length; li++) {
|
|
407
|
+
if (matchedLocal.has(li))
|
|
408
|
+
continue;
|
|
409
|
+
const lSig = semanticSig(local[li]);
|
|
410
|
+
if (lSig && lSig === rSig) {
|
|
411
|
+
matchedLocal.add(li);
|
|
412
|
+
matchedRemote.add(ri);
|
|
413
|
+
break;
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
const unmatchedLocalChecks = local
|
|
418
|
+
.map((c, i) => ({ c, i }))
|
|
419
|
+
.filter(({ c, i }) => !matchedLocal.has(i) && (c.type || '').toUpperCase() === 'CHECK');
|
|
420
|
+
const unmatchedRemoteChecks = remote
|
|
421
|
+
.map((c, i) => ({ c, i }))
|
|
422
|
+
.filter(({ c, i }) => !matchedRemote.has(i) && (c.type || '').toUpperCase() === 'CHECK');
|
|
423
|
+
for (const { c: rc, i: ri } of unmatchedRemoteChecks) {
|
|
424
|
+
if (matchedRemote.has(ri))
|
|
425
|
+
continue;
|
|
426
|
+
if (!rc.name)
|
|
427
|
+
continue;
|
|
428
|
+
for (const { c: lc, i: li } of unmatchedLocalChecks) {
|
|
429
|
+
if (matchedLocal.has(li))
|
|
430
|
+
continue;
|
|
431
|
+
if (lc.name && (lc.name === rc.name ||
|
|
432
|
+
(lc.name.endsWith('_check') && rc.name.endsWith('_check') &&
|
|
433
|
+
lc.name.split('_').slice(-2).join('_') === rc.name.split('_').slice(-2).join('_')))) {
|
|
434
|
+
matchedLocal.add(li);
|
|
435
|
+
matchedRemote.add(ri);
|
|
436
|
+
break;
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
for (let ri = 0; ri < remote.length; ri++) {
|
|
441
|
+
if (!matchedRemote.has(ri)) {
|
|
442
|
+
const c = remote[ri];
|
|
356
443
|
diffs.push({ name: c.name || c.definition || 'unnamed', type: 'added', after: toConstraintInfo(c) });
|
|
357
444
|
}
|
|
358
445
|
}
|
|
359
|
-
for (
|
|
360
|
-
if (!
|
|
446
|
+
for (let li = 0; li < local.length; li++) {
|
|
447
|
+
if (!matchedLocal.has(li)) {
|
|
448
|
+
const c = local[li];
|
|
361
449
|
diffs.push({ name: c.name || c.definition || 'unnamed', type: 'removed', before: toConstraintInfo(c) });
|
|
362
450
|
}
|
|
363
451
|
}
|
|
@@ -531,32 +619,43 @@ export function formatCategorizedSummary(diff) {
|
|
|
531
619
|
idxRemoved++;
|
|
532
620
|
}
|
|
533
621
|
for (const con of table.constraints || []) {
|
|
534
|
-
const name = (con.name || '').toLowerCase();
|
|
535
|
-
const isFk = name.includes('fkey') || name.includes('foreign');
|
|
536
|
-
const isUq = name.includes('unique') || name.includes('_key');
|
|
537
|
-
const isPk = name.includes('pkey') || name.includes('primary');
|
|
538
622
|
const before = con.before;
|
|
539
623
|
const after = con.after;
|
|
540
624
|
const conType = (before?.type || after?.type || '').toUpperCase();
|
|
541
|
-
|
|
625
|
+
let category = 'OTHER';
|
|
626
|
+
if (conType) {
|
|
627
|
+
category = conType;
|
|
628
|
+
}
|
|
629
|
+
else {
|
|
630
|
+
const name = (con.name || '').toLowerCase();
|
|
631
|
+
if (name.includes('pkey') || name.includes('primary'))
|
|
632
|
+
category = 'PRIMARY KEY';
|
|
633
|
+
else if (name.includes('fkey') || name.includes('foreign'))
|
|
634
|
+
category = 'FOREIGN KEY';
|
|
635
|
+
else if (name.includes('_check'))
|
|
636
|
+
category = 'CHECK';
|
|
637
|
+
else if (name.includes('_key') || name.includes('unique'))
|
|
638
|
+
category = 'UNIQUE';
|
|
639
|
+
}
|
|
640
|
+
if (category === 'FOREIGN KEY') {
|
|
542
641
|
if (con.type === 'added')
|
|
543
642
|
fkAdded++;
|
|
544
643
|
else if (con.type === 'removed')
|
|
545
644
|
fkRemoved++;
|
|
546
645
|
}
|
|
547
|
-
else if (
|
|
646
|
+
else if (category === 'UNIQUE') {
|
|
548
647
|
if (con.type === 'added')
|
|
549
648
|
uniqueAdded++;
|
|
550
649
|
else if (con.type === 'removed')
|
|
551
650
|
uniqueRemoved++;
|
|
552
651
|
}
|
|
553
|
-
else if (
|
|
652
|
+
else if (category === 'PRIMARY KEY') {
|
|
554
653
|
if (con.type === 'added')
|
|
555
654
|
pkAdded++;
|
|
556
655
|
else if (con.type === 'removed')
|
|
557
656
|
pkRemoved++;
|
|
558
657
|
}
|
|
559
|
-
else if (
|
|
658
|
+
else if (category === 'CHECK') {
|
|
560
659
|
if (con.type === 'added')
|
|
561
660
|
checkAdded++;
|
|
562
661
|
else if (con.type === 'removed')
|
|
@@ -385,6 +385,18 @@ export function tableToAST(table) {
|
|
|
385
385
|
col.isUnique = true;
|
|
386
386
|
}
|
|
387
387
|
}
|
|
388
|
+
const hasPKConstraint = constraints.some(c => c.type === 'PRIMARY KEY');
|
|
389
|
+
if (!hasPKConstraint) {
|
|
390
|
+
const pkCols = columns.filter(c => c.isPrimaryKey).map(c => c.name);
|
|
391
|
+
if (pkCols.length > 0) {
|
|
392
|
+
constraints.push({
|
|
393
|
+
name: `${table.$name}_pkey`,
|
|
394
|
+
type: 'PRIMARY KEY',
|
|
395
|
+
columns: pkCols,
|
|
396
|
+
trackingId: `${table.$name}_pkey`,
|
|
397
|
+
});
|
|
398
|
+
}
|
|
399
|
+
}
|
|
388
400
|
return {
|
|
389
401
|
name: table.$name,
|
|
390
402
|
schema: table.$schema || 'public',
|