@restforgejs/platform 4.3.8 → 5.0.1
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/bin/sdf-tools.exe +0 -0
- package/build-info.json +2 -2
- package/cli/consumer-deploy.js +1 -1
- package/cli/consumer.js +1 -1
- package/generators/cli/init.js +4 -104
- package/generators/cli/payload/migrate.js +96 -96
- package/generators/cli/schema/list.js +82 -18
- package/generators/cli/schema/migrate.js +23 -3
- package/generators/lib/dbschema-kit/apply-engine.js +211 -46
- package/generators/lib/dbschema-kit/diff-engine.js +715 -703
- package/generators/lib/dbschema-kit/emitters/alter-table.js +96 -2
- package/generators/lib/dbschema-kit/introspect-mapper.js +9 -0
- package/generators/lib/migrate/backend-payload-migrator.js +221 -221
- package/generators/lib/migrate/field-type-resolver.js +325 -319
- package/generators/lib/migrate/label-generator.js +38 -38
- package/generators/lib/migrate/migrate-runner.js +244 -38
- package/generators/lib/migrate/naming.js +52 -43
- package/generators/lib/migrate/sql-parser.js +124 -124
- package/generators/lib/templates/dashboard-catalog.js +1 -1
- package/generators/lib/templates/db-connection-env.js +1 -1
- package/generators/lib/templates/dbschema-catalog.js +1 -1
- package/generators/lib/templates/field-validation-catalog.js +1 -1
- package/generators/lib/templates/mysql-template.js +1 -1
- package/generators/lib/templates/oracle-template.js +1 -1
- package/generators/lib/templates/postgres-template.js +1 -1
- package/generators/lib/templates/query-declarative-catalog.js +1 -1
- package/generators/lib/templates/sqlite-template.js +1 -1
- package/integrity-manifest.json +18 -18
- package/node_modules/brace-expansion/index.js +1 -1
- package/node_modules/brace-expansion/package.json +1 -1
- package/node_modules/dayjs/CHANGELOG.md +7 -0
- package/node_modules/dayjs/README.md +12 -10
- package/node_modules/dayjs/dayjs.min.js +1 -1
- package/node_modules/dayjs/esm/constant.js +1 -1
- package/node_modules/dayjs/esm/plugin/duration/index.js +5 -4
- package/node_modules/dayjs/locale.json +1 -1
- package/node_modules/dayjs/package.json +2 -2
- package/node_modules/dayjs/plugin/duration.js +1 -1
- package/node_modules/tmp/lib/tmp.js +37 -7
- package/node_modules/tmp/package.json +4 -16
- package/package.json +1 -1
- package/scripts/verify-integrity.js +1 -1
- package/server.js +1 -1
- package/src/components/handlers/adjust_handler.js +1 -1
- package/src/components/handlers/audit_handler.js +1 -1
- package/src/components/handlers/delete_handler.js +1 -1
- package/src/components/handlers/export_handler.js +1 -1
- package/src/components/handlers/import_handler.js +1 -1
- package/src/components/handlers/insert_handler.js +1 -1
- package/src/components/handlers/update_handler.js +1 -1
- package/src/components/handlers/upload_handler.js +1 -1
- package/src/components/handlers/workflow_handler.js +1 -1
- package/src/components/integrations/webhook.js +1 -1
- package/src/consumers/baseConsumer.js +1 -1
- package/src/consumers/declarativeMapper.js +1 -1
- package/src/consumers/handlers/apiHandler.js +1 -1
- package/src/consumers/handlers/consoleHandler.js +1 -1
- package/src/consumers/handlers/databaseHandler.js +1 -1
- package/src/consumers/handlers/index.js +1 -1
- package/src/consumers/handlers/kafkaHandler.js +1 -1
- package/src/consumers/index.js +1 -1
- package/src/consumers/messageTransformer.js +1 -1
- package/src/consumers/validator.js +1 -1
- package/src/core/db/dialect/base-dialect.js +1 -1
- package/src/core/db/dialect/index.js +1 -1
- package/src/core/db/dialect/mysql-dialect.js +1 -1
- package/src/core/db/dialect/oracle-dialect.js +1 -1
- package/src/core/db/dialect/postgres-dialect.js +1 -1
- package/src/core/db/dialect/sqlite-dialect.js +1 -1
- package/src/core/db/flatten-helper.js +1 -1
- package/src/core/db/query-builder-error.js +1 -1
- package/src/core/db/query-builder.js +1 -1
- package/src/core/db/relation-helper.js +1 -1
- package/src/core/handlers/delete_handler.js +1 -1
- package/src/core/handlers/insert_handler.js +1 -1
- package/src/core/handlers/update_handler.js +1 -1
- package/src/core/models/base-model.js +1 -1
- package/src/core/utils/cache-manager.js +1 -1
- package/src/core/utils/component-engine.js +1 -1
- package/src/core/utils/context-builder.js +1 -1
- package/src/core/utils/datetime-formatter.js +1 -1
- package/src/core/utils/datetime-parser.js +1 -1
- package/src/core/utils/db.js +1 -1
- package/src/core/utils/logger.js +1 -1
- package/src/core/utils/payload-loader.js +1 -1
- package/src/core/utils/security-checks.js +1 -1
- package/src/middleware/body-options.js +1 -1
- package/src/middleware/cors.js +1 -1
- package/src/middleware/idempotency.js +1 -1
- package/src/middleware/rate-limiter.js +1 -1
- package/src/middleware/request-logger.js +1 -1
- package/src/middleware/security-headers.js +1 -1
- package/src/models/base-model-mysql.js +1 -1
- package/src/models/base-model-oracle.js +1 -1
- package/src/models/base-model-sqlite.js +1 -1
- package/src/models/base-model.js +1 -1
- package/src/pro/caching/redis-client.js +1 -1
- package/src/pro/caching/redis-helper.js +1 -1
- package/src/pro/consumers/baseConsumer.js +1 -1
- package/src/pro/consumers/declarativeMapper.js +1 -1
- package/src/pro/consumers/handlers/apiHandler.js +1 -1
- package/src/pro/consumers/handlers/consoleHandler.js +1 -1
- package/src/pro/consumers/handlers/databaseHandler.js +1 -1
- package/src/pro/consumers/handlers/index.js +1 -1
- package/src/pro/consumers/handlers/kafkaHandler.js +1 -1
- package/src/pro/consumers/index.js +1 -1
- package/src/pro/consumers/messageTransformer.js +1 -1
- package/src/pro/consumers/validator.js +1 -1
- package/src/pro/database/base-model-mysql.js +1 -1
- package/src/pro/database/base-model-oracle.js +1 -1
- package/src/pro/database/base-model-sqlite.js +1 -1
- package/src/pro/database/db-mysql.js +1 -1
- package/src/pro/database/db-oracle.js +1 -1
- package/src/pro/database/db-sqlite.js +1 -1
- package/src/pro/excel/excel-generator.js +1 -1
- package/src/pro/excel/excel-parser.js +1 -1
- package/src/pro/excel/export-service.js +1 -1
- package/src/pro/excel/export_handler.js +1 -1
- package/src/pro/excel/import-service.js +1 -1
- package/src/pro/excel/import-validator.js +1 -1
- package/src/pro/excel/import_handler.js +1 -1
- package/src/pro/excel/upsert-builder.js +1 -1
- package/src/pro/idgen/idgen-routes.js +1 -1
- package/src/pro/integrations/lookup-resolver.js +1 -1
- package/src/pro/integrations/upload-handler-v2.js +1 -1
- package/src/pro/integrations/upload-handler.js +1 -1
- package/src/pro/integrations/webhook.js +1 -1
- package/src/pro/locking/lock-routes.js +1 -1
- package/src/pro/locking/resource-lock-manager.js +1 -1
- package/src/pro/messaging/kafkaConsumerService.js +1 -1
- package/src/pro/messaging/kafkaService.js +1 -1
- package/src/pro/messaging/messagehubService.js +1 -1
- package/src/pro/messaging/rabbitmqService.js +1 -1
- package/src/pro/scheduler/job-manager.js +1 -1
- package/src/pro/scheduler/job-routes.js +1 -1
- package/src/pro/scheduler/job-validator.js +1 -1
- package/src/pro/storage/base-storage-provider.js +1 -1
- package/src/pro/storage/file-metadata-helper.js +1 -1
- package/src/pro/storage/index.js +1 -1
- package/src/pro/storage/local-storage-provider.js +1 -1
- package/src/pro/storage/s3-storage-provider.js +1 -1
- package/src/pro/storage/upload-cleanup-job.js +1 -1
- package/src/pro/storage/upload-cleanup-scheduler.js +1 -1
- package/src/pro/storage/upload-pending-tracker.js +1 -1
- package/src/pro/websocket/broadcast-helper.js +1 -1
- package/src/pro/websocket/index.js +1 -1
- package/src/pro/websocket/livesync-server.js +1 -1
- package/src/pro/websocket/ws-broadcaster.js +1 -1
- package/src/services/export-service.js +1 -1
- package/src/services/import-service.js +1 -1
- package/src/services/kafkaConsumerService.js +1 -1
- package/src/services/kafkaService.js +1 -1
- package/src/services/messagehubService.js +1 -1
- package/src/services/rabbitmqService.js +1 -1
- package/src/utils/cache-invalidation-registry.js +1 -1
- package/src/utils/cache-manager.js +1 -1
- package/src/utils/component-engine.js +1 -1
- package/src/utils/config-extractor.js +1 -1
- package/src/utils/consumerLogger.js +1 -1
- package/src/utils/context-builder.js +1 -1
- package/src/utils/dashboard-helpers.js +1 -1
- package/src/utils/dateHelper.js +1 -1
- package/src/utils/datetime-formatter.js +1 -1
- package/src/utils/datetime-parser.js +1 -1
- package/src/utils/db-bootstrap.js +1 -1
- package/src/utils/db-mysql.js +1 -1
- package/src/utils/db-oracle.js +1 -1
- package/src/utils/db-sqlite.js +1 -1
- package/src/utils/db.js +1 -1
- package/src/utils/demo-generator.js +1 -1
- package/src/utils/excel-generator.js +1 -1
- package/src/utils/excel-parser.js +1 -1
- package/src/utils/file-watcher.js +1 -1
- package/src/utils/id-generator.js +1 -1
- package/src/utils/idempotency-manager.js +1 -1
- package/src/utils/import-validator.js +1 -1
- package/src/utils/license-client.js +1 -1
- package/src/utils/lock-manager.js +1 -1
- package/src/utils/logger.js +1 -1
- package/src/utils/lookup-resolver.js +1 -1
- package/src/utils/payload-loader.js +1 -1
- package/src/utils/processor-response.js +1 -1
- package/src/utils/rabbitmq.js +1 -1
- package/src/utils/redis-client.js +1 -1
- package/src/utils/redis-helper.js +1 -1
- package/src/utils/request-scope.js +1 -1
- package/src/utils/security-checks.js +1 -1
- package/src/utils/service-resolver.js +1 -1
- package/src/utils/shutdown-coordinator.js +1 -1
- package/src/utils/trusted-keys.js +1 -1
- package/src/utils/upload-handler.js +1 -1
- package/src/utils/upsert-builder.js +1 -1
- package/src/utils/workflow-hook-executor.js +1 -1
|
@@ -5,34 +5,40 @@
|
|
|
5
5
|
* statement incremental. Komplemen `ddl-generator.js` yang menghasilkan full
|
|
6
6
|
* CREATE TABLE.
|
|
7
7
|
*
|
|
8
|
-
* Operasi yang di-handle
|
|
9
|
-
* - ADD COLUMN
|
|
10
|
-
* - DROP COLUMN
|
|
11
|
-
* - MODIFY COLUMN
|
|
12
|
-
* - CREATE INDEX
|
|
13
|
-
* - DROP INDEX
|
|
14
|
-
* - ADD CONSTRAINT UNIQUE
|
|
15
|
-
* - DROP CONSTRAINT UNIQUE
|
|
8
|
+
* Operasi yang di-handle:
|
|
9
|
+
* - ADD COLUMN (additive, default)
|
|
10
|
+
* - DROP COLUMN (destruktif, butuh allowDrop)
|
|
11
|
+
* - MODIFY COLUMN (length / nullable, butuh allowModify)
|
|
12
|
+
* - CREATE INDEX (additive)
|
|
13
|
+
* - DROP INDEX (destruktif, butuh allowDrop)
|
|
14
|
+
* - ADD CONSTRAINT UNIQUE (additive)
|
|
15
|
+
* - DROP CONSTRAINT UNIQUE (destruktif, butuh allowDrop)
|
|
16
|
+
* - ADD FOREIGN KEY (additive, default — kecuali sqlite)
|
|
17
|
+
* - DROP FOREIGN KEY (destruktif, butuh allowDrop — kecuali sqlite)
|
|
18
|
+
* - REPLACE FOREIGN KEY action (destruktif: DROP+ADD, butuh allowModify — kecuali sqlite)
|
|
16
19
|
*
|
|
17
20
|
* Operasi yang TIDAK di-handle (deferred):
|
|
18
21
|
* - ALTER COLUMN type change (butuh data conversion strategy per dialect)
|
|
19
22
|
* - PK changes (butuh rebuild table)
|
|
20
|
-
* - FK changes (butuh cross-model validation + dialect syntax kompleks)
|
|
21
23
|
* - DEFAULT value changes (defer ke fase berikutnya)
|
|
22
24
|
* - CHECK constraint changes
|
|
25
|
+
* - FK changes pada sqlite (butuh rebuild table)
|
|
23
26
|
*
|
|
24
|
-
* SQLite tidak mendukung ALTER COLUMN / DROP COLUMN
|
|
25
|
-
* Untuk dialect ini, MODIFY
|
|
26
|
-
* 'sqlite limitation' walaupun flag opt-in
|
|
27
|
+
* SQLite tidak mendukung ALTER COLUMN / DROP COLUMN / ALTER ADD CONSTRAINT FK
|
|
28
|
+
* tanpa rebuild table. Untuk dialect ini, MODIFY/DROP COLUMN dan semua perubahan
|
|
29
|
+
* FK otomatis di-skip dengan reason 'sqlite limitation' walaupun flag opt-in
|
|
30
|
+
* aktif.
|
|
27
31
|
*
|
|
28
32
|
* Output dipesan dengan urutan aman untuk dependency:
|
|
29
|
-
* 1. ADD COLUMN
|
|
30
|
-
* 2. CREATE INDEX
|
|
31
|
-
* 3. ADD CONSTRAINT UNIQUE
|
|
32
|
-
* 4.
|
|
33
|
-
* 5.
|
|
34
|
-
* 6. DROP
|
|
35
|
-
* 7. DROP
|
|
33
|
+
* 1. ADD COLUMN (no dependencies)
|
|
34
|
+
* 2. CREATE INDEX (depend on column existence)
|
|
35
|
+
* 3. ADD CONSTRAINT UNIQUE (depend on column existence)
|
|
36
|
+
* 4. ADD CONSTRAINT FOREIGN KEY (depend on column existence di kedua sisi)
|
|
37
|
+
* 5. ALTER/MODIFY COLUMN (modify after additive done)
|
|
38
|
+
* 6. DROP CONSTRAINT FK (release FK sebelum drop unique/index/column)
|
|
39
|
+
* 7. DROP CONSTRAINT UNIQUE (before drop column to avoid dependency error)
|
|
40
|
+
* 8. DROP INDEX (before drop column)
|
|
41
|
+
* 9. DROP COLUMN (last)
|
|
36
42
|
*
|
|
37
43
|
* @module lib/dbschema-kit/apply-engine
|
|
38
44
|
*/
|
|
@@ -45,7 +51,9 @@ const {
|
|
|
45
51
|
emitCreateIndex,
|
|
46
52
|
emitDropIndex,
|
|
47
53
|
emitAddUnique,
|
|
48
|
-
emitDropUnique
|
|
54
|
+
emitDropUnique,
|
|
55
|
+
emitAddForeignKey,
|
|
56
|
+
emitDropForeignKey
|
|
49
57
|
} = require('./emitters/alter-table');
|
|
50
58
|
|
|
51
59
|
const VALID_DIALECTS = ['postgres', 'mysql', 'oracle', 'sqlite'];
|
|
@@ -142,7 +150,10 @@ function buildBuckets() {
|
|
|
142
150
|
addColumns: [],
|
|
143
151
|
createIndexes: [],
|
|
144
152
|
addUniques: [],
|
|
153
|
+
addForeignKeys: [],
|
|
145
154
|
modifyColumns: [],
|
|
155
|
+
dropForeignKeys: [],
|
|
156
|
+
replaceForeignKeysAdd: [],
|
|
146
157
|
dropConstraints: [],
|
|
147
158
|
dropIndexes: [],
|
|
148
159
|
dropColumns: []
|
|
@@ -154,7 +165,10 @@ function flattenBuckets(buckets) {
|
|
|
154
165
|
...buckets.addColumns,
|
|
155
166
|
...buckets.createIndexes,
|
|
156
167
|
...buckets.addUniques,
|
|
168
|
+
...buckets.addForeignKeys,
|
|
157
169
|
...buckets.modifyColumns,
|
|
170
|
+
...buckets.dropForeignKeys,
|
|
171
|
+
...buckets.replaceForeignKeysAdd,
|
|
158
172
|
...buckets.dropConstraints,
|
|
159
173
|
...buckets.dropIndexes,
|
|
160
174
|
...buckets.dropColumns
|
|
@@ -412,51 +426,201 @@ function processUniques(delta, tableIR, dialect, options, buckets, skipped, summ
|
|
|
412
426
|
}
|
|
413
427
|
}
|
|
414
428
|
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
const sdf = Array.isArray(delta.primaryKey.sdf) ? delta.primaryKey.sdf : [];
|
|
419
|
-
const db = Array.isArray(delta.primaryKey.db) ? delta.primaryKey.db : [];
|
|
420
|
-
skipped.push({
|
|
421
|
-
table: delta.tableName,
|
|
422
|
-
kind: 'primary-key',
|
|
423
|
-
target: `pk(${sdf.join(',') || '∅'} vs ${db.join(',') || '∅'})`,
|
|
424
|
-
reason: 'deferred',
|
|
425
|
-
description: `Primary key changes for ${delta.tableName} deferred from MVP (requires table rebuild)`
|
|
426
|
-
});
|
|
427
|
-
}
|
|
429
|
+
// ─────────────────────────────────────────────────────────────
|
|
430
|
+
// FK delta processing
|
|
431
|
+
// ─────────────────────────────────────────────────────────────
|
|
428
432
|
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
+
// FK delta entries dari diff-engine memuat triplet (localKey, target, references)
|
|
434
|
+
// plus optional `name` (relName dari relations map) dan `dbName` (untuk mismatched).
|
|
435
|
+
// Apply path hanya menjalankan validator opsional, jadi kita fallback ke localKey
|
|
436
|
+
// untuk derive constraint name bila relName tidak tersedia.
|
|
437
|
+
function pickRelName(fk) {
|
|
438
|
+
if (fk && typeof fk.name === 'string' && fk.name !== '') return fk.name;
|
|
439
|
+
if (fk && typeof fk.localKey === 'string' && fk.localKey !== '') return fk.localKey;
|
|
440
|
+
return null;
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
function fkSkipTarget(fk) {
|
|
444
|
+
const local = fk && fk.localKey ? fk.localKey : '?';
|
|
445
|
+
const target = fk && fk.target ? fk.target : '?';
|
|
446
|
+
const ref = fk && fk.references ? fk.references : '?';
|
|
447
|
+
return `${local} -> ${target}.${ref}`;
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
function processForeignKeys(delta, tableIR, dialect, options, buckets, skipped, summary) {
|
|
451
|
+
if (!delta.foreignKeys) return;
|
|
452
|
+
|
|
453
|
+
const fkOnlySdf = Array.isArray(delta.foreignKeys.onlyInSdf) ? delta.foreignKeys.onlyInSdf : [];
|
|
454
|
+
const fkOnlyDb = Array.isArray(delta.foreignKeys.onlyInDb) ? delta.foreignKeys.onlyInDb : [];
|
|
455
|
+
const fkMismatch = Array.isArray(delta.foreignKeys.mismatched) ? delta.foreignKeys.mismatched : [];
|
|
456
|
+
|
|
457
|
+
// SQLite: semua perubahan FK butuh table rebuild — defer dengan reason akurat
|
|
458
|
+
if (dialect.name === 'sqlite') {
|
|
433
459
|
for (const fk of fkOnlySdf) {
|
|
434
460
|
skipped.push({
|
|
435
461
|
table: delta.tableName,
|
|
436
462
|
kind: 'foreign-key',
|
|
437
|
-
target:
|
|
438
|
-
reason: '
|
|
439
|
-
description: `
|
|
463
|
+
target: fkSkipTarget(fk),
|
|
464
|
+
reason: 'sqlite limitation',
|
|
465
|
+
description: `SQLite does not support ALTER TABLE ADD CONSTRAINT FOREIGN KEY (${delta.tableName}.${fk.localKey})`
|
|
440
466
|
});
|
|
441
467
|
}
|
|
442
468
|
for (const fk of fkOnlyDb) {
|
|
443
469
|
skipped.push({
|
|
444
470
|
table: delta.tableName,
|
|
445
471
|
kind: 'foreign-key',
|
|
446
|
-
target:
|
|
447
|
-
reason: '
|
|
448
|
-
description: `
|
|
472
|
+
target: fkSkipTarget(fk),
|
|
473
|
+
reason: 'sqlite limitation',
|
|
474
|
+
description: `SQLite does not support ALTER TABLE DROP CONSTRAINT FOREIGN KEY (${delta.tableName}.${fk.localKey})`
|
|
449
475
|
});
|
|
450
476
|
}
|
|
451
477
|
for (const fk of fkMismatch) {
|
|
452
478
|
skipped.push({
|
|
453
479
|
table: delta.tableName,
|
|
454
480
|
kind: 'foreign-key',
|
|
455
|
-
target:
|
|
456
|
-
reason: '
|
|
457
|
-
description: `
|
|
481
|
+
target: fkSkipTarget(fk),
|
|
482
|
+
reason: 'sqlite limitation',
|
|
483
|
+
description: `SQLite does not support ALTER TABLE for FK action change (${delta.tableName}.${fk.localKey})`
|
|
484
|
+
});
|
|
485
|
+
}
|
|
486
|
+
return;
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
// FK additive (onlyInSdf) — non-destruktif, emit langsung tanpa flag opt-in
|
|
490
|
+
for (const fk of fkOnlySdf) {
|
|
491
|
+
const relName = pickRelName(fk);
|
|
492
|
+
if (!relName) {
|
|
493
|
+
skipped.push({
|
|
494
|
+
table: delta.tableName,
|
|
495
|
+
kind: 'foreign-key',
|
|
496
|
+
target: fkSkipTarget(fk),
|
|
497
|
+
reason: 'emit-error',
|
|
498
|
+
description: `Foreign key for ${delta.tableName} missing both name and localKey`
|
|
458
499
|
});
|
|
500
|
+
continue;
|
|
459
501
|
}
|
|
502
|
+
try {
|
|
503
|
+
buckets.addForeignKeys.push(emitAddForeignKey(tableIR, relName, fk, dialect));
|
|
504
|
+
summary.totalAdditions++;
|
|
505
|
+
} catch (err) {
|
|
506
|
+
skipped.push({
|
|
507
|
+
table: delta.tableName,
|
|
508
|
+
kind: 'foreign-key',
|
|
509
|
+
target: fkSkipTarget(fk),
|
|
510
|
+
reason: 'emit-error',
|
|
511
|
+
description: err && err.message ? err.message : String(err)
|
|
512
|
+
});
|
|
513
|
+
}
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
// FK drop (onlyInDb) — destruktif, butuh --allow-drop
|
|
517
|
+
for (const fk of fkOnlyDb) {
|
|
518
|
+
const relName = pickRelName(fk);
|
|
519
|
+
if (!relName) {
|
|
520
|
+
skipped.push({
|
|
521
|
+
table: delta.tableName,
|
|
522
|
+
kind: 'foreign-key',
|
|
523
|
+
target: fkSkipTarget(fk),
|
|
524
|
+
reason: 'emit-error',
|
|
525
|
+
description: `Foreign key for ${delta.tableName} missing both name and localKey`
|
|
526
|
+
});
|
|
527
|
+
continue;
|
|
528
|
+
}
|
|
529
|
+
if (!options.allowDrop) {
|
|
530
|
+
skipped.push({
|
|
531
|
+
table: delta.tableName,
|
|
532
|
+
kind: 'foreign-key',
|
|
533
|
+
target: fkSkipTarget(fk),
|
|
534
|
+
reason: 'requires --allow-drop',
|
|
535
|
+
description: `Drop foreign key on ${delta.tableName}.${fk.localKey} requires --allow-drop`
|
|
536
|
+
});
|
|
537
|
+
continue;
|
|
538
|
+
}
|
|
539
|
+
try {
|
|
540
|
+
// DROP memakai conname aktual dari introspeksi (fk.dbConstraintName) agar
|
|
541
|
+
// statement menargetkan constraint yang benar-benar ada di DB. Bila conname
|
|
542
|
+
// tidak tersedia (driver kustom), emitter fallback ke derivasi nama lama
|
|
543
|
+
// (generateConstraintName) — perilaku legacy, tanpa entry skipped/warning.
|
|
544
|
+
buckets.dropForeignKeys.push(
|
|
545
|
+
emitDropForeignKey(tableIR, relName, dialect, { name: fk.dbConstraintName })
|
|
546
|
+
);
|
|
547
|
+
summary.totalDeletions++;
|
|
548
|
+
} catch (err) {
|
|
549
|
+
skipped.push({
|
|
550
|
+
table: delta.tableName,
|
|
551
|
+
kind: 'foreign-key',
|
|
552
|
+
target: fkSkipTarget(fk),
|
|
553
|
+
reason: 'emit-error',
|
|
554
|
+
description: err && err.message ? err.message : String(err)
|
|
555
|
+
});
|
|
556
|
+
}
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
// FK mismatch (action change) — destruktif (DROP + ADD), butuh --allow-modify
|
|
560
|
+
for (const fk of fkMismatch) {
|
|
561
|
+
const relName = pickRelName(fk);
|
|
562
|
+
if (!relName) {
|
|
563
|
+
skipped.push({
|
|
564
|
+
table: delta.tableName,
|
|
565
|
+
kind: 'foreign-key',
|
|
566
|
+
target: fkSkipTarget(fk),
|
|
567
|
+
reason: 'emit-error',
|
|
568
|
+
description: `Foreign key for ${delta.tableName} missing both name and localKey`
|
|
569
|
+
});
|
|
570
|
+
continue;
|
|
571
|
+
}
|
|
572
|
+
if (!options.allowModify) {
|
|
573
|
+
skipped.push({
|
|
574
|
+
table: delta.tableName,
|
|
575
|
+
kind: 'foreign-key',
|
|
576
|
+
target: fkSkipTarget(fk),
|
|
577
|
+
reason: 'requires --allow-modify',
|
|
578
|
+
description: `Foreign key action change on ${delta.tableName}.${fk.localKey} requires --allow-modify`
|
|
579
|
+
});
|
|
580
|
+
continue;
|
|
581
|
+
}
|
|
582
|
+
try {
|
|
583
|
+
const sdfActions = fk.sdf || {};
|
|
584
|
+
const addRel = {
|
|
585
|
+
localKey: fk.localKey,
|
|
586
|
+
target: fk.target,
|
|
587
|
+
references: fk.references,
|
|
588
|
+
onDelete: sdfActions.onDelete,
|
|
589
|
+
onUpdate: sdfActions.onUpdate
|
|
590
|
+
};
|
|
591
|
+
// DROP memakai conname aktual dari DB (fk.dbConstraintName, fallback derive
|
|
592
|
+
// bila absen); ADD memakai nama SDF-derive (relName) konsisten dengan
|
|
593
|
+
// create-table. ADD masuk replaceForeignKeysAdd bucket supaya terbit SETELAH
|
|
594
|
+
// semua DROP FK, mencegah konflik bila nama lama dan baru kebetulan sama.
|
|
595
|
+
buckets.dropForeignKeys.push(
|
|
596
|
+
emitDropForeignKey(tableIR, relName, dialect, { name: fk.dbConstraintName })
|
|
597
|
+
);
|
|
598
|
+
buckets.replaceForeignKeysAdd.push(emitAddForeignKey(tableIR, relName, addRel, dialect));
|
|
599
|
+
summary.totalModifications++;
|
|
600
|
+
} catch (err) {
|
|
601
|
+
skipped.push({
|
|
602
|
+
table: delta.tableName,
|
|
603
|
+
kind: 'foreign-key',
|
|
604
|
+
target: fkSkipTarget(fk),
|
|
605
|
+
reason: 'emit-error',
|
|
606
|
+
description: err && err.message ? err.message : String(err)
|
|
607
|
+
});
|
|
608
|
+
}
|
|
609
|
+
}
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
function noteDeferredSections(delta, skipped) {
|
|
613
|
+
// PK changes — deferred dari MVP karena butuh rebuild table.
|
|
614
|
+
if (delta.primaryKey && delta.primaryKey.match === false) {
|
|
615
|
+
const sdf = Array.isArray(delta.primaryKey.sdf) ? delta.primaryKey.sdf : [];
|
|
616
|
+
const db = Array.isArray(delta.primaryKey.db) ? delta.primaryKey.db : [];
|
|
617
|
+
skipped.push({
|
|
618
|
+
table: delta.tableName,
|
|
619
|
+
kind: 'primary-key',
|
|
620
|
+
target: `pk(${sdf.join(',') || '∅'} vs ${db.join(',') || '∅'})`,
|
|
621
|
+
reason: 'deferred',
|
|
622
|
+
description: `Primary key changes for ${delta.tableName} deferred from MVP (requires table rebuild)`
|
|
623
|
+
});
|
|
460
624
|
}
|
|
461
625
|
|
|
462
626
|
if (delta.checks) {
|
|
@@ -548,6 +712,7 @@ function generateAlterStatements(deltas, options) {
|
|
|
548
712
|
processFieldsMismatched(delta, tableIR, dialect, localOptions, buckets, skipped, sdfModels, summary);
|
|
549
713
|
processIndexes(delta, tableIR, dialect, localOptions, buckets, skipped, summary);
|
|
550
714
|
processUniques(delta, tableIR, dialect, localOptions, buckets, skipped, summary);
|
|
715
|
+
processForeignKeys(delta, tableIR, dialect, localOptions, buckets, skipped, summary);
|
|
551
716
|
noteDeferredSections(delta, skipped);
|
|
552
717
|
|
|
553
718
|
const flat = flattenBuckets(buckets);
|