@prisma-next/family-sql 0.12.0-dev.17 → 0.12.0-dev.2

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.
Files changed (34) hide show
  1. package/dist/control-adapter.d.mts +109 -2
  2. package/dist/control-adapter.d.mts.map +1 -0
  3. package/dist/control.d.mts +2 -2
  4. package/dist/control.mjs +13 -20
  5. package/dist/control.mjs.map +1 -1
  6. package/dist/migration.d.mts +1 -1
  7. package/dist/runtime.d.mts +1 -3
  8. package/dist/runtime.d.mts.map +1 -1
  9. package/dist/runtime.mjs +1 -3
  10. package/dist/runtime.mjs.map +1 -1
  11. package/dist/schema-verify.d.mts +1 -1
  12. package/dist/schema-verify.d.mts.map +1 -1
  13. package/dist/schema-verify.mjs +1 -1
  14. package/dist/{types-BQiqw6kR.d.mts → types-CeeCStqw.d.mts} +5 -14
  15. package/dist/types-CeeCStqw.d.mts.map +1 -0
  16. package/dist/{verify-sql-schema-Cj3c2t5Z.d.mts → verify-sql-schema-CN7pPoTC.d.mts} +2 -8
  17. package/dist/verify-sql-schema-CN7pPoTC.d.mts.map +1 -0
  18. package/dist/{verify-sql-schema-CXW_D9dW.mjs → verify-sql-schema-CYLsGCFO.mjs} +310 -403
  19. package/dist/verify-sql-schema-CYLsGCFO.mjs.map +1 -0
  20. package/package.json +21 -21
  21. package/src/core/control-adapter.ts +3 -44
  22. package/src/core/control-instance.ts +41 -40
  23. package/src/core/migrations/types.ts +1 -8
  24. package/src/core/schema-verify/verify-helpers.ts +110 -151
  25. package/src/core/schema-verify/verify-sql-schema.ts +155 -291
  26. package/src/exports/runtime.ts +0 -7
  27. package/dist/control-adapter-8tV9WgWK.d.mts +0 -134
  28. package/dist/control-adapter-8tV9WgWK.d.mts.map +0 -1
  29. package/dist/types-BQiqw6kR.d.mts.map +0 -1
  30. package/dist/verify-sql-schema-CXW_D9dW.mjs.map +0 -1
  31. package/dist/verify-sql-schema-Cj3c2t5Z.d.mts.map +0 -1
  32. package/src/core/default-namespace.ts +0 -9
  33. package/src/core/schema-verify/control-verify-emit.ts +0 -46
  34. package/src/core/schema-verify/verifier-disposition.ts +0 -53
@@ -6,14 +6,12 @@
6
6
  * by migration planners and other tools that need to compare schema states.
7
7
  */
8
8
 
9
- import type { ColumnDefault, Contract, ControlPolicy } from '@prisma-next/contract/types';
10
- import { effectiveControlPolicy } from '@prisma-next/contract/types';
9
+ import type { ColumnDefault, Contract } from '@prisma-next/contract/types';
11
10
  import type { TargetBoundComponentDescriptor } from '@prisma-next/framework-components/components';
12
11
  import type {
13
12
  OperationContext,
14
13
  SchemaIssue,
15
14
  SchemaVerificationNode,
16
- VerificationStatus,
17
15
  VerifyDatabaseSchemaResult,
18
16
  } from '@prisma-next/framework-components/control';
19
17
  import { UNBOUND_NAMESPACE_ID } from '@prisma-next/framework-components/ir';
@@ -31,8 +29,6 @@ import { canonicalStringify } from '@prisma-next/utils/canonical-stringify';
31
29
  import { ifDefined } from '@prisma-next/utils/defined';
32
30
  import { extractCodecControlHooks } from '../assembly';
33
31
  import type { CodecControlHooks } from '../migrations/types';
34
- import { emitIssueAndNodeUnderControlPolicy } from './control-verify-emit';
35
- import { verifierDisposition } from './verifier-disposition';
36
32
  import {
37
33
  arraysEqual,
38
34
  computeCounts,
@@ -42,8 +38,6 @@ import {
42
38
  verifyUniqueConstraints,
43
39
  } from './verify-helpers';
44
40
 
45
- export type ColumnsCompatible = (declared: string, live: string) => boolean;
46
-
47
41
  /**
48
42
  * Function type for normalizing raw database default expressions into ColumnDefault.
49
43
  * Target-specific implementations handle database dialect differences.
@@ -107,11 +101,6 @@ export interface VerifySqlSchemaOptions {
107
101
  enumType: PostgresEnumStorageEntry,
108
102
  namespaceId: string,
109
103
  ) => readonly string[] | null;
110
- /**
111
- * Target-supplied compatible-shape relation for `external` column type checks.
112
- * Defaults to exact string equality when omitted.
113
- */
114
- readonly columnsCompatible?: ColumnsCompatible;
115
104
  }
116
105
 
117
106
  /**
@@ -134,7 +123,6 @@ export function verifySqlSchema(options: VerifySqlSchemaOptions): VerifyDatabase
134
123
  normalizeDefault,
135
124
  normalizeNativeType,
136
125
  resolveExistingEnumValues,
137
- columnsCompatible,
138
126
  } = options;
139
127
  const startTime = Date.now();
140
128
 
@@ -171,7 +159,6 @@ export function verifySqlSchema(options: VerifySqlSchemaOptions): VerifyDatabase
171
159
  storageTypes,
172
160
  ...ifDefined('normalizeDefault', normalizeDefault),
173
161
  ...ifDefined('normalizeNativeType', normalizeNativeType),
174
- ...ifDefined('columnsCompatible', columnsCompatible),
175
162
  });
176
163
 
177
164
  validateFrameworkComponentsForExtensions(contract, options.frameworkComponents);
@@ -184,39 +171,24 @@ export function verifySqlSchema(options: VerifySqlSchemaOptions): VerifyDatabase
184
171
  // namespace coordinate — a bare-name aggregation would collapse them
185
172
  // (last-write-wins) and verify only one.
186
173
  const typeNodes: SchemaVerificationNode[] = [];
187
- // Storage-type findings dispatch through the same control policy as tables
188
- // and columns: each issue's disposition (fail / warn / suppress) is resolved
189
- // from the type's effective control so an `external`/`observed` enum no longer
190
- // hard-fails on value drift. `suppress` drops the issue entirely; the node
191
- // status is the worst surviving disposition.
192
174
  const pushTypeNode = (
193
175
  typeName: string,
194
176
  contractPath: string,
195
177
  typeIssues: readonly SchemaIssue[],
196
- controlPolicy: ControlPolicy,
197
178
  ): void => {
198
- let status: VerificationStatus = 'pass';
199
- let code = '';
200
- let emitted = 0;
201
- for (const issue of typeIssues) {
202
- const disposition = verifierDisposition(controlPolicy, issue.kind);
203
- if (disposition === 'suppress') continue;
204
- issues.push(issue);
205
- emitted += 1;
206
- if (code === '') code = issue.kind;
207
- if (disposition === 'fail') {
208
- status = 'fail';
209
- } else if (disposition === 'warn' && status !== 'fail') {
210
- status = 'warn';
211
- }
179
+ if (typeIssues.length > 0) {
180
+ issues.push(...typeIssues);
212
181
  }
213
182
  typeNodes.push({
214
- status,
183
+ status: typeIssues.length > 0 ? 'fail' : 'pass',
215
184
  kind: 'storageType',
216
185
  name: `type ${typeName}`,
217
186
  contractPath,
218
- code: status === 'pass' ? '' : code,
219
- message: emitted > 0 ? `${emitted} issue${emitted === 1 ? '' : 's'}` : '',
187
+ code: typeIssues.length > 0 ? (typeIssues[0]?.kind ?? '') : '',
188
+ message:
189
+ typeIssues.length > 0
190
+ ? `${typeIssues.length} issue${typeIssues.length === 1 ? '' : 's'}`
191
+ : '',
220
192
  expected: undefined,
221
193
  actual: undefined,
222
194
  children: [],
@@ -237,7 +209,6 @@ export function verifySqlSchema(options: VerifySqlSchemaOptions): VerifyDatabase
237
209
  resolveExistingEnumValues,
238
210
  namespaceId: UNBOUND_NAMESPACE_ID,
239
211
  }),
240
- effectiveControlPolicy(typeInstance.control, contract.defaultControl),
241
212
  );
242
213
  } else if (isStorageTypeInstance(typeInstance)) {
243
214
  const hook = codecHooks.get(typeInstance.codecId);
@@ -245,7 +216,6 @@ export function verifySqlSchema(options: VerifySqlSchemaOptions): VerifyDatabase
245
216
  typeName,
246
217
  `storage.types.${typeName}`,
247
218
  hook?.verifyType ? hook.verifyType({ typeName, typeInstance, schema }) : [],
248
- effectiveControlPolicy(undefined, contract.defaultControl),
249
219
  );
250
220
  }
251
221
  }
@@ -268,17 +238,12 @@ export function verifySqlSchema(options: VerifySqlSchemaOptions): VerifyDatabase
268
238
  resolveExistingEnumValues,
269
239
  namespaceId: nsId,
270
240
  }),
271
- effectiveControlPolicy(entry.control, contract.defaultControl),
272
241
  );
273
242
  }
274
243
  }
275
244
 
276
245
  if (typeNodes.length > 0) {
277
- const typesStatus: VerificationStatus = typeNodes.some((n) => n.status === 'fail')
278
- ? 'fail'
279
- : typeNodes.some((n) => n.status === 'warn')
280
- ? 'warn'
281
- : 'pass';
246
+ const typesStatus = typeNodes.some((n) => n.status === 'fail') ? 'fail' : 'pass';
282
247
  rootChildren.push({
283
248
  status: typesStatus,
284
249
  kind: 'storageTypes',
@@ -338,6 +303,8 @@ export function verifySqlSchema(options: VerifySqlSchemaOptions): VerifyDatabase
338
303
  };
339
304
  }
340
305
 
306
+ type VerificationStatus = 'pass' | 'warn' | 'fail';
307
+
341
308
  /**
342
309
  * Native verification walk for `PostgresEnumStorageEntry` instances (no codec hook).
343
310
  *
@@ -419,7 +386,6 @@ function verifySchemaTables(options: {
419
386
  storageTypes: Readonly<Record<string, StorageTypeInstance | PostgresEnumStorageEntry>>;
420
387
  normalizeDefault?: DefaultNormalizer;
421
388
  normalizeNativeType?: NativeTypeNormalizer;
422
- columnsCompatible?: ColumnsCompatible;
423
389
  }): { issues: SchemaIssue[]; rootChildren: SchemaVerificationNode[] } {
424
390
  const {
425
391
  contract,
@@ -430,9 +396,7 @@ function verifySchemaTables(options: {
430
396
  storageTypes,
431
397
  normalizeDefault,
432
398
  normalizeNativeType,
433
- columnsCompatible,
434
399
  } = options;
435
- const contractDefaultControl = contract.defaultControl;
436
400
  const issues: SchemaIssue[] = [];
437
401
  const rootChildren: SchemaVerificationNode[] = [];
438
402
  const schemaTables = schema.tables;
@@ -450,37 +414,27 @@ function verifySchemaTables(options: {
450
414
  );
451
415
  }
452
416
  const contractTable = contractTableRaw;
453
- const tableControlPolicy = effectiveControlPolicy(
454
- contractTable.control,
455
- contractDefaultControl,
456
- );
457
417
  const schemaTable = schemaTables[tableName];
458
418
  const tablePath = `storage.namespaces.${namespaceId}.tables.${tableName}`;
459
419
 
460
420
  if (!schemaTable) {
461
- const issue: SchemaIssue = {
421
+ issues.push({
462
422
  kind: 'missing_table',
463
423
  table: tableName,
464
424
  namespaceId,
465
425
  message: `Table "${tableName}" is missing from database`,
466
- };
467
- emitIssueAndNodeUnderControlPolicy(
468
- tableControlPolicy,
469
- issue,
470
- {
471
- status: 'fail',
472
- kind: 'table',
473
- name: `table ${tableName}`,
474
- contractPath: tablePath,
475
- code: 'missing_table',
476
- message: `Table "${tableName}" is missing`,
477
- expected: undefined,
478
- actual: undefined,
479
- children: [],
480
- },
481
- issues,
482
- rootChildren,
483
- );
426
+ });
427
+ rootChildren.push({
428
+ status: 'fail',
429
+ kind: 'table',
430
+ name: `table ${tableName}`,
431
+ contractPath: tablePath,
432
+ code: 'missing_table',
433
+ message: `Table "${tableName}" is missing`,
434
+ expected: undefined,
435
+ actual: undefined,
436
+ children: [],
437
+ });
484
438
  continue;
485
439
  }
486
440
 
@@ -490,7 +444,6 @@ function verifySchemaTables(options: {
490
444
  tableName,
491
445
  namespaceId,
492
446
  tablePath,
493
- tableControlPolicy,
494
447
  issues,
495
448
  strict,
496
449
  typeMetadataRegistry,
@@ -498,7 +451,6 @@ function verifySchemaTables(options: {
498
451
  storageTypes,
499
452
  ...ifDefined('normalizeDefault', normalizeDefault),
500
453
  ...ifDefined('normalizeNativeType', normalizeNativeType),
501
- ...ifDefined('columnsCompatible', columnsCompatible),
502
454
  });
503
455
  rootChildren.push(buildTableNode(tableName, tablePath, tableChildren));
504
456
  }
@@ -510,29 +462,26 @@ function verifySchemaTables(options: {
510
462
  (namespaceId) => contract.storage.namespaces[namespaceId]?.tables[tableName] !== undefined,
511
463
  );
512
464
  if (!claimed) {
513
- const extraTableControlPolicy = effectiveControlPolicy(undefined, contractDefaultControl);
514
- const issue: SchemaIssue = {
465
+ // `namespaceId` is intentionally absent: an extra table exists in the
466
+ // live database but is not claimed by any contract namespace, so there
467
+ // is no contract coordinate to stamp here. Planners that consume this
468
+ // issue must handle the unstamped case (drop / quarantine by name).
469
+ issues.push({
515
470
  kind: 'extra_table',
516
471
  table: tableName,
517
472
  message: `Extra table "${tableName}" found in database (not in contract)`,
518
- };
519
- emitIssueAndNodeUnderControlPolicy(
520
- extraTableControlPolicy,
521
- issue,
522
- {
523
- status: 'fail',
524
- kind: 'table',
525
- name: `table ${tableName}`,
526
- contractPath: `storage.namespaces.*.tables.${tableName}`,
527
- code: 'extra_table',
528
- message: `Extra table "${tableName}" found`,
529
- expected: undefined,
530
- actual: undefined,
531
- children: [],
532
- },
533
- issues,
534
- rootChildren,
535
- );
473
+ });
474
+ rootChildren.push({
475
+ status: 'fail',
476
+ kind: 'table',
477
+ name: `table ${tableName}`,
478
+ contractPath: `storage.namespaces.*.tables.${tableName}`,
479
+ code: 'extra_table',
480
+ message: `Extra table "${tableName}" found`,
481
+ expected: undefined,
482
+ actual: undefined,
483
+ children: [],
484
+ });
536
485
  }
537
486
  }
538
487
  }
@@ -546,7 +495,6 @@ function verifyTableChildren(options: {
546
495
  tableName: string;
547
496
  namespaceId: string;
548
497
  tablePath: string;
549
- tableControlPolicy: ControlPolicy;
550
498
  issues: SchemaIssue[];
551
499
  strict: boolean;
552
500
  typeMetadataRegistry: ReadonlyMap<string, { nativeType?: string }>;
@@ -554,7 +502,6 @@ function verifyTableChildren(options: {
554
502
  storageTypes: Readonly<Record<string, StorageTypeInstance | PostgresEnumStorageEntry>>;
555
503
  normalizeDefault?: DefaultNormalizer;
556
504
  normalizeNativeType?: NativeTypeNormalizer;
557
- columnsCompatible?: ColumnsCompatible;
558
505
  }): SchemaVerificationNode[] {
559
506
  const {
560
507
  contractTable,
@@ -562,7 +509,6 @@ function verifyTableChildren(options: {
562
509
  tableName,
563
510
  namespaceId,
564
511
  tablePath,
565
- tableControlPolicy,
566
512
  issues,
567
513
  strict,
568
514
  typeMetadataRegistry,
@@ -570,7 +516,6 @@ function verifyTableChildren(options: {
570
516
  storageTypes,
571
517
  normalizeDefault,
572
518
  normalizeNativeType,
573
- columnsCompatible,
574
519
  } = options;
575
520
  const tableChildren: SchemaVerificationNode[] = [];
576
521
  const columnNodes = collectContractColumnNodes({
@@ -579,7 +524,6 @@ function verifyTableChildren(options: {
579
524
  tableName,
580
525
  namespaceId,
581
526
  tablePath,
582
- tableControlPolicy,
583
527
  issues,
584
528
  strict,
585
529
  typeMetadataRegistry,
@@ -587,7 +531,6 @@ function verifyTableChildren(options: {
587
531
  storageTypes,
588
532
  ...ifDefined('normalizeDefault', normalizeDefault),
589
533
  ...ifDefined('normalizeNativeType', normalizeNativeType),
590
- ...ifDefined('columnsCompatible', columnsCompatible),
591
534
  });
592
535
  if (columnNodes.length > 0) {
593
536
  tableChildren.push(buildColumnsNode(tablePath, columnNodes));
@@ -599,7 +542,6 @@ function verifyTableChildren(options: {
599
542
  tableName,
600
543
  namespaceId,
601
544
  tablePath,
602
- tableControlPolicy,
603
545
  issues,
604
546
  columnNodes,
605
547
  });
@@ -611,7 +553,6 @@ function verifyTableChildren(options: {
611
553
  schemaTable.primaryKey,
612
554
  tableName,
613
555
  namespaceId,
614
- tableControlPolicy,
615
556
  issues,
616
557
  );
617
558
  if (pkStatus === 'fail') {
@@ -626,18 +567,6 @@ function verifyTableChildren(options: {
626
567
  actual: schemaTable.primaryKey,
627
568
  children: [],
628
569
  });
629
- } else if (pkStatus === 'warn') {
630
- tableChildren.push({
631
- status: 'warn',
632
- kind: 'primaryKey',
633
- name: `primary key: ${contractTable.primaryKey.columns.join(', ')}`,
634
- contractPath: `${tablePath}.primaryKey`,
635
- code: 'primary_key_mismatch',
636
- message: 'Primary key mismatch',
637
- expected: contractTable.primaryKey,
638
- actual: schemaTable.primaryKey,
639
- children: [],
640
- });
641
570
  } else {
642
571
  tableChildren.push({
643
572
  status: 'pass',
@@ -652,29 +581,23 @@ function verifyTableChildren(options: {
652
581
  });
653
582
  }
654
583
  } else if (schemaTable.primaryKey && strict) {
655
- const issue: SchemaIssue = {
584
+ issues.push({
656
585
  kind: 'extra_primary_key',
657
586
  table: tableName,
658
587
  namespaceId,
659
588
  message: 'Extra primary key found in database (not in contract)',
660
- };
661
- emitIssueAndNodeUnderControlPolicy(
662
- tableControlPolicy,
663
- issue,
664
- {
665
- status: 'fail',
666
- kind: 'primaryKey',
667
- name: `primary key: ${schemaTable.primaryKey.columns.join(', ')}`,
668
- contractPath: `${tablePath}.primaryKey`,
669
- code: 'extra_primary_key',
670
- message: 'Extra primary key found',
671
- expected: undefined,
672
- actual: schemaTable.primaryKey,
673
- children: [],
674
- },
675
- issues,
676
- tableChildren,
677
- );
589
+ });
590
+ tableChildren.push({
591
+ status: 'fail',
592
+ kind: 'primaryKey',
593
+ name: `primary key: ${schemaTable.primaryKey.columns.join(', ')}`,
594
+ contractPath: `${tablePath}.primaryKey`,
595
+ code: 'extra_primary_key',
596
+ message: 'Extra primary key found',
597
+ expected: undefined,
598
+ actual: schemaTable.primaryKey,
599
+ children: [],
600
+ });
678
601
  }
679
602
 
680
603
  // Verify FK constraints only for FKs with constraint: true.
@@ -688,7 +611,6 @@ function verifyTableChildren(options: {
688
611
  tableName,
689
612
  namespaceId,
690
613
  tablePath,
691
- tableControlPolicy,
692
614
  issues,
693
615
  strict,
694
616
  );
@@ -702,7 +624,6 @@ function verifyTableChildren(options: {
702
624
  tableName,
703
625
  namespaceId,
704
626
  tablePath,
705
- tableControlPolicy,
706
627
  issues,
707
628
  strict,
708
629
  );
@@ -727,7 +648,6 @@ function verifyTableChildren(options: {
727
648
  tableName,
728
649
  namespaceId,
729
650
  tablePath,
730
- tableControlPolicy,
731
651
  issues,
732
652
  strict,
733
653
  );
@@ -742,7 +662,6 @@ function collectContractColumnNodes(options: {
742
662
  tableName: string;
743
663
  namespaceId: string;
744
664
  tablePath: string;
745
- tableControlPolicy: ControlPolicy;
746
665
  issues: SchemaIssue[];
747
666
  strict: boolean;
748
667
  typeMetadataRegistry: ReadonlyMap<string, { nativeType?: string }>;
@@ -750,7 +669,6 @@ function collectContractColumnNodes(options: {
750
669
  storageTypes: Readonly<Record<string, StorageTypeInstance | PostgresEnumStorageEntry>>;
751
670
  normalizeDefault?: DefaultNormalizer;
752
671
  normalizeNativeType?: NativeTypeNormalizer;
753
- columnsCompatible?: ColumnsCompatible;
754
672
  }): SchemaVerificationNode[] {
755
673
  const {
756
674
  contractTable,
@@ -758,7 +676,6 @@ function collectContractColumnNodes(options: {
758
676
  tableName,
759
677
  namespaceId,
760
678
  tablePath,
761
- tableControlPolicy,
762
679
  issues,
763
680
  strict,
764
681
  typeMetadataRegistry,
@@ -766,7 +683,6 @@ function collectContractColumnNodes(options: {
766
683
  storageTypes,
767
684
  normalizeDefault,
768
685
  normalizeNativeType,
769
- columnsCompatible,
770
686
  } = options;
771
687
  const columnNodes: SchemaVerificationNode[] = [];
772
688
 
@@ -775,30 +691,24 @@ function collectContractColumnNodes(options: {
775
691
  const columnPath = `${tablePath}.columns.${columnName}`;
776
692
 
777
693
  if (!schemaColumn) {
778
- const issue: SchemaIssue = {
694
+ issues.push({
779
695
  kind: 'missing_column',
780
696
  table: tableName,
781
697
  namespaceId,
782
698
  column: columnName,
783
699
  message: `Column "${tableName}"."${columnName}" is missing from database`,
784
- };
785
- emitIssueAndNodeUnderControlPolicy(
786
- tableControlPolicy,
787
- issue,
788
- {
789
- status: 'fail',
790
- kind: 'column',
791
- name: `${columnName}: missing`,
792
- contractPath: columnPath,
793
- code: 'missing_column',
794
- message: `Column "${columnName}" is missing`,
795
- expected: undefined,
796
- actual: undefined,
797
- children: [],
798
- },
799
- issues,
800
- columnNodes,
801
- );
700
+ });
701
+ columnNodes.push({
702
+ status: 'fail',
703
+ kind: 'column',
704
+ name: `${columnName}: missing`,
705
+ contractPath: columnPath,
706
+ code: 'missing_column',
707
+ message: `Column "${columnName}" is missing`,
708
+ expected: undefined,
709
+ actual: undefined,
710
+ children: [],
711
+ });
802
712
  continue;
803
713
  }
804
714
 
@@ -810,7 +720,6 @@ function collectContractColumnNodes(options: {
810
720
  contractColumn,
811
721
  schemaColumn,
812
722
  columnPath,
813
- tableControlPolicy,
814
723
  issues,
815
724
  strict,
816
725
  typeMetadataRegistry,
@@ -818,7 +727,6 @@ function collectContractColumnNodes(options: {
818
727
  storageTypes,
819
728
  ...ifDefined('normalizeDefault', normalizeDefault),
820
729
  ...ifDefined('normalizeNativeType', normalizeNativeType),
821
- ...ifDefined('columnsCompatible', columnsCompatible),
822
730
  }),
823
731
  );
824
732
  }
@@ -832,46 +740,31 @@ function appendExtraColumnNodes(options: {
832
740
  tableName: string;
833
741
  namespaceId: string;
834
742
  tablePath: string;
835
- tableControlPolicy: ControlPolicy;
836
743
  issues: SchemaIssue[];
837
744
  columnNodes: SchemaVerificationNode[];
838
745
  }): void {
839
- const {
840
- contractTable,
841
- schemaTable,
842
- tableName,
843
- namespaceId,
844
- tablePath,
845
- tableControlPolicy,
846
- issues,
847
- columnNodes,
848
- } = options;
746
+ const { contractTable, schemaTable, tableName, namespaceId, tablePath, issues, columnNodes } =
747
+ options;
849
748
  for (const [columnName, { nativeType }] of Object.entries(schemaTable.columns)) {
850
749
  if (!contractTable.columns[columnName]) {
851
- const issue: SchemaIssue = {
750
+ issues.push({
852
751
  kind: 'extra_column',
853
752
  table: tableName,
854
753
  namespaceId,
855
754
  column: columnName,
856
755
  message: `Extra column "${tableName}"."${columnName}" found in database (not in contract)`,
857
- };
858
- emitIssueAndNodeUnderControlPolicy(
859
- tableControlPolicy,
860
- issue,
861
- {
862
- status: 'fail',
863
- kind: 'column',
864
- name: `${columnName}: extra`,
865
- contractPath: `${tablePath}.columns.${columnName}`,
866
- code: 'extra_column',
867
- message: `Extra column "${columnName}" found`,
868
- expected: undefined,
869
- actual: nativeType,
870
- children: [],
871
- },
872
- issues,
873
- columnNodes,
874
- );
756
+ });
757
+ columnNodes.push({
758
+ status: 'fail',
759
+ kind: 'column',
760
+ name: `${columnName}: extra`,
761
+ contractPath: `${tablePath}.columns.${columnName}`,
762
+ code: 'extra_column',
763
+ message: `Extra column "${columnName}" found`,
764
+ expected: undefined,
765
+ actual: nativeType,
766
+ children: [],
767
+ });
875
768
  }
876
769
  }
877
770
  }
@@ -883,7 +776,6 @@ function verifyColumn(options: {
883
776
  contractColumn: StorageTable['columns'][string];
884
777
  schemaColumn: SqlSchemaIR['tables'][string]['columns'][string];
885
778
  columnPath: string;
886
- tableControlPolicy: ControlPolicy;
887
779
  issues: SchemaIssue[];
888
780
  strict: boolean;
889
781
  typeMetadataRegistry: ReadonlyMap<string, { nativeType?: string }>;
@@ -891,7 +783,6 @@ function verifyColumn(options: {
891
783
  storageTypes: Readonly<Record<string, StorageTypeInstance | PostgresEnumStorageEntry>>;
892
784
  normalizeDefault?: DefaultNormalizer;
893
785
  normalizeNativeType?: NativeTypeNormalizer;
894
- columnsCompatible?: ColumnsCompatible;
895
786
  }): SchemaVerificationNode {
896
787
  const {
897
788
  tableName,
@@ -900,14 +791,12 @@ function verifyColumn(options: {
900
791
  contractColumn,
901
792
  schemaColumn,
902
793
  columnPath,
903
- tableControlPolicy,
904
794
  issues,
905
795
  strict,
906
796
  codecHooks,
907
797
  storageTypes,
908
798
  normalizeDefault,
909
799
  normalizeNativeType,
910
- columnsCompatible,
911
800
  } = options;
912
801
  const columnChildren: SchemaVerificationNode[] = [];
913
802
  let columnStatus: VerificationStatus = 'pass';
@@ -923,14 +812,8 @@ function verifyColumn(options: {
923
812
  const schemaNativeType =
924
813
  normalizeNativeType?.(schemaColumn.nativeType) ?? schemaColumn.nativeType;
925
814
 
926
- const typesMatch =
927
- contractNativeType === schemaNativeType ||
928
- (tableControlPolicy === 'external' &&
929
- (columnsCompatible?.(contractNativeType, schemaNativeType) ??
930
- contractNativeType === schemaNativeType));
931
-
932
- if (!typesMatch) {
933
- const issue: SchemaIssue = {
815
+ if (contractNativeType !== schemaNativeType) {
816
+ issues.push({
934
817
  kind: 'type_mismatch',
935
818
  table: tableName,
936
819
  namespaceId,
@@ -938,23 +821,19 @@ function verifyColumn(options: {
938
821
  expected: contractNativeType,
939
822
  actual: schemaNativeType,
940
823
  message: `Column "${tableName}"."${columnName}" has type mismatch: expected "${contractNativeType}", got "${schemaNativeType}"`,
941
- };
942
- const disposition = verifierDisposition(tableControlPolicy, issue.kind);
943
- if (disposition !== 'suppress') {
944
- issues.push(issue);
945
- columnChildren.push({
946
- status: disposition,
947
- kind: 'type',
948
- name: 'type',
949
- contractPath: `${columnPath}.nativeType`,
950
- code: 'type_mismatch',
951
- message: `Type mismatch: expected ${contractNativeType}, got ${schemaNativeType}`,
952
- expected: contractNativeType,
953
- actual: schemaNativeType,
954
- children: [],
955
- });
956
- columnStatus = disposition;
957
- }
824
+ });
825
+ columnChildren.push({
826
+ status: 'fail',
827
+ kind: 'type',
828
+ name: 'type',
829
+ contractPath: `${columnPath}.nativeType`,
830
+ code: 'type_mismatch',
831
+ message: `Type mismatch: expected ${contractNativeType}, got ${schemaNativeType}`,
832
+ expected: contractNativeType,
833
+ actual: schemaNativeType,
834
+ children: [],
835
+ });
836
+ columnStatus = 'fail';
958
837
  }
959
838
 
960
839
  if (resolvedContractColumn.codecId) {
@@ -990,7 +869,7 @@ function verifyColumn(options: {
990
869
  }
991
870
 
992
871
  if (contractColumn.nullable !== schemaColumn.nullable) {
993
- const issue: SchemaIssue = {
872
+ issues.push({
994
873
  kind: 'nullability_mismatch',
995
874
  table: tableName,
996
875
  namespaceId,
@@ -998,52 +877,44 @@ function verifyColumn(options: {
998
877
  expected: String(contractColumn.nullable),
999
878
  actual: String(schemaColumn.nullable),
1000
879
  message: `Column "${tableName}"."${columnName}" has nullability mismatch: expected ${contractColumn.nullable ? 'nullable' : 'not null'}, got ${schemaColumn.nullable ? 'nullable' : 'not null'}`,
1001
- };
1002
- const disposition = verifierDisposition(tableControlPolicy, issue.kind);
1003
- if (disposition !== 'suppress') {
1004
- issues.push(issue);
1005
- columnChildren.push({
1006
- status: disposition,
1007
- kind: 'nullability',
1008
- name: 'nullability',
1009
- contractPath: `${columnPath}.nullable`,
1010
- code: 'nullability_mismatch',
1011
- message: `Nullability mismatch: expected ${contractColumn.nullable ? 'nullable' : 'not null'}, got ${schemaColumn.nullable ? 'nullable' : 'not null'}`,
1012
- expected: contractColumn.nullable,
1013
- actual: schemaColumn.nullable,
1014
- children: [],
1015
- });
1016
- columnStatus = disposition;
1017
- }
880
+ });
881
+ columnChildren.push({
882
+ status: 'fail',
883
+ kind: 'nullability',
884
+ name: 'nullability',
885
+ contractPath: `${columnPath}.nullable`,
886
+ code: 'nullability_mismatch',
887
+ message: `Nullability mismatch: expected ${contractColumn.nullable ? 'nullable' : 'not null'}, got ${schemaColumn.nullable ? 'nullable' : 'not null'}`,
888
+ expected: contractColumn.nullable,
889
+ actual: schemaColumn.nullable,
890
+ children: [],
891
+ });
892
+ columnStatus = 'fail';
1018
893
  }
1019
894
 
1020
895
  if (contractColumn.default) {
1021
896
  if (!schemaColumn.default) {
1022
897
  const defaultDescription = describeColumnDefault(contractColumn.default);
1023
- const issue: SchemaIssue = {
898
+ issues.push({
1024
899
  kind: 'default_missing',
1025
900
  table: tableName,
1026
901
  namespaceId,
1027
902
  column: columnName,
1028
903
  expected: defaultDescription,
1029
904
  message: `Column "${tableName}"."${columnName}" should have default ${defaultDescription} but database has no default`,
1030
- };
1031
- const disposition = verifierDisposition(tableControlPolicy, issue.kind);
1032
- if (disposition !== 'suppress') {
1033
- issues.push(issue);
1034
- columnChildren.push({
1035
- status: disposition,
1036
- kind: 'default',
1037
- name: 'default',
1038
- contractPath: `${columnPath}.default`,
1039
- code: 'default_missing',
1040
- message: `Default missing: expected ${defaultDescription}`,
1041
- expected: defaultDescription,
1042
- actual: undefined,
1043
- children: [],
1044
- });
1045
- columnStatus = disposition;
1046
- }
905
+ });
906
+ columnChildren.push({
907
+ status: 'fail',
908
+ kind: 'default',
909
+ name: 'default',
910
+ contractPath: `${columnPath}.default`,
911
+ code: 'default_missing',
912
+ message: `Default missing: expected ${defaultDescription}`,
913
+ expected: defaultDescription,
914
+ actual: undefined,
915
+ children: [],
916
+ });
917
+ columnStatus = 'fail';
1047
918
  } else if (
1048
919
  !columnDefaultsEqual(
1049
920
  contractColumn.default,
@@ -1053,8 +924,9 @@ function verifyColumn(options: {
1053
924
  )
1054
925
  ) {
1055
926
  const expectedDescription = describeColumnDefault(contractColumn.default);
927
+ // schemaColumn.default is now a raw string, describe it as-is
1056
928
  const actualDescription = schemaColumn.default;
1057
- const issue: SchemaIssue = {
929
+ issues.push({
1058
930
  kind: 'default_mismatch',
1059
931
  table: tableName,
1060
932
  namespaceId,
@@ -1062,49 +934,41 @@ function verifyColumn(options: {
1062
934
  expected: expectedDescription,
1063
935
  actual: actualDescription,
1064
936
  message: `Column "${tableName}"."${columnName}" has default mismatch: expected ${expectedDescription}, got ${actualDescription}`,
1065
- };
1066
- const disposition = verifierDisposition(tableControlPolicy, issue.kind);
1067
- if (disposition !== 'suppress') {
1068
- issues.push(issue);
1069
- columnChildren.push({
1070
- status: disposition,
1071
- kind: 'default',
1072
- name: 'default',
1073
- contractPath: `${columnPath}.default`,
1074
- code: 'default_mismatch',
1075
- message: `Default mismatch: expected ${expectedDescription}, got ${actualDescription}`,
1076
- expected: expectedDescription,
1077
- actual: actualDescription,
1078
- children: [],
1079
- });
1080
- columnStatus = disposition;
1081
- }
937
+ });
938
+ columnChildren.push({
939
+ status: 'fail',
940
+ kind: 'default',
941
+ name: 'default',
942
+ contractPath: `${columnPath}.default`,
943
+ code: 'default_mismatch',
944
+ message: `Default mismatch: expected ${expectedDescription}, got ${actualDescription}`,
945
+ expected: expectedDescription,
946
+ actual: actualDescription,
947
+ children: [],
948
+ });
949
+ columnStatus = 'fail';
1082
950
  }
1083
951
  } else if (strict && schemaColumn.default) {
1084
- const issue: SchemaIssue = {
952
+ issues.push({
1085
953
  kind: 'extra_default',
1086
954
  table: tableName,
1087
955
  namespaceId,
1088
956
  column: columnName,
1089
957
  actual: schemaColumn.default,
1090
958
  message: `Column "${tableName}"."${columnName}" has default ${schemaColumn.default} in database but contract specifies no default`,
1091
- };
1092
- const disposition = verifierDisposition(tableControlPolicy, issue.kind);
1093
- if (disposition !== 'suppress') {
1094
- issues.push(issue);
1095
- columnChildren.push({
1096
- status: disposition,
1097
- kind: 'default',
1098
- name: 'default',
1099
- contractPath: `${columnPath}.default`,
1100
- code: 'extra_default',
1101
- message: `Extra default: ${schemaColumn.default}`,
1102
- expected: undefined,
1103
- actual: schemaColumn.default,
1104
- children: [],
1105
- });
1106
- columnStatus = disposition;
1107
- }
959
+ });
960
+ columnChildren.push({
961
+ status: 'fail',
962
+ kind: 'default',
963
+ name: 'default',
964
+ contractPath: `${columnPath}.default`,
965
+ code: 'extra_default',
966
+ message: `Extra default: ${schemaColumn.default}`,
967
+ expected: undefined,
968
+ actual: schemaColumn.default,
969
+ children: [],
970
+ });
971
+ columnStatus = 'fail';
1108
972
  }
1109
973
 
1110
974
  // Single-pass aggregation for better performance