@prisma-next/target-postgres 0.13.0-dev.3 → 0.13.0-dev.30
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-CTikp1if.mjs → codec-ids-BvytN2P8.mjs} +3 -3
- package/dist/codec-ids-BvytN2P8.mjs.map +1 -0
- package/dist/{codec-ids-B1vOchLE.d.mts → codec-ids-CnXu9Qy3.d.mts} +3 -3
- package/dist/codec-ids-CnXu9Qy3.d.mts.map +1 -0
- package/dist/codec-ids.d.mts +2 -2
- package/dist/codec-ids.mjs +2 -2
- package/dist/{codec-types-CnFiNML4.d.mts → codec-types-DHCkwPKE.d.mts} +3 -3
- package/dist/{codec-types-CnFiNML4.d.mts.map → codec-types-DHCkwPKE.d.mts.map} +1 -1
- package/dist/codec-types.d.mts +1 -1
- package/dist/{codecs-CBpEv4s5.d.mts → codecs--0A5_4Bq.d.mts} +26 -23
- package/dist/codecs--0A5_4Bq.d.mts.map +1 -0
- package/dist/codecs.d.mts +2 -2
- package/dist/codecs.mjs +28 -35
- package/dist/codecs.mjs.map +1 -1
- package/dist/contract-free.d.mts +17 -2
- package/dist/contract-free.d.mts.map +1 -1
- package/dist/contract-free.mjs +3 -3
- package/dist/control.d.mts.map +1 -1
- package/dist/control.mjs +21 -27
- package/dist/control.mjs.map +1 -1
- package/dist/{data-transform-D25tLeYU.mjs → data-transform-BOWpliq8.mjs} +9 -17
- package/dist/data-transform-BOWpliq8.mjs.map +1 -0
- package/dist/{data-transform-DGOqcLrf.d.mts → data-transform-DDgWdB5o.d.mts} +2 -2
- package/dist/data-transform-DDgWdB5o.d.mts.map +1 -0
- package/dist/data-transform.d.mts +1 -1
- package/dist/data-transform.mjs +1 -1
- package/dist/{ddl-77SyXgFt.mjs → ddl-DY2R_Yqz.mjs} +18 -3
- package/dist/ddl-DY2R_Yqz.mjs.map +1 -0
- package/dist/ddl.d.mts +2 -2
- package/dist/ddl.mjs +2 -2
- package/dist/{descriptor-meta-DKmj-IMN.mjs → descriptor-meta-BKma_hQ5.mjs} +2 -2
- package/dist/{descriptor-meta-DKmj-IMN.mjs.map → descriptor-meta-BKma_hQ5.mjs.map} +1 -1
- package/dist/descriptor-meta-runtime-e5f2tscJ.mjs +131 -0
- package/dist/descriptor-meta-runtime-e5f2tscJ.mjs.map +1 -0
- package/dist/{issue-planner-Br0pt1Ea.mjs → issue-planner-DsjB7xDj.mjs} +48 -252
- package/dist/issue-planner-DsjB7xDj.mjs.map +1 -0
- package/dist/issue-planner.d.mts +8 -11
- package/dist/issue-planner.d.mts.map +1 -1
- package/dist/issue-planner.mjs +1 -1
- package/dist/migration.d.mts +4 -15
- package/dist/migration.d.mts.map +1 -1
- package/dist/migration.mjs +4 -4
- package/dist/{nodes-DZk2JZG3.mjs → nodes-Bbhs2rwj.mjs} +31 -2
- package/dist/nodes-Bbhs2rwj.mjs.map +1 -0
- package/dist/{nodes-779hmCfL.d.mts → nodes-pLeLgdis.d.mts} +30 -3
- package/dist/nodes-pLeLgdis.d.mts.map +1 -0
- package/dist/{op-factory-call-DMA86_2D.d.mts → op-factory-call-CdtMyrlU.d.mts} +12 -56
- package/dist/op-factory-call-CdtMyrlU.d.mts.map +1 -0
- package/dist/{op-factory-call-D2aAUhmS.mjs → op-factory-call-CjR846f7.mjs} +70 -198
- package/dist/op-factory-call-CjR846f7.mjs.map +1 -0
- package/dist/op-factory-call.d.mts +2 -2
- package/dist/op-factory-call.mjs +2 -2
- package/dist/pack.d.mts +36 -15
- package/dist/pack.d.mts.map +1 -1
- package/dist/pack.mjs +1 -1
- package/dist/{planner-CAYPJObw.mjs → planner-_FOL4I21.mjs} +25 -45
- package/dist/planner-_FOL4I21.mjs.map +1 -0
- package/dist/{planner-ddl-builders-Cw2n2llW.mjs → planner-ddl-builders-B2wOwLqI.mjs} +2 -2
- package/dist/planner-ddl-builders-B2wOwLqI.mjs.map +1 -0
- package/dist/planner-ddl-builders.d.mts +4 -4
- package/dist/planner-ddl-builders.d.mts.map +1 -1
- package/dist/planner-ddl-builders.mjs +1 -1
- package/dist/{planner-identity-values-BIpa5p2I.mjs → planner-identity-values-CJPha2Sz.mjs} +3 -9
- package/dist/planner-identity-values-CJPha2Sz.mjs.map +1 -0
- package/dist/planner-identity-values.d.mts +1 -1
- package/dist/planner-identity-values.d.mts.map +1 -1
- package/dist/planner-identity-values.mjs +1 -1
- package/dist/{planner-produced-postgres-migration-NSEhWL0L.mjs → planner-produced-postgres-migration-BmCpyWLJ.mjs} +6 -4
- package/dist/planner-produced-postgres-migration-BmCpyWLJ.mjs.map +1 -0
- package/dist/{planner-produced-postgres-migration-B4EDvLdz.d.mts → planner-produced-postgres-migration-wLhnJMMA.d.mts} +5 -6
- package/dist/planner-produced-postgres-migration-wLhnJMMA.d.mts.map +1 -0
- package/dist/planner-produced-postgres-migration.d.mts +1 -1
- package/dist/planner-produced-postgres-migration.mjs +1 -1
- package/dist/{planner-sql-checks-DAdhnI2c.mjs → planner-sql-checks-CJJtPfDH.mjs} +3 -3
- package/dist/planner-sql-checks-CJJtPfDH.mjs.map +1 -0
- package/dist/planner-sql-checks.d.mts +2 -2
- package/dist/planner-sql-checks.d.mts.map +1 -1
- package/dist/planner-sql-checks.mjs +1 -1
- package/dist/{planner-type-resolution-836DExFN.mjs → planner-type-resolution-Bt2f_q-F.mjs} +1 -6
- package/dist/planner-type-resolution-Bt2f_q-F.mjs.map +1 -0
- package/dist/planner.d.mts +4 -4
- package/dist/planner.d.mts.map +1 -1
- package/dist/planner.mjs +1 -1
- package/dist/{postgres-contract-serializer-DYTyXjPf.mjs → postgres-contract-serializer-CyAe8ZFv.mjs} +27 -37
- package/dist/postgres-contract-serializer-CyAe8ZFv.mjs.map +1 -0
- package/dist/{postgres-migration-DZ_gLUOW.d.mts → postgres-migration-DLXL0GBf.d.mts} +10 -5
- package/dist/postgres-migration-DLXL0GBf.d.mts.map +1 -0
- package/dist/{postgres-migration-COore9Mz.mjs → postgres-migration-dG-J0aI8.mjs} +7 -3
- package/dist/postgres-migration-dG-J0aI8.mjs.map +1 -0
- package/dist/{postgres-schema-BuxCxbvB.mjs → postgres-schema-CTKYiTHu.mjs} +30 -13
- package/dist/postgres-schema-CTKYiTHu.mjs.map +1 -0
- package/dist/{render-ops-BpjstrKQ.mjs → render-ops-BREh1kHe.mjs} +10 -5
- package/dist/render-ops-BREh1kHe.mjs.map +1 -0
- package/dist/render-ops.d.mts +2 -2
- package/dist/render-ops.d.mts.map +1 -1
- package/dist/render-ops.mjs +1 -1
- package/dist/runtime.d.mts.map +1 -1
- package/dist/runtime.mjs +2 -2
- package/dist/{shared-DarONYBZ.d.mts → shared-jcsbXxiW.d.mts} +2 -20
- package/dist/shared-jcsbXxiW.d.mts.map +1 -0
- package/dist/types.d.mts +8 -13
- package/dist/types.d.mts.map +1 -1
- package/dist/types.mjs +2 -3
- package/package.json +17 -18
- package/src/contract-free/ddl.ts +28 -1
- package/src/core/authoring.ts +43 -44
- package/src/core/codec-helpers.ts +0 -17
- package/src/core/codec-ids.ts +1 -1
- package/src/core/codec-type-map.ts +2 -2
- package/src/core/codecs.ts +43 -48
- package/src/core/ddl/nodes.ts +59 -1
- package/src/core/migrations/control-policy.ts +17 -47
- package/src/core/migrations/issue-planner.ts +34 -70
- package/src/core/migrations/op-factory-call.ts +89 -142
- package/src/core/migrations/operations/data-transform.ts +15 -18
- package/src/core/migrations/planner-ddl-builders.ts +3 -4
- package/src/core/migrations/planner-identity-values.ts +4 -28
- package/src/core/migrations/planner-produced-postgres-migration.ts +15 -7
- package/src/core/migrations/planner-recipes.ts +2 -6
- package/src/core/migrations/planner-sql-checks.ts +2 -6
- package/src/core/migrations/planner-strategies.ts +51 -376
- package/src/core/migrations/planner-type-resolution.ts +2 -20
- package/src/core/migrations/planner.ts +6 -6
- package/src/core/migrations/postgres-migration.ts +19 -4
- package/src/core/migrations/render-ops.ts +26 -13
- package/src/core/migrations/runner.ts +26 -20
- package/src/core/postgres-contract-serializer.ts +32 -54
- package/src/core/postgres-enum-type-schema.ts +17 -0
- package/src/core/postgres-schema.ts +56 -34
- package/src/exports/codecs.ts +2 -2
- package/src/exports/contract-free.ts +1 -1
- package/src/exports/control.ts +0 -22
- package/src/exports/ddl.ts +4 -0
- package/src/exports/migration.ts +0 -7
- package/src/exports/op-factory-call.ts +0 -4
- package/src/exports/types.ts +0 -1
- package/dist/codec-ids-B1vOchLE.d.mts.map +0 -1
- package/dist/codec-ids-CTikp1if.mjs.map +0 -1
- package/dist/codecs-CBpEv4s5.d.mts.map +0 -1
- package/dist/data-transform-D25tLeYU.mjs.map +0 -1
- package/dist/data-transform-DGOqcLrf.d.mts.map +0 -1
- package/dist/ddl-77SyXgFt.mjs.map +0 -1
- package/dist/descriptor-meta-runtime-My8_s4cs.mjs +0 -130
- package/dist/descriptor-meta-runtime-My8_s4cs.mjs.map +0 -1
- package/dist/enum-planning-BCyvlFHk.mjs +0 -0
- package/dist/enum-planning-BCyvlFHk.mjs.map +0 -1
- package/dist/enum-planning.d.mts +0 -86
- package/dist/enum-planning.d.mts.map +0 -1
- package/dist/enum-planning.mjs +0 -2
- package/dist/issue-planner-Br0pt1Ea.mjs.map +0 -1
- package/dist/nodes-779hmCfL.d.mts.map +0 -1
- package/dist/nodes-DZk2JZG3.mjs.map +0 -1
- package/dist/op-factory-call-D2aAUhmS.mjs.map +0 -1
- package/dist/op-factory-call-DMA86_2D.d.mts.map +0 -1
- package/dist/planner-CAYPJObw.mjs.map +0 -1
- package/dist/planner-ddl-builders-Cw2n2llW.mjs.map +0 -1
- package/dist/planner-identity-values-BIpa5p2I.mjs.map +0 -1
- package/dist/planner-produced-postgres-migration-B4EDvLdz.d.mts.map +0 -1
- package/dist/planner-produced-postgres-migration-NSEhWL0L.mjs.map +0 -1
- package/dist/planner-sql-checks-DAdhnI2c.mjs.map +0 -1
- package/dist/planner-type-resolution-836DExFN.mjs.map +0 -1
- package/dist/postgres-contract-serializer-DYTyXjPf.mjs.map +0 -1
- package/dist/postgres-enum-type-BVn63a89.d.mts +0 -72
- package/dist/postgres-enum-type-BVn63a89.d.mts.map +0 -1
- package/dist/postgres-enum-type-DPKqCBem.mjs +0 -62
- package/dist/postgres-enum-type-DPKqCBem.mjs.map +0 -1
- package/dist/postgres-migration-COore9Mz.mjs.map +0 -1
- package/dist/postgres-migration-DZ_gLUOW.d.mts.map +0 -1
- package/dist/postgres-schema-BuxCxbvB.mjs.map +0 -1
- package/dist/render-ops-BpjstrKQ.mjs.map +0 -1
- package/dist/shared-DarONYBZ.d.mts.map +0 -1
- package/src/core/migrations/enum-planning.ts +0 -213
- package/src/core/migrations/operations/enums.ts +0 -114
- package/src/core/postgres-enum-type.ts +0 -89
- package/src/exports/enum-planning.ts +0 -11
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
* two journeys differ only in `policy.allowedOperationClasses`:
|
|
12
12
|
*
|
|
13
13
|
* - When `'data'` is in the policy, data-safe strategies (NOT NULL backfill,
|
|
14
|
-
* nullability tightening, unsafe type changes
|
|
14
|
+
* nullability tightening, unsafe type changes) emit
|
|
15
15
|
* `DataTransformCall` placeholders that the user fills in.
|
|
16
16
|
* - When `'data'` is excluded, those strategies short-circuit so the
|
|
17
17
|
* downstream walk-schema strategies (codec-hook type ops and temp-default
|
|
@@ -29,40 +29,29 @@ import type { TargetBoundComponentDescriptor } from '@prisma-next/framework-comp
|
|
|
29
29
|
import type { SchemaIssue } from '@prisma-next/framework-components/control';
|
|
30
30
|
import { UNBOUND_NAMESPACE_ID } from '@prisma-next/framework-components/ir';
|
|
31
31
|
import {
|
|
32
|
-
isPostgresEnumStorageEntry,
|
|
33
|
-
type PostgresEnumStorageEntry,
|
|
34
32
|
type SqlStorage,
|
|
35
33
|
StorageTable,
|
|
36
34
|
type StorageTypeInstance,
|
|
37
35
|
} from '@prisma-next/sql-contract/types';
|
|
36
|
+
import type { CodecRef, DdlColumn } from '@prisma-next/sql-relational-core/ast';
|
|
37
|
+
import { col } from '@prisma-next/sql-relational-core/contract-free';
|
|
38
38
|
import type { SqlSchemaIR } from '@prisma-next/sql-schema-ir/types';
|
|
39
39
|
import { blindCast } from '@prisma-next/utils/casts';
|
|
40
|
-
import {
|
|
40
|
+
import { ifDefined } from '@prisma-next/utils/defined';
|
|
41
|
+
import type { JsonValue } from '@prisma-next/utils/json';
|
|
41
42
|
import { isPostgresSchema } from '../postgres-schema';
|
|
42
|
-
import {
|
|
43
|
-
determineEnumDiff,
|
|
44
|
-
readExistingEnumValues,
|
|
45
|
-
resolveDdlSchemaForNamespaceStorage,
|
|
46
|
-
} from './enum-planning';
|
|
47
43
|
import {
|
|
48
44
|
AddCheckConstraintCall,
|
|
49
45
|
AddColumnCall,
|
|
50
|
-
AddEnumValuesCall,
|
|
51
46
|
AlterColumnTypeCall,
|
|
52
|
-
CreateEnumTypeCall,
|
|
53
47
|
DataTransformCall,
|
|
54
48
|
DropCheckConstraintCall,
|
|
55
|
-
DropEnumTypeCall,
|
|
56
49
|
type PostgresOpFactoryCall,
|
|
50
|
+
postgresDefaultToDdlColumnDefault,
|
|
57
51
|
RawSqlCall,
|
|
58
|
-
RenameTypeCall,
|
|
59
52
|
SetNotNullCall,
|
|
60
53
|
} from './op-factory-call';
|
|
61
|
-
import {
|
|
62
|
-
buildAddColumnSql,
|
|
63
|
-
buildColumnDefaultSql,
|
|
64
|
-
buildColumnTypeSql,
|
|
65
|
-
} from './planner-ddl-builders';
|
|
54
|
+
import { buildAddColumnSql, buildColumnTypeSql } from './planner-ddl-builders';
|
|
66
55
|
import { resolveIdentityValue } from './planner-identity-values';
|
|
67
56
|
import {
|
|
68
57
|
buildAddColumnOperationIdentity,
|
|
@@ -77,8 +66,7 @@ import {
|
|
|
77
66
|
tableIsEmptyCheck,
|
|
78
67
|
} from './planner-sql-checks';
|
|
79
68
|
import { buildTargetDetails, type PostgresPlanTargetDetails } from './planner-target-details';
|
|
80
|
-
|
|
81
|
-
const REBUILD_SUFFIX = '__prisma_next_new';
|
|
69
|
+
import { resolveColumnTypeMetadata } from './planner-type-resolution';
|
|
82
70
|
|
|
83
71
|
/**
|
|
84
72
|
* Look up a storage table by its explicit namespace coordinate. Returns
|
|
@@ -95,9 +83,9 @@ export function tableAt(
|
|
|
95
83
|
namespaceId: string,
|
|
96
84
|
tableName: string,
|
|
97
85
|
): StorageTable | undefined {
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
return
|
|
86
|
+
const ns = storage.namespaces[namespaceId];
|
|
87
|
+
if (ns === undefined) return undefined;
|
|
88
|
+
return ns.entries.table?.[tableName];
|
|
101
89
|
}
|
|
102
90
|
|
|
103
91
|
/**
|
|
@@ -131,63 +119,6 @@ export function resolveDdlSchemaForNamespace(ctx: StrategyContext, namespaceId:
|
|
|
131
119
|
return namespaceId;
|
|
132
120
|
}
|
|
133
121
|
|
|
134
|
-
/** Default Postgres enum landing namespace — where contract-level (`types:`)
|
|
135
|
-
* enums are placed by the authoring builder when no explicit namespace is
|
|
136
|
-
* given. Mirrors `POSTGRES_ENUM_NAMESPACE_ID` in the contract-ts builder. */
|
|
137
|
-
const DEFAULT_ENUM_NAMESPACE_ID = 'public';
|
|
138
|
-
|
|
139
|
-
function namespaceHasEnum(storage: SqlStorage, namespaceId: string, typeName: string): boolean {
|
|
140
|
-
const ns = storage.namespaces[namespaceId];
|
|
141
|
-
if (!isPostgresSchema(ns)) return false;
|
|
142
|
-
return ns.entries.type[typeName] !== undefined;
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
/**
|
|
146
|
-
* Resolves which namespace's enum a column's bare `typeRef` binds to.
|
|
147
|
-
*
|
|
148
|
-
* Columns carry a bare (non-namespace-qualified) `typeRef`; the enum it names
|
|
149
|
-
* may live in a different namespace than the column's own (the authoring
|
|
150
|
-
* builder places contract-level `types:` enums in the default `public`
|
|
151
|
-
* namespace while a model's table may sit in the unbound namespace). The
|
|
152
|
-
* binding rule: an enum declared in the column's *own* namespace shadows
|
|
153
|
-
* everything; otherwise the column references the ambient enum — the sole
|
|
154
|
-
* namespace that defines `typeName`, preferring the default `public`
|
|
155
|
-
* namespace when several do. Returns `undefined` when no namespace defines it.
|
|
156
|
-
*/
|
|
157
|
-
function resolveColumnEnumNamespace(
|
|
158
|
-
storage: SqlStorage,
|
|
159
|
-
columnNamespaceId: string,
|
|
160
|
-
typeName: string,
|
|
161
|
-
): string | undefined {
|
|
162
|
-
if (namespaceHasEnum(storage, columnNamespaceId, typeName)) return columnNamespaceId;
|
|
163
|
-
const owners = Object.keys(storage.namespaces).filter((nsId) =>
|
|
164
|
-
namespaceHasEnum(storage, nsId, typeName),
|
|
165
|
-
);
|
|
166
|
-
if (owners.length === 1) return owners[0];
|
|
167
|
-
if (owners.includes(DEFAULT_ENUM_NAMESPACE_ID)) return DEFAULT_ENUM_NAMESPACE_ID;
|
|
168
|
-
return owners[0];
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
/**
|
|
172
|
-
* Finds a type entry by explicit namespace coordinate. Namespace types (e.g.
|
|
173
|
-
* Postgres enums) live under `storage.namespaces[nsId].entries.type`. Returns the
|
|
174
|
-
* entry from the named namespace only — never scans other namespaces, so two
|
|
175
|
-
* namespaces that hold an enum with the same name resolve independently.
|
|
176
|
-
*/
|
|
177
|
-
function locateNamespaceType(
|
|
178
|
-
storage: SqlStorage,
|
|
179
|
-
namespaceId: string,
|
|
180
|
-
typeName: string,
|
|
181
|
-
): PostgresEnumStorageEntry | undefined {
|
|
182
|
-
const ns = storage.namespaces[namespaceId];
|
|
183
|
-
const raw = ns?.entries['type']?.[typeName];
|
|
184
|
-
if (raw === undefined) return undefined;
|
|
185
|
-
return blindCast<
|
|
186
|
-
PostgresEnumStorageEntry,
|
|
187
|
-
'postgres type slot carries PostgresEnumStorageEntry at the postgres target layer'
|
|
188
|
-
>(raw);
|
|
189
|
-
}
|
|
190
|
-
|
|
191
122
|
// ============================================================================
|
|
192
123
|
// Strategy types
|
|
193
124
|
// ============================================================================
|
|
@@ -206,7 +137,7 @@ export interface StrategyContext {
|
|
|
206
137
|
readonly fromContract: Contract<SqlStorage> | null;
|
|
207
138
|
readonly schemaName: string;
|
|
208
139
|
readonly codecHooks: ReadonlyMap<string, CodecControlHooks>;
|
|
209
|
-
readonly storageTypes: Readonly<Record<string, StorageTypeInstance
|
|
140
|
+
readonly storageTypes: Readonly<Record<string, StorageTypeInstance>>;
|
|
210
141
|
readonly schema: SqlSchemaIR;
|
|
211
142
|
readonly policy: MigrationOperationPolicy;
|
|
212
143
|
readonly frameworkComponents: ReadonlyArray<TargetBoundComponentDescriptor<'sql', string>>;
|
|
@@ -227,12 +158,9 @@ export type CallMigrationStrategy = (
|
|
|
227
158
|
/**
|
|
228
159
|
* `true` for strategies that emit cohesive sequential recipes whose
|
|
229
160
|
* calls must stay contiguous and in the returned order — e.g.
|
|
230
|
-
* `
|
|
231
|
-
*
|
|
232
|
-
*
|
|
233
|
-
* (addColumn → dataTransform → setNotNull). Defaults to `false`,
|
|
234
|
-
* which lets `planIssues` hoist individual calls into their DDL
|
|
235
|
-
* sequencing bucket.
|
|
161
|
+
* `notNullBackfillCallStrategy` (addColumn → dataTransform → setNotNull).
|
|
162
|
+
* Defaults to `false`, which lets `planIssues` hoist individual calls
|
|
163
|
+
* into their DDL sequencing bucket.
|
|
236
164
|
*/
|
|
237
165
|
recipe?: boolean;
|
|
238
166
|
}
|
|
@@ -244,21 +172,34 @@ function buildColumnSpec(
|
|
|
244
172
|
column: string,
|
|
245
173
|
ctx: StrategyContext,
|
|
246
174
|
overrides?: { nullable?: boolean },
|
|
247
|
-
) {
|
|
248
|
-
const
|
|
249
|
-
if (!
|
|
175
|
+
): DdlColumn {
|
|
176
|
+
const storageCol = tableAt(ctx.toContract.storage, namespaceId, table)?.columns[column];
|
|
177
|
+
if (!storageCol)
|
|
178
|
+
throw new Error(`Column "${table}"."${column}" not found in destination contract`);
|
|
250
179
|
const mutableHooks = ctx.codecHooks as Map<string, CodecControlHooks>;
|
|
251
|
-
const mutableTypes = ctx.storageTypes as Record<
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
180
|
+
const mutableTypes = ctx.storageTypes as Record<string, StorageTypeInstance>;
|
|
181
|
+
const typeSql = buildColumnTypeSql(storageCol, mutableHooks, mutableTypes);
|
|
182
|
+
const ddlDefault = postgresDefaultToDdlColumnDefault(storageCol.default);
|
|
183
|
+
const resolved = resolveColumnTypeMetadata(storageCol, mutableTypes);
|
|
184
|
+
const typeParams =
|
|
185
|
+
resolved.typeParams === undefined
|
|
186
|
+
? undefined
|
|
187
|
+
: blindCast<
|
|
188
|
+
JsonValue,
|
|
189
|
+
'resolved.typeParams is JsonValue-shaped storage metadata; the narrowed value lands in CodecRef.typeParams which is JsonValue'
|
|
190
|
+
>(resolved.typeParams);
|
|
191
|
+
const codecRef: CodecRef | undefined = resolved.codecId
|
|
192
|
+
? {
|
|
193
|
+
codecId: resolved.codecId,
|
|
194
|
+
...ifDefined('typeParams', typeParams),
|
|
195
|
+
}
|
|
196
|
+
: undefined;
|
|
197
|
+
const nullable = overrides?.nullable ?? storageCol.nullable;
|
|
198
|
+
return col(column, typeSql, {
|
|
199
|
+
...(!nullable ? { notNull: true } : {}),
|
|
200
|
+
...ifDefined('default', ddlDefault),
|
|
201
|
+
...ifDefined('codecRef', codecRef),
|
|
202
|
+
});
|
|
262
203
|
}
|
|
263
204
|
|
|
264
205
|
function buildAlterTypeOptions(
|
|
@@ -271,10 +212,7 @@ function buildAlterTypeOptions(
|
|
|
271
212
|
const col = tableAt(ctx.toContract.storage, namespaceId, table)?.columns[column];
|
|
272
213
|
if (!col) throw new Error(`Column "${table}"."${column}" not found in destination contract`);
|
|
273
214
|
const mutableHooks = ctx.codecHooks as Map<string, CodecControlHooks>;
|
|
274
|
-
const mutableTypes = ctx.storageTypes as Record<
|
|
275
|
-
string,
|
|
276
|
-
StorageTypeInstance | PostgresEnumStorageEntry
|
|
277
|
-
>;
|
|
215
|
+
const mutableTypes = ctx.storageTypes as Record<string, StorageTypeInstance>;
|
|
278
216
|
const qualifiedTargetType = buildColumnTypeSql(col, mutableHooks, mutableTypes, false);
|
|
279
217
|
const formatTypeExpected = buildExpectedFormatType(col, mutableHooks, mutableTypes);
|
|
280
218
|
return {
|
|
@@ -417,249 +355,6 @@ export const nullableTighteningCallStrategy: CallMigrationStrategy = (issues, ct
|
|
|
417
355
|
};
|
|
418
356
|
};
|
|
419
357
|
|
|
420
|
-
function enumRebuildCallRecipe(
|
|
421
|
-
namespaceId: string,
|
|
422
|
-
typeName: string,
|
|
423
|
-
ctx: StrategyContext,
|
|
424
|
-
): readonly PostgresOpFactoryCall[] {
|
|
425
|
-
const toType = locateNamespaceType(ctx.toContract.storage, namespaceId, typeName);
|
|
426
|
-
if (!toType) return [];
|
|
427
|
-
const isEnum = isPostgresEnumStorageEntry(toType);
|
|
428
|
-
const nativeType = toType.nativeType;
|
|
429
|
-
const desiredValues: readonly string[] = isEnum
|
|
430
|
-
? toType.values
|
|
431
|
-
: (((toType as StorageTypeInstance).typeParams['values'] ?? []) as readonly string[]);
|
|
432
|
-
const tempName = `${nativeType}${REBUILD_SUFFIX}`;
|
|
433
|
-
// Type DDL targets the enum's real schema — the unbound coordinate resolves
|
|
434
|
-
// to the introspected `current_schema()`, never the `__unbound__` sentinel.
|
|
435
|
-
const ddlSchema = resolveDdlSchemaForNamespaceStorage(
|
|
436
|
-
ctx.toContract.storage,
|
|
437
|
-
namespaceId,
|
|
438
|
-
ctx.schema,
|
|
439
|
-
);
|
|
440
|
-
|
|
441
|
-
// Migrate every column whose `typeRef` binds to *this* enum. The column's
|
|
442
|
-
// bare `typeRef` resolves to an enum namespace (own-namespace shadows;
|
|
443
|
-
// otherwise the ambient/default `public` enum), so a column in the unbound
|
|
444
|
-
// namespace correctly binds to a `public`-namespace enum, while two
|
|
445
|
-
// same-named enums in distinct namespaces keep their columns disjoint.
|
|
446
|
-
const columnRefs: { namespaceId: string; table: string; column: string }[] = [];
|
|
447
|
-
for (const [nsId, ns] of Object.entries(ctx.toContract.storage.namespaces)) {
|
|
448
|
-
for (const [tableName, tableNode] of Object.entries(ns.entries.table)) {
|
|
449
|
-
const table = tableNode as StorageTable;
|
|
450
|
-
for (const [columnName, column] of Object.entries(table.columns)) {
|
|
451
|
-
if (
|
|
452
|
-
column.typeRef === typeName &&
|
|
453
|
-
resolveColumnEnumNamespace(ctx.toContract.storage, nsId, typeName) === namespaceId
|
|
454
|
-
) {
|
|
455
|
-
columnRefs.push({ namespaceId: nsId, table: tableName, column: columnName });
|
|
456
|
-
}
|
|
457
|
-
}
|
|
458
|
-
}
|
|
459
|
-
}
|
|
460
|
-
|
|
461
|
-
return [
|
|
462
|
-
new CreateEnumTypeCall(ddlSchema, tempName, desiredValues),
|
|
463
|
-
...columnRefs.map((ref) => {
|
|
464
|
-
const using = `${ref.column}::text::${tempName}`;
|
|
465
|
-
return new AlterColumnTypeCall(
|
|
466
|
-
resolveDdlSchemaForNamespace(ctx, ref.namespaceId),
|
|
467
|
-
ref.table,
|
|
468
|
-
ref.column,
|
|
469
|
-
{
|
|
470
|
-
qualifiedTargetType: tempName,
|
|
471
|
-
formatTypeExpected: tempName,
|
|
472
|
-
rawTargetTypeForLabel: tempName,
|
|
473
|
-
using,
|
|
474
|
-
},
|
|
475
|
-
);
|
|
476
|
-
}),
|
|
477
|
-
new DropEnumTypeCall(ddlSchema, nativeType),
|
|
478
|
-
new RenameTypeCall(ddlSchema, tempName, nativeType),
|
|
479
|
-
];
|
|
480
|
-
}
|
|
481
|
-
|
|
482
|
-
// ============================================================================
|
|
483
|
-
// Native enum planner strategy
|
|
484
|
-
// ============================================================================
|
|
485
|
-
|
|
486
|
-
/**
|
|
487
|
-
* Single planner strategy for `PostgresEnumType` instances. Walks
|
|
488
|
-
* `toContract.storage.types` directly (no codec-hook dispatch) and
|
|
489
|
-
* resolves existing values via `readExistingEnumValues`, the same
|
|
490
|
-
* Postgres bridging adapter the verifier uses.
|
|
491
|
-
*
|
|
492
|
-
* Per-enum dispatch:
|
|
493
|
-
*
|
|
494
|
-
* - No existing type → `CreateEnumTypeCall` with the contract's desired
|
|
495
|
-
* values.
|
|
496
|
-
* - Diff is `unchanged` → no calls emitted (consumes the matching
|
|
497
|
-
* `enum_values_changed` issue if present).
|
|
498
|
-
* - Diff is `add_values` → `AddEnumValuesCall` with the new labels.
|
|
499
|
-
* - Diff is `rebuild` → the create-temp / migrate-columns /
|
|
500
|
-
* drop-original / rename rebuild recipe. When
|
|
501
|
-
* `policy.allowedOperationClasses` includes `'data'` and the rebuild
|
|
502
|
-
* removes labels (`removedValues.length > 0`), prepend a
|
|
503
|
-
* `DataTransformCall` placeholder so the user can author the value
|
|
504
|
-
* remap before the destructive recipe runs. Without `'data'` in the
|
|
505
|
-
* policy (`db update` / `db init`), the rebuild's PG `USING ::text`
|
|
506
|
-
* cast surfaces any value-removal data loss as a runtime error rather
|
|
507
|
-
* than silent loss.
|
|
508
|
-
*
|
|
509
|
-
* Returns `recipe: true` only when a rebuild recipe was emitted (its
|
|
510
|
-
* `createEnumType(temp) → alterColumnType → dropEnumType(orig) →
|
|
511
|
-
* renameType` sequence mixes `dep`-class and `alter`-class calls that
|
|
512
|
-
* would mis-order if the planner hoisted them into its DDL sequencing
|
|
513
|
-
* buckets). For the create-only and add-values paths the strategy
|
|
514
|
-
* returns `recipe: false` so the planner hoists `CreateEnumTypeCall`
|
|
515
|
-
* into the `dep` bucket — i.e. `CREATE TYPE` runs before any
|
|
516
|
-
* `CreateTableCall` that references the new enum.
|
|
517
|
-
*/
|
|
518
|
-
/**
|
|
519
|
-
* Separator character for compound enum map keys (`namespaceId\u0000typeName`).
|
|
520
|
-
* NUL (`\u0000`) is invalid in both Postgres identifiers and TypeScript symbol
|
|
521
|
-
* names so it cannot appear in either component — unambiguous separator.
|
|
522
|
-
*/
|
|
523
|
-
const COMPOUND_KEY_SEP = '\u0000';
|
|
524
|
-
|
|
525
|
-
/** Builds the compound map key for a namespace-qualified enum entry. */
|
|
526
|
-
function enumCompoundKey(namespaceId: string, typeName: string): string {
|
|
527
|
-
return `${namespaceId}${COMPOUND_KEY_SEP}${typeName}`;
|
|
528
|
-
}
|
|
529
|
-
|
|
530
|
-
export const nativeEnumPlanCallStrategy: CallMigrationStrategy = (issues, ctx) => {
|
|
531
|
-
const enumTypes = collectPostgresEnumTypes(ctx.toContract.storage);
|
|
532
|
-
if (enumTypes.size === 0) return { kind: 'no_match' };
|
|
533
|
-
|
|
534
|
-
const dataAllowed = ctx.policy.allowedOperationClasses.includes('data');
|
|
535
|
-
|
|
536
|
-
const calls: PostgresOpFactoryCall[] = [];
|
|
537
|
-
const handledKeys = new Set<string>();
|
|
538
|
-
const introducedKeys = new Set<string>();
|
|
539
|
-
const rebuiltKeys = new Set<string>();
|
|
540
|
-
let emittedRebuildRecipe = false;
|
|
541
|
-
|
|
542
|
-
for (const [key, enumType] of enumTypes) {
|
|
543
|
-
const sepIdx = key.indexOf(COMPOUND_KEY_SEP);
|
|
544
|
-
const enumNamespaceId = key.slice(0, sepIdx);
|
|
545
|
-
const typeName = key.slice(sepIdx + 1);
|
|
546
|
-
|
|
547
|
-
const desired = enumType.values;
|
|
548
|
-
// The enum's live schema: for the unbound coordinate this resolves to the
|
|
549
|
-
// introspected `current_schema()` (e.g. `public`), never the `__unbound__`
|
|
550
|
-
// DDL-emit sentinel — so both the existing-values lookup key and the
|
|
551
|
-
// emitted `CREATE TYPE` / `ALTER TYPE` target the real schema the type
|
|
552
|
-
// lives in. Named namespaces resolve to their own DDL schema.
|
|
553
|
-
const ddlSchema = resolveDdlSchemaForNamespaceStorage(
|
|
554
|
-
ctx.toContract.storage,
|
|
555
|
-
enumNamespaceId,
|
|
556
|
-
ctx.schema,
|
|
557
|
-
);
|
|
558
|
-
const existing = readExistingEnumValues(ctx.schema, ddlSchema, enumType.nativeType);
|
|
559
|
-
if (!existing) {
|
|
560
|
-
calls.push(new CreateEnumTypeCall(ddlSchema, typeName, desired, enumType.nativeType));
|
|
561
|
-
handledKeys.add(key);
|
|
562
|
-
introducedKeys.add(key);
|
|
563
|
-
continue;
|
|
564
|
-
}
|
|
565
|
-
const diff = determineEnumDiff(existing, desired);
|
|
566
|
-
if (diff.kind === 'unchanged') {
|
|
567
|
-
handledKeys.add(key);
|
|
568
|
-
continue;
|
|
569
|
-
}
|
|
570
|
-
if (diff.kind === 'add_values') {
|
|
571
|
-
calls.push(new AddEnumValuesCall(ddlSchema, typeName, enumType.nativeType, diff.values));
|
|
572
|
-
handledKeys.add(key);
|
|
573
|
-
continue;
|
|
574
|
-
}
|
|
575
|
-
if (dataAllowed && diff.removedValues.length > 0) {
|
|
576
|
-
calls.push(
|
|
577
|
-
new DataTransformCall(
|
|
578
|
-
`migrate-${typeName}-values`,
|
|
579
|
-
`migrate-${typeName}-values:check`,
|
|
580
|
-
`migrate-${typeName}-values:run`,
|
|
581
|
-
),
|
|
582
|
-
);
|
|
583
|
-
}
|
|
584
|
-
calls.push(...enumRebuildCallRecipe(enumNamespaceId, typeName, ctx));
|
|
585
|
-
emittedRebuildRecipe = true;
|
|
586
|
-
handledKeys.add(key);
|
|
587
|
-
rebuiltKeys.add(key);
|
|
588
|
-
}
|
|
589
|
-
|
|
590
|
-
// The strategy emits a single `recipe` flag for the entire pass,
|
|
591
|
-
// which routes every emitted call to either the contiguous recipe
|
|
592
|
-
// slot (rebuild path) or the `dep` bucket (introduce / add-values
|
|
593
|
-
// path). A plan that needs both shapes simultaneously cannot be
|
|
594
|
-
// expressed today — the introduced `CreateEnumTypeCall` would land
|
|
595
|
-
// in the recipe slot and any `CreateTableCall` referencing the new
|
|
596
|
-
// enum would fail at runtime with a confusing `type "X" does not
|
|
597
|
-
// exist` error. Surface the unrepresentable case here as a
|
|
598
|
-
// planner-time error so the failure mode is loud, not silent.
|
|
599
|
-
if (introducedKeys.size > 0 && rebuiltKeys.size > 0) {
|
|
600
|
-
const introducedDisplay = [...introducedKeys]
|
|
601
|
-
.sort()
|
|
602
|
-
.map((k) => k.replace(COMPOUND_KEY_SEP, '.'))
|
|
603
|
-
.join(', ');
|
|
604
|
-
const rebuiltDisplay = [...rebuiltKeys]
|
|
605
|
-
.sort()
|
|
606
|
-
.map((k) => k.replace(COMPOUND_KEY_SEP, '.'))
|
|
607
|
-
.join(', ');
|
|
608
|
-
throw new Error(
|
|
609
|
-
`nativeEnumPlanCallStrategy: cannot emit both a brand-new enum and a rebuild on a different enum in the same plan; the single recipe flag cannot route them to different buckets. Introduced: [${introducedDisplay}]; rebuilt: [${rebuiltDisplay}]. Split the strategy or grow the \`match\` return type before this case lands.`,
|
|
610
|
-
);
|
|
611
|
-
}
|
|
612
|
-
|
|
613
|
-
const remaining = issues.filter(
|
|
614
|
-
(issue) =>
|
|
615
|
-
!(
|
|
616
|
-
(issue.kind === 'type_missing' || issue.kind === 'enum_values_changed') &&
|
|
617
|
-
issue.typeName &&
|
|
618
|
-
handledKeys.has(enumCompoundKey(resolveNamespaceIdForIssue(issue), issue.typeName))
|
|
619
|
-
),
|
|
620
|
-
);
|
|
621
|
-
|
|
622
|
-
if (calls.length === 0 && remaining.length === issues.length) {
|
|
623
|
-
return { kind: 'no_match' };
|
|
624
|
-
}
|
|
625
|
-
// `recipe: true` is required for the rebuild path — its
|
|
626
|
-
// `createEnumType(temp) → alterColumnType → dropEnumType(orig) →
|
|
627
|
-
// renameType` mixes `dep`-class and `alter`-class calls that would
|
|
628
|
-
// mis-order if the planner hoisted them into its DDL sequencing
|
|
629
|
-
// buckets. For the type_missing / add_values paths we want the
|
|
630
|
-
// opposite: hoisted into the `dep` bucket so a brand-new
|
|
631
|
-
// `CreateEnumTypeCall` runs *before* the `CreateTableCall` that
|
|
632
|
-
// references it. The two cases never co-occur in the same plan
|
|
633
|
-
// (introducing a new enum type and rebuilding an existing one in
|
|
634
|
-
// one shot would require both buckets — a shape today's interface
|
|
635
|
-
// does not surface; if that combination ever needs to land we'd
|
|
636
|
-
// split this strategy or grow the `match` return type).
|
|
637
|
-
return { kind: 'match', issues: remaining, calls, recipe: emittedRebuildRecipe };
|
|
638
|
-
};
|
|
639
|
-
|
|
640
|
-
/**
|
|
641
|
-
* Collects every `PostgresEnumType` instance across all declared namespaces,
|
|
642
|
-
* returning a compound-keyed map (`${namespaceId}\u0000${typeName}`). Two
|
|
643
|
-
* namespaces that declare an enum with the same name produce two distinct
|
|
644
|
-
* entries — no name collision, no last-write-wins.
|
|
645
|
-
*
|
|
646
|
-
* Entries within each namespace are sorted by name for deterministic ordering.
|
|
647
|
-
*/
|
|
648
|
-
function collectPostgresEnumTypes(storage: SqlStorage): ReadonlyMap<string, PostgresEnumType> {
|
|
649
|
-
const result = new Map<string, PostgresEnumType>();
|
|
650
|
-
for (const [nsId, ns] of Object.entries(storage.namespaces)) {
|
|
651
|
-
if (!isPostgresSchema(ns)) continue;
|
|
652
|
-
for (const [name, instance] of Object.entries(ns.entries.type).sort(([a], [b]) =>
|
|
653
|
-
a.localeCompare(b),
|
|
654
|
-
)) {
|
|
655
|
-
if (instance instanceof PostgresEnumType) {
|
|
656
|
-
result.set(enumCompoundKey(nsId, name), instance);
|
|
657
|
-
}
|
|
658
|
-
}
|
|
659
|
-
}
|
|
660
|
-
return result;
|
|
661
|
-
}
|
|
662
|
-
|
|
663
358
|
/**
|
|
664
359
|
* Collects every check constraint from a table in the contract storage.
|
|
665
360
|
* Returns an empty array when the table has no checks or the table is absent.
|
|
@@ -670,7 +365,7 @@ function collectContractChecks(
|
|
|
670
365
|
tableName: string,
|
|
671
366
|
): ReadonlyArray<{ name: string; column: string; permittedValues: readonly string[] }> {
|
|
672
367
|
const ns = storage.namespaces[namespaceId];
|
|
673
|
-
const tableRaw = ns
|
|
368
|
+
const tableRaw = ns !== undefined ? ns.entries.table?.[tableName] : undefined;
|
|
674
369
|
if (!(tableRaw instanceof StorageTable)) return [];
|
|
675
370
|
const checks = tableRaw.checks;
|
|
676
371
|
if (!checks || checks.length === 0) return [];
|
|
@@ -708,15 +403,13 @@ function checkValueSetsEqual(a: readonly string[], b: readonly string[]): boolea
|
|
|
708
403
|
* be altered in place).
|
|
709
404
|
*
|
|
710
405
|
* Consumes `check_missing`, `check_removed`, and `check_mismatch` issues.
|
|
711
|
-
* Does not touch the native enum path (`nativeEnumPlanCallStrategy` is
|
|
712
|
-
* unchanged).
|
|
713
406
|
*/
|
|
714
407
|
export const checkConstraintPlanCallStrategy: CallMigrationStrategy = (issues, ctx) => {
|
|
715
408
|
const calls: PostgresOpFactoryCall[] = [];
|
|
716
409
|
const handledIssueKeys = new Set<string>();
|
|
717
410
|
|
|
718
411
|
for (const [namespaceId, ns] of Object.entries(ctx.toContract.storage.namespaces)) {
|
|
719
|
-
for (const tableName of Object.keys(ns.entries.table)) {
|
|
412
|
+
for (const tableName of Object.keys(ns.entries.table ?? {})) {
|
|
720
413
|
const contractChecks = collectContractChecks(ctx.toContract.storage, namespaceId, tableName);
|
|
721
414
|
if (contractChecks.length === 0) continue;
|
|
722
415
|
|
|
@@ -788,10 +481,9 @@ export const checkConstraintPlanCallStrategy: CallMigrationStrategy = (issues, c
|
|
|
788
481
|
};
|
|
789
482
|
|
|
790
483
|
/**
|
|
791
|
-
* Dispatches
|
|
484
|
+
* Dispatches codec-typed storage types through their codec's
|
|
792
485
|
* `planTypeOperations` hook (the authoritative source for codec-driven DDL
|
|
793
|
-
* such as custom type creation).
|
|
794
|
-
* `nativeEnumPlanCallStrategy` and no longer relies on codec hooks.
|
|
486
|
+
* such as custom type creation).
|
|
795
487
|
*/
|
|
796
488
|
export const storageTypePlanCallStrategy: CallMigrationStrategy = (issues, ctx) => {
|
|
797
489
|
const storageTypes = ctx.toContract.storage.types ?? {};
|
|
@@ -803,10 +495,6 @@ export const storageTypePlanCallStrategy: CallMigrationStrategy = (issues, ctx)
|
|
|
803
495
|
for (const [typeName, typeInstance] of Object.entries(storageTypes).sort(([a], [b]) =>
|
|
804
496
|
a.localeCompare(b),
|
|
805
497
|
)) {
|
|
806
|
-
// Enums walk natively in `nativeEnumPlanCallStrategy`; codec-hook
|
|
807
|
-
// dispatch here is reserved for genuinely codec-typed entries
|
|
808
|
-
// (decimal, varchar, pgvector, …).
|
|
809
|
-
if (isPostgresEnumStorageEntry(typeInstance)) continue;
|
|
810
498
|
const codecInstance = typeInstance as StorageTypeInstance;
|
|
811
499
|
const hook = ctx.codecHooks.get(codecInstance.codecId);
|
|
812
500
|
if (!hook?.planTypeOperations) continue;
|
|
@@ -874,10 +562,7 @@ export const notNullAddColumnCallStrategy: CallMigrationStrategy = (issues, ctx)
|
|
|
874
562
|
const schemaLookups = buildSchemaLookupMap(ctx.schema);
|
|
875
563
|
|
|
876
564
|
const mutableCodecHooks = ctx.codecHooks as Map<string, CodecControlHooks>;
|
|
877
|
-
const mutableStorageTypes = ctx.storageTypes as Record<
|
|
878
|
-
string,
|
|
879
|
-
StorageTypeInstance | PostgresEnumType
|
|
880
|
-
>;
|
|
565
|
+
const mutableStorageTypes = ctx.storageTypes as Record<string, StorageTypeInstance>;
|
|
881
566
|
|
|
882
567
|
for (const issue of issues) {
|
|
883
568
|
if (issue.kind !== 'missing_column' || !issue.table || !issue.column) continue;
|
|
@@ -1033,8 +718,7 @@ function canUseSharedTemporaryDefaultStrategy(options: {
|
|
|
1033
718
|
*
|
|
1034
719
|
* - When `'data'` is allowed (`migration plan`), the data-safe strategies
|
|
1035
720
|
* (`notNullBackfillCallStrategy`, `typeChangeCallStrategy`,
|
|
1036
|
-
* `nullableTighteningCallStrategy`)
|
|
1037
|
-
* (`nativeEnumPlanCallStrategy`) consume their matching issues and emit
|
|
721
|
+
* `nullableTighteningCallStrategy`) consume their matching issues and emit
|
|
1038
722
|
* `DataTransformCall` placeholders or recipe ops.
|
|
1039
723
|
*
|
|
1040
724
|
* - When `'data'` is not allowed (`db update` / `db init`), the
|
|
@@ -1042,23 +726,14 @@ function canUseSharedTemporaryDefaultStrategy(options: {
|
|
|
1042
726
|
* the issue for the downstream walk-schema strategies
|
|
1043
727
|
* (`storageTypePlanCallStrategy`, `notNullAddColumnCallStrategy`) or the
|
|
1044
728
|
* `mapIssueToCall` default to handle with direct DDL.
|
|
1045
|
-
* `nativeEnumPlanCallStrategy` runs in both modes; under `db update` /
|
|
1046
|
-
* `db init` it emits the rebuild recipe without the data-transform
|
|
1047
|
-
* placeholder so value-removal data loss surfaces as a runtime cast
|
|
1048
|
-
* error rather than silent loss.
|
|
1049
729
|
*
|
|
1050
|
-
*
|
|
1051
|
-
* `
|
|
1052
|
-
* rebuild recipe (`recipe: true`, contiguous slot) or hoist the call
|
|
1053
|
-
* into the `dep` bucket (`recipe: false`, so a brand-new
|
|
1054
|
-
* `CreateEnumTypeCall` runs before any `CreateTableCall` referencing
|
|
1055
|
-
* it). Codec-typed entries continue through `storageTypePlanCallStrategy`.
|
|
730
|
+
* Codec-typed storage type entries are dispatched through
|
|
731
|
+
* `storageTypePlanCallStrategy`.
|
|
1056
732
|
*/
|
|
1057
733
|
export const postgresPlannerStrategies: readonly CallMigrationStrategy[] = [
|
|
1058
734
|
notNullBackfillCallStrategy,
|
|
1059
735
|
typeChangeCallStrategy,
|
|
1060
736
|
nullableTighteningCallStrategy,
|
|
1061
|
-
nativeEnumPlanCallStrategy,
|
|
1062
737
|
checkConstraintPlanCallStrategy,
|
|
1063
738
|
storageTypePlanCallStrategy,
|
|
1064
739
|
notNullAddColumnCallStrategy,
|
|
@@ -1,9 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
isPostgresEnumStorageEntry,
|
|
3
|
-
type PostgresEnumStorageEntry,
|
|
4
|
-
type StorageColumn,
|
|
5
|
-
type StorageTypeInstance,
|
|
6
|
-
} from '@prisma-next/sql-contract/types';
|
|
1
|
+
import type { StorageColumn, StorageTypeInstance } from '@prisma-next/sql-contract/types';
|
|
7
2
|
|
|
8
3
|
export type ResolvedColumnTypeMetadata = Pick<
|
|
9
4
|
StorageColumn,
|
|
@@ -12,7 +7,7 @@ export type ResolvedColumnTypeMetadata = Pick<
|
|
|
12
7
|
|
|
13
8
|
export function resolveColumnTypeMetadata(
|
|
14
9
|
column: StorageColumn,
|
|
15
|
-
storageTypes: Readonly<Record<string, StorageTypeInstance
|
|
10
|
+
storageTypes: Readonly<Record<string, StorageTypeInstance>>,
|
|
16
11
|
): ResolvedColumnTypeMetadata {
|
|
17
12
|
if (!column.typeRef) {
|
|
18
13
|
return column;
|
|
@@ -23,19 +18,6 @@ export function resolveColumnTypeMetadata(
|
|
|
23
18
|
return column;
|
|
24
19
|
}
|
|
25
20
|
|
|
26
|
-
if (isPostgresEnumStorageEntry(referencedType)) {
|
|
27
|
-
// Enum types are referenced by name (`quoteIdentifier(nativeType)`),
|
|
28
|
-
// not via parameterised codec expansion. The structural shape
|
|
29
|
-
// carries `codecId` as an enumerable property (mirroring the
|
|
30
|
-
// codec-typed view); `typeParams` is intentionally omitted here so
|
|
31
|
-
// `expandParameterizedTypeSql` does not try to look up a
|
|
32
|
-
// (deliberately absent) `expandNativeType` hook for `pg/enum@*`.
|
|
33
|
-
return {
|
|
34
|
-
codecId: referencedType.codecId,
|
|
35
|
-
nativeType: referencedType.nativeType,
|
|
36
|
-
};
|
|
37
|
-
}
|
|
38
|
-
|
|
39
21
|
return {
|
|
40
22
|
codecId: referencedType.codecId,
|
|
41
23
|
nativeType: referencedType.nativeType,
|
|
@@ -12,7 +12,7 @@ import {
|
|
|
12
12
|
planFieldEventOperations,
|
|
13
13
|
plannerFailure,
|
|
14
14
|
} from '@prisma-next/family-sql/control';
|
|
15
|
-
import type {
|
|
15
|
+
import type { ExecuteRequestLowerer } from '@prisma-next/family-sql/control-adapter';
|
|
16
16
|
import { verifySqlSchema } from '@prisma-next/family-sql/schema-verify';
|
|
17
17
|
import type { TargetBoundComponentDescriptor } from '@prisma-next/framework-components/components';
|
|
18
18
|
import type {
|
|
@@ -31,7 +31,6 @@ import {
|
|
|
31
31
|
resolvePostgresIssueControlPolicySubject,
|
|
32
32
|
resolvePostgresIssueCreationFactoryName,
|
|
33
33
|
} from './control-policy';
|
|
34
|
-
import { createResolveExistingEnumValues } from './enum-planning';
|
|
35
34
|
import { planIssues } from './issue-planner';
|
|
36
35
|
import type { PostgresOpFactoryCall } from './op-factory-call';
|
|
37
36
|
import { TypeScriptRenderablePostgresMigration } from './planner-produced-postgres-migration';
|
|
@@ -52,7 +51,9 @@ type VerifySqlSchemaOptionsWithComponents = Parameters<typeof verifySqlSchema>[0
|
|
|
52
51
|
readonly frameworkComponents: PlannerFrameworkComponents;
|
|
53
52
|
};
|
|
54
53
|
|
|
55
|
-
export function createPostgresMigrationPlanner(
|
|
54
|
+
export function createPostgresMigrationPlanner(
|
|
55
|
+
lowerer: ExecuteRequestLowerer,
|
|
56
|
+
): PostgresMigrationPlanner {
|
|
56
57
|
return new PostgresMigrationPlanner(lowerer);
|
|
57
58
|
}
|
|
58
59
|
|
|
@@ -88,9 +89,9 @@ export type PostgresPlanResult =
|
|
|
88
89
|
* authoring surface.
|
|
89
90
|
*/
|
|
90
91
|
export class PostgresMigrationPlanner implements MigrationPlanner<'sql', 'postgres'> {
|
|
91
|
-
readonly #lowerer:
|
|
92
|
+
readonly #lowerer: ExecuteRequestLowerer | undefined;
|
|
92
93
|
|
|
93
|
-
constructor(lowerer?:
|
|
94
|
+
constructor(lowerer?: ExecuteRequestLowerer) {
|
|
94
95
|
this.#lowerer = lowerer;
|
|
95
96
|
}
|
|
96
97
|
|
|
@@ -266,7 +267,6 @@ export class PostgresMigrationPlanner implements MigrationPlanner<'sql', 'postgr
|
|
|
266
267
|
frameworkComponents: options.frameworkComponents,
|
|
267
268
|
normalizeDefault: parsePostgresDefault,
|
|
268
269
|
normalizeNativeType: normalizeSchemaNativeType,
|
|
269
|
-
resolveExistingEnumValues: createResolveExistingEnumValues(options.contract.storage),
|
|
270
270
|
};
|
|
271
271
|
const verifyResult = verifySqlSchema(verifyOptions);
|
|
272
272
|
// Schema presence is a Postgres-specific concern (no equivalent in
|