@prisma-next/target-postgres 0.5.0-dev.8 → 0.5.0-dev.81
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-C5qzBqus.mjs} +4 -4
- package/dist/{codec-ids-CojIXVf9.mjs.map → codec-ids-C5qzBqus.mjs.map} +1 -1
- package/dist/codec-ids-CplrEfmx.d.mts +29 -0
- package/dist/codec-ids-CplrEfmx.d.mts.map +1 -0
- package/dist/codec-ids.d.mts +2 -28
- package/dist/codec-ids.mjs +2 -3
- package/dist/codec-types-lrsb3N07.d.mts +79 -0
- package/dist/codec-types-lrsb3N07.d.mts.map +1 -0
- package/dist/codec-types.d.mts +2 -42
- package/dist/codec-types.mjs +1 -4
- package/dist/codecs-Cue97Xqf.d.mts +558 -0
- package/dist/codecs-Cue97Xqf.d.mts.map +1 -0
- package/dist/codecs.d.mts +13 -2
- package/dist/codecs.d.mts.map +1 -0
- package/dist/codecs.mjs +738 -3
- package/dist/codecs.mjs.map +1 -0
- package/dist/control.d.mts +1 -1
- package/dist/control.d.mts.map +1 -1
- package/dist/control.mjs +131 -94
- package/dist/control.mjs.map +1 -1
- package/dist/{data-transform-VfEGzXWt.mjs → data-transform-DKWXdHuZ.mjs} +25 -7
- package/dist/data-transform-DKWXdHuZ.mjs.map +1 -0
- package/dist/data-transform-bIeAcZIJ.d.mts +38 -0
- package/dist/data-transform-bIeAcZIJ.d.mts.map +1 -0
- package/dist/data-transform.d.mts +1 -1
- package/dist/data-transform.mjs +2 -3
- package/dist/{default-normalizer-DNOpRoOF.mjs → default-normalizer-C8XyZj85.mjs} +2 -2
- package/dist/{default-normalizer-DNOpRoOF.mjs.map → default-normalizer-C8XyZj85.mjs.map} +1 -1
- package/dist/default-normalizer.d.mts +0 -1
- package/dist/default-normalizer.d.mts.map +1 -1
- package/dist/default-normalizer.mjs +2 -3
- package/dist/descriptor-meta-Dde_BS3K.mjs +99 -0
- package/dist/descriptor-meta-Dde_BS3K.mjs.map +1 -0
- package/dist/{errors-AFvEPZ1R.mjs → errors-Chm2bKcS.mjs} +2 -3
- package/dist/{errors-AFvEPZ1R.mjs.map → errors-Chm2bKcS.mjs.map} +1 -1
- package/dist/errors.d.mts +0 -1
- package/dist/errors.d.mts.map +1 -1
- package/dist/errors.mjs +2 -3
- package/dist/{issue-planner-CFjB0_oO.mjs → issue-planner-B10B70JF.mjs} +9 -13
- package/dist/issue-planner-B10B70JF.mjs.map +1 -0
- package/dist/issue-planner.d.mts +2 -4
- package/dist/issue-planner.d.mts.map +1 -1
- package/dist/issue-planner.mjs +2 -3
- package/dist/migration.d.mts +3 -3
- package/dist/migration.d.mts.map +1 -1
- package/dist/migration.mjs +4 -5
- package/dist/migration.mjs.map +1 -1
- package/dist/{native-type-normalizer-CInai_oY.mjs → native-type-normalizer-Cry4QoLf.mjs} +2 -2
- package/dist/native-type-normalizer-Cry4QoLf.mjs.map +1 -0
- package/dist/native-type-normalizer.d.mts.map +1 -1
- package/dist/native-type-normalizer.mjs +2 -3
- package/dist/{op-factory-call-C3bWXKSP.d.mts → op-factory-call-CW8pzxmB.d.mts} +3 -4
- package/dist/op-factory-call-CW8pzxmB.d.mts.map +1 -0
- package/dist/{op-factory-call-BKlruaiC.mjs → op-factory-call-Cq8s4Fz1.mjs} +3 -4
- package/dist/{op-factory-call-BKlruaiC.mjs.map → op-factory-call-Cq8s4Fz1.mjs.map} +1 -1
- package/dist/op-factory-call.d.mts +1 -2
- package/dist/op-factory-call.mjs +2 -3
- package/dist/pack.d.mts +28 -9
- package/dist/pack.d.mts.map +1 -1
- package/dist/pack.mjs +2 -3
- package/dist/{planner-CLUvVhUN.mjs → planner-BvKUuqG-.mjs} +22 -16
- package/dist/planner-BvKUuqG-.mjs.map +1 -0
- package/dist/{planner-ddl-builders-Dxvw1LHw.mjs → planner-ddl-builders-CLB7Umhh.mjs} +4 -5
- package/dist/planner-ddl-builders-CLB7Umhh.mjs.map +1 -0
- package/dist/planner-ddl-builders.d.mts +1 -1
- package/dist/planner-ddl-builders.d.mts.map +1 -1
- package/dist/planner-ddl-builders.mjs +2 -3
- package/dist/{planner-identity-values-Dju-o5GF.mjs → planner-identity-values-DTx0gePL.mjs} +2 -3
- package/dist/{planner-identity-values-Dju-o5GF.mjs.map → planner-identity-values-DTx0gePL.mjs.map} +1 -1
- package/dist/planner-identity-values.d.mts +0 -1
- package/dist/planner-identity-values.d.mts.map +1 -1
- package/dist/planner-identity-values.mjs +2 -3
- package/dist/{planner-produced-postgres-migration-CRRTno6Z.d.mts → planner-produced-postgres-migration-CjxWIVgh.d.mts} +11 -7
- package/dist/planner-produced-postgres-migration-CjxWIVgh.d.mts.map +1 -0
- package/dist/{planner-produced-postgres-migration-DSSPq8QS.mjs → planner-produced-postgres-migration-DphktB2N.mjs} +16 -8
- package/dist/planner-produced-postgres-migration-DphktB2N.mjs.map +1 -0
- package/dist/planner-produced-postgres-migration.d.mts +1 -4
- package/dist/planner-produced-postgres-migration.mjs +2 -3
- package/dist/{planner-schema-lookup-B7lkypwn.mjs → planner-schema-lookup-B1ags8ys.mjs} +2 -2
- package/dist/{planner-schema-lookup-B7lkypwn.mjs.map → planner-schema-lookup-B1ags8ys.mjs.map} +1 -1
- package/dist/planner-schema-lookup.d.mts +0 -1
- package/dist/planner-schema-lookup.d.mts.map +1 -1
- package/dist/planner-schema-lookup.mjs +2 -3
- package/dist/{planner-sql-checks-7jkgm9TX.mjs → planner-sql-checks-DwZvGlV4.mjs} +3 -5
- package/dist/planner-sql-checks-DwZvGlV4.mjs.map +1 -0
- package/dist/planner-sql-checks.d.mts.map +1 -1
- package/dist/planner-sql-checks.mjs +2 -3
- package/dist/{planner-target-details-DH-azLu-.d.mts → planner-target-details-bVVcanWh.d.mts} +1 -1
- package/dist/planner-target-details-bVVcanWh.d.mts.map +1 -0
- package/dist/planner-target-details.d.mts +1 -1
- package/dist/planner-target-details.mjs +1 -1
- package/dist/planner.d.mts +21 -12
- package/dist/planner.d.mts.map +1 -1
- package/dist/planner.mjs +2 -4
- package/dist/{postgres-migration-qtmtbONe.mjs → postgres-migration-Bkv140RW.mjs} +4 -5
- package/dist/postgres-migration-Bkv140RW.mjs.map +1 -0
- package/dist/{postgres-migration-BjA3Zmts.d.mts → postgres-migration-UkcHfZAA.d.mts} +6 -6
- package/dist/postgres-migration-UkcHfZAA.d.mts.map +1 -0
- package/dist/render-ops--1nnfNus.mjs +23 -0
- package/dist/render-ops--1nnfNus.mjs.map +1 -0
- package/dist/render-ops.d.mts +3 -4
- package/dist/render-ops.d.mts.map +1 -1
- package/dist/render-ops.mjs +2 -3
- package/dist/{render-typescript-1rF_SB4g.mjs → render-typescript-D3doH-vX.mjs} +2 -14
- package/dist/render-typescript-D3doH-vX.mjs.map +1 -0
- package/dist/render-typescript.d.mts +3 -6
- package/dist/render-typescript.d.mts.map +1 -1
- package/dist/render-typescript.mjs +2 -3
- package/dist/runtime.d.mts +5 -9
- package/dist/runtime.d.mts.map +1 -1
- package/dist/runtime.mjs +7 -14
- package/dist/runtime.mjs.map +1 -1
- package/dist/{shared-Bxkt8pNO.d.mts → shared-MpwjwAjM.d.mts} +2 -2
- package/dist/shared-MpwjwAjM.d.mts.map +1 -0
- package/dist/{sql-utils-r-Lw535w.mjs → sql-utils-CggjWNij.mjs} +4 -2
- package/dist/sql-utils-CggjWNij.mjs.map +1 -0
- package/dist/sql-utils.d.mts.map +1 -1
- package/dist/sql-utils.mjs +2 -3
- package/dist/{statement-builders-BPnmt6wx.mjs → statement-builders-BT889jV0.mjs} +28 -13
- package/dist/statement-builders-BT889jV0.mjs.map +1 -0
- package/dist/statement-builders.d.mts +31 -3
- package/dist/statement-builders.d.mts.map +1 -1
- package/dist/statement-builders.mjs +2 -3
- package/dist/{tables-BmdW_FWO.mjs → tables-Ej122-iI.mjs} +4 -11
- package/dist/tables-Ej122-iI.mjs.map +1 -0
- package/dist/{types-ClK03Ojd.d.mts → types-CTqpysRY.d.mts} +1 -1
- package/dist/types-CTqpysRY.d.mts.map +1 -0
- package/dist/types.d.mts +1 -1
- package/dist/types.mjs +1 -1
- package/package.json +21 -19
- package/src/core/authoring.ts +5 -11
- package/src/core/codec-helpers.ts +135 -0
- package/src/core/codec-ids.ts +1 -0
- package/src/core/codec-type-map.ts +81 -0
- package/src/core/codecs.ts +941 -547
- package/src/core/descriptor-meta.ts +1 -1
- package/src/core/migrations/operations/data-transform.ts +86 -21
- package/src/core/migrations/planner-produced-postgres-migration.ts +17 -5
- package/src/core/migrations/planner.ts +62 -20
- package/src/core/migrations/postgres-migration.ts +3 -6
- package/src/core/migrations/render-ops.ts +26 -3
- package/src/core/migrations/render-typescript.ts +5 -9
- package/src/core/migrations/runner.ts +172 -151
- package/src/core/migrations/statement-builders.ts +49 -10
- package/src/core/registry.ts +11 -0
- package/src/exports/codec-types.ts +4 -13
- package/src/exports/codecs.ts +49 -2
- package/src/exports/runtime.ts +6 -11
- package/src/exports/statement-builders.ts +2 -1
- package/dist/codec-ids.d.mts.map +0 -1
- package/dist/codec-types.d.mts.map +0 -1
- package/dist/codecs-BQEm9_oo.d.mts +0 -319
- package/dist/codecs-BQEm9_oo.d.mts.map +0 -1
- package/dist/codecs-BoahtY_Q.mjs +0 -385
- package/dist/codecs-BoahtY_Q.mjs.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 +0 -120
- package/dist/descriptor-meta-BVoVtyp-.mjs.map +0 -1
- package/dist/issue-planner-CFjB0_oO.mjs.map +0 -1
- package/dist/native-type-normalizer-CInai_oY.mjs.map +0 -1
- package/dist/op-factory-call-C3bWXKSP.d.mts.map +0 -1
- package/dist/planner-CLUvVhUN.mjs.map +0 -1
- package/dist/planner-ddl-builders-Dxvw1LHw.mjs.map +0 -1
- package/dist/planner-produced-postgres-migration-CRRTno6Z.d.mts.map +0 -1
- package/dist/planner-produced-postgres-migration-DSSPq8QS.mjs.map +0 -1
- package/dist/planner-sql-checks-7jkgm9TX.mjs.map +0 -1
- package/dist/planner-target-details-DH-azLu-.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-ops-D6_DHdOK.mjs +0 -8
- package/dist/render-ops-D6_DHdOK.mjs.map +0 -1
- package/dist/render-typescript-1rF_SB4g.mjs.map +0 -1
- package/dist/shared-Bxkt8pNO.d.mts.map +0 -1
- package/dist/sql-utils-r-Lw535w.mjs.map +0 -1
- package/dist/statement-builders-BPnmt6wx.mjs.map +0 -1
- package/dist/tables-BmdW_FWO.mjs.map +0 -1
- package/dist/types-ClK03Ojd.d.mts.map +0 -1
- package/src/core/json-schema-type-expression.ts +0 -131
|
@@ -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
|
|
|
@@ -24,10 +24,12 @@
|
|
|
24
24
|
*/
|
|
25
25
|
|
|
26
26
|
import type { SqlMigrationPlanOperation } from '@prisma-next/family-sql/control';
|
|
27
|
-
import type {
|
|
27
|
+
import type {
|
|
28
|
+
MigrationPlanWithAuthoringSurface,
|
|
29
|
+
OpFactoryCall,
|
|
30
|
+
} from '@prisma-next/framework-components/control';
|
|
28
31
|
import type { MigrationMeta } from '@prisma-next/migration-tools/migration';
|
|
29
32
|
import { ifDefined } from '@prisma-next/utils/defined';
|
|
30
|
-
import type { PostgresOpFactoryCall } from './op-factory-call';
|
|
31
33
|
import type { PostgresPlanTargetDetails } from './planner-target-details';
|
|
32
34
|
import { PostgresMigration } from './postgres-migration';
|
|
33
35
|
import { renderOps } from './render-ops';
|
|
@@ -39,13 +41,15 @@ export class TypeScriptRenderablePostgresMigration
|
|
|
39
41
|
extends PostgresMigration
|
|
40
42
|
implements MigrationPlanWithAuthoringSurface
|
|
41
43
|
{
|
|
42
|
-
readonly #calls: readonly
|
|
44
|
+
readonly #calls: readonly OpFactoryCall[];
|
|
43
45
|
readonly #meta: MigrationMeta;
|
|
46
|
+
readonly #spaceId: string;
|
|
44
47
|
|
|
45
|
-
constructor(calls: readonly
|
|
48
|
+
constructor(calls: readonly OpFactoryCall[], meta: MigrationMeta, spaceId: string) {
|
|
46
49
|
super();
|
|
47
50
|
this.#calls = calls;
|
|
48
51
|
this.#meta = meta;
|
|
52
|
+
this.#spaceId = spaceId;
|
|
49
53
|
}
|
|
50
54
|
|
|
51
55
|
override get operations(): readonly Op[] {
|
|
@@ -56,11 +60,19 @@ export class TypeScriptRenderablePostgresMigration
|
|
|
56
60
|
return this.#meta;
|
|
57
61
|
}
|
|
58
62
|
|
|
63
|
+
/**
|
|
64
|
+
* Contract space this planner-produced plan applies to. Threaded
|
|
65
|
+
* from the planner options so the runner keys the marker row by
|
|
66
|
+
* the right space when executing the plan.
|
|
67
|
+
*/
|
|
68
|
+
get spaceId(): string {
|
|
69
|
+
return this.#spaceId;
|
|
70
|
+
}
|
|
71
|
+
|
|
59
72
|
renderTypeScript(): string {
|
|
60
73
|
return renderCallsToTypeScript(this.#calls, {
|
|
61
74
|
from: this.#meta.from,
|
|
62
75
|
to: this.#meta.to,
|
|
63
|
-
...ifDefined('kind', this.#meta.kind),
|
|
64
76
|
...ifDefined('labels', this.#meta.labels),
|
|
65
77
|
});
|
|
66
78
|
}
|
|
@@ -1,9 +1,14 @@
|
|
|
1
|
+
import type { Contract } from '@prisma-next/contract/types';
|
|
1
2
|
import type {
|
|
2
3
|
MigrationOperationPolicy,
|
|
3
4
|
SqlMigrationPlannerPlanOptions,
|
|
4
5
|
SqlPlannerFailureResult,
|
|
5
6
|
} from '@prisma-next/family-sql/control';
|
|
6
|
-
import {
|
|
7
|
+
import {
|
|
8
|
+
extractCodecControlHooks,
|
|
9
|
+
planFieldEventOperations,
|
|
10
|
+
plannerFailure,
|
|
11
|
+
} from '@prisma-next/family-sql/control';
|
|
7
12
|
import { verifySqlSchema } from '@prisma-next/family-sql/schema-verify';
|
|
8
13
|
import type { TargetBoundComponentDescriptor } from '@prisma-next/framework-components/components';
|
|
9
14
|
import type {
|
|
@@ -83,30 +88,48 @@ export class PostgresMigrationPlanner implements MigrationPlanner<'sql', 'postgr
|
|
|
83
88
|
readonly contract: unknown;
|
|
84
89
|
readonly schema: unknown;
|
|
85
90
|
readonly policy: MigrationOperationPolicy;
|
|
86
|
-
readonly fromHash?: string;
|
|
87
91
|
/**
|
|
88
92
|
* The "from" contract (state the planner assumes the database starts
|
|
89
|
-
* at)
|
|
90
|
-
*
|
|
91
|
-
*
|
|
92
|
-
*
|
|
93
|
-
* activate.
|
|
93
|
+
* at), or `null` for reconciliation flows. Only `migration plan` ever
|
|
94
|
+
* supplies a non-null value; `db update` / `db init` reconcile against
|
|
95
|
+
* the live schema and pass `null`. When present alongside the
|
|
96
|
+
* `'data'` operation class, strategies that need from/to column-shape
|
|
97
|
+
* comparisons (unsafe type change, nullability tightening) activate.
|
|
98
|
+
*
|
|
99
|
+
* Typed as the framework `Contract | null` to satisfy the
|
|
100
|
+
* `MigrationPlanner` interface contract; `planSql` narrows to the SQL
|
|
101
|
+
* shape via `SqlMigrationPlannerPlanOptions`. Used to populate
|
|
102
|
+
* `describe().from` on the produced plan as
|
|
103
|
+
* `fromContract?.storage.storageHash ?? null`.
|
|
94
104
|
*/
|
|
95
|
-
readonly fromContract
|
|
105
|
+
readonly fromContract: Contract | null;
|
|
96
106
|
readonly schemaName?: string;
|
|
97
107
|
readonly frameworkComponents: ReadonlyArray<TargetBoundComponentDescriptor<'sql', string>>;
|
|
108
|
+
/**
|
|
109
|
+
* Contract space this plan applies to. Stamped onto the produced
|
|
110
|
+
* {@link TypeScriptRenderablePostgresMigration.spaceId} so the runner keys
|
|
111
|
+
* the marker row by the right space.
|
|
112
|
+
*/
|
|
113
|
+
readonly spaceId: string;
|
|
98
114
|
}): PostgresPlanResult {
|
|
99
|
-
return this.planSql(options as SqlMigrationPlannerPlanOptions
|
|
115
|
+
return this.planSql(options as SqlMigrationPlannerPlanOptions);
|
|
100
116
|
}
|
|
101
117
|
|
|
102
|
-
emptyMigration(
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
118
|
+
emptyMigration(
|
|
119
|
+
context: MigrationScaffoldContext,
|
|
120
|
+
spaceId: string,
|
|
121
|
+
): MigrationPlanWithAuthoringSurface {
|
|
122
|
+
return new TypeScriptRenderablePostgresMigration(
|
|
123
|
+
[],
|
|
124
|
+
{
|
|
125
|
+
from: context.fromHash,
|
|
126
|
+
to: context.toHash,
|
|
127
|
+
},
|
|
128
|
+
spaceId,
|
|
129
|
+
);
|
|
107
130
|
}
|
|
108
131
|
|
|
109
|
-
private planSql(options: SqlMigrationPlannerPlanOptions
|
|
132
|
+
private planSql(options: SqlMigrationPlannerPlanOptions): PostgresPlanResult {
|
|
110
133
|
const schemaName = options.schemaName ?? this.config.defaultSchema;
|
|
111
134
|
const policyResult = this.ensureAdditivePolicy(options.policy);
|
|
112
135
|
if (policyResult) {
|
|
@@ -125,7 +148,7 @@ export class PostgresMigrationPlanner implements MigrationPlanner<'sql', 'postgr
|
|
|
125
148
|
// from/to comparisons (unsafe type change, nullable tightening) are
|
|
126
149
|
// inapplicable there — reconciliation falls through to
|
|
127
150
|
// `mapIssueToCall`'s direct destructive handlers.
|
|
128
|
-
fromContract: options.fromContract
|
|
151
|
+
fromContract: options.fromContract,
|
|
129
152
|
schemaName,
|
|
130
153
|
codecHooks,
|
|
131
154
|
storageTypes,
|
|
@@ -139,12 +162,31 @@ export class PostgresMigrationPlanner implements MigrationPlanner<'sql', 'postgr
|
|
|
139
162
|
return plannerFailure(result.failure);
|
|
140
163
|
}
|
|
141
164
|
|
|
165
|
+
// Inline `onFieldEvent`-emitted ops after structural DDL. The fixed
|
|
166
|
+
// ordering is `structural → added → dropped → altered`, with
|
|
167
|
+
// within-group sorting by `(tableName, fieldName)` so re-emits are
|
|
168
|
+
// byte-stable. The hook fires only at the application emitter —
|
|
169
|
+
// extension-space planning never reaches this helper.
|
|
170
|
+
const fieldEventOps = planFieldEventOperations({
|
|
171
|
+
priorContract: options.fromContract,
|
|
172
|
+
newContract: options.contract,
|
|
173
|
+
codecHooks,
|
|
174
|
+
});
|
|
175
|
+
// Codec-emitted calls already conform to `OpFactoryCall` — render +
|
|
176
|
+
// toOp + importRequirements ride directly through the same emit path
|
|
177
|
+
// as structural ops, no `RawSqlCall` wrap.
|
|
178
|
+
const calls = [...result.value.calls, ...fieldEventOps];
|
|
179
|
+
|
|
142
180
|
return Object.freeze({
|
|
143
181
|
kind: 'success' as const,
|
|
144
|
-
plan: new TypeScriptRenderablePostgresMigration(
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
182
|
+
plan: new TypeScriptRenderablePostgresMigration(
|
|
183
|
+
calls,
|
|
184
|
+
{
|
|
185
|
+
from: options.fromContract?.storage.storageHash ?? null,
|
|
186
|
+
to: options.contract.storage.storageHash,
|
|
187
|
+
},
|
|
188
|
+
options.spaceId,
|
|
189
|
+
),
|
|
148
190
|
});
|
|
149
191
|
}
|
|
150
192
|
|
|
@@ -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
|
}
|
|
@@ -1,9 +1,32 @@
|
|
|
1
1
|
import type { SqlMigrationPlanOperation } from '@prisma-next/family-sql/control';
|
|
2
|
-
import type {
|
|
2
|
+
import type { OpFactoryCall } from '@prisma-next/framework-components/control';
|
|
3
3
|
import type { PostgresPlanTargetDetails } from './planner-target-details';
|
|
4
4
|
|
|
5
5
|
type Op = SqlMigrationPlanOperation<PostgresPlanTargetDetails>;
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
7
|
+
/**
|
|
8
|
+
* Asserts an op materialised by an `OpFactoryCall` targets postgres. The
|
|
9
|
+
* extension surface lets any contributor emit calls, so this is the
|
|
10
|
+
* integration boundary where a stray non-postgres op would otherwise
|
|
11
|
+
* silently flow through to postgres-shaped renderers — exactly the
|
|
12
|
+
* place to fail loudly with op metadata (`id` + `target.id`).
|
|
13
|
+
*/
|
|
14
|
+
function assertPostgresOp(
|
|
15
|
+
op: ReturnType<OpFactoryCall['toOp']>,
|
|
16
|
+
callFactoryName: string,
|
|
17
|
+
): asserts op is Op {
|
|
18
|
+
const targetId = (op as Partial<Op>).target?.id;
|
|
19
|
+
if (targetId !== 'postgres') {
|
|
20
|
+
throw new Error(
|
|
21
|
+
`renderOps: expected postgres op but got target.id="${String(targetId)}" for op.id="${op.id}" (factoryName="${callFactoryName}"). An OpFactoryCall produced an op for a different target on the postgres planner path; check the call's target binding.`,
|
|
22
|
+
);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export function renderOps(calls: readonly OpFactoryCall[]): Op[] {
|
|
27
|
+
return calls.map((c) => {
|
|
28
|
+
const op = c.toOp();
|
|
29
|
+
assertPostgresOp(op, c.factoryName);
|
|
30
|
+
return op;
|
|
31
|
+
});
|
|
9
32
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Polymorphic TypeScript emitter for the Postgres migration IR.
|
|
3
3
|
*
|
|
4
|
-
* Each `
|
|
4
|
+
* Each `OpFactoryCall` renders itself via `renderTypeScript()` and
|
|
5
5
|
* declares its own `importRequirements()`; this file just composes the module
|
|
6
6
|
* source around those contributions. The design mirrors the Mongo target's
|
|
7
7
|
* `render-typescript.ts` deliberately — byte-for-byte alignment isn't required
|
|
@@ -9,14 +9,13 @@
|
|
|
9
9
|
* shape is, so future consolidation to a framework-level helper is mechanical.
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
|
+
import type { OpFactoryCall } from '@prisma-next/framework-components/control';
|
|
12
13
|
import { detectScaffoldRuntime, shebangLineFor } from '@prisma-next/migration-tools/migration-ts';
|
|
13
14
|
import { type ImportRequirement, jsonToTsSource, renderImports } from '@prisma-next/ts-render';
|
|
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
|
|
|
@@ -44,7 +43,7 @@ const BASE_IMPORTS: readonly ImportRequirement[] = [
|
|
|
44
43
|
];
|
|
45
44
|
|
|
46
45
|
export function renderCallsToTypeScript(
|
|
47
|
-
calls: ReadonlyArray<
|
|
46
|
+
calls: ReadonlyArray<OpFactoryCall>,
|
|
48
47
|
meta: RenderMigrationMeta,
|
|
49
48
|
): string {
|
|
50
49
|
const imports = buildImports(calls);
|
|
@@ -68,7 +67,7 @@ export function renderCallsToTypeScript(
|
|
|
68
67
|
].join('\n');
|
|
69
68
|
}
|
|
70
69
|
|
|
71
|
-
function buildImports(calls: ReadonlyArray<
|
|
70
|
+
function buildImports(calls: ReadonlyArray<OpFactoryCall>): string {
|
|
72
71
|
const requirements: ImportRequirement[] = [...BASE_IMPORTS];
|
|
73
72
|
for (const call of calls) {
|
|
74
73
|
for (const req of call.importRequirements()) {
|
|
@@ -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
|
}
|