@prisma-next/target-postgres 0.3.0-pr.99.6 → 0.4.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/LICENSE +201 -0
- package/README.md +17 -8
- package/dist/control.d.mts +19 -0
- package/dist/control.d.mts.map +1 -0
- package/dist/control.mjs +5382 -0
- package/dist/control.mjs.map +1 -0
- package/dist/descriptor-meta-CAf16lsJ.mjs +32 -0
- package/dist/descriptor-meta-CAf16lsJ.mjs.map +1 -0
- package/dist/migration-builders.d.mts +88 -0
- package/dist/migration-builders.d.mts.map +1 -0
- package/dist/migration-builders.mjs +3 -0
- package/dist/operation-descriptors-CxymFSgK.mjs +52 -0
- package/dist/operation-descriptors-CxymFSgK.mjs.map +1 -0
- package/dist/pack.d.mts +45 -0
- package/dist/pack.d.mts.map +1 -0
- package/dist/pack.mjs +9 -0
- package/dist/pack.mjs.map +1 -0
- package/dist/runtime.d.mts +9 -0
- package/dist/runtime.d.mts.map +1 -0
- package/dist/runtime.mjs +20 -0
- package/dist/runtime.mjs.map +1 -0
- package/package.json +31 -29
- package/src/core/authoring.ts +15 -0
- package/src/core/descriptor-meta.ts +5 -0
- package/src/core/migrations/descriptor-planner.ts +466 -0
- package/src/core/migrations/operation-descriptors.ts +166 -0
- package/src/core/migrations/operation-resolver.ts +929 -0
- package/src/core/migrations/planner-ddl-builders.ts +256 -0
- package/src/core/migrations/planner-identity-values.ts +135 -0
- package/src/core/migrations/planner-recipes.ts +91 -0
- package/src/core/migrations/planner-reconciliation.ts +798 -0
- package/src/core/migrations/planner-schema-lookup.ts +54 -0
- package/src/core/migrations/planner-sql-checks.ts +322 -0
- package/src/core/migrations/planner-strategies.ts +262 -0
- package/src/core/migrations/planner-target-details.ts +38 -0
- package/src/core/migrations/planner-type-resolution.ts +26 -0
- package/src/core/migrations/planner.ts +410 -460
- package/src/core/migrations/runner.ts +134 -38
- package/src/core/migrations/statement-builders.ts +6 -6
- package/src/core/types.ts +5 -0
- package/src/exports/control.ts +182 -12
- package/src/exports/migration-builders.ts +56 -0
- package/src/exports/pack.ts +7 -3
- package/src/exports/runtime.ts +6 -12
- package/dist/chunk-RKEXRSSI.js +0 -14
- package/dist/chunk-RKEXRSSI.js.map +0 -1
- package/dist/core/descriptor-meta.d.ts +0 -9
- package/dist/core/descriptor-meta.d.ts.map +0 -1
- package/dist/core/migrations/planner.d.ts +0 -14
- package/dist/core/migrations/planner.d.ts.map +0 -1
- package/dist/core/migrations/runner.d.ts +0 -8
- package/dist/core/migrations/runner.d.ts.map +0 -1
- package/dist/core/migrations/statement-builders.d.ts +0 -30
- package/dist/core/migrations/statement-builders.d.ts.map +0 -1
- package/dist/exports/control.d.ts +0 -8
- package/dist/exports/control.d.ts.map +0 -1
- package/dist/exports/control.js +0 -1260
- package/dist/exports/control.js.map +0 -1
- package/dist/exports/pack.d.ts +0 -4
- package/dist/exports/pack.d.ts.map +0 -1
- package/dist/exports/pack.js +0 -11
- package/dist/exports/pack.js.map +0 -1
- package/dist/exports/runtime.d.ts +0 -12
- package/dist/exports/runtime.d.ts.map +0 -1
- package/dist/exports/runtime.js +0 -19
- package/dist/exports/runtime.js.map +0 -1
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
import {
|
|
2
|
+
normalizeSchemaNativeType,
|
|
3
|
+
parsePostgresDefault,
|
|
4
|
+
} from '@prisma-next/adapter-postgres/control';
|
|
1
5
|
import type { ContractMarkerRecord } from '@prisma-next/contract/types';
|
|
2
6
|
import type {
|
|
3
7
|
MigrationOperationPolicy,
|
|
@@ -13,10 +17,12 @@ import type {
|
|
|
13
17
|
import { runnerFailure, runnerSuccess } from '@prisma-next/family-sql/control';
|
|
14
18
|
import { verifySqlSchema } from '@prisma-next/family-sql/schema-verify';
|
|
15
19
|
import { readMarker } from '@prisma-next/family-sql/verify';
|
|
20
|
+
import type { DataTransformOperation } from '@prisma-next/framework-components/control';
|
|
16
21
|
import { SqlQueryError } from '@prisma-next/sql-errors';
|
|
22
|
+
import { ifDefined } from '@prisma-next/utils/defined';
|
|
17
23
|
import type { Result } from '@prisma-next/utils/result';
|
|
18
24
|
import { ok, okVoid } from '@prisma-next/utils/result';
|
|
19
|
-
import type { PostgresPlanTargetDetails } from './planner';
|
|
25
|
+
import type { PostgresPlanTargetDetails } from './planner-target-details';
|
|
20
26
|
import {
|
|
21
27
|
buildLedgerInsertStatement,
|
|
22
28
|
buildWriteMarkerStatements,
|
|
@@ -41,6 +47,18 @@ const DEFAULT_CONFIG: RunnerConfig = {
|
|
|
41
47
|
|
|
42
48
|
const LOCK_DOMAIN = 'prisma_next.contract.marker';
|
|
43
49
|
|
|
50
|
+
function isDataTransformOperation(op: unknown): op is DataTransformOperation {
|
|
51
|
+
return (
|
|
52
|
+
typeof op === 'object' &&
|
|
53
|
+
op !== null &&
|
|
54
|
+
'operationClass' in op &&
|
|
55
|
+
(op as { operationClass: string }).operationClass === 'data' &&
|
|
56
|
+
'name' in op &&
|
|
57
|
+
'check' in op &&
|
|
58
|
+
'run' in op
|
|
59
|
+
);
|
|
60
|
+
}
|
|
61
|
+
|
|
44
62
|
/**
|
|
45
63
|
* Deep clones and freezes a record object to prevent mutation.
|
|
46
64
|
* Recursively clones nested objects and arrays to ensure complete isolation.
|
|
@@ -112,11 +130,12 @@ class PostgresMigrationRunner implements SqlMigrationRunner<PostgresPlanTargetDe
|
|
|
112
130
|
return markerCheck;
|
|
113
131
|
}
|
|
114
132
|
|
|
115
|
-
//
|
|
133
|
+
// db update (origin: null) always applies; migration-apply (origin set) skips if marker matches.
|
|
116
134
|
const markerAtDestination = this.markerMatchesDestination(existingMarker, options.plan);
|
|
135
|
+
const skipOperations = markerAtDestination && options.plan.origin != null;
|
|
117
136
|
let applyValue: ApplyPlanSuccessValue;
|
|
118
137
|
|
|
119
|
-
if (
|
|
138
|
+
if (skipOperations) {
|
|
120
139
|
applyValue = { operationsExecuted: 0, executedOperations: [] };
|
|
121
140
|
} else {
|
|
122
141
|
const applyResult = await this.applyPlan(driver, options);
|
|
@@ -130,7 +149,7 @@ class PostgresMigrationRunner implements SqlMigrationRunner<PostgresPlanTargetDe
|
|
|
130
149
|
// Step 1: Introspect live schema (DB I/O, family-owned)
|
|
131
150
|
const schemaIR = await this.family.introspect({
|
|
132
151
|
driver,
|
|
133
|
-
|
|
152
|
+
contract: options.destinationContract,
|
|
134
153
|
});
|
|
135
154
|
|
|
136
155
|
// Step 2: Pure verification (no DB I/O)
|
|
@@ -141,6 +160,8 @@ class PostgresMigrationRunner implements SqlMigrationRunner<PostgresPlanTargetDe
|
|
|
141
160
|
context: options.context ?? {},
|
|
142
161
|
typeMetadataRegistry: this.family.typeMetadataRegistry,
|
|
143
162
|
frameworkComponents: options.frameworkComponents,
|
|
163
|
+
normalizeDefault: parsePostgresDefault,
|
|
164
|
+
normalizeNativeType: normalizeSchemaNativeType,
|
|
144
165
|
});
|
|
145
166
|
if (!schemaVerifyResult.ok) {
|
|
146
167
|
return runnerFailure('SCHEMA_VERIFY_FAILED', schemaVerifyResult.summary, {
|
|
@@ -182,6 +203,19 @@ class PostgresMigrationRunner implements SqlMigrationRunner<PostgresPlanTargetDe
|
|
|
182
203
|
for (const operation of options.plan.operations) {
|
|
183
204
|
options.callbacks?.onOperationStart?.(operation);
|
|
184
205
|
try {
|
|
206
|
+
// Data transform operations have a different execution lifecycle
|
|
207
|
+
if (operation.operationClass === 'data' && isDataTransformOperation(operation)) {
|
|
208
|
+
const dtResult = await this.executeDataTransform(driver, operation, {
|
|
209
|
+
runIdempotency,
|
|
210
|
+
});
|
|
211
|
+
if (!dtResult.ok) {
|
|
212
|
+
return dtResult;
|
|
213
|
+
}
|
|
214
|
+
executedOperations.push(operation);
|
|
215
|
+
operationsExecuted += 1;
|
|
216
|
+
continue;
|
|
217
|
+
}
|
|
218
|
+
|
|
185
219
|
// Idempotency probe: only run if both postchecks and idempotency checks are enabled
|
|
186
220
|
if (runPostchecks && runIdempotency) {
|
|
187
221
|
const postcheckAlreadySatisfied = await this.expectationsAreSatisfied(
|
|
@@ -234,6 +268,80 @@ class PostgresMigrationRunner implements SqlMigrationRunner<PostgresPlanTargetDe
|
|
|
234
268
|
return ok({ operationsExecuted, executedOperations });
|
|
235
269
|
}
|
|
236
270
|
|
|
271
|
+
/**
|
|
272
|
+
* Executes a data transform operation with the check → (skip or run) → check lifecycle.
|
|
273
|
+
*
|
|
274
|
+
* 1. If check is a query AST: render to SQL, execute. Empty result = already applied (skip).
|
|
275
|
+
* 2. If check is `true`: always skip. If `false`: always run.
|
|
276
|
+
* 3. Execute run ASTs (rendered to SQL) sequentially.
|
|
277
|
+
* 4. Re-execute check as post-run validation. If violations remain, fail.
|
|
278
|
+
*/
|
|
279
|
+
private async executeDataTransform(
|
|
280
|
+
driver: SqlMigrationRunnerExecuteOptions<PostgresPlanTargetDetails>['driver'],
|
|
281
|
+
op: DataTransformOperation,
|
|
282
|
+
options: { runIdempotency: boolean },
|
|
283
|
+
): Promise<Result<void, SqlMigrationRunnerFailure>> {
|
|
284
|
+
// Step 1: Check (skip guard)
|
|
285
|
+
if (op.check === true) {
|
|
286
|
+
// Always skip, regardless of idempotency setting
|
|
287
|
+
return okVoid();
|
|
288
|
+
}
|
|
289
|
+
if (options.runIdempotency && op.check !== null && op.check !== false) {
|
|
290
|
+
const checkResult = await driver.query(op.check.sql, op.check.params);
|
|
291
|
+
if (checkResult.rows.length === 0) {
|
|
292
|
+
// No violations — already applied, skip
|
|
293
|
+
return okVoid();
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
// Step 2: Execute run steps
|
|
298
|
+
if (op.run) {
|
|
299
|
+
for (const plan of op.run) {
|
|
300
|
+
try {
|
|
301
|
+
await driver.query(plan.sql, plan.params);
|
|
302
|
+
} catch (error: unknown) {
|
|
303
|
+
if (SqlQueryError.is(error)) {
|
|
304
|
+
return runnerFailure(
|
|
305
|
+
'EXECUTION_FAILED',
|
|
306
|
+
`Data transform "${op.name}" failed: ${error.message}`,
|
|
307
|
+
{
|
|
308
|
+
why: error.message,
|
|
309
|
+
meta: {
|
|
310
|
+
operationId: op.id,
|
|
311
|
+
dataTransformName: op.name,
|
|
312
|
+
sql: plan.sql,
|
|
313
|
+
sqlState: error.sqlState,
|
|
314
|
+
},
|
|
315
|
+
},
|
|
316
|
+
);
|
|
317
|
+
}
|
|
318
|
+
throw error;
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
// Step 3: Post-run validation (check again)
|
|
324
|
+
if (op.check !== null && op.check !== false) {
|
|
325
|
+
const checkResult = await driver.query(op.check.sql, op.check.params);
|
|
326
|
+
if (checkResult.rows.length > 0) {
|
|
327
|
+
return runnerFailure(
|
|
328
|
+
'POSTCHECK_FAILED',
|
|
329
|
+
`Data transform "${op.name}" did not resolve all violations (${checkResult.rows.length} remaining)`,
|
|
330
|
+
{
|
|
331
|
+
why: `After executing the data transform, the check query still returns ${checkResult.rows.length} violation(s).`,
|
|
332
|
+
meta: {
|
|
333
|
+
operationId: op.id,
|
|
334
|
+
dataTransformName: op.name,
|
|
335
|
+
remainingViolations: checkResult.rows.length,
|
|
336
|
+
},
|
|
337
|
+
},
|
|
338
|
+
);
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
return okVoid();
|
|
343
|
+
}
|
|
344
|
+
|
|
237
345
|
private async ensureControlTables(
|
|
238
346
|
driver: SqlMigrationRunnerExecuteOptions<PostgresPlanTargetDetails>['driver'],
|
|
239
347
|
): Promise<void> {
|
|
@@ -371,13 +479,13 @@ class PostgresMigrationRunner implements SqlMigrationRunner<PostgresPlanTargetDe
|
|
|
371
479
|
return Object.freeze({
|
|
372
480
|
id: operation.id,
|
|
373
481
|
label: operation.label,
|
|
374
|
-
...(
|
|
482
|
+
...ifDefined('summary', operation.summary),
|
|
375
483
|
operationClass: operation.operationClass,
|
|
376
484
|
target: operation.target, // Already frozen from plan creation
|
|
377
485
|
precheck: Object.freeze([]),
|
|
378
486
|
execute: Object.freeze([]),
|
|
379
487
|
postcheck: frozenPostcheck,
|
|
380
|
-
...(operation.meta || mergedMeta ?
|
|
488
|
+
...ifDefined('meta', operation.meta || mergedMeta ? mergedMeta : undefined),
|
|
381
489
|
});
|
|
382
490
|
}
|
|
383
491
|
|
|
@@ -388,7 +496,7 @@ class PostgresMigrationRunner implements SqlMigrationRunner<PostgresPlanTargetDe
|
|
|
388
496
|
if (!marker) {
|
|
389
497
|
return false;
|
|
390
498
|
}
|
|
391
|
-
if (marker.
|
|
499
|
+
if (marker.storageHash !== plan.destination.storageHash) {
|
|
392
500
|
return false;
|
|
393
501
|
}
|
|
394
502
|
if (plan.destination.profileHash && marker.profileHash !== plan.destination.profileHash) {
|
|
@@ -427,43 +535,31 @@ class PostgresMigrationRunner implements SqlMigrationRunner<PostgresPlanTargetDe
|
|
|
427
535
|
): Result<void, SqlMigrationRunnerFailure> {
|
|
428
536
|
const origin = plan.origin ?? null;
|
|
429
537
|
if (!origin) {
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
return okVoid();
|
|
435
|
-
}
|
|
436
|
-
return runnerFailure(
|
|
437
|
-
'MARKER_ORIGIN_MISMATCH',
|
|
438
|
-
`Existing contract marker (${marker.coreHash}) does not match plan origin (no marker expected).`,
|
|
439
|
-
{
|
|
440
|
-
meta: {
|
|
441
|
-
markerCoreHash: marker.coreHash,
|
|
442
|
-
expectedOrigin: null,
|
|
443
|
-
},
|
|
444
|
-
},
|
|
445
|
-
);
|
|
538
|
+
// No origin assertion on the plan — the caller does not want origin validation.
|
|
539
|
+
// This is the case for `db update`, which introspects the live schema and does not
|
|
540
|
+
// rely on marker continuity. `db init` handles its own marker checks before the runner.
|
|
541
|
+
return okVoid();
|
|
446
542
|
}
|
|
447
543
|
|
|
448
544
|
if (!marker) {
|
|
449
545
|
return runnerFailure(
|
|
450
546
|
'MARKER_ORIGIN_MISMATCH',
|
|
451
|
-
`Missing contract marker: expected origin
|
|
547
|
+
`Missing contract marker: expected origin storage hash ${origin.storageHash}.`,
|
|
452
548
|
{
|
|
453
549
|
meta: {
|
|
454
|
-
|
|
550
|
+
expectedOriginStorageHash: origin.storageHash,
|
|
455
551
|
},
|
|
456
552
|
},
|
|
457
553
|
);
|
|
458
554
|
}
|
|
459
|
-
if (marker.
|
|
555
|
+
if (marker.storageHash !== origin.storageHash) {
|
|
460
556
|
return runnerFailure(
|
|
461
557
|
'MARKER_ORIGIN_MISMATCH',
|
|
462
|
-
`Existing contract marker (${marker.
|
|
558
|
+
`Existing contract marker (${marker.storageHash}) does not match plan origin (${origin.storageHash}).`,
|
|
463
559
|
{
|
|
464
560
|
meta: {
|
|
465
|
-
|
|
466
|
-
|
|
561
|
+
markerStorageHash: marker.storageHash,
|
|
562
|
+
expectedOriginStorageHash: origin.storageHash,
|
|
467
563
|
},
|
|
468
564
|
},
|
|
469
565
|
);
|
|
@@ -487,14 +583,14 @@ class PostgresMigrationRunner implements SqlMigrationRunner<PostgresPlanTargetDe
|
|
|
487
583
|
destination: SqlMigrationPlanContractInfo,
|
|
488
584
|
contract: SqlMigrationRunnerExecuteOptions<PostgresPlanTargetDetails>['destinationContract'],
|
|
489
585
|
): Result<void, SqlMigrationRunnerFailure> {
|
|
490
|
-
if (destination.
|
|
586
|
+
if (destination.storageHash !== contract.storage.storageHash) {
|
|
491
587
|
return runnerFailure(
|
|
492
588
|
'DESTINATION_CONTRACT_MISMATCH',
|
|
493
|
-
`Plan destination
|
|
589
|
+
`Plan destination storage hash (${destination.storageHash}) does not match provided contract storage hash (${contract.storage.storageHash}).`,
|
|
494
590
|
{
|
|
495
591
|
meta: {
|
|
496
|
-
|
|
497
|
-
|
|
592
|
+
planStorageHash: destination.storageHash,
|
|
593
|
+
contractStorageHash: contract.storage.storageHash,
|
|
498
594
|
},
|
|
499
595
|
},
|
|
500
596
|
);
|
|
@@ -524,11 +620,11 @@ class PostgresMigrationRunner implements SqlMigrationRunner<PostgresPlanTargetDe
|
|
|
524
620
|
existingMarker: ContractMarkerRecord | null,
|
|
525
621
|
): Promise<void> {
|
|
526
622
|
const writeStatements = buildWriteMarkerStatements({
|
|
527
|
-
|
|
623
|
+
storageHash: options.plan.destination.storageHash,
|
|
528
624
|
profileHash:
|
|
529
625
|
options.plan.destination.profileHash ??
|
|
530
626
|
options.destinationContract.profileHash ??
|
|
531
|
-
options.plan.destination.
|
|
627
|
+
options.plan.destination.storageHash,
|
|
532
628
|
contractJson: options.destinationContract,
|
|
533
629
|
canonicalVersion: null,
|
|
534
630
|
meta: {},
|
|
@@ -544,13 +640,13 @@ class PostgresMigrationRunner implements SqlMigrationRunner<PostgresPlanTargetDe
|
|
|
544
640
|
executedOperations: readonly SqlMigrationPlanOperation<PostgresPlanTargetDetails>[],
|
|
545
641
|
): Promise<void> {
|
|
546
642
|
const ledgerStatement = buildLedgerInsertStatement({
|
|
547
|
-
|
|
643
|
+
originStorageHash: existingMarker?.storageHash ?? null,
|
|
548
644
|
originProfileHash: existingMarker?.profileHash ?? null,
|
|
549
|
-
|
|
645
|
+
destinationStorageHash: options.plan.destination.storageHash,
|
|
550
646
|
destinationProfileHash:
|
|
551
647
|
options.plan.destination.profileHash ??
|
|
552
648
|
options.destinationContract.profileHash ??
|
|
553
|
-
options.plan.destination.
|
|
649
|
+
options.plan.destination.storageHash,
|
|
554
650
|
contractJsonBefore: existingMarker?.contractJson ?? null,
|
|
555
651
|
contractJsonAfter: options.destinationContract,
|
|
556
652
|
operations: executedOperations,
|
|
@@ -38,7 +38,7 @@ export const ensureLedgerTableStatement: SqlStatement = {
|
|
|
38
38
|
};
|
|
39
39
|
|
|
40
40
|
export interface WriteMarkerInput {
|
|
41
|
-
readonly
|
|
41
|
+
readonly storageHash: string;
|
|
42
42
|
readonly profileHash: string;
|
|
43
43
|
readonly contractJson?: unknown;
|
|
44
44
|
readonly canonicalVersion?: number | null;
|
|
@@ -52,7 +52,7 @@ export function buildWriteMarkerStatements(input: WriteMarkerInput): {
|
|
|
52
52
|
} {
|
|
53
53
|
const params: readonly unknown[] = [
|
|
54
54
|
1,
|
|
55
|
-
input.
|
|
55
|
+
input.storageHash,
|
|
56
56
|
input.profileHash,
|
|
57
57
|
jsonParam(input.contractJson),
|
|
58
58
|
input.canonicalVersion ?? null,
|
|
@@ -99,9 +99,9 @@ export function buildWriteMarkerStatements(input: WriteMarkerInput): {
|
|
|
99
99
|
}
|
|
100
100
|
|
|
101
101
|
export interface LedgerInsertInput {
|
|
102
|
-
readonly
|
|
102
|
+
readonly originStorageHash?: string | null;
|
|
103
103
|
readonly originProfileHash?: string | null;
|
|
104
|
-
readonly
|
|
104
|
+
readonly destinationStorageHash: string;
|
|
105
105
|
readonly destinationProfileHash?: string | null;
|
|
106
106
|
readonly contractJsonBefore?: unknown;
|
|
107
107
|
readonly contractJsonAfter?: unknown;
|
|
@@ -128,9 +128,9 @@ export function buildLedgerInsertStatement(input: LedgerInsertInput): SqlStateme
|
|
|
128
128
|
$7::jsonb
|
|
129
129
|
)`,
|
|
130
130
|
params: [
|
|
131
|
-
input.
|
|
131
|
+
input.originStorageHash ?? null,
|
|
132
132
|
input.originProfileHash ?? null,
|
|
133
|
-
input.
|
|
133
|
+
input.destinationStorageHash,
|
|
134
134
|
input.destinationProfileHash ?? null,
|
|
135
135
|
jsonParam(input.contractJsonBefore),
|
|
136
136
|
jsonParam(input.contractJsonAfter),
|
package/src/exports/control.ts
CHANGED
|
@@ -1,28 +1,130 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
} from '@prisma-next/
|
|
1
|
+
import {
|
|
2
|
+
normalizeSchemaNativeType,
|
|
3
|
+
parsePostgresDefault,
|
|
4
|
+
} from '@prisma-next/adapter-postgres/control';
|
|
5
|
+
import type { ColumnDefault, Contract } from '@prisma-next/contract/types';
|
|
6
6
|
import type {
|
|
7
7
|
SqlControlFamilyInstance,
|
|
8
8
|
SqlControlTargetDescriptor,
|
|
9
9
|
} from '@prisma-next/family-sql/control';
|
|
10
|
+
import {
|
|
11
|
+
collectInitDependencies,
|
|
12
|
+
contractToSchemaIR,
|
|
13
|
+
extractCodecControlHooks,
|
|
14
|
+
} from '@prisma-next/family-sql/control';
|
|
15
|
+
import { MigrationDescriptorArraySchema } from '@prisma-next/family-sql/operation-descriptors';
|
|
16
|
+
import { verifySqlSchema } from '@prisma-next/family-sql/schema-verify';
|
|
17
|
+
import type { TargetBoundComponentDescriptor } from '@prisma-next/framework-components/components';
|
|
18
|
+
import type {
|
|
19
|
+
ControlTargetInstance,
|
|
20
|
+
MigrationPlanner,
|
|
21
|
+
MigrationRunner,
|
|
22
|
+
OperationDescriptor,
|
|
23
|
+
} from '@prisma-next/framework-components/control';
|
|
24
|
+
import { sql } from '@prisma-next/sql-builder/runtime';
|
|
25
|
+
import type { SqlStorage, StorageColumn } from '@prisma-next/sql-contract/types';
|
|
26
|
+
import type { SqlOperationEntry } from '@prisma-next/sql-operations';
|
|
27
|
+
import { ifDefined } from '@prisma-next/utils/defined';
|
|
28
|
+
import { type } from 'arktype';
|
|
10
29
|
import { postgresTargetDescriptorMeta } from '../core/descriptor-meta';
|
|
11
|
-
import
|
|
30
|
+
import { planDescriptors } from '../core/migrations/descriptor-planner';
|
|
31
|
+
import { resolveOperations } from '../core/migrations/operation-resolver';
|
|
12
32
|
import { createPostgresMigrationPlanner } from '../core/migrations/planner';
|
|
33
|
+
import { renderDefaultLiteral } from '../core/migrations/planner-ddl-builders';
|
|
34
|
+
import type { PostgresPlanTargetDetails } from '../core/migrations/planner-target-details';
|
|
13
35
|
import { createPostgresMigrationRunner } from '../core/migrations/runner';
|
|
14
36
|
|
|
37
|
+
function parseDescriptors(descriptors: readonly OperationDescriptor[]) {
|
|
38
|
+
const result = MigrationDescriptorArraySchema([...descriptors]);
|
|
39
|
+
if (result instanceof type.errors) {
|
|
40
|
+
throw new Error(`Invalid migration descriptors:\n${result.summary}`);
|
|
41
|
+
}
|
|
42
|
+
return result;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function collectQueryOperationTypes(
|
|
46
|
+
frameworkComponents?: ReadonlyArray<TargetBoundComponentDescriptor<'sql', 'postgres'>>,
|
|
47
|
+
): Readonly<Record<string, SqlOperationEntry>> {
|
|
48
|
+
const entries: Record<string, SqlOperationEntry> = {};
|
|
49
|
+
if (!frameworkComponents) return entries;
|
|
50
|
+
for (const component of frameworkComponents) {
|
|
51
|
+
const ops = (
|
|
52
|
+
component as {
|
|
53
|
+
queryOperations?: () => ReadonlyArray<{ method: string } & SqlOperationEntry>;
|
|
54
|
+
}
|
|
55
|
+
).queryOperations?.();
|
|
56
|
+
if (!ops) continue;
|
|
57
|
+
for (const { method, ...entry } of ops) {
|
|
58
|
+
entries[method] = entry;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
return entries;
|
|
62
|
+
}
|
|
63
|
+
|
|
15
64
|
/**
|
|
16
|
-
*
|
|
65
|
+
* Creates a SQL DSL client for migration authoring.
|
|
66
|
+
* Only the fields used by the builder are populated — operations, codecs,
|
|
67
|
+
* and types are unused by sql() and stubbed to satisfy the ExecutionContext type.
|
|
17
68
|
*/
|
|
69
|
+
function createMigrationClient(
|
|
70
|
+
toContract: Contract<SqlStorage>,
|
|
71
|
+
frameworkComponents?: ReadonlyArray<TargetBoundComponentDescriptor<'sql', 'postgres'>>,
|
|
72
|
+
) {
|
|
73
|
+
const queryOperationTypes = collectQueryOperationTypes(frameworkComponents);
|
|
74
|
+
// sql() only reads contract, queryOperations.entries(), and applyMutationDefaults
|
|
75
|
+
// from the context. The other fields are for runtime execution, not query building.
|
|
76
|
+
return sql({
|
|
77
|
+
context: {
|
|
78
|
+
contract: toContract,
|
|
79
|
+
queryOperations: { entries: () => queryOperationTypes },
|
|
80
|
+
applyMutationDefaults: () => [],
|
|
81
|
+
} as never,
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function buildNativeTypeExpander(
|
|
86
|
+
frameworkComponents?: ReadonlyArray<TargetBoundComponentDescriptor<'sql', 'postgres'>>,
|
|
87
|
+
) {
|
|
88
|
+
if (!frameworkComponents) {
|
|
89
|
+
return undefined;
|
|
90
|
+
}
|
|
91
|
+
const codecHooks = extractCodecControlHooks(frameworkComponents);
|
|
92
|
+
return (input: {
|
|
93
|
+
readonly nativeType: string;
|
|
94
|
+
readonly codecId?: string;
|
|
95
|
+
readonly typeParams?: Record<string, unknown>;
|
|
96
|
+
}) => {
|
|
97
|
+
if (!input.typeParams) return input.nativeType;
|
|
98
|
+
|
|
99
|
+
if (!input.codecId) {
|
|
100
|
+
throw new Error(
|
|
101
|
+
`Column declares typeParams for nativeType "${input.nativeType}" but has no codecId. ` +
|
|
102
|
+
'Ensure the column is associated with a codec.',
|
|
103
|
+
);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
const hooks = codecHooks.get(input.codecId);
|
|
107
|
+
if (!hooks?.expandNativeType) {
|
|
108
|
+
throw new Error(
|
|
109
|
+
`Column declares typeParams for nativeType "${input.nativeType}" ` +
|
|
110
|
+
`but no expandNativeType hook is registered for codecId "${input.codecId}". ` +
|
|
111
|
+
'Ensure the extension providing this codec is included in extensionPacks.',
|
|
112
|
+
);
|
|
113
|
+
}
|
|
114
|
+
return hooks.expandNativeType(input);
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
export function postgresRenderDefault(def: ColumnDefault, column: StorageColumn): string {
|
|
119
|
+
if (def.kind === 'function') {
|
|
120
|
+
return def.expression;
|
|
121
|
+
}
|
|
122
|
+
return renderDefaultLiteral(def.value, column);
|
|
123
|
+
}
|
|
124
|
+
|
|
18
125
|
const postgresTargetDescriptor: SqlControlTargetDescriptor<'postgres', PostgresPlanTargetDetails> =
|
|
19
126
|
{
|
|
20
127
|
...postgresTargetDescriptorMeta,
|
|
21
|
-
/**
|
|
22
|
-
* Migrations capability for CLI to access planner/runner via core types.
|
|
23
|
-
* The SQL-specific planner/runner types are compatible with the generic
|
|
24
|
-
* MigrationPlanner/MigrationRunner interfaces at runtime.
|
|
25
|
-
*/
|
|
26
128
|
migrations: {
|
|
27
129
|
createPlanner(_family: SqlControlFamilyInstance) {
|
|
28
130
|
return createPostgresMigrationPlanner() as MigrationPlanner<'sql', 'postgres'>;
|
|
@@ -30,6 +132,74 @@ const postgresTargetDescriptor: SqlControlTargetDescriptor<'postgres', PostgresP
|
|
|
30
132
|
createRunner(family) {
|
|
31
133
|
return createPostgresMigrationRunner(family) as MigrationRunner<'sql', 'postgres'>;
|
|
32
134
|
},
|
|
135
|
+
contractToSchema(contract, frameworkComponents) {
|
|
136
|
+
const expander = buildNativeTypeExpander(frameworkComponents);
|
|
137
|
+
return contractToSchemaIR(contract as Contract<SqlStorage> | null, {
|
|
138
|
+
annotationNamespace: 'pg',
|
|
139
|
+
...ifDefined('expandNativeType', expander),
|
|
140
|
+
renderDefault: postgresRenderDefault,
|
|
141
|
+
frameworkComponents: frameworkComponents ?? [],
|
|
142
|
+
});
|
|
143
|
+
},
|
|
144
|
+
planWithDescriptors(context) {
|
|
145
|
+
const toContract = context.toContract as Contract<SqlStorage>;
|
|
146
|
+
const fromContract = context.fromContract as Contract<SqlStorage> | null;
|
|
147
|
+
|
|
148
|
+
// Synthesize schema IR from the fromContract (same as contractToSchema flow)
|
|
149
|
+
const expander = buildNativeTypeExpander(context.frameworkComponents);
|
|
150
|
+
const fromSchemaIR = contractToSchemaIR(fromContract, {
|
|
151
|
+
annotationNamespace: 'pg',
|
|
152
|
+
...ifDefined('expandNativeType', expander),
|
|
153
|
+
renderDefault: postgresRenderDefault,
|
|
154
|
+
frameworkComponents: context.frameworkComponents ?? [],
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
// Collect schema issues via verifier
|
|
158
|
+
const verifyResult = verifySqlSchema({
|
|
159
|
+
contract: toContract,
|
|
160
|
+
schema: fromSchemaIR,
|
|
161
|
+
strict: true,
|
|
162
|
+
typeMetadataRegistry: new Map(),
|
|
163
|
+
frameworkComponents: context.frameworkComponents ?? [],
|
|
164
|
+
normalizeDefault: parsePostgresDefault,
|
|
165
|
+
normalizeNativeType: normalizeSchemaNativeType,
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
// Run descriptor planner
|
|
169
|
+
const planResult = planDescriptors({
|
|
170
|
+
issues: verifyResult.schema.issues,
|
|
171
|
+
toContract,
|
|
172
|
+
fromContract,
|
|
173
|
+
});
|
|
174
|
+
if (!planResult.ok) {
|
|
175
|
+
return { ok: false as const, conflicts: planResult.failure };
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
return {
|
|
179
|
+
ok: true as const,
|
|
180
|
+
descriptors: planResult.value.descriptors,
|
|
181
|
+
needsDataMigration: planResult.value.needsDataMigration,
|
|
182
|
+
};
|
|
183
|
+
},
|
|
184
|
+
|
|
185
|
+
resolveDescriptors(descriptors, context) {
|
|
186
|
+
const validated = parseDescriptors(descriptors);
|
|
187
|
+
const codecHooks = context.frameworkComponents
|
|
188
|
+
? extractCodecControlHooks(context.frameworkComponents)
|
|
189
|
+
: new Map();
|
|
190
|
+
const dependencies = context.frameworkComponents
|
|
191
|
+
? collectInitDependencies(context.frameworkComponents)
|
|
192
|
+
: [];
|
|
193
|
+
const toContract = context.toContract as Contract<SqlStorage>;
|
|
194
|
+
const db = createMigrationClient(toContract, context.frameworkComponents);
|
|
195
|
+
return resolveOperations(validated, {
|
|
196
|
+
toContract,
|
|
197
|
+
schemaName: context.schemaName ?? 'public',
|
|
198
|
+
codecHooks,
|
|
199
|
+
dependencies,
|
|
200
|
+
db,
|
|
201
|
+
});
|
|
202
|
+
},
|
|
33
203
|
},
|
|
34
204
|
create(): ControlTargetInstance<'sql', 'postgres'> {
|
|
35
205
|
return {
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
// Re-export everything from the local operation-descriptors module,
|
|
2
|
+
// which itself re-exports structural descriptors from sql-family
|
|
3
|
+
// and adds postgres-specific data transform support.
|
|
4
|
+
export {
|
|
5
|
+
type AddColumnDescriptor,
|
|
6
|
+
type AddEnumValuesDescriptor,
|
|
7
|
+
type AddForeignKeyDescriptor,
|
|
8
|
+
type AddPrimaryKeyDescriptor,
|
|
9
|
+
type AddUniqueDescriptor,
|
|
10
|
+
type AlterColumnTypeDescriptor,
|
|
11
|
+
addColumn,
|
|
12
|
+
addEnumValues,
|
|
13
|
+
addForeignKey,
|
|
14
|
+
addPrimaryKey,
|
|
15
|
+
addUnique,
|
|
16
|
+
alterColumnType,
|
|
17
|
+
type Buildable,
|
|
18
|
+
builders,
|
|
19
|
+
type CreateDependencyDescriptor,
|
|
20
|
+
type CreateEnumTypeDescriptor,
|
|
21
|
+
type CreateIndexDescriptor,
|
|
22
|
+
type CreateTableDescriptor,
|
|
23
|
+
createBuilders,
|
|
24
|
+
createDependency,
|
|
25
|
+
createEnumType,
|
|
26
|
+
createIndex,
|
|
27
|
+
createTable,
|
|
28
|
+
type DataTransformDescriptor,
|
|
29
|
+
type DropColumnDescriptor,
|
|
30
|
+
type DropConstraintDescriptor,
|
|
31
|
+
type DropDefaultDescriptor,
|
|
32
|
+
type DropEnumTypeDescriptor,
|
|
33
|
+
type DropIndexDescriptor,
|
|
34
|
+
type DropNotNullDescriptor,
|
|
35
|
+
type DropTableDescriptor,
|
|
36
|
+
dataTransform,
|
|
37
|
+
dropColumn,
|
|
38
|
+
dropConstraint,
|
|
39
|
+
dropDefault,
|
|
40
|
+
dropEnumType,
|
|
41
|
+
dropIndex,
|
|
42
|
+
dropNotNull,
|
|
43
|
+
dropTable,
|
|
44
|
+
type PostgresMigrationOpDescriptor,
|
|
45
|
+
type QueryPlanInput,
|
|
46
|
+
type RenameTypeDescriptor,
|
|
47
|
+
type RunInput,
|
|
48
|
+
renameType,
|
|
49
|
+
type SetDefaultDescriptor,
|
|
50
|
+
type SetNotNullDescriptor,
|
|
51
|
+
type SqlMigrationOpDescriptor,
|
|
52
|
+
setDefault,
|
|
53
|
+
setNotNull,
|
|
54
|
+
TODO,
|
|
55
|
+
type TodoMarker,
|
|
56
|
+
} from '../core/migrations/operation-descriptors';
|
package/src/exports/pack.ts
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { CodecTypes } from '@prisma-next/adapter-postgres/codec-types';
|
|
2
|
+
import type { TargetPackRef } from '@prisma-next/framework-components/components';
|
|
2
3
|
import { postgresTargetDescriptorMeta } from '../core/descriptor-meta';
|
|
3
4
|
|
|
4
|
-
const postgresPack
|
|
5
|
+
const postgresPack = postgresTargetDescriptorMeta;
|
|
5
6
|
|
|
6
|
-
export default postgresPack
|
|
7
|
+
export default postgresPack as typeof postgresTargetDescriptorMeta &
|
|
8
|
+
TargetPackRef<'sql', 'postgres'> & {
|
|
9
|
+
readonly __codecTypes?: CodecTypes;
|
|
10
|
+
};
|
package/src/exports/runtime.ts
CHANGED
|
@@ -1,23 +1,17 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
} from '@prisma-next/core-execution-plane/types';
|
|
1
|
+
import type { RuntimeTargetInstance } from '@prisma-next/framework-components/execution';
|
|
2
|
+
import { createCodecRegistry } from '@prisma-next/sql-relational-core/ast';
|
|
3
|
+
import type { SqlRuntimeTargetDescriptor } from '@prisma-next/sql-runtime';
|
|
5
4
|
import { postgresTargetDescriptorMeta } from '../core/descriptor-meta';
|
|
6
5
|
|
|
7
|
-
/**
|
|
8
|
-
* Postgres runtime target instance interface.
|
|
9
|
-
*/
|
|
10
6
|
export interface PostgresRuntimeTargetInstance extends RuntimeTargetInstance<'sql', 'postgres'> {}
|
|
11
7
|
|
|
12
|
-
|
|
13
|
-
* Postgres target descriptor for runtime plane.
|
|
14
|
-
*/
|
|
15
|
-
const postgresRuntimeTargetDescriptor: RuntimeTargetDescriptor<
|
|
16
|
-
'sql',
|
|
8
|
+
const postgresRuntimeTargetDescriptor: SqlRuntimeTargetDescriptor<
|
|
17
9
|
'postgres',
|
|
18
10
|
PostgresRuntimeTargetInstance
|
|
19
11
|
> = {
|
|
20
12
|
...postgresTargetDescriptorMeta,
|
|
13
|
+
codecs: () => createCodecRegistry(),
|
|
14
|
+
parameterizedCodecs: () => [],
|
|
21
15
|
create(): PostgresRuntimeTargetInstance {
|
|
22
16
|
return {
|
|
23
17
|
familyId: 'sql',
|
package/dist/chunk-RKEXRSSI.js
DELETED
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
// src/core/descriptor-meta.ts
|
|
2
|
-
var postgresTargetDescriptorMeta = {
|
|
3
|
-
kind: "target",
|
|
4
|
-
familyId: "sql",
|
|
5
|
-
targetId: "postgres",
|
|
6
|
-
id: "postgres",
|
|
7
|
-
version: "0.0.1",
|
|
8
|
-
capabilities: {}
|
|
9
|
-
};
|
|
10
|
-
|
|
11
|
-
export {
|
|
12
|
-
postgresTargetDescriptorMeta
|
|
13
|
-
};
|
|
14
|
-
//# sourceMappingURL=chunk-RKEXRSSI.js.map
|