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