@prisma-next/family-sql 0.12.0-dev.4 → 0.12.0-dev.41

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 (47) hide show
  1. package/dist/control-adapter-uYnmu04p.d.mts +181 -0
  2. package/dist/control-adapter-uYnmu04p.d.mts.map +1 -0
  3. package/dist/control-adapter.d.mts +2 -109
  4. package/dist/control.d.mts +118 -4
  5. package/dist/control.d.mts.map +1 -1
  6. package/dist/control.mjs +226 -40
  7. package/dist/control.mjs.map +1 -1
  8. package/dist/ir.d.mts +2 -2
  9. package/dist/ir.d.mts.map +1 -1
  10. package/dist/ir.mjs +1 -1
  11. package/dist/migration.d.mts +1 -1
  12. package/dist/runtime.d.mts +4 -2
  13. package/dist/runtime.d.mts.map +1 -1
  14. package/dist/runtime.mjs +3 -1
  15. package/dist/runtime.mjs.map +1 -1
  16. package/dist/schema-verify.d.mts +2 -1
  17. package/dist/schema-verify.d.mts.map +1 -1
  18. package/dist/schema-verify.mjs +1 -1
  19. package/dist/{sql-contract-serializer-8axtK4lg.mjs → sql-contract-serializer-nNw1yk9P.mjs} +14 -35
  20. package/dist/sql-contract-serializer-nNw1yk9P.mjs.map +1 -0
  21. package/dist/{types-CeeCStqw.d.mts → types-DxNgxFkF.d.mts} +61 -7
  22. package/dist/types-DxNgxFkF.d.mts.map +1 -0
  23. package/dist/{verify-sql-schema-CN7pPoTC.d.mts → verify-sql-schema-Cj3c2t5Z.d.mts} +8 -2
  24. package/dist/verify-sql-schema-Cj3c2t5Z.d.mts.map +1 -0
  25. package/dist/{verify-sql-schema-CYLsGCFO.mjs → verify-sql-schema-DV_d2XCG.mjs} +410 -323
  26. package/dist/verify-sql-schema-DV_d2XCG.mjs.map +1 -0
  27. package/package.json +21 -21
  28. package/src/core/control-adapter.ts +112 -3
  29. package/src/core/control-instance.ts +143 -56
  30. package/src/core/default-namespace.ts +9 -0
  31. package/src/core/ir/sql-contract-serializer-base.ts +42 -57
  32. package/src/core/migrations/contract-to-schema-ir.ts +12 -8
  33. package/src/core/migrations/control-policy.ts +322 -0
  34. package/src/core/migrations/field-event-planner.ts +2 -2
  35. package/src/core/migrations/plan-helpers.ts +16 -0
  36. package/src/core/migrations/types.ts +12 -2
  37. package/src/core/schema-verify/control-verify-emit.ts +46 -0
  38. package/src/core/schema-verify/verifier-disposition.ts +53 -0
  39. package/src/core/schema-verify/verify-helpers.ts +151 -110
  40. package/src/core/schema-verify/verify-sql-schema.ts +305 -178
  41. package/src/exports/control.ts +6 -0
  42. package/src/exports/runtime.ts +7 -0
  43. package/dist/control-adapter.d.mts.map +0 -1
  44. package/dist/sql-contract-serializer-8axtK4lg.mjs.map +0 -1
  45. package/dist/types-CeeCStqw.d.mts.map +0 -1
  46. package/dist/verify-sql-schema-CN7pPoTC.d.mts.map +0 -1
  47. package/dist/verify-sql-schema-CYLsGCFO.mjs.map +0 -1
@@ -1,7 +1,9 @@
1
- import { assertUniqueCodecOwner } from "@prisma-next/framework-components/control";
1
+ import { assertUniqueCodecOwner, dispositionForCategory } from "@prisma-next/framework-components/control";
2
2
  import { ifDefined } from "@prisma-next/utils/defined";
3
3
  import { UNBOUND_NAMESPACE_ID } from "@prisma-next/framework-components/ir";
4
4
  import { StorageTable, isPostgresEnumStorageEntry, isStorageTypeInstance } from "@prisma-next/sql-contract/types";
5
+ import { blindCast } from "@prisma-next/utils/casts";
6
+ import { effectiveControlPolicy } from "@prisma-next/contract/types";
5
7
  import { canonicalStringify } from "@prisma-next/utils/canonical-stringify";
6
8
  //#region src/core/assembly.ts
7
9
  function hasCodecControlHooks(descriptor) {
@@ -31,6 +33,71 @@ function extractCodecControlHooks(descriptors) {
31
33
  return hooks;
32
34
  }
33
35
  //#endregion
36
+ //#region src/core/schema-verify/verifier-disposition.ts
37
+ /**
38
+ * Classifies the relational verifier issue kinds the SQL family emits (tables,
39
+ * columns, constraints, indexes, defaults, enum types) into the target-neutral
40
+ * categories the framework grades. The relational vocabulary lives here, in the
41
+ * SQL domain — the framework never switches over `extra_foreign_key` and friends.
42
+ */
43
+ function classifySqlVerifierIssueKind(kind) {
44
+ switch (kind) {
45
+ case "extra_column": return "extraNestedElement";
46
+ case "extra_primary_key":
47
+ case "extra_foreign_key":
48
+ case "extra_unique_constraint":
49
+ case "extra_index":
50
+ case "extra_validator":
51
+ case "extra_default": return "extraAuxiliary";
52
+ case "extra_table": return "extraTopLevelObject";
53
+ case "missing_schema":
54
+ case "missing_table":
55
+ case "missing_column":
56
+ case "type_missing":
57
+ case "default_missing": return "declaredMissing";
58
+ case "type_values_mismatch":
59
+ case "enum_values_changed": return "valueDrift";
60
+ case "type_mismatch":
61
+ case "nullability_mismatch":
62
+ case "primary_key_mismatch":
63
+ case "foreign_key_mismatch":
64
+ case "unique_constraint_mismatch":
65
+ case "index_mismatch":
66
+ case "default_mismatch": return "declaredIncompatible";
67
+ }
68
+ }
69
+ function verifierDisposition(controlPolicy, issueKind) {
70
+ return dispositionForCategory(controlPolicy, classifySqlVerifierIssueKind(issueKind));
71
+ }
72
+ //#endregion
73
+ //#region src/core/schema-verify/control-verify-emit.ts
74
+ /**
75
+ * Grades `issue` under `controlPolicy` and, unless suppressed, pushes both the
76
+ * issue and a status-stamped verification node. Returns the resolved outcome so
77
+ * the caller never re-grades the same issue.
78
+ */
79
+ function emitIssueAndNodeUnderControlPolicy(controlPolicy, issue, node, issues, nodes) {
80
+ const disposition = verifierDisposition(controlPolicy, issue.kind);
81
+ if (disposition === "suppress") return disposition;
82
+ issues.push(issue);
83
+ nodes.push({
84
+ ...node,
85
+ status: disposition
86
+ });
87
+ return disposition;
88
+ }
89
+ /**
90
+ * Grades `issue` under `controlPolicy` and, unless suppressed, pushes the issue
91
+ * (no verification node). Returns the resolved outcome so the caller maps it to
92
+ * a node status itself without re-grading.
93
+ */
94
+ function emitIssueUnderControlPolicy(controlPolicy, issue, issues) {
95
+ const disposition = verifierDisposition(controlPolicy, issue.kind);
96
+ if (disposition === "suppress") return disposition;
97
+ issues.push(issue);
98
+ return disposition;
99
+ }
100
+ //#endregion
34
101
  //#region src/core/schema-verify/verify-helpers.ts
35
102
  function indexOptionsLooselyEqual(a, b) {
36
103
  const aKeys = a ? Object.keys(a).sort() : [];
@@ -92,27 +159,27 @@ function isIndexSatisfied(indexes, uniques, columns) {
92
159
  * Uses semantic satisfaction: identity is based on (table + kind + columns).
93
160
  * Name differences are ignored by default (names are for DDL/diagnostics, not identity).
94
161
  */
95
- function verifyPrimaryKey(contractPK, schemaPK, tableName, namespaceId, issues) {
162
+ function verifyPrimaryKey(contractPK, schemaPK, tableName, namespaceId, tableControlPolicy, issues) {
96
163
  if (!schemaPK) {
97
- issues.push({
164
+ const outcome = emitIssueUnderControlPolicy(tableControlPolicy, {
98
165
  kind: "primary_key_mismatch",
99
166
  table: tableName,
100
167
  namespaceId,
101
168
  expected: contractPK.columns.join(", "),
102
169
  message: `Table "${tableName}" is missing primary key`
103
- });
104
- return "fail";
170
+ }, issues);
171
+ return outcome === "suppress" ? "pass" : outcome;
105
172
  }
106
173
  if (!arraysEqual(contractPK.columns, schemaPK.columns)) {
107
- issues.push({
174
+ const outcome = emitIssueUnderControlPolicy(tableControlPolicy, {
108
175
  kind: "primary_key_mismatch",
109
176
  table: tableName,
110
177
  namespaceId,
111
178
  expected: contractPK.columns.join(", "),
112
179
  actual: schemaPK.columns.join(", "),
113
180
  message: `Table "${tableName}" has primary key mismatch: expected columns [${contractPK.columns.join(", ")}], got [${schemaPK.columns.join(", ")}]`
114
- });
115
- return "fail";
181
+ }, issues);
182
+ return outcome === "suppress" ? "pass" : outcome;
116
183
  }
117
184
  return "pass";
118
185
  }
@@ -123,7 +190,7 @@ function verifyPrimaryKey(contractPK, schemaPK, tableName, namespaceId, issues)
123
190
  * Uses semantic satisfaction: identity is based on (table + columns + referenced table + referenced columns).
124
191
  * Name differences are ignored by default (names are for DDL/diagnostics, not identity).
125
192
  */
126
- function verifyForeignKeys(contractFKs, schemaFKs, tableName, namespaceId, tablePath, issues, strict) {
193
+ function verifyForeignKeys(contractFKs, schemaFKs, tableName, namespaceId, tablePath, tableControlPolicy, issues, strict) {
127
194
  const nodes = [];
128
195
  for (const contractFK of contractFKs) {
129
196
  const fkPath = `${tablePath}.foreignKeys[${contractFK.source.columns.join(",")}]`;
@@ -131,32 +198,30 @@ function verifyForeignKeys(contractFKs, schemaFKs, tableName, namespaceId, table
131
198
  const tablesMatch = fk.referencedSchema !== void 0 && contractFK.target.namespaceId !== UNBOUND_NAMESPACE_ID ? fk.referencedSchema === contractFK.target.namespaceId && fk.referencedTable === contractFK.target.tableName : fk.referencedTable === contractFK.target.tableName;
132
199
  return arraysEqual(fk.columns, contractFK.source.columns) && tablesMatch && arraysEqual(fk.referencedColumns, contractFK.target.columns);
133
200
  });
134
- if (!matchingFK) {
135
- issues.push({
136
- kind: "foreign_key_mismatch",
137
- table: tableName,
138
- namespaceId,
139
- expected: `${contractFK.source.columns.join(", ")} -> ${contractFK.target.tableName}(${contractFK.target.columns.join(", ")})`,
140
- message: `Table "${tableName}" is missing foreign key: ${contractFK.source.columns.join(", ")} -> ${contractFK.target.tableName}(${contractFK.target.columns.join(", ")})`
141
- });
142
- nodes.push({
143
- status: "fail",
144
- kind: "foreignKey",
145
- name: `foreignKey(${contractFK.source.columns.join(", ")})`,
146
- contractPath: fkPath,
147
- code: "foreign_key_mismatch",
148
- message: "Foreign key missing",
149
- expected: contractFK,
150
- actual: void 0,
151
- children: []
152
- });
153
- } else {
201
+ if (!matchingFK) emitIssueAndNodeUnderControlPolicy(tableControlPolicy, {
202
+ kind: "foreign_key_mismatch",
203
+ table: tableName,
204
+ namespaceId,
205
+ expected: `${contractFK.source.columns.join(", ")} -> ${contractFK.target.tableName}(${contractFK.target.columns.join(", ")})`,
206
+ message: `Table "${tableName}" is missing foreign key: ${contractFK.source.columns.join(", ")} -> ${contractFK.target.tableName}(${contractFK.target.columns.join(", ")})`
207
+ }, {
208
+ status: "fail",
209
+ kind: "foreignKey",
210
+ name: `foreignKey(${contractFK.source.columns.join(", ")})`,
211
+ contractPath: fkPath,
212
+ code: "foreign_key_mismatch",
213
+ message: "Foreign key missing",
214
+ expected: contractFK,
215
+ actual: void 0,
216
+ children: []
217
+ }, issues, nodes);
218
+ else {
154
219
  const actionMismatches = getReferentialActionMismatches(contractFK, matchingFK);
155
220
  if (actionMismatches.length > 0) {
156
221
  const combinedMessage = actionMismatches.map((m) => m.message).join("; ");
157
222
  const combinedExpected = actionMismatches.map((m) => m.expected).join(", ");
158
223
  const combinedActual = actionMismatches.map((m) => m.actual).join(", ");
159
- issues.push({
224
+ emitIssueAndNodeUnderControlPolicy(tableControlPolicy, {
160
225
  kind: "foreign_key_mismatch",
161
226
  table: tableName,
162
227
  namespaceId,
@@ -164,8 +229,7 @@ function verifyForeignKeys(contractFKs, schemaFKs, tableName, namespaceId, table
164
229
  expected: combinedExpected,
165
230
  actual: combinedActual,
166
231
  message: `Table "${tableName}" foreign key ${contractFK.source.columns.join(", ")} -> ${contractFK.target.tableName}: ${combinedMessage}`
167
- });
168
- nodes.push({
232
+ }, {
169
233
  status: "fail",
170
234
  kind: "foreignKey",
171
235
  name: `foreignKey(${contractFK.source.columns.join(", ")})`,
@@ -175,7 +239,7 @@ function verifyForeignKeys(contractFKs, schemaFKs, tableName, namespaceId, table
175
239
  expected: contractFK,
176
240
  actual: matchingFK,
177
241
  children: []
178
- });
242
+ }, issues, nodes);
179
243
  } else nodes.push({
180
244
  status: "pass",
181
245
  kind: "foreignKey",
@@ -193,26 +257,23 @@ function verifyForeignKeys(contractFKs, schemaFKs, tableName, namespaceId, table
193
257
  for (const schemaFK of schemaFKs) if (!contractFKs.find((fk) => {
194
258
  const tablesMatch = schemaFK.referencedSchema !== void 0 && fk.target.namespaceId !== UNBOUND_NAMESPACE_ID ? schemaFK.referencedSchema === fk.target.namespaceId && schemaFK.referencedTable === fk.target.tableName : schemaFK.referencedTable === fk.target.tableName;
195
259
  return arraysEqual(fk.source.columns, schemaFK.columns) && tablesMatch && arraysEqual(fk.target.columns, schemaFK.referencedColumns);
196
- })) {
197
- issues.push({
198
- kind: "extra_foreign_key",
199
- table: tableName,
200
- namespaceId,
201
- indexOrConstraint: schemaFK.name ?? `fk(${schemaFK.columns.join(",")})`,
202
- message: `Extra foreign key found in database (not in contract): ${schemaFK.columns.join(", ")} -> ${schemaFK.referencedTable}(${schemaFK.referencedColumns.join(", ")})`
203
- });
204
- nodes.push({
205
- status: "fail",
206
- kind: "foreignKey",
207
- name: `foreignKey(${schemaFK.columns.join(", ")})`,
208
- contractPath: `${tablePath}.foreignKeys[${schemaFK.columns.join(",")}]`,
209
- code: "extra_foreign_key",
210
- message: "Extra foreign key found",
211
- expected: void 0,
212
- actual: schemaFK,
213
- children: []
214
- });
215
- }
260
+ })) emitIssueAndNodeUnderControlPolicy(tableControlPolicy, {
261
+ kind: "extra_foreign_key",
262
+ table: tableName,
263
+ namespaceId,
264
+ indexOrConstraint: schemaFK.name ?? `fk(${schemaFK.columns.join(",")})`,
265
+ message: `Extra foreign key found in database (not in contract): ${schemaFK.columns.join(", ")} -> ${schemaFK.referencedTable}(${schemaFK.referencedColumns.join(", ")})`
266
+ }, {
267
+ status: "fail",
268
+ kind: "foreignKey",
269
+ name: `foreignKey(${schemaFK.columns.join(", ")})`,
270
+ contractPath: `${tablePath}.foreignKeys[${schemaFK.columns.join(",")}]`,
271
+ code: "extra_foreign_key",
272
+ message: "Extra foreign key found",
273
+ expected: void 0,
274
+ actual: schemaFK,
275
+ children: []
276
+ }, issues, nodes);
216
277
  }
217
278
  return nodes;
218
279
  }
@@ -227,32 +288,30 @@ function verifyForeignKeys(contractFKs, schemaFKs, tableName, namespaceId, table
227
288
  *
228
289
  * Name differences are ignored by default (names are for DDL/diagnostics, not identity).
229
290
  */
230
- function verifyUniqueConstraints(contractUniques, schemaUniques, schemaIndexes, tableName, namespaceId, tablePath, issues, strict) {
291
+ function verifyUniqueConstraints(contractUniques, schemaUniques, schemaIndexes, tableName, namespaceId, tablePath, tableControlPolicy, issues, strict) {
231
292
  const nodes = [];
232
293
  for (const contractUnique of contractUniques) {
233
294
  const uniquePath = `${tablePath}.uniques[${contractUnique.columns.join(",")}]`;
234
295
  const matchingUnique = schemaUniques.find((u) => arraysEqual(u.columns, contractUnique.columns));
235
296
  const matchingUniqueIndex = !matchingUnique && schemaIndexes.find((idx) => idx.unique && arraysEqual(idx.columns, contractUnique.columns));
236
- if (!matchingUnique && !matchingUniqueIndex) {
237
- issues.push({
238
- kind: "unique_constraint_mismatch",
239
- table: tableName,
240
- namespaceId,
241
- expected: contractUnique.columns.join(", "),
242
- message: `Table "${tableName}" is missing unique constraint: ${contractUnique.columns.join(", ")}`
243
- });
244
- nodes.push({
245
- status: "fail",
246
- kind: "unique",
247
- name: `unique(${contractUnique.columns.join(", ")})`,
248
- contractPath: uniquePath,
249
- code: "unique_constraint_mismatch",
250
- message: "Unique constraint missing",
251
- expected: contractUnique,
252
- actual: void 0,
253
- children: []
254
- });
255
- } else nodes.push({
297
+ if (!matchingUnique && !matchingUniqueIndex) emitIssueAndNodeUnderControlPolicy(tableControlPolicy, {
298
+ kind: "unique_constraint_mismatch",
299
+ table: tableName,
300
+ namespaceId,
301
+ expected: contractUnique.columns.join(", "),
302
+ message: `Table "${tableName}" is missing unique constraint: ${contractUnique.columns.join(", ")}`
303
+ }, {
304
+ status: "fail",
305
+ kind: "unique",
306
+ name: `unique(${contractUnique.columns.join(", ")})`,
307
+ contractPath: uniquePath,
308
+ code: "unique_constraint_mismatch",
309
+ message: "Unique constraint missing",
310
+ expected: contractUnique,
311
+ actual: void 0,
312
+ children: []
313
+ }, issues, nodes);
314
+ else nodes.push({
256
315
  status: "pass",
257
316
  kind: "unique",
258
317
  name: `unique(${contractUnique.columns.join(", ")})`,
@@ -265,26 +324,23 @@ function verifyUniqueConstraints(contractUniques, schemaUniques, schemaIndexes,
265
324
  });
266
325
  }
267
326
  if (strict) {
268
- for (const schemaUnique of schemaUniques) if (!contractUniques.find((u) => arraysEqual(u.columns, schemaUnique.columns))) {
269
- issues.push({
270
- kind: "extra_unique_constraint",
271
- table: tableName,
272
- namespaceId,
273
- indexOrConstraint: schemaUnique.name ?? `unique(${schemaUnique.columns.join(",")})`,
274
- message: `Extra unique constraint found in database (not in contract): ${schemaUnique.columns.join(", ")}`
275
- });
276
- nodes.push({
277
- status: "fail",
278
- kind: "unique",
279
- name: `unique(${schemaUnique.columns.join(", ")})`,
280
- contractPath: `${tablePath}.uniques[${schemaUnique.columns.join(",")}]`,
281
- code: "extra_unique_constraint",
282
- message: "Extra unique constraint found",
283
- expected: void 0,
284
- actual: schemaUnique,
285
- children: []
286
- });
287
- }
327
+ for (const schemaUnique of schemaUniques) if (!contractUniques.find((u) => arraysEqual(u.columns, schemaUnique.columns))) emitIssueAndNodeUnderControlPolicy(tableControlPolicy, {
328
+ kind: "extra_unique_constraint",
329
+ table: tableName,
330
+ namespaceId,
331
+ indexOrConstraint: schemaUnique.name ?? `unique(${schemaUnique.columns.join(",")})`,
332
+ message: `Extra unique constraint found in database (not in contract): ${schemaUnique.columns.join(", ")}`
333
+ }, {
334
+ status: "fail",
335
+ kind: "unique",
336
+ name: `unique(${schemaUnique.columns.join(", ")})`,
337
+ contractPath: `${tablePath}.uniques[${schemaUnique.columns.join(",")}]`,
338
+ code: "extra_unique_constraint",
339
+ message: "Extra unique constraint found",
340
+ expected: void 0,
341
+ actual: schemaUnique,
342
+ children: []
343
+ }, issues, nodes);
288
344
  }
289
345
  return nodes;
290
346
  }
@@ -299,32 +355,30 @@ function verifyUniqueConstraints(contractUniques, schemaUniques, schemaIndexes,
299
355
  *
300
356
  * Name differences are ignored by default (names are for DDL/diagnostics, not identity).
301
357
  */
302
- function verifyIndexes(contractIndexes, schemaIndexes, schemaUniques, tableName, namespaceId, tablePath, issues, strict) {
358
+ function verifyIndexes(contractIndexes, schemaIndexes, schemaUniques, tableName, namespaceId, tablePath, tableControlPolicy, issues, strict) {
303
359
  const nodes = [];
304
360
  for (const contractIndex of contractIndexes) {
305
361
  const indexPath = `${tablePath}.indexes[${contractIndex.columns.join(",")}]`;
306
362
  const matchingIndex = schemaIndexes.find((idx) => arraysEqual(idx.columns, contractIndex.columns) && indexExtrasMatch(contractIndex, idx));
307
363
  const matchingUniqueConstraint = !matchingIndex && contractIndex.type === void 0 && contractIndex.options === void 0 && schemaUniques.find((u) => arraysEqual(u.columns, contractIndex.columns));
308
- if (!matchingIndex && !matchingUniqueConstraint) {
309
- issues.push({
310
- kind: "index_mismatch",
311
- table: tableName,
312
- namespaceId,
313
- expected: contractIndex.columns.join(", "),
314
- message: `Table "${tableName}" is missing index: ${contractIndex.columns.join(", ")}`
315
- });
316
- nodes.push({
317
- status: "fail",
318
- kind: "index",
319
- name: `index(${contractIndex.columns.join(", ")})`,
320
- contractPath: indexPath,
321
- code: "index_mismatch",
322
- message: "Index missing",
323
- expected: contractIndex,
324
- actual: void 0,
325
- children: []
326
- });
327
- } else nodes.push({
364
+ if (!matchingIndex && !matchingUniqueConstraint) emitIssueAndNodeUnderControlPolicy(tableControlPolicy, {
365
+ kind: "index_mismatch",
366
+ table: tableName,
367
+ namespaceId,
368
+ expected: contractIndex.columns.join(", "),
369
+ message: `Table "${tableName}" is missing index: ${contractIndex.columns.join(", ")}`
370
+ }, {
371
+ status: "fail",
372
+ kind: "index",
373
+ name: `index(${contractIndex.columns.join(", ")})`,
374
+ contractPath: indexPath,
375
+ code: "index_mismatch",
376
+ message: "Index missing",
377
+ expected: contractIndex,
378
+ actual: void 0,
379
+ children: []
380
+ }, issues, nodes);
381
+ else nodes.push({
328
382
  status: "pass",
329
383
  kind: "index",
330
384
  name: `index(${contractIndex.columns.join(", ")})`,
@@ -338,26 +392,23 @@ function verifyIndexes(contractIndexes, schemaIndexes, schemaUniques, tableName,
338
392
  }
339
393
  if (strict) for (const schemaIndex of schemaIndexes) {
340
394
  if (schemaIndex.unique) continue;
341
- if (!contractIndexes.find((idx) => arraysEqual(idx.columns, schemaIndex.columns) && indexExtrasMatch(idx, schemaIndex))) {
342
- issues.push({
343
- kind: "extra_index",
344
- table: tableName,
345
- namespaceId,
346
- indexOrConstraint: schemaIndex.name ?? `idx(${schemaIndex.columns.join(",")})`,
347
- message: `Extra index found in database (not in contract): ${schemaIndex.columns.join(", ")}`
348
- });
349
- nodes.push({
350
- status: "fail",
351
- kind: "index",
352
- name: `index(${schemaIndex.columns.join(", ")})`,
353
- contractPath: `${tablePath}.indexes[${schemaIndex.columns.join(",")}]`,
354
- code: "extra_index",
355
- message: "Extra index found",
356
- expected: void 0,
357
- actual: schemaIndex,
358
- children: []
359
- });
360
- }
395
+ if (!contractIndexes.find((idx) => arraysEqual(idx.columns, schemaIndex.columns) && indexExtrasMatch(idx, schemaIndex))) emitIssueAndNodeUnderControlPolicy(tableControlPolicy, {
396
+ kind: "extra_index",
397
+ table: tableName,
398
+ namespaceId,
399
+ indexOrConstraint: schemaIndex.name ?? `idx(${schemaIndex.columns.join(",")})`,
400
+ message: `Extra index found in database (not in contract): ${schemaIndex.columns.join(", ")}`
401
+ }, {
402
+ status: "fail",
403
+ kind: "index",
404
+ name: `index(${schemaIndex.columns.join(", ")})`,
405
+ contractPath: `${tablePath}.indexes[${schemaIndex.columns.join(",")}]`,
406
+ code: "extra_index",
407
+ message: "Extra index found",
408
+ expected: void 0,
409
+ actual: schemaIndex,
410
+ children: []
411
+ }, issues, nodes);
361
412
  }
362
413
  return nodes;
363
414
  }
@@ -429,13 +480,13 @@ function normalizeReferentialAction(action) {
429
480
  * @returns VerifyDatabaseSchemaResult with verification tree and issues
430
481
  */
431
482
  function verifySqlSchema(options) {
432
- const { contract, schema, strict, context, typeMetadataRegistry, normalizeDefault, normalizeNativeType, resolveExistingEnumValues } = options;
483
+ const { contract, schema, strict, context, typeMetadataRegistry, normalizeDefault, normalizeNativeType, resolveExistingEnumValues, columnsCompatible } = options;
433
484
  const startTime = Date.now();
434
485
  const codecHooks = extractCodecControlHooks(options.frameworkComponents);
435
486
  const { contractStorageHash, contractProfileHash, contractTarget } = extractContractMetadata(contract);
436
487
  const allStorageTypesMap = { ...contract.storage.types ?? {} };
437
488
  for (const ns of Object.values(contract.storage.namespaces)) {
438
- const nsEnums = ns.enum;
489
+ const nsEnums = blindCast(ns.entries).type;
439
490
  if (nsEnums) for (const [k, v] of Object.entries(nsEnums)) allStorageTypesMap[k] = v;
440
491
  }
441
492
  const { issues, rootChildren } = verifySchemaTables({
@@ -446,57 +497,62 @@ function verifySqlSchema(options) {
446
497
  codecHooks,
447
498
  storageTypes: allStorageTypesMap,
448
499
  ...ifDefined("normalizeDefault", normalizeDefault),
449
- ...ifDefined("normalizeNativeType", normalizeNativeType)
500
+ ...ifDefined("normalizeNativeType", normalizeNativeType),
501
+ ...ifDefined("columnsCompatible", columnsCompatible)
450
502
  });
451
503
  validateFrameworkComponentsForExtensions(contract, options.frameworkComponents);
452
504
  const typeNodes = [];
453
- const pushTypeNode = (typeName, contractPath, typeIssues) => {
454
- if (typeIssues.length > 0) issues.push(...typeIssues);
505
+ const pushTypeNode = (typeName, contractPath, typeIssues, controlPolicy) => {
506
+ let status = "pass";
507
+ let code = "";
508
+ let emitted = 0;
509
+ for (const issue of typeIssues) {
510
+ const disposition = verifierDisposition(controlPolicy, issue.kind);
511
+ if (disposition === "suppress") continue;
512
+ issues.push(issue);
513
+ emitted += 1;
514
+ if (code === "") code = issue.kind;
515
+ if (disposition === "fail") status = "fail";
516
+ else if (disposition === "warn" && status !== "fail") status = "warn";
517
+ }
455
518
  typeNodes.push({
456
- status: typeIssues.length > 0 ? "fail" : "pass",
519
+ status,
457
520
  kind: "storageType",
458
521
  name: `type ${typeName}`,
459
522
  contractPath,
460
- code: typeIssues.length > 0 ? typeIssues[0]?.kind ?? "" : "",
461
- message: typeIssues.length > 0 ? `${typeIssues.length} issue${typeIssues.length === 1 ? "" : "s"}` : "",
523
+ code: status === "pass" ? "" : code,
524
+ message: emitted > 0 ? `${emitted} issue${emitted === 1 ? "" : "s"}` : "",
462
525
  expected: void 0,
463
526
  actual: void 0,
464
527
  children: []
465
528
  });
466
529
  };
467
- for (const [typeName, typeInstance] of Object.entries(contract.storage.types ?? {})) if (isPostgresEnumStorageEntry(typeInstance)) pushTypeNode(typeName, `storage.types.${typeName}`, verifyEnumType({
468
- typeName,
469
- typeInstance,
470
- schema,
471
- resolveExistingEnumValues,
472
- namespaceId: UNBOUND_NAMESPACE_ID
473
- }));
474
- else if (isStorageTypeInstance(typeInstance)) {
530
+ for (const [typeName, typeInstance] of Object.entries(contract.storage.types ?? {})) if (isStorageTypeInstance(typeInstance)) {
475
531
  const hook = codecHooks.get(typeInstance.codecId);
476
532
  pushTypeNode(typeName, `storage.types.${typeName}`, hook?.verifyType ? hook.verifyType({
477
533
  typeName,
478
534
  typeInstance,
479
535
  schema
480
- }) : []);
536
+ }) : [], effectiveControlPolicy(void 0, contract.defaultControlPolicy));
481
537
  }
482
538
  for (const nsId of Object.keys(contract.storage.namespaces)) {
483
539
  const ns = contract.storage.namespaces[nsId];
484
540
  if (!ns) continue;
485
- const nsEnums = ns.enum;
541
+ const nsEnums = ns.entries["type"];
486
542
  if (!nsEnums) continue;
487
543
  for (const [typeName, entry] of Object.entries(nsEnums)) {
488
544
  if (!isPostgresEnumStorageEntry(entry)) continue;
489
- pushTypeNode(typeName, `storage.namespaces.${nsId}.enum.${typeName}`, verifyEnumType({
545
+ pushTypeNode(typeName, `storage.namespaces.${nsId}.entries.type.${typeName}`, verifyEnumType({
490
546
  typeName,
491
547
  typeInstance: entry,
492
548
  schema,
493
549
  resolveExistingEnumValues,
494
550
  namespaceId: nsId
495
- }));
551
+ }), effectiveControlPolicy(entry.control, contract.defaultControlPolicy));
496
552
  }
497
553
  }
498
554
  if (typeNodes.length > 0) {
499
- const typesStatus = typeNodes.some((n) => n.status === "fail") ? "fail" : "pass";
555
+ const typesStatus = typeNodes.some((n) => n.status === "fail") ? "fail" : typeNodes.some((n) => n.status === "warn") ? "warn" : "pass";
500
556
  rootChildren.push({
501
557
  status: typesStatus,
502
558
  kind: "storageTypes",
@@ -580,7 +636,8 @@ function extractContractMetadata(contract) {
580
636
  };
581
637
  }
582
638
  function verifySchemaTables(options) {
583
- const { contract, schema, strict, typeMetadataRegistry, codecHooks, storageTypes, normalizeDefault, normalizeNativeType } = options;
639
+ const { contract, schema, strict, typeMetadataRegistry, codecHooks, storageTypes, normalizeDefault, normalizeNativeType, columnsCompatible } = options;
640
+ const contractDefaultControl = contract.defaultControlPolicy;
584
641
  const issues = [];
585
642
  const rootChildren = [];
586
643
  const schemaTables = schema.tables;
@@ -588,19 +645,19 @@ function verifySchemaTables(options) {
588
645
  for (const namespaceId of namespaceIds) {
589
646
  const ns = contract.storage.namespaces[namespaceId];
590
647
  if (!ns) continue;
591
- for (const [tableName, contractTableRaw] of Object.entries(ns.tables)) {
592
- if (!(contractTableRaw instanceof StorageTable)) throw new Error(`verifySqlSchema: expected StorageTable at storage.namespaces.${namespaceId}.tables.${tableName}`);
648
+ for (const [tableName, contractTableRaw] of Object.entries(ns.entries.table)) {
649
+ if (!(contractTableRaw instanceof StorageTable)) throw new Error(`verifySqlSchema: expected StorageTable at storage.namespaces.${namespaceId}.entries.table.${tableName}`);
593
650
  const contractTable = contractTableRaw;
651
+ const tableControlPolicy = effectiveControlPolicy(contractTable.control, contractDefaultControl);
594
652
  const schemaTable = schemaTables[tableName];
595
- const tablePath = `storage.namespaces.${namespaceId}.tables.${tableName}`;
653
+ const tablePath = `storage.namespaces.${namespaceId}.entries.table.${tableName}`;
596
654
  if (!schemaTable) {
597
- issues.push({
655
+ emitIssueAndNodeUnderControlPolicy(tableControlPolicy, {
598
656
  kind: "missing_table",
599
657
  table: tableName,
600
658
  namespaceId,
601
659
  message: `Table "${tableName}" is missing from database`
602
- });
603
- rootChildren.push({
660
+ }, {
604
661
  status: "fail",
605
662
  kind: "table",
606
663
  name: `table ${tableName}`,
@@ -610,7 +667,7 @@ function verifySchemaTables(options) {
610
667
  expected: void 0,
611
668
  actual: void 0,
612
669
  children: []
613
- });
670
+ }, issues, rootChildren);
614
671
  continue;
615
672
  }
616
673
  const tableChildren = verifyTableChildren({
@@ -619,36 +676,35 @@ function verifySchemaTables(options) {
619
676
  tableName,
620
677
  namespaceId,
621
678
  tablePath,
679
+ tableControlPolicy,
622
680
  issues,
623
681
  strict,
624
682
  typeMetadataRegistry,
625
683
  codecHooks,
626
684
  storageTypes,
627
685
  ...ifDefined("normalizeDefault", normalizeDefault),
628
- ...ifDefined("normalizeNativeType", normalizeNativeType)
686
+ ...ifDefined("normalizeNativeType", normalizeNativeType),
687
+ ...ifDefined("columnsCompatible", columnsCompatible)
629
688
  });
630
689
  rootChildren.push(buildTableNode(tableName, tablePath, tableChildren));
631
690
  }
632
691
  }
633
692
  if (strict) {
634
- for (const tableName of Object.keys(schemaTables)) if (!namespaceIds.some((namespaceId) => contract.storage.namespaces[namespaceId]?.tables[tableName] !== void 0)) {
635
- issues.push({
636
- kind: "extra_table",
637
- table: tableName,
638
- message: `Extra table "${tableName}" found in database (not in contract)`
639
- });
640
- rootChildren.push({
641
- status: "fail",
642
- kind: "table",
643
- name: `table ${tableName}`,
644
- contractPath: `storage.namespaces.*.tables.${tableName}`,
645
- code: "extra_table",
646
- message: `Extra table "${tableName}" found`,
647
- expected: void 0,
648
- actual: void 0,
649
- children: []
650
- });
651
- }
693
+ for (const tableName of Object.keys(schemaTables)) if (!namespaceIds.some((namespaceId) => contract.storage.namespaces[namespaceId]?.entries.table[tableName] !== void 0)) emitIssueAndNodeUnderControlPolicy(effectiveControlPolicy(void 0, contractDefaultControl), {
694
+ kind: "extra_table",
695
+ table: tableName,
696
+ message: `Extra table "${tableName}" found in database (not in contract)`
697
+ }, {
698
+ status: "fail",
699
+ kind: "table",
700
+ name: `table ${tableName}`,
701
+ contractPath: `storage.namespaces.*.entries.table.${tableName}`,
702
+ code: "extra_table",
703
+ message: `Extra table "${tableName}" found`,
704
+ expected: void 0,
705
+ actual: void 0,
706
+ children: []
707
+ }, issues, rootChildren);
652
708
  }
653
709
  return {
654
710
  issues,
@@ -656,7 +712,7 @@ function verifySchemaTables(options) {
656
712
  };
657
713
  }
658
714
  function verifyTableChildren(options) {
659
- const { contractTable, schemaTable, tableName, namespaceId, tablePath, issues, strict, typeMetadataRegistry, codecHooks, storageTypes, normalizeDefault, normalizeNativeType } = options;
715
+ const { contractTable, schemaTable, tableName, namespaceId, tablePath, tableControlPolicy, issues, strict, typeMetadataRegistry, codecHooks, storageTypes, normalizeDefault, normalizeNativeType, columnsCompatible } = options;
660
716
  const tableChildren = [];
661
717
  const columnNodes = collectContractColumnNodes({
662
718
  contractTable,
@@ -664,13 +720,15 @@ function verifyTableChildren(options) {
664
720
  tableName,
665
721
  namespaceId,
666
722
  tablePath,
723
+ tableControlPolicy,
667
724
  issues,
668
725
  strict,
669
726
  typeMetadataRegistry,
670
727
  codecHooks,
671
728
  storageTypes,
672
729
  ...ifDefined("normalizeDefault", normalizeDefault),
673
- ...ifDefined("normalizeNativeType", normalizeNativeType)
730
+ ...ifDefined("normalizeNativeType", normalizeNativeType),
731
+ ...ifDefined("columnsCompatible", columnsCompatible)
674
732
  });
675
733
  if (columnNodes.length > 0) tableChildren.push(buildColumnsNode(tablePath, columnNodes));
676
734
  if (strict) appendExtraColumnNodes({
@@ -679,77 +737,87 @@ function verifyTableChildren(options) {
679
737
  tableName,
680
738
  namespaceId,
681
739
  tablePath,
740
+ tableControlPolicy,
682
741
  issues,
683
742
  columnNodes
684
743
  });
685
- if (contractTable.primaryKey) if (verifyPrimaryKey(contractTable.primaryKey, schemaTable.primaryKey, tableName, namespaceId, issues) === "fail") tableChildren.push({
686
- status: "fail",
687
- kind: "primaryKey",
688
- name: `primary key: ${contractTable.primaryKey.columns.join(", ")}`,
689
- contractPath: `${tablePath}.primaryKey`,
690
- code: "primary_key_mismatch",
691
- message: "Primary key mismatch",
692
- expected: contractTable.primaryKey,
693
- actual: schemaTable.primaryKey,
694
- children: []
695
- });
696
- else tableChildren.push({
697
- status: "pass",
698
- kind: "primaryKey",
699
- name: `primary key: ${contractTable.primaryKey.columns.join(", ")}`,
700
- contractPath: `${tablePath}.primaryKey`,
701
- code: "",
702
- message: "",
703
- expected: void 0,
704
- actual: void 0,
705
- children: []
706
- });
707
- else if (schemaTable.primaryKey && strict) {
708
- issues.push({
709
- kind: "extra_primary_key",
710
- table: tableName,
711
- namespaceId,
712
- message: "Extra primary key found in database (not in contract)"
713
- });
714
- tableChildren.push({
744
+ if (contractTable.primaryKey) {
745
+ const pkStatus = verifyPrimaryKey(contractTable.primaryKey, schemaTable.primaryKey, tableName, namespaceId, tableControlPolicy, issues);
746
+ if (pkStatus === "fail") tableChildren.push({
715
747
  status: "fail",
716
748
  kind: "primaryKey",
717
- name: `primary key: ${schemaTable.primaryKey.columns.join(", ")}`,
749
+ name: `primary key: ${contractTable.primaryKey.columns.join(", ")}`,
718
750
  contractPath: `${tablePath}.primaryKey`,
719
- code: "extra_primary_key",
720
- message: "Extra primary key found",
721
- expected: void 0,
751
+ code: "primary_key_mismatch",
752
+ message: "Primary key mismatch",
753
+ expected: contractTable.primaryKey,
722
754
  actual: schemaTable.primaryKey,
723
755
  children: []
724
756
  });
725
- }
757
+ else if (pkStatus === "warn") tableChildren.push({
758
+ status: "warn",
759
+ kind: "primaryKey",
760
+ name: `primary key: ${contractTable.primaryKey.columns.join(", ")}`,
761
+ contractPath: `${tablePath}.primaryKey`,
762
+ code: "primary_key_mismatch",
763
+ message: "Primary key mismatch",
764
+ expected: contractTable.primaryKey,
765
+ actual: schemaTable.primaryKey,
766
+ children: []
767
+ });
768
+ else tableChildren.push({
769
+ status: "pass",
770
+ kind: "primaryKey",
771
+ name: `primary key: ${contractTable.primaryKey.columns.join(", ")}`,
772
+ contractPath: `${tablePath}.primaryKey`,
773
+ code: "",
774
+ message: "",
775
+ expected: void 0,
776
+ actual: void 0,
777
+ children: []
778
+ });
779
+ } else if (schemaTable.primaryKey && strict) emitIssueAndNodeUnderControlPolicy(tableControlPolicy, {
780
+ kind: "extra_primary_key",
781
+ table: tableName,
782
+ namespaceId,
783
+ message: "Extra primary key found in database (not in contract)"
784
+ }, {
785
+ status: "fail",
786
+ kind: "primaryKey",
787
+ name: `primary key: ${schemaTable.primaryKey.columns.join(", ")}`,
788
+ contractPath: `${tablePath}.primaryKey`,
789
+ code: "extra_primary_key",
790
+ message: "Extra primary key found",
791
+ expected: void 0,
792
+ actual: schemaTable.primaryKey,
793
+ children: []
794
+ }, issues, tableChildren);
726
795
  const constraintFks = contractTable.foreignKeys.filter((fk) => fk.constraint === true);
727
796
  if (constraintFks.length > 0 || strict) {
728
- const fkStatuses = verifyForeignKeys(constraintFks, schemaTable.foreignKeys, tableName, namespaceId, tablePath, issues, strict);
797
+ const fkStatuses = verifyForeignKeys(constraintFks, schemaTable.foreignKeys, tableName, namespaceId, tablePath, tableControlPolicy, issues, strict);
729
798
  tableChildren.push(...fkStatuses);
730
799
  }
731
- const uniqueStatuses = verifyUniqueConstraints(contractTable.uniques, schemaTable.uniques, schemaTable.indexes, tableName, namespaceId, tablePath, issues, strict);
800
+ const uniqueStatuses = verifyUniqueConstraints(contractTable.uniques, schemaTable.uniques, schemaTable.indexes, tableName, namespaceId, tablePath, tableControlPolicy, issues, strict);
732
801
  tableChildren.push(...uniqueStatuses);
733
802
  const fkBackingIndexes = contractTable.foreignKeys.filter((fk) => fk.index === true && !contractTable.indexes.some((idx) => arraysEqual(idx.columns, fk.source.columns))).map((fk) => ({ columns: fk.source.columns }));
734
- const indexStatuses = verifyIndexes([...contractTable.indexes, ...fkBackingIndexes], schemaTable.indexes, schemaTable.uniques, tableName, namespaceId, tablePath, issues, strict);
803
+ const indexStatuses = verifyIndexes([...contractTable.indexes, ...fkBackingIndexes], schemaTable.indexes, schemaTable.uniques, tableName, namespaceId, tablePath, tableControlPolicy, issues, strict);
735
804
  tableChildren.push(...indexStatuses);
736
805
  return tableChildren;
737
806
  }
738
807
  function collectContractColumnNodes(options) {
739
- const { contractTable, schemaTable, tableName, namespaceId, tablePath, issues, strict, typeMetadataRegistry, codecHooks, storageTypes, normalizeDefault, normalizeNativeType } = options;
808
+ const { contractTable, schemaTable, tableName, namespaceId, tablePath, tableControlPolicy, issues, strict, typeMetadataRegistry, codecHooks, storageTypes, normalizeDefault, normalizeNativeType, columnsCompatible } = options;
740
809
  const columnNodes = [];
741
810
  for (const [columnName, contractColumn] of Object.entries(contractTable.columns)) {
742
811
  const schemaColumn = schemaTable.columns[columnName];
743
812
  const columnPath = `${tablePath}.columns.${columnName}`;
744
813
  if (!schemaColumn) {
745
- issues.push({
814
+ emitIssueAndNodeUnderControlPolicy(tableControlPolicy, {
746
815
  kind: "missing_column",
747
816
  table: tableName,
748
817
  namespaceId,
749
818
  column: columnName,
750
819
  message: `Column "${tableName}"."${columnName}" is missing from database`
751
- });
752
- columnNodes.push({
820
+ }, {
753
821
  status: "fail",
754
822
  kind: "column",
755
823
  name: `${columnName}: missing`,
@@ -759,7 +827,7 @@ function collectContractColumnNodes(options) {
759
827
  expected: void 0,
760
828
  actual: void 0,
761
829
  children: []
762
- });
830
+ }, issues, columnNodes);
763
831
  continue;
764
832
  }
765
833
  columnNodes.push(verifyColumn({
@@ -769,42 +837,41 @@ function collectContractColumnNodes(options) {
769
837
  contractColumn,
770
838
  schemaColumn,
771
839
  columnPath,
840
+ tableControlPolicy,
772
841
  issues,
773
842
  strict,
774
843
  typeMetadataRegistry,
775
844
  codecHooks,
776
845
  storageTypes,
777
846
  ...ifDefined("normalizeDefault", normalizeDefault),
778
- ...ifDefined("normalizeNativeType", normalizeNativeType)
847
+ ...ifDefined("normalizeNativeType", normalizeNativeType),
848
+ ...ifDefined("columnsCompatible", columnsCompatible)
779
849
  }));
780
850
  }
781
851
  return columnNodes;
782
852
  }
783
853
  function appendExtraColumnNodes(options) {
784
- const { contractTable, schemaTable, tableName, namespaceId, tablePath, issues, columnNodes } = options;
785
- for (const [columnName, { nativeType }] of Object.entries(schemaTable.columns)) if (!contractTable.columns[columnName]) {
786
- issues.push({
787
- kind: "extra_column",
788
- table: tableName,
789
- namespaceId,
790
- column: columnName,
791
- message: `Extra column "${tableName}"."${columnName}" found in database (not in contract)`
792
- });
793
- columnNodes.push({
794
- status: "fail",
795
- kind: "column",
796
- name: `${columnName}: extra`,
797
- contractPath: `${tablePath}.columns.${columnName}`,
798
- code: "extra_column",
799
- message: `Extra column "${columnName}" found`,
800
- expected: void 0,
801
- actual: nativeType,
802
- children: []
803
- });
804
- }
854
+ const { contractTable, schemaTable, tableName, namespaceId, tablePath, tableControlPolicy, issues, columnNodes } = options;
855
+ for (const [columnName, { nativeType }] of Object.entries(schemaTable.columns)) if (!contractTable.columns[columnName]) emitIssueAndNodeUnderControlPolicy(tableControlPolicy, {
856
+ kind: "extra_column",
857
+ table: tableName,
858
+ namespaceId,
859
+ column: columnName,
860
+ message: `Extra column "${tableName}"."${columnName}" found in database (not in contract)`
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: void 0,
869
+ actual: nativeType,
870
+ children: []
871
+ }, issues, columnNodes);
805
872
  }
806
873
  function verifyColumn(options) {
807
- const { tableName, namespaceId, columnName, contractColumn, schemaColumn, columnPath, issues, strict, codecHooks, storageTypes, normalizeDefault, normalizeNativeType } = options;
874
+ const { tableName, namespaceId, columnName, contractColumn, schemaColumn, columnPath, tableControlPolicy, issues, strict, codecHooks, storageTypes, normalizeDefault, normalizeNativeType, columnsCompatible } = options;
808
875
  const columnChildren = [];
809
876
  let columnStatus = "pass";
810
877
  const resolvedContractColumn = resolveContractColumnTypeMetadata(contractColumn, storageTypes, {
@@ -816,8 +883,8 @@ function verifyColumn(options) {
816
883
  columnName
817
884
  });
818
885
  const schemaNativeType = normalizeNativeType?.(schemaColumn.nativeType) ?? schemaColumn.nativeType;
819
- if (contractNativeType !== schemaNativeType) {
820
- issues.push({
886
+ if (!(contractNativeType === schemaNativeType || tableControlPolicy === "external" && (columnsCompatible?.(contractNativeType, schemaNativeType) ?? contractNativeType === schemaNativeType))) {
887
+ const issue = {
821
888
  kind: "type_mismatch",
822
889
  table: tableName,
823
890
  namespaceId,
@@ -825,19 +892,23 @@ function verifyColumn(options) {
825
892
  expected: contractNativeType,
826
893
  actual: schemaNativeType,
827
894
  message: `Column "${tableName}"."${columnName}" has type mismatch: expected "${contractNativeType}", got "${schemaNativeType}"`
828
- });
829
- columnChildren.push({
830
- status: "fail",
831
- kind: "type",
832
- name: "type",
833
- contractPath: `${columnPath}.nativeType`,
834
- code: "type_mismatch",
835
- message: `Type mismatch: expected ${contractNativeType}, got ${schemaNativeType}`,
836
- expected: contractNativeType,
837
- actual: schemaNativeType,
838
- children: []
839
- });
840
- columnStatus = "fail";
895
+ };
896
+ const disposition = verifierDisposition(tableControlPolicy, issue.kind);
897
+ if (disposition !== "suppress") {
898
+ issues.push(issue);
899
+ columnChildren.push({
900
+ status: disposition,
901
+ kind: "type",
902
+ name: "type",
903
+ contractPath: `${columnPath}.nativeType`,
904
+ code: "type_mismatch",
905
+ message: `Type mismatch: expected ${contractNativeType}, got ${schemaNativeType}`,
906
+ expected: contractNativeType,
907
+ actual: schemaNativeType,
908
+ children: []
909
+ });
910
+ columnStatus = disposition;
911
+ }
841
912
  }
842
913
  if (resolvedContractColumn.codecId) {
843
914
  const typeMetadata = options.typeMetadataRegistry.get(resolvedContractColumn.codecId);
@@ -865,7 +936,7 @@ function verifyColumn(options) {
865
936
  });
866
937
  }
867
938
  if (contractColumn.nullable !== schemaColumn.nullable) {
868
- issues.push({
939
+ const issue = {
869
940
  kind: "nullability_mismatch",
870
941
  table: tableName,
871
942
  namespaceId,
@@ -873,47 +944,55 @@ function verifyColumn(options) {
873
944
  expected: String(contractColumn.nullable),
874
945
  actual: String(schemaColumn.nullable),
875
946
  message: `Column "${tableName}"."${columnName}" has nullability mismatch: expected ${contractColumn.nullable ? "nullable" : "not null"}, got ${schemaColumn.nullable ? "nullable" : "not null"}`
876
- });
877
- columnChildren.push({
878
- status: "fail",
879
- kind: "nullability",
880
- name: "nullability",
881
- contractPath: `${columnPath}.nullable`,
882
- code: "nullability_mismatch",
883
- message: `Nullability mismatch: expected ${contractColumn.nullable ? "nullable" : "not null"}, got ${schemaColumn.nullable ? "nullable" : "not null"}`,
884
- expected: contractColumn.nullable,
885
- actual: schemaColumn.nullable,
886
- children: []
887
- });
888
- columnStatus = "fail";
947
+ };
948
+ const disposition = verifierDisposition(tableControlPolicy, issue.kind);
949
+ if (disposition !== "suppress") {
950
+ issues.push(issue);
951
+ columnChildren.push({
952
+ status: disposition,
953
+ kind: "nullability",
954
+ name: "nullability",
955
+ contractPath: `${columnPath}.nullable`,
956
+ code: "nullability_mismatch",
957
+ message: `Nullability mismatch: expected ${contractColumn.nullable ? "nullable" : "not null"}, got ${schemaColumn.nullable ? "nullable" : "not null"}`,
958
+ expected: contractColumn.nullable,
959
+ actual: schemaColumn.nullable,
960
+ children: []
961
+ });
962
+ columnStatus = disposition;
963
+ }
889
964
  }
890
965
  if (contractColumn.default) {
891
966
  if (!schemaColumn.default) {
892
967
  const defaultDescription = describeColumnDefault(contractColumn.default);
893
- issues.push({
968
+ const issue = {
894
969
  kind: "default_missing",
895
970
  table: tableName,
896
971
  namespaceId,
897
972
  column: columnName,
898
973
  expected: defaultDescription,
899
974
  message: `Column "${tableName}"."${columnName}" should have default ${defaultDescription} but database has no default`
900
- });
901
- columnChildren.push({
902
- status: "fail",
903
- kind: "default",
904
- name: "default",
905
- contractPath: `${columnPath}.default`,
906
- code: "default_missing",
907
- message: `Default missing: expected ${defaultDescription}`,
908
- expected: defaultDescription,
909
- actual: void 0,
910
- children: []
911
- });
912
- columnStatus = "fail";
975
+ };
976
+ const disposition = verifierDisposition(tableControlPolicy, issue.kind);
977
+ if (disposition !== "suppress") {
978
+ issues.push(issue);
979
+ columnChildren.push({
980
+ status: disposition,
981
+ kind: "default",
982
+ name: "default",
983
+ contractPath: `${columnPath}.default`,
984
+ code: "default_missing",
985
+ message: `Default missing: expected ${defaultDescription}`,
986
+ expected: defaultDescription,
987
+ actual: void 0,
988
+ children: []
989
+ });
990
+ columnStatus = disposition;
991
+ }
913
992
  } else if (!columnDefaultsEqual(contractColumn.default, schemaColumn.default, normalizeDefault, schemaNativeType)) {
914
993
  const expectedDescription = describeColumnDefault(contractColumn.default);
915
994
  const actualDescription = schemaColumn.default;
916
- issues.push({
995
+ const issue = {
917
996
  kind: "default_mismatch",
918
997
  table: tableName,
919
998
  namespaceId,
@@ -921,41 +1000,49 @@ function verifyColumn(options) {
921
1000
  expected: expectedDescription,
922
1001
  actual: actualDescription,
923
1002
  message: `Column "${tableName}"."${columnName}" has default mismatch: expected ${expectedDescription}, got ${actualDescription}`
924
- });
925
- columnChildren.push({
926
- status: "fail",
927
- kind: "default",
928
- name: "default",
929
- contractPath: `${columnPath}.default`,
930
- code: "default_mismatch",
931
- message: `Default mismatch: expected ${expectedDescription}, got ${actualDescription}`,
932
- expected: expectedDescription,
933
- actual: actualDescription,
934
- children: []
935
- });
936
- columnStatus = "fail";
1003
+ };
1004
+ const disposition = verifierDisposition(tableControlPolicy, issue.kind);
1005
+ if (disposition !== "suppress") {
1006
+ issues.push(issue);
1007
+ columnChildren.push({
1008
+ status: disposition,
1009
+ kind: "default",
1010
+ name: "default",
1011
+ contractPath: `${columnPath}.default`,
1012
+ code: "default_mismatch",
1013
+ message: `Default mismatch: expected ${expectedDescription}, got ${actualDescription}`,
1014
+ expected: expectedDescription,
1015
+ actual: actualDescription,
1016
+ children: []
1017
+ });
1018
+ columnStatus = disposition;
1019
+ }
937
1020
  }
938
1021
  } else if (strict && schemaColumn.default) {
939
- issues.push({
1022
+ const issue = {
940
1023
  kind: "extra_default",
941
1024
  table: tableName,
942
1025
  namespaceId,
943
1026
  column: columnName,
944
1027
  actual: schemaColumn.default,
945
1028
  message: `Column "${tableName}"."${columnName}" has default ${schemaColumn.default} in database but contract specifies no default`
946
- });
947
- columnChildren.push({
948
- status: "fail",
949
- kind: "default",
950
- name: "default",
951
- contractPath: `${columnPath}.default`,
952
- code: "extra_default",
953
- message: `Extra default: ${schemaColumn.default}`,
954
- expected: void 0,
955
- actual: schemaColumn.default,
956
- children: []
957
- });
958
- columnStatus = "fail";
1029
+ };
1030
+ const disposition = verifierDisposition(tableControlPolicy, issue.kind);
1031
+ if (disposition !== "suppress") {
1032
+ issues.push(issue);
1033
+ columnChildren.push({
1034
+ status: disposition,
1035
+ kind: "default",
1036
+ name: "default",
1037
+ contractPath: `${columnPath}.default`,
1038
+ code: "extra_default",
1039
+ message: `Extra default: ${schemaColumn.default}`,
1040
+ expected: void 0,
1041
+ actual: schemaColumn.default,
1042
+ children: []
1043
+ });
1044
+ columnStatus = disposition;
1045
+ }
959
1046
  }
960
1047
  const aggregated = aggregateChildState(columnChildren, columnStatus);
961
1048
  const nullableText = contractColumn.nullable ? "nullable" : "not nullable";
@@ -1154,4 +1241,4 @@ function formatLiteralValue(value) {
1154
1241
  //#endregion
1155
1242
  export { extractCodecControlHooks as a, isUniqueConstraintSatisfied as i, arraysEqual as n, isIndexSatisfied as r, verifySqlSchema as t };
1156
1243
 
1157
- //# sourceMappingURL=verify-sql-schema-CYLsGCFO.mjs.map
1244
+ //# sourceMappingURL=verify-sql-schema-DV_d2XCG.mjs.map