@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
|
@@ -3,7 +3,6 @@
|
|
|
3
3
|
* These functions verify schema IR against contract requirements.
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import type { ControlPolicy } from '@prisma-next/contract/types';
|
|
7
6
|
import type {
|
|
8
7
|
SchemaIssue,
|
|
9
8
|
SchemaVerificationNode,
|
|
@@ -16,10 +15,6 @@ import type {
|
|
|
16
15
|
UniqueConstraint,
|
|
17
16
|
} from '@prisma-next/sql-contract/types';
|
|
18
17
|
import type { SqlForeignKeyIR, SqlIndexIR, SqlUniqueIR } from '@prisma-next/sql-schema-ir/types';
|
|
19
|
-
import {
|
|
20
|
-
emitIssueAndNodeUnderControlPolicy,
|
|
21
|
-
emitIssueUnderControlPolicy,
|
|
22
|
-
} from './control-verify-emit';
|
|
23
18
|
|
|
24
19
|
function indexOptionsLooselyEqual(
|
|
25
20
|
a: Record<string, unknown> | undefined,
|
|
@@ -140,34 +135,34 @@ export function verifyPrimaryKey(
|
|
|
140
135
|
schemaPK: PrimaryKey | undefined,
|
|
141
136
|
tableName: string,
|
|
142
137
|
namespaceId: string,
|
|
143
|
-
tableControlPolicy: ControlPolicy,
|
|
144
138
|
issues: SchemaIssue[],
|
|
145
|
-
): 'pass' | '
|
|
139
|
+
): 'pass' | 'fail' {
|
|
146
140
|
if (!schemaPK) {
|
|
147
|
-
|
|
141
|
+
issues.push({
|
|
148
142
|
kind: 'primary_key_mismatch',
|
|
149
143
|
table: tableName,
|
|
150
144
|
namespaceId,
|
|
151
145
|
expected: contractPK.columns.join(', '),
|
|
152
146
|
message: `Table "${tableName}" is missing primary key`,
|
|
153
|
-
};
|
|
154
|
-
|
|
155
|
-
return outcome === 'suppress' ? 'pass' : outcome;
|
|
147
|
+
});
|
|
148
|
+
return 'fail';
|
|
156
149
|
}
|
|
157
150
|
|
|
158
151
|
if (!arraysEqual(contractPK.columns, schemaPK.columns)) {
|
|
159
|
-
|
|
152
|
+
issues.push({
|
|
160
153
|
kind: 'primary_key_mismatch',
|
|
161
154
|
table: tableName,
|
|
162
155
|
namespaceId,
|
|
163
156
|
expected: contractPK.columns.join(', '),
|
|
164
157
|
actual: schemaPK.columns.join(', '),
|
|
165
158
|
message: `Table "${tableName}" has primary key mismatch: expected columns [${contractPK.columns.join(', ')}], got [${schemaPK.columns.join(', ')}]`,
|
|
166
|
-
};
|
|
167
|
-
|
|
168
|
-
return outcome === 'suppress' ? 'pass' : outcome;
|
|
159
|
+
});
|
|
160
|
+
return 'fail';
|
|
169
161
|
}
|
|
170
162
|
|
|
163
|
+
// Name differences are ignored for semantic satisfaction.
|
|
164
|
+
// Names are persisted for deterministic DDL and diagnostics but are not identity.
|
|
165
|
+
|
|
171
166
|
return 'pass';
|
|
172
167
|
}
|
|
173
168
|
|
|
@@ -184,7 +179,6 @@ export function verifyForeignKeys(
|
|
|
184
179
|
tableName: string,
|
|
185
180
|
namespaceId: string,
|
|
186
181
|
tablePath: string,
|
|
187
|
-
tableControlPolicy: ControlPolicy,
|
|
188
182
|
issues: SchemaIssue[],
|
|
189
183
|
strict: boolean,
|
|
190
184
|
): SchemaVerificationNode[] {
|
|
@@ -213,62 +207,52 @@ export function verifyForeignKeys(
|
|
|
213
207
|
});
|
|
214
208
|
|
|
215
209
|
if (!matchingFK) {
|
|
216
|
-
|
|
210
|
+
issues.push({
|
|
217
211
|
kind: 'foreign_key_mismatch',
|
|
218
212
|
table: tableName,
|
|
219
213
|
namespaceId,
|
|
220
214
|
expected: `${contractFK.source.columns.join(', ')} -> ${contractFK.target.tableName}(${contractFK.target.columns.join(', ')})`,
|
|
221
215
|
message: `Table "${tableName}" is missing foreign key: ${contractFK.source.columns.join(', ')} -> ${contractFK.target.tableName}(${contractFK.target.columns.join(', ')})`,
|
|
222
|
-
};
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
{
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
actual: undefined,
|
|
235
|
-
children: [],
|
|
236
|
-
},
|
|
237
|
-
issues,
|
|
238
|
-
nodes,
|
|
239
|
-
);
|
|
216
|
+
});
|
|
217
|
+
nodes.push({
|
|
218
|
+
status: 'fail',
|
|
219
|
+
kind: 'foreignKey',
|
|
220
|
+
name: `foreignKey(${contractFK.source.columns.join(', ')})`,
|
|
221
|
+
contractPath: fkPath,
|
|
222
|
+
code: 'foreign_key_mismatch',
|
|
223
|
+
message: 'Foreign key missing',
|
|
224
|
+
expected: contractFK,
|
|
225
|
+
actual: undefined,
|
|
226
|
+
children: [],
|
|
227
|
+
});
|
|
240
228
|
} else {
|
|
241
229
|
const actionMismatches = getReferentialActionMismatches(contractFK, matchingFK);
|
|
242
230
|
if (actionMismatches.length > 0) {
|
|
243
231
|
const combinedMessage = actionMismatches.map((m) => m.message).join('; ');
|
|
244
232
|
const combinedExpected = actionMismatches.map((m) => m.expected).join(', ');
|
|
245
233
|
const combinedActual = actionMismatches.map((m) => m.actual).join(', ');
|
|
246
|
-
|
|
234
|
+
issues.push({
|
|
247
235
|
kind: 'foreign_key_mismatch',
|
|
248
236
|
table: tableName,
|
|
249
237
|
namespaceId,
|
|
238
|
+
// Set indexOrConstraint so the planner classifies this as a non-additive
|
|
239
|
+
// conflict (existing FK with wrong actions cannot be fixed additively).
|
|
250
240
|
indexOrConstraint: matchingFK.name ?? `fk(${contractFK.source.columns.join(',')})`,
|
|
251
241
|
expected: combinedExpected,
|
|
252
242
|
actual: combinedActual,
|
|
253
243
|
message: `Table "${tableName}" foreign key ${contractFK.source.columns.join(', ')} -> ${contractFK.target.tableName}: ${combinedMessage}`,
|
|
254
|
-
};
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
{
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
actual: matchingFK,
|
|
267
|
-
children: [],
|
|
268
|
-
},
|
|
269
|
-
issues,
|
|
270
|
-
nodes,
|
|
271
|
-
);
|
|
244
|
+
});
|
|
245
|
+
nodes.push({
|
|
246
|
+
status: 'fail',
|
|
247
|
+
kind: 'foreignKey',
|
|
248
|
+
name: `foreignKey(${contractFK.source.columns.join(', ')})`,
|
|
249
|
+
contractPath: fkPath,
|
|
250
|
+
code: 'foreign_key_mismatch',
|
|
251
|
+
message: combinedMessage,
|
|
252
|
+
expected: contractFK,
|
|
253
|
+
actual: matchingFK,
|
|
254
|
+
children: [],
|
|
255
|
+
});
|
|
272
256
|
} else {
|
|
273
257
|
nodes.push({
|
|
274
258
|
status: 'pass',
|
|
@@ -302,30 +286,24 @@ export function verifyForeignKeys(
|
|
|
302
286
|
});
|
|
303
287
|
|
|
304
288
|
if (!matchingFK) {
|
|
305
|
-
|
|
289
|
+
issues.push({
|
|
306
290
|
kind: 'extra_foreign_key',
|
|
307
291
|
table: tableName,
|
|
308
292
|
namespaceId,
|
|
309
293
|
indexOrConstraint: schemaFK.name ?? `fk(${schemaFK.columns.join(',')})`,
|
|
310
294
|
message: `Extra foreign key found in database (not in contract): ${schemaFK.columns.join(', ')} -> ${schemaFK.referencedTable}(${schemaFK.referencedColumns.join(', ')})`,
|
|
311
|
-
};
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
{
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
actual: schemaFK,
|
|
324
|
-
children: [],
|
|
325
|
-
},
|
|
326
|
-
issues,
|
|
327
|
-
nodes,
|
|
328
|
-
);
|
|
295
|
+
});
|
|
296
|
+
nodes.push({
|
|
297
|
+
status: 'fail',
|
|
298
|
+
kind: 'foreignKey',
|
|
299
|
+
name: `foreignKey(${schemaFK.columns.join(', ')})`,
|
|
300
|
+
contractPath: `${tablePath}.foreignKeys[${schemaFK.columns.join(',')}]`,
|
|
301
|
+
code: 'extra_foreign_key',
|
|
302
|
+
message: 'Extra foreign key found',
|
|
303
|
+
expected: undefined,
|
|
304
|
+
actual: schemaFK,
|
|
305
|
+
children: [],
|
|
306
|
+
});
|
|
329
307
|
}
|
|
330
308
|
}
|
|
331
309
|
}
|
|
@@ -351,7 +329,6 @@ export function verifyUniqueConstraints(
|
|
|
351
329
|
tableName: string,
|
|
352
330
|
namespaceId: string,
|
|
353
331
|
tablePath: string,
|
|
354
|
-
tableControlPolicy: ControlPolicy,
|
|
355
332
|
issues: SchemaIssue[],
|
|
356
333
|
strict: boolean,
|
|
357
334
|
): SchemaVerificationNode[] {
|
|
@@ -372,31 +349,27 @@ export function verifyUniqueConstraints(
|
|
|
372
349
|
schemaIndexes.find((idx) => idx.unique && arraysEqual(idx.columns, contractUnique.columns));
|
|
373
350
|
|
|
374
351
|
if (!matchingUnique && !matchingUniqueIndex) {
|
|
375
|
-
|
|
352
|
+
issues.push({
|
|
376
353
|
kind: 'unique_constraint_mismatch',
|
|
377
354
|
table: tableName,
|
|
378
355
|
namespaceId,
|
|
379
356
|
expected: contractUnique.columns.join(', '),
|
|
380
357
|
message: `Table "${tableName}" is missing unique constraint: ${contractUnique.columns.join(', ')}`,
|
|
381
|
-
};
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
{
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
actual: undefined,
|
|
394
|
-
children: [],
|
|
395
|
-
},
|
|
396
|
-
issues,
|
|
397
|
-
nodes,
|
|
398
|
-
);
|
|
358
|
+
});
|
|
359
|
+
nodes.push({
|
|
360
|
+
status: 'fail',
|
|
361
|
+
kind: 'unique',
|
|
362
|
+
name: `unique(${contractUnique.columns.join(', ')})`,
|
|
363
|
+
contractPath: uniquePath,
|
|
364
|
+
code: 'unique_constraint_mismatch',
|
|
365
|
+
message: 'Unique constraint missing',
|
|
366
|
+
expected: contractUnique,
|
|
367
|
+
actual: undefined,
|
|
368
|
+
children: [],
|
|
369
|
+
});
|
|
399
370
|
} else {
|
|
371
|
+
// Name differences are ignored for semantic satisfaction.
|
|
372
|
+
// Names are persisted for deterministic DDL and diagnostics but are not identity.
|
|
400
373
|
nodes.push({
|
|
401
374
|
status: 'pass',
|
|
402
375
|
kind: 'unique',
|
|
@@ -411,6 +384,7 @@ export function verifyUniqueConstraints(
|
|
|
411
384
|
}
|
|
412
385
|
}
|
|
413
386
|
|
|
387
|
+
// Check for extra uniques in strict mode
|
|
414
388
|
if (strict) {
|
|
415
389
|
for (const schemaUnique of schemaUniques) {
|
|
416
390
|
const matchingUnique = contractUniques.find((u) =>
|
|
@@ -418,30 +392,24 @@ export function verifyUniqueConstraints(
|
|
|
418
392
|
);
|
|
419
393
|
|
|
420
394
|
if (!matchingUnique) {
|
|
421
|
-
|
|
395
|
+
issues.push({
|
|
422
396
|
kind: 'extra_unique_constraint',
|
|
423
397
|
table: tableName,
|
|
424
398
|
namespaceId,
|
|
425
399
|
indexOrConstraint: schemaUnique.name ?? `unique(${schemaUnique.columns.join(',')})`,
|
|
426
400
|
message: `Extra unique constraint found in database (not in contract): ${schemaUnique.columns.join(', ')}`,
|
|
427
|
-
};
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
{
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
actual: schemaUnique,
|
|
440
|
-
children: [],
|
|
441
|
-
},
|
|
442
|
-
issues,
|
|
443
|
-
nodes,
|
|
444
|
-
);
|
|
401
|
+
});
|
|
402
|
+
nodes.push({
|
|
403
|
+
status: 'fail',
|
|
404
|
+
kind: 'unique',
|
|
405
|
+
name: `unique(${schemaUnique.columns.join(', ')})`,
|
|
406
|
+
contractPath: `${tablePath}.uniques[${schemaUnique.columns.join(',')}]`,
|
|
407
|
+
code: 'extra_unique_constraint',
|
|
408
|
+
message: 'Extra unique constraint found',
|
|
409
|
+
expected: undefined,
|
|
410
|
+
actual: schemaUnique,
|
|
411
|
+
children: [],
|
|
412
|
+
});
|
|
445
413
|
}
|
|
446
414
|
}
|
|
447
415
|
}
|
|
@@ -467,7 +435,6 @@ export function verifyIndexes(
|
|
|
467
435
|
tableName: string,
|
|
468
436
|
namespaceId: string,
|
|
469
437
|
tablePath: string,
|
|
470
|
-
tableControlPolicy: ControlPolicy,
|
|
471
438
|
issues: SchemaIssue[],
|
|
472
439
|
strict: boolean,
|
|
473
440
|
): SchemaVerificationNode[] {
|
|
@@ -494,31 +461,27 @@ export function verifyIndexes(
|
|
|
494
461
|
schemaUniques.find((u) => arraysEqual(u.columns, contractIndex.columns));
|
|
495
462
|
|
|
496
463
|
if (!matchingIndex && !matchingUniqueConstraint) {
|
|
497
|
-
|
|
464
|
+
issues.push({
|
|
498
465
|
kind: 'index_mismatch',
|
|
499
466
|
table: tableName,
|
|
500
467
|
namespaceId,
|
|
501
468
|
expected: contractIndex.columns.join(', '),
|
|
502
469
|
message: `Table "${tableName}" is missing index: ${contractIndex.columns.join(', ')}`,
|
|
503
|
-
};
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
{
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
actual: undefined,
|
|
516
|
-
children: [],
|
|
517
|
-
},
|
|
518
|
-
issues,
|
|
519
|
-
nodes,
|
|
520
|
-
);
|
|
470
|
+
});
|
|
471
|
+
nodes.push({
|
|
472
|
+
status: 'fail',
|
|
473
|
+
kind: 'index',
|
|
474
|
+
name: `index(${contractIndex.columns.join(', ')})`,
|
|
475
|
+
contractPath: indexPath,
|
|
476
|
+
code: 'index_mismatch',
|
|
477
|
+
message: 'Index missing',
|
|
478
|
+
expected: contractIndex,
|
|
479
|
+
actual: undefined,
|
|
480
|
+
children: [],
|
|
481
|
+
});
|
|
521
482
|
} else {
|
|
483
|
+
// Name differences are ignored for semantic satisfaction.
|
|
484
|
+
// Names are persisted for deterministic DDL and diagnostics but are not identity.
|
|
522
485
|
nodes.push({
|
|
523
486
|
status: 'pass',
|
|
524
487
|
kind: 'index',
|
|
@@ -533,8 +496,10 @@ export function verifyIndexes(
|
|
|
533
496
|
}
|
|
534
497
|
}
|
|
535
498
|
|
|
499
|
+
// Check for extra indexes in strict mode
|
|
536
500
|
if (strict) {
|
|
537
501
|
for (const schemaIndex of schemaIndexes) {
|
|
502
|
+
// Skip unique indexes (they're handled as unique constraints)
|
|
538
503
|
if (schemaIndex.unique) {
|
|
539
504
|
continue;
|
|
540
505
|
}
|
|
@@ -545,30 +510,24 @@ export function verifyIndexes(
|
|
|
545
510
|
);
|
|
546
511
|
|
|
547
512
|
if (!matchingIndex) {
|
|
548
|
-
|
|
513
|
+
issues.push({
|
|
549
514
|
kind: 'extra_index',
|
|
550
515
|
table: tableName,
|
|
551
516
|
namespaceId,
|
|
552
517
|
indexOrConstraint: schemaIndex.name ?? `idx(${schemaIndex.columns.join(',')})`,
|
|
553
518
|
message: `Extra index found in database (not in contract): ${schemaIndex.columns.join(', ')}`,
|
|
554
|
-
};
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
{
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
actual: schemaIndex,
|
|
567
|
-
children: [],
|
|
568
|
-
},
|
|
569
|
-
issues,
|
|
570
|
-
nodes,
|
|
571
|
-
);
|
|
519
|
+
});
|
|
520
|
+
nodes.push({
|
|
521
|
+
status: 'fail',
|
|
522
|
+
kind: 'index',
|
|
523
|
+
name: `index(${schemaIndex.columns.join(', ')})`,
|
|
524
|
+
contractPath: `${tablePath}.indexes[${schemaIndex.columns.join(',')}]`,
|
|
525
|
+
code: 'extra_index',
|
|
526
|
+
message: 'Extra index found',
|
|
527
|
+
expected: undefined,
|
|
528
|
+
actual: schemaIndex,
|
|
529
|
+
children: [],
|
|
530
|
+
});
|
|
572
531
|
}
|
|
573
532
|
}
|
|
574
533
|
}
|