@prisma-next/target-postgres 0.5.0-dev.6 → 0.5.0-dev.60
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/{codec-ids-CojIXVf9.mjs → codec-ids-ckQX9Kcg.mjs} +3 -2
- package/dist/{codec-ids-CojIXVf9.mjs.map → codec-ids-ckQX9Kcg.mjs.map} +1 -1
- package/dist/codec-ids.d.mts +2 -1
- package/dist/codec-ids.d.mts.map +1 -1
- package/dist/codec-ids.mjs +2 -2
- package/dist/codec-types.d.mts +1 -1
- package/dist/codec-types.mjs +2 -1
- package/dist/{codecs-D-F2KJqt.d.mts → codecs-B03dFv94.d.mts} +64 -30
- package/dist/codecs-B03dFv94.d.mts.map +1 -0
- package/dist/{codecs-BoahtY_Q.mjs → codecs-D0oXyJIH.mjs} +24 -104
- package/dist/codecs-D0oXyJIH.mjs.map +1 -0
- package/dist/codecs.d.mts +1 -1
- package/dist/codecs.mjs +2 -1
- package/dist/control.mjs +21 -63
- package/dist/control.mjs.map +1 -1
- package/dist/{data-transform-VfEGzXWt.mjs → data-transform-Be_i_DBc.mjs} +24 -5
- package/dist/data-transform-Be_i_DBc.mjs.map +1 -0
- package/dist/data-transform-CrpmG4uJ.d.mts +39 -0
- package/dist/data-transform-CrpmG4uJ.d.mts.map +1 -0
- package/dist/data-transform.d.mts +1 -1
- package/dist/data-transform.mjs +1 -1
- package/dist/{descriptor-meta-BVoVtyp-.mjs → descriptor-meta-Ieg1XLOs.mjs} +7 -12
- package/dist/descriptor-meta-Ieg1XLOs.mjs.map +1 -0
- package/dist/issue-planner.d.mts +1 -1
- package/dist/migration.d.mts +2 -2
- package/dist/migration.mjs +2 -2
- package/dist/op-factory-call-C3bWXKSP.d.mts.map +1 -1
- package/dist/pack.d.mts +28 -9
- package/dist/pack.d.mts.map +1 -1
- package/dist/pack.mjs +1 -1
- package/dist/{planner-CLUvVhUN.mjs → planner-Cm-ZLutk.mjs} +6 -6
- package/dist/planner-Cm-ZLutk.mjs.map +1 -0
- package/dist/{planner-produced-postgres-migration-DSSPq8QS.mjs → planner-produced-postgres-migration-Bi-RWO4-.mjs} +3 -4
- package/dist/{planner-produced-postgres-migration-DSSPq8QS.mjs.map → planner-produced-postgres-migration-Bi-RWO4-.mjs.map} +1 -1
- package/dist/{planner-produced-postgres-migration-CRRTno6Z.d.mts → planner-produced-postgres-migration-M3EfhWSS.d.mts} +2 -2
- package/dist/planner-produced-postgres-migration-M3EfhWSS.d.mts.map +1 -0
- package/dist/planner-produced-postgres-migration.d.mts +3 -2
- package/dist/planner-produced-postgres-migration.mjs +1 -1
- package/dist/planner.d.mts +17 -10
- package/dist/planner.d.mts.map +1 -1
- package/dist/planner.mjs +1 -1
- package/dist/{postgres-migration-BjA3Zmts.d.mts → postgres-migration-BFjbb25b.d.mts} +4 -3
- package/dist/postgres-migration-BFjbb25b.d.mts.map +1 -0
- package/dist/{postgres-migration-qtmtbONe.mjs → postgres-migration-BS9vQW97.mjs} +2 -2
- package/dist/postgres-migration-BS9vQW97.mjs.map +1 -0
- package/dist/{render-typescript-1rF_SB4g.mjs → render-typescript-Co3Emwgz.mjs} +1 -2
- package/dist/render-typescript-Co3Emwgz.mjs.map +1 -0
- package/dist/render-typescript.d.mts +1 -2
- package/dist/render-typescript.d.mts.map +1 -1
- package/dist/render-typescript.mjs +1 -1
- package/dist/runtime.mjs +1 -1
- package/dist/{statement-builders-BPnmt6wx.mjs → statement-builders-CHqCtSfe.mjs} +13 -8
- package/dist/statement-builders-CHqCtSfe.mjs.map +1 -0
- package/dist/statement-builders.d.mts +10 -3
- package/dist/statement-builders.d.mts.map +1 -1
- package/dist/statement-builders.mjs +2 -2
- package/package.json +15 -14
- package/src/core/authoring.ts +5 -11
- package/src/core/codec-ids.ts +1 -0
- package/src/core/codecs.ts +53 -40
- package/src/core/migrations/operations/data-transform.ts +86 -21
- package/src/core/migrations/planner-produced-postgres-migration.ts +0 -1
- package/src/core/migrations/planner.ts +17 -11
- package/src/core/migrations/postgres-migration.ts +3 -6
- package/src/core/migrations/render-typescript.ts +1 -5
- package/src/core/migrations/runner.ts +43 -112
- package/src/core/migrations/statement-builders.ts +22 -6
- package/src/exports/statement-builders.ts +1 -1
- package/dist/codecs-BoahtY_Q.mjs.map +0 -1
- package/dist/codecs-D-F2KJqt.d.mts.map +0 -1
- package/dist/data-transform-CxFRBIUp.d.mts +0 -32
- package/dist/data-transform-CxFRBIUp.d.mts.map +0 -1
- package/dist/data-transform-VfEGzXWt.mjs.map +0 -1
- package/dist/descriptor-meta-BVoVtyp-.mjs.map +0 -1
- package/dist/planner-CLUvVhUN.mjs.map +0 -1
- package/dist/planner-produced-postgres-migration-CRRTno6Z.d.mts.map +0 -1
- package/dist/postgres-migration-BjA3Zmts.d.mts.map +0 -1
- package/dist/postgres-migration-qtmtbONe.mjs.map +0 -1
- package/dist/render-typescript-1rF_SB4g.mjs.map +0 -1
- package/dist/statement-builders-BPnmt6wx.mjs.map +0 -1
- package/src/core/json-schema-type-expression.ts +0 -131
package/src/core/codecs.ts
CHANGED
|
@@ -19,6 +19,7 @@ import { type as arktype } from 'arktype';
|
|
|
19
19
|
import {
|
|
20
20
|
PG_BIT_CODEC_ID,
|
|
21
21
|
PG_BOOL_CODEC_ID,
|
|
22
|
+
PG_BYTEA_CODEC_ID,
|
|
22
23
|
PG_CHAR_CODEC_ID,
|
|
23
24
|
PG_ENUM_CODEC_ID,
|
|
24
25
|
PG_FLOAT_CODEC_ID,
|
|
@@ -40,7 +41,6 @@ import {
|
|
|
40
41
|
PG_VARBIT_CODEC_ID,
|
|
41
42
|
PG_VARCHAR_CODEC_ID,
|
|
42
43
|
} from './codec-ids';
|
|
43
|
-
import { renderTypeScriptTypeFromJsonSchema } from './json-schema-type-expression';
|
|
44
44
|
|
|
45
45
|
const lengthParamsSchema = arktype({
|
|
46
46
|
length: 'number.integer > 0',
|
|
@@ -85,19 +85,13 @@ function renderPrecision(typeName: string, typeParams: Record<string, unknown>):
|
|
|
85
85
|
return `${typeName}<${precision}>`;
|
|
86
86
|
}
|
|
87
87
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
return renderTypeScriptTypeFromJsonSchema(schema);
|
|
96
|
-
}
|
|
97
|
-
throw new Error(
|
|
98
|
-
`renderOutputType: JSON codec typeParams must contain "type" (string) or "schemaJson" (object), got keys: ${Object.keys(typeParams).join(', ')}`,
|
|
99
|
-
);
|
|
100
|
-
}
|
|
88
|
+
// Phase C: postgres' raw json/jsonb codecs no longer carry a
|
|
89
|
+
// `renderOutputType` slot — the schema-typed JSON surface that drove
|
|
90
|
+
// `typeParams: { schemaJson, type? }` retired in favor of the per-library
|
|
91
|
+
// extension package (`@prisma-next/extension-arktype-json`). Untyped
|
|
92
|
+
// json/jsonb columns have no typeParams; the framework emit path falls
|
|
93
|
+
// through to the generic `CodecTypes['pg/jsonb@1']['output']` accessor
|
|
94
|
+
// (which resolves to `JsonValue` via the codec-types map).
|
|
101
95
|
|
|
102
96
|
function aliasCodec<
|
|
103
97
|
Id extends string,
|
|
@@ -339,22 +333,15 @@ const pgFloat8Codec = codec({
|
|
|
339
333
|
const pgTimestampCodec = codec<
|
|
340
334
|
typeof PG_TIMESTAMP_CODEC_ID,
|
|
341
335
|
readonly ['equality', 'order'],
|
|
342
|
-
|
|
343
|
-
|
|
336
|
+
Date,
|
|
337
|
+
Date
|
|
344
338
|
>({
|
|
345
339
|
typeId: PG_TIMESTAMP_CODEC_ID,
|
|
346
340
|
targetTypes: ['timestamp'],
|
|
347
341
|
traits: ['equality', 'order'],
|
|
348
|
-
encode: (value:
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
return String(value);
|
|
352
|
-
},
|
|
353
|
-
decode: (wire: string | Date): string => {
|
|
354
|
-
if (wire instanceof Date) return wire.toISOString();
|
|
355
|
-
return wire;
|
|
356
|
-
},
|
|
357
|
-
encodeJson: (value: string | Date) => (value instanceof Date ? value.toISOString() : value),
|
|
342
|
+
encode: (value: Date): Date => value,
|
|
343
|
+
decode: (wire: Date): Date => wire,
|
|
344
|
+
encodeJson: (value: Date) => value.toISOString(),
|
|
358
345
|
decodeJson: (json) => {
|
|
359
346
|
if (typeof json !== 'string') {
|
|
360
347
|
throw new Error(`Expected ISO date string for pg/timestamp@1, got ${typeof json}`);
|
|
@@ -381,22 +368,15 @@ const pgTimestampCodec = codec<
|
|
|
381
368
|
const pgTimestamptzCodec = codec<
|
|
382
369
|
typeof PG_TIMESTAMPTZ_CODEC_ID,
|
|
383
370
|
readonly ['equality', 'order'],
|
|
384
|
-
|
|
385
|
-
|
|
371
|
+
Date,
|
|
372
|
+
Date
|
|
386
373
|
>({
|
|
387
374
|
typeId: PG_TIMESTAMPTZ_CODEC_ID,
|
|
388
375
|
targetTypes: ['timestamptz'],
|
|
389
376
|
traits: ['equality', 'order'],
|
|
390
|
-
encode: (value:
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
return String(value);
|
|
394
|
-
},
|
|
395
|
-
decode: (wire: string | Date): string => {
|
|
396
|
-
if (wire instanceof Date) return wire.toISOString();
|
|
397
|
-
return wire;
|
|
398
|
-
},
|
|
399
|
-
encodeJson: (value: string | Date) => (value instanceof Date ? value.toISOString() : value),
|
|
377
|
+
encode: (value: Date): Date => value,
|
|
378
|
+
decode: (wire: Date): Date => wire,
|
|
379
|
+
encodeJson: (value: Date) => value.toISOString(),
|
|
400
380
|
decodeJson: (json) => {
|
|
401
381
|
if (typeof json !== 'string') {
|
|
402
382
|
throw new Error(`Expected ISO date string for pg/timestamptz@1, got ${typeof json}`);
|
|
@@ -523,6 +503,40 @@ const pgVarbitCodec = codec<
|
|
|
523
503
|
},
|
|
524
504
|
});
|
|
525
505
|
|
|
506
|
+
const pgByteaCodec = codec({
|
|
507
|
+
typeId: PG_BYTEA_CODEC_ID,
|
|
508
|
+
targetTypes: ['bytea'],
|
|
509
|
+
traits: ['equality'],
|
|
510
|
+
encode: (value: Uint8Array): Uint8Array => value,
|
|
511
|
+
decode: (wire: Uint8Array): Uint8Array =>
|
|
512
|
+
// Postgres node drivers commonly return Buffer instances (which extend
|
|
513
|
+
// Uint8Array) — normalize to a plain Uint8Array view so engine-agnostic
|
|
514
|
+
// consumers don't accidentally observe Buffer-specific APIs.
|
|
515
|
+
wire instanceof Uint8Array && wire.constructor === Uint8Array
|
|
516
|
+
? wire
|
|
517
|
+
: new Uint8Array(wire.buffer, wire.byteOffset, wire.byteLength),
|
|
518
|
+
encodeJson: (value: Uint8Array): string => Buffer.from(value).toString('base64'),
|
|
519
|
+
decodeJson: (json): Uint8Array => {
|
|
520
|
+
if (typeof json !== 'string') {
|
|
521
|
+
throw new Error(`Expected base64 string for pg/bytea@1, got ${typeof json}`);
|
|
522
|
+
}
|
|
523
|
+
const decoded = Buffer.from(json, 'base64');
|
|
524
|
+
if (decoded.toString('base64') !== json) {
|
|
525
|
+
throw new Error(`Invalid base64 string for pg/bytea@1 (length: ${json.length})`);
|
|
526
|
+
}
|
|
527
|
+
return new Uint8Array(decoded);
|
|
528
|
+
},
|
|
529
|
+
meta: {
|
|
530
|
+
db: {
|
|
531
|
+
sql: {
|
|
532
|
+
postgres: {
|
|
533
|
+
nativeType: 'bytea',
|
|
534
|
+
},
|
|
535
|
+
},
|
|
536
|
+
},
|
|
537
|
+
},
|
|
538
|
+
});
|
|
539
|
+
|
|
526
540
|
const pgEnumCodec = codec({
|
|
527
541
|
typeId: PG_ENUM_CODEC_ID,
|
|
528
542
|
targetTypes: ['enum'],
|
|
@@ -576,7 +590,6 @@ const pgJsonCodec = codec({
|
|
|
576
590
|
encode: (value: string | JsonValue): string => JSON.stringify(value),
|
|
577
591
|
decode: (wire: string | JsonValue): JsonValue =>
|
|
578
592
|
typeof wire === 'string' ? JSON.parse(wire) : wire,
|
|
579
|
-
renderOutputType: renderJsonOutputType,
|
|
580
593
|
meta: {
|
|
581
594
|
db: {
|
|
582
595
|
sql: {
|
|
@@ -595,7 +608,6 @@ const pgJsonbCodec = codec({
|
|
|
595
608
|
encode: (value: string | JsonValue): string => JSON.stringify(value),
|
|
596
609
|
decode: (wire: string | JsonValue): JsonValue =>
|
|
597
610
|
typeof wire === 'string' ? JSON.parse(wire) : wire,
|
|
598
|
-
renderOutputType: renderJsonOutputType,
|
|
599
611
|
meta: {
|
|
600
612
|
db: {
|
|
601
613
|
sql: {
|
|
@@ -633,6 +645,7 @@ const codecs = defineCodecs()
|
|
|
633
645
|
.add('bool', pgBoolCodec)
|
|
634
646
|
.add('bit', pgBitCodec)
|
|
635
647
|
.add('bit varying', pgVarbitCodec)
|
|
648
|
+
.add('bytea', pgByteaCodec)
|
|
636
649
|
.add('interval', pgIntervalCodec)
|
|
637
650
|
.add('enum', pgEnumCodec)
|
|
638
651
|
.add('json', pgJsonCodec)
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
* override get operations() {
|
|
12
12
|
* return [
|
|
13
13
|
* this.dataTransform(endContract, 'backfill emails', {
|
|
14
|
-
* check: () => db.users.
|
|
14
|
+
* check: () => db.users.select('id').where(({ email }) => email.isNull()).limit(1),
|
|
15
15
|
* run: () => db.users.update({ email: '' }).where(({ email }) => email.isNull()),
|
|
16
16
|
* }),
|
|
17
17
|
* ];
|
|
@@ -23,20 +23,49 @@
|
|
|
23
23
|
* invokes each one, asserts that its `meta.storageHash` matches the
|
|
24
24
|
* `contract` it was handed (→ `PN-MIG-2005` on mismatch), and lowers the
|
|
25
25
|
* plan via the supplied control adapter to a serialized `{sql, params}`
|
|
26
|
-
* payload
|
|
27
|
-
*
|
|
28
|
-
*
|
|
26
|
+
* payload.
|
|
27
|
+
*
|
|
28
|
+
* The factory then lowers the data transform to the unified migration-op
|
|
29
|
+
* shape `{ precheck, execute, postcheck }`. The user's `check` plan is
|
|
30
|
+
* wrapped twice with opposite truth values:
|
|
31
|
+
*
|
|
32
|
+
* - precheck `SELECT EXISTS (<check>) AS ok` asserts there is work to do
|
|
33
|
+
* (precheck is short-circuited by the runner's pre-satisfied-skip path
|
|
34
|
+
* when nothing remains to backfill).
|
|
35
|
+
* - postcheck `SELECT NOT EXISTS (<check>) AS ok` asserts the work is
|
|
36
|
+
* complete after the run steps execute.
|
|
37
|
+
*
|
|
38
|
+
* The `check` plan is therefore expected to be a **rowset query whose
|
|
39
|
+
* presence of any row signals "work remains"** — typically `select('id')
|
|
40
|
+
* .where(<violation predicate>).limit(1)`. Scalar/aggregate shapes
|
|
41
|
+
* (`count(*)`, `bool_and(...)`) do not work under this contract: they
|
|
42
|
+
* always return exactly one row, so `EXISTS` is always true and
|
|
43
|
+
* `NOT EXISTS` is always false. (This is the same row-presence contract
|
|
44
|
+
* the pre-unification runner relied on; the wrapping is just lifting it
|
|
45
|
+
* into SQL.)
|
|
46
|
+
*
|
|
47
|
+
* Each `run` plan becomes an execute step. Because the `Step.params`
|
|
48
|
+
* field threads through `driver.query(sql, params)`, the user's bound
|
|
49
|
+
* values flow through the driver's parameter binder rather than being
|
|
50
|
+
* inlined into the SQL text.
|
|
51
|
+
*
|
|
52
|
+
* The free factory remains usable standalone (tests, ad-hoc tooling,
|
|
53
|
+
* non-class contexts) by passing the adapter explicitly as the fourth
|
|
54
|
+
* argument.
|
|
29
55
|
*/
|
|
30
56
|
|
|
31
57
|
import type { Contract } from '@prisma-next/contract/types';
|
|
32
58
|
import { errorDataTransformContractMismatch } from '@prisma-next/errors/migration';
|
|
33
|
-
import type { SqlControlAdapter } from '@prisma-next/family-sql/control-adapter';
|
|
34
59
|
import type {
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
} from '@prisma-next/
|
|
60
|
+
SqlMigrationPlanOperation,
|
|
61
|
+
SqlMigrationPlanOperationStep,
|
|
62
|
+
} from '@prisma-next/family-sql/control';
|
|
63
|
+
import type { SqlControlAdapter } from '@prisma-next/family-sql/control-adapter';
|
|
64
|
+
import type { SerializedQueryPlan } from '@prisma-next/framework-components/control';
|
|
38
65
|
import type { SqlStorage } from '@prisma-next/sql-contract/types';
|
|
39
66
|
import type { SqlQueryPlan } from '@prisma-next/sql-relational-core/plan';
|
|
67
|
+
import { ifDefined } from '@prisma-next/utils/defined';
|
|
68
|
+
import type { PostgresPlanTargetDetails } from '../planner-target-details';
|
|
40
69
|
|
|
41
70
|
interface Buildable<R = unknown> {
|
|
42
71
|
build(): SqlQueryPlan<R>;
|
|
@@ -49,36 +78,72 @@ interface Buildable<R = unknown> {
|
|
|
49
78
|
export type DataTransformClosure = () => SqlQueryPlan | Buildable;
|
|
50
79
|
|
|
51
80
|
export interface DataTransformOptions {
|
|
52
|
-
/**
|
|
81
|
+
/**
|
|
82
|
+
* Optional opt-in routing identity. Presence opts the transform into
|
|
83
|
+
* invariant-aware routing; absence means it is path-dependent and
|
|
84
|
+
* not referenceable from refs.
|
|
85
|
+
*/
|
|
86
|
+
readonly invariantId?: string;
|
|
87
|
+
/**
|
|
88
|
+
* Optional pre-flight query. `undefined` means "no check". When
|
|
89
|
+
* supplied, the closure must return a **rowset query** whose
|
|
90
|
+
* presence of any row signals "violations remain". Conventional
|
|
91
|
+
* shape: `db.<table>.select('id').where(<violation>).limit(1)`.
|
|
92
|
+
* Scalar/aggregate shapes do not satisfy this contract.
|
|
93
|
+
*/
|
|
53
94
|
readonly check?: DataTransformClosure;
|
|
54
95
|
/** One or more mutation queries to execute. */
|
|
55
96
|
readonly run: DataTransformClosure | readonly DataTransformClosure[];
|
|
56
97
|
}
|
|
57
98
|
|
|
58
|
-
/**
|
|
59
|
-
* Concrete Postgres flavor of `DataTransformOperation`, re-exported so the
|
|
60
|
-
* `PostgresMigration.dataTransform` instance method can name it without
|
|
61
|
-
* leaking the framework-components symbol into call sites.
|
|
62
|
-
*/
|
|
63
|
-
export type PostgresDataTransformOperation = DataTransformOperation;
|
|
64
|
-
|
|
65
99
|
export function dataTransform<TContract extends Contract<SqlStorage>>(
|
|
66
100
|
contract: TContract,
|
|
67
101
|
name: string,
|
|
68
102
|
options: DataTransformOptions,
|
|
69
103
|
adapter: SqlControlAdapter<'postgres'>,
|
|
70
|
-
):
|
|
104
|
+
): SqlMigrationPlanOperation<PostgresPlanTargetDetails> {
|
|
71
105
|
const runClosures: readonly DataTransformClosure[] = Array.isArray(options.run)
|
|
72
106
|
? options.run
|
|
73
107
|
: [options.run as DataTransformClosure];
|
|
108
|
+
|
|
109
|
+
const checkPlan = options.check ? invokeAndLower(options.check, contract, adapter, name) : null;
|
|
110
|
+
const runPlans = runClosures.map((closure) => invokeAndLower(closure, contract, adapter, name));
|
|
111
|
+
|
|
112
|
+
const precheck: readonly SqlMigrationPlanOperationStep[] = checkPlan
|
|
113
|
+
? [
|
|
114
|
+
{
|
|
115
|
+
description: `Check ${name} has work to do`,
|
|
116
|
+
sql: `SELECT EXISTS (${checkPlan.sql}) AS ok`,
|
|
117
|
+
params: checkPlan.params,
|
|
118
|
+
},
|
|
119
|
+
]
|
|
120
|
+
: [];
|
|
121
|
+
|
|
122
|
+
const execute: readonly SqlMigrationPlanOperationStep[] = runPlans.map((plan) => ({
|
|
123
|
+
description: `Run ${name}`,
|
|
124
|
+
sql: plan.sql,
|
|
125
|
+
params: plan.params,
|
|
126
|
+
}));
|
|
127
|
+
|
|
128
|
+
const postcheck: readonly SqlMigrationPlanOperationStep[] = checkPlan
|
|
129
|
+
? [
|
|
130
|
+
{
|
|
131
|
+
description: `Verify ${name} resolved all violations`,
|
|
132
|
+
sql: `SELECT NOT EXISTS (${checkPlan.sql}) AS ok`,
|
|
133
|
+
params: checkPlan.params,
|
|
134
|
+
},
|
|
135
|
+
]
|
|
136
|
+
: [];
|
|
137
|
+
|
|
74
138
|
return {
|
|
75
139
|
id: `data_migration.${name}`,
|
|
76
140
|
label: `Data transform: ${name}`,
|
|
77
141
|
operationClass: 'data',
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
142
|
+
...ifDefined('invariantId', options.invariantId),
|
|
143
|
+
target: { id: 'postgres' },
|
|
144
|
+
precheck,
|
|
145
|
+
execute,
|
|
146
|
+
postcheck,
|
|
82
147
|
};
|
|
83
148
|
}
|
|
84
149
|
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { Contract } from '@prisma-next/contract/types';
|
|
1
2
|
import type {
|
|
2
3
|
MigrationOperationPolicy,
|
|
3
4
|
SqlMigrationPlannerPlanOptions,
|
|
@@ -83,20 +84,25 @@ export class PostgresMigrationPlanner implements MigrationPlanner<'sql', 'postgr
|
|
|
83
84
|
readonly contract: unknown;
|
|
84
85
|
readonly schema: unknown;
|
|
85
86
|
readonly policy: MigrationOperationPolicy;
|
|
86
|
-
readonly fromHash?: string;
|
|
87
87
|
/**
|
|
88
88
|
* The "from" contract (state the planner assumes the database starts
|
|
89
|
-
* at)
|
|
90
|
-
*
|
|
91
|
-
*
|
|
92
|
-
*
|
|
93
|
-
* activate.
|
|
89
|
+
* at), or `null` for reconciliation flows. Only `migration plan` ever
|
|
90
|
+
* supplies a non-null value; `db update` / `db init` reconcile against
|
|
91
|
+
* the live schema and pass `null`. When present alongside the
|
|
92
|
+
* `'data'` operation class, strategies that need from/to column-shape
|
|
93
|
+
* comparisons (unsafe type change, nullability tightening) activate.
|
|
94
|
+
*
|
|
95
|
+
* Typed as the framework `Contract | null` to satisfy the
|
|
96
|
+
* `MigrationPlanner` interface contract; `planSql` narrows to the SQL
|
|
97
|
+
* shape via `SqlMigrationPlannerPlanOptions`. Used to populate
|
|
98
|
+
* `describe().from` on the produced plan as
|
|
99
|
+
* `fromContract?.storage.storageHash ?? null`.
|
|
94
100
|
*/
|
|
95
|
-
readonly fromContract
|
|
101
|
+
readonly fromContract: Contract | null;
|
|
96
102
|
readonly schemaName?: string;
|
|
97
103
|
readonly frameworkComponents: ReadonlyArray<TargetBoundComponentDescriptor<'sql', string>>;
|
|
98
104
|
}): PostgresPlanResult {
|
|
99
|
-
return this.planSql(options as SqlMigrationPlannerPlanOptions
|
|
105
|
+
return this.planSql(options as SqlMigrationPlannerPlanOptions);
|
|
100
106
|
}
|
|
101
107
|
|
|
102
108
|
emptyMigration(context: MigrationScaffoldContext): MigrationPlanWithAuthoringSurface {
|
|
@@ -106,7 +112,7 @@ export class PostgresMigrationPlanner implements MigrationPlanner<'sql', 'postgr
|
|
|
106
112
|
});
|
|
107
113
|
}
|
|
108
114
|
|
|
109
|
-
private planSql(options: SqlMigrationPlannerPlanOptions
|
|
115
|
+
private planSql(options: SqlMigrationPlannerPlanOptions): PostgresPlanResult {
|
|
110
116
|
const schemaName = options.schemaName ?? this.config.defaultSchema;
|
|
111
117
|
const policyResult = this.ensureAdditivePolicy(options.policy);
|
|
112
118
|
if (policyResult) {
|
|
@@ -125,7 +131,7 @@ export class PostgresMigrationPlanner implements MigrationPlanner<'sql', 'postgr
|
|
|
125
131
|
// from/to comparisons (unsafe type change, nullable tightening) are
|
|
126
132
|
// inapplicable there — reconciliation falls through to
|
|
127
133
|
// `mapIssueToCall`'s direct destructive handlers.
|
|
128
|
-
fromContract: options.fromContract
|
|
134
|
+
fromContract: options.fromContract,
|
|
129
135
|
schemaName,
|
|
130
136
|
codecHooks,
|
|
131
137
|
storageTypes,
|
|
@@ -142,7 +148,7 @@ export class PostgresMigrationPlanner implements MigrationPlanner<'sql', 'postgr
|
|
|
142
148
|
return Object.freeze({
|
|
143
149
|
kind: 'success' as const,
|
|
144
150
|
plan: new TypeScriptRenderablePostgresMigration(result.value.calls, {
|
|
145
|
-
from:
|
|
151
|
+
from: options.fromContract?.storage.storageHash ?? null,
|
|
146
152
|
to: options.contract.storage.storageHash,
|
|
147
153
|
}),
|
|
148
154
|
});
|
|
@@ -1,14 +1,11 @@
|
|
|
1
1
|
import type { Contract } from '@prisma-next/contract/types';
|
|
2
|
+
import type { SqlMigrationPlanOperation } from '@prisma-next/family-sql/control';
|
|
2
3
|
import type { SqlControlAdapter } from '@prisma-next/family-sql/control-adapter';
|
|
3
4
|
import { Migration as SqlMigration } from '@prisma-next/family-sql/migration';
|
|
4
5
|
import type { ControlStack } from '@prisma-next/framework-components/control';
|
|
5
6
|
import type { SqlStorage } from '@prisma-next/sql-contract/types';
|
|
6
7
|
import { errorPostgresMigrationStackMissing } from '../errors';
|
|
7
|
-
import {
|
|
8
|
-
type DataTransformOptions,
|
|
9
|
-
dataTransform,
|
|
10
|
-
type PostgresDataTransformOperation,
|
|
11
|
-
} from './operations/data-transform';
|
|
8
|
+
import { type DataTransformOptions, dataTransform } from './operations/data-transform';
|
|
12
9
|
import type { PostgresPlanTargetDetails } from './planner-target-details';
|
|
13
10
|
|
|
14
11
|
/**
|
|
@@ -64,7 +61,7 @@ export abstract class PostgresMigration extends SqlMigration<
|
|
|
64
61
|
contract: TContract,
|
|
65
62
|
name: string,
|
|
66
63
|
options: DataTransformOptions,
|
|
67
|
-
):
|
|
64
|
+
): SqlMigrationPlanOperation<PostgresPlanTargetDetails> {
|
|
68
65
|
if (!this.controlAdapter) {
|
|
69
66
|
throw errorPostgresMigrationStackMissing();
|
|
70
67
|
}
|
|
@@ -14,9 +14,8 @@ import { type ImportRequirement, jsonToTsSource, renderImports } from '@prisma-n
|
|
|
14
14
|
import type { PostgresOpFactoryCall } from './op-factory-call';
|
|
15
15
|
|
|
16
16
|
export interface RenderMigrationMeta {
|
|
17
|
-
readonly from: string;
|
|
17
|
+
readonly from: string | null;
|
|
18
18
|
readonly to: string;
|
|
19
|
-
readonly kind?: string;
|
|
20
19
|
readonly labels?: readonly string[];
|
|
21
20
|
}
|
|
22
21
|
|
|
@@ -84,9 +83,6 @@ function buildDescribeMethod(meta: RenderMigrationMeta): string {
|
|
|
84
83
|
lines.push(' return {');
|
|
85
84
|
lines.push(` from: ${JSON.stringify(meta.from)},`);
|
|
86
85
|
lines.push(` to: ${JSON.stringify(meta.to)},`);
|
|
87
|
-
if (meta.kind) {
|
|
88
|
-
lines.push(` kind: ${JSON.stringify(meta.kind)},`);
|
|
89
|
-
}
|
|
90
86
|
if (meta.labels && meta.labels.length > 0) {
|
|
91
87
|
lines.push(` labels: ${jsonToTsSource(meta.labels)},`);
|
|
92
88
|
}
|