@prisma-next/target-postgres 0.13.0 → 0.14.0
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 +163 -15
- package/dist/contract-free.d.mts.map +1 -1
- package/dist/contract-free.mjs +4 -17
- package/dist/contract-free.mjs.map +1 -1
- 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-QDyOSeLc.mjs +251 -0
- package/dist/ddl-QDyOSeLc.mjs.map +1 -0
- package/dist/ddl.d.mts +2 -2
- package/dist/ddl.mjs +2 -2
- package/dist/descriptor-meta-CpGygXpI.mjs +140 -0
- package/dist/descriptor-meta-CpGygXpI.mjs.map +1 -0
- package/dist/{issue-planner-Br0pt1Ea.mjs → issue-planner-DL6g3CmE.mjs} +52 -366
- package/dist/issue-planner-DL6g3CmE.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 +5 -92
- 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-D2aAUhmS.mjs → op-factory-call-D_p5vxwt.mjs} +601 -418
- package/dist/op-factory-call-D_p5vxwt.mjs.map +1 -0
- package/dist/{op-factory-call-DMA86_2D.d.mts → op-factory-call-DmQEc3XV.d.mts} +119 -72
- package/dist/op-factory-call-DmQEc3XV.d.mts.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-Bs_baQax.mjs} +25 -45
- package/dist/planner-Bs_baQax.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-Cji5vxUf.mjs} +6 -4
- package/dist/planner-produced-postgres-migration-Cji5vxUf.mjs.map +1 -0
- package/dist/{planner-produced-postgres-migration-B4EDvLdz.d.mts → planner-produced-postgres-migration-QqHa2C2l.d.mts} +5 -6
- package/dist/planner-produced-postgres-migration-QqHa2C2l.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-jqUUGyQR.mjs +152 -0
- package/dist/planner-sql-checks-jqUUGyQR.mjs.map +1 -0
- package/dist/planner-sql-checks.d.mts +3 -49
- package/dist/planner-sql-checks.d.mts.map +1 -1
- package/dist/planner-sql-checks.mjs +2 -2
- 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-k3TAcPMY.mjs} +30 -37
- package/dist/postgres-contract-serializer-k3TAcPMY.mjs.map +1 -0
- package/dist/postgres-migration-B5jKrXv3.mjs +145 -0
- package/dist/postgres-migration-B5jKrXv3.mjs.map +1 -0
- package/dist/postgres-migration-Y4YBJqkS.d.mts +181 -0
- package/dist/postgres-migration-Y4YBJqkS.d.mts.map +1 -0
- package/dist/{postgres-schema-BuxCxbvB.mjs → postgres-schema-COGZ1ark.mjs} +82 -23
- package/dist/postgres-schema-COGZ1ark.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 +1 -0
- package/dist/runtime.d.mts.map +1 -1
- package/dist/runtime.mjs +2 -2
- package/dist/table-source-BvFo7gVs.d.mts +15 -0
- package/dist/table-source-BvFo7gVs.d.mts.map +1 -0
- package/dist/types.d.mts +34 -19
- package/dist/types.d.mts.map +1 -1
- package/dist/types.mjs +2 -3
- package/package.json +17 -18
- package/src/contract-free/checks.ts +363 -0
- 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 +486 -215
- package/src/core/migrations/operations/columns.ts +175 -140
- package/src/core/migrations/operations/constraints.ts +79 -108
- package/src/core/migrations/operations/data-transform.ts +15 -18
- package/src/core/migrations/operations/dependencies.ts +16 -14
- package/src/core/migrations/operations/indexes.ts +31 -28
- package/src/core/migrations/operations/shared.ts +2 -2
- package/src/core/migrations/operations/tables.ts +13 -14
- 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 +44 -39
- package/src/core/migrations/planner-sql-checks.ts +3 -178
- package/src/core/migrations/planner-strategies.ts +76 -449
- 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 +287 -7
- package/src/core/migrations/render-ops.ts +26 -13
- package/src/core/migrations/runner.ts +26 -20
- package/src/core/postgres-contract-serializer.ts +37 -54
- package/src/core/postgres-enum-type-schema.ts +17 -0
- package/src/core/postgres-schema.ts +86 -46
- package/src/exports/codecs.ts +2 -2
- package/src/exports/contract-free.ts +22 -1
- package/src/exports/control.ts +0 -22
- package/src/exports/ddl.ts +4 -0
- package/src/exports/migration.ts +1 -29
- package/src/exports/op-factory-call.ts +0 -4
- package/src/exports/planner-sql-checks.ts +0 -7
- 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 +0 -30
- package/dist/ddl-77SyXgFt.mjs.map +0 -1
- package/dist/descriptor-meta-DKmj-IMN.mjs +0 -14
- package/dist/descriptor-meta-DKmj-IMN.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 +0 -272
- 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 +0 -71
- package/dist/postgres-migration-COore9Mz.mjs.map +0 -1
- package/dist/postgres-migration-DZ_gLUOW.d.mts +0 -72
- 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 +0 -43
- 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,56 +29,36 @@ 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
|
-
|
|
46
|
+
AddNotNullColumnDirectCall,
|
|
47
|
+
AddNotNullColumnWithTempDefaultCall,
|
|
51
48
|
AlterColumnTypeCall,
|
|
52
|
-
CreateEnumTypeCall,
|
|
53
49
|
DataTransformCall,
|
|
54
50
|
DropCheckConstraintCall,
|
|
55
|
-
DropEnumTypeCall,
|
|
56
51
|
type PostgresOpFactoryCall,
|
|
52
|
+
postgresDefaultToDdlColumnDefault,
|
|
57
53
|
RawSqlCall,
|
|
58
|
-
RenameTypeCall,
|
|
59
54
|
SetNotNullCall,
|
|
60
55
|
} from './op-factory-call';
|
|
61
|
-
import {
|
|
62
|
-
buildAddColumnSql,
|
|
63
|
-
buildColumnDefaultSql,
|
|
64
|
-
buildColumnTypeSql,
|
|
65
|
-
} from './planner-ddl-builders';
|
|
56
|
+
import { buildAddColumnSql, buildColumnTypeSql } from './planner-ddl-builders';
|
|
66
57
|
import { resolveIdentityValue } from './planner-identity-values';
|
|
67
|
-
import {
|
|
68
|
-
buildAddColumnOperationIdentity,
|
|
69
|
-
buildAddNotNullColumnWithTemporaryDefaultOperation,
|
|
70
|
-
} from './planner-recipes';
|
|
71
58
|
import { buildSchemaLookupMap, hasForeignKey, hasUniqueConstraint } from './planner-schema-lookup';
|
|
72
|
-
import {
|
|
73
|
-
buildExpectedFormatType,
|
|
74
|
-
columnExistsCheck,
|
|
75
|
-
columnNullabilityCheck,
|
|
76
|
-
qualifyTableName,
|
|
77
|
-
tableIsEmptyCheck,
|
|
78
|
-
} from './planner-sql-checks';
|
|
59
|
+
import { buildExpectedFormatType, qualifyTableName } from './planner-sql-checks';
|
|
79
60
|
import { buildTargetDetails, type PostgresPlanTargetDetails } from './planner-target-details';
|
|
80
|
-
|
|
81
|
-
const REBUILD_SUFFIX = '__prisma_next_new';
|
|
61
|
+
import { resolveColumnTypeMetadata } from './planner-type-resolution';
|
|
82
62
|
|
|
83
63
|
/**
|
|
84
64
|
* Look up a storage table by its explicit namespace coordinate. Returns
|
|
@@ -95,9 +75,9 @@ export function tableAt(
|
|
|
95
75
|
namespaceId: string,
|
|
96
76
|
tableName: string,
|
|
97
77
|
): StorageTable | undefined {
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
return
|
|
78
|
+
const ns = storage.namespaces[namespaceId];
|
|
79
|
+
if (ns === undefined) return undefined;
|
|
80
|
+
return ns.entries.table?.[tableName];
|
|
101
81
|
}
|
|
102
82
|
|
|
103
83
|
/**
|
|
@@ -131,63 +111,6 @@ export function resolveDdlSchemaForNamespace(ctx: StrategyContext, namespaceId:
|
|
|
131
111
|
return namespaceId;
|
|
132
112
|
}
|
|
133
113
|
|
|
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
114
|
// ============================================================================
|
|
192
115
|
// Strategy types
|
|
193
116
|
// ============================================================================
|
|
@@ -206,7 +129,7 @@ export interface StrategyContext {
|
|
|
206
129
|
readonly fromContract: Contract<SqlStorage> | null;
|
|
207
130
|
readonly schemaName: string;
|
|
208
131
|
readonly codecHooks: ReadonlyMap<string, CodecControlHooks>;
|
|
209
|
-
readonly storageTypes: Readonly<Record<string, StorageTypeInstance
|
|
132
|
+
readonly storageTypes: Readonly<Record<string, StorageTypeInstance>>;
|
|
210
133
|
readonly schema: SqlSchemaIR;
|
|
211
134
|
readonly policy: MigrationOperationPolicy;
|
|
212
135
|
readonly frameworkComponents: ReadonlyArray<TargetBoundComponentDescriptor<'sql', string>>;
|
|
@@ -227,12 +150,9 @@ export type CallMigrationStrategy = (
|
|
|
227
150
|
/**
|
|
228
151
|
* `true` for strategies that emit cohesive sequential recipes whose
|
|
229
152
|
* 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.
|
|
153
|
+
* `notNullBackfillCallStrategy` (addColumn → dataTransform → setNotNull).
|
|
154
|
+
* Defaults to `false`, which lets `planIssues` hoist individual calls
|
|
155
|
+
* into their DDL sequencing bucket.
|
|
236
156
|
*/
|
|
237
157
|
recipe?: boolean;
|
|
238
158
|
}
|
|
@@ -244,21 +164,34 @@ function buildColumnSpec(
|
|
|
244
164
|
column: string,
|
|
245
165
|
ctx: StrategyContext,
|
|
246
166
|
overrides?: { nullable?: boolean },
|
|
247
|
-
) {
|
|
248
|
-
const
|
|
249
|
-
if (!
|
|
167
|
+
): DdlColumn {
|
|
168
|
+
const storageCol = tableAt(ctx.toContract.storage, namespaceId, table)?.columns[column];
|
|
169
|
+
if (!storageCol)
|
|
170
|
+
throw new Error(`Column "${table}"."${column}" not found in destination contract`);
|
|
250
171
|
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
|
-
|
|
172
|
+
const mutableTypes = ctx.storageTypes as Record<string, StorageTypeInstance>;
|
|
173
|
+
const typeSql = buildColumnTypeSql(storageCol, mutableHooks, mutableTypes);
|
|
174
|
+
const ddlDefault = postgresDefaultToDdlColumnDefault(storageCol.default);
|
|
175
|
+
const resolved = resolveColumnTypeMetadata(storageCol, mutableTypes);
|
|
176
|
+
const typeParams =
|
|
177
|
+
resolved.typeParams === undefined
|
|
178
|
+
? undefined
|
|
179
|
+
: blindCast<
|
|
180
|
+
JsonValue,
|
|
181
|
+
'resolved.typeParams is JsonValue-shaped storage metadata; the narrowed value lands in CodecRef.typeParams which is JsonValue'
|
|
182
|
+
>(resolved.typeParams);
|
|
183
|
+
const codecRef: CodecRef | undefined = resolved.codecId
|
|
184
|
+
? {
|
|
185
|
+
codecId: resolved.codecId,
|
|
186
|
+
...ifDefined('typeParams', typeParams),
|
|
187
|
+
}
|
|
188
|
+
: undefined;
|
|
189
|
+
const nullable = overrides?.nullable ?? storageCol.nullable;
|
|
190
|
+
return col(column, typeSql, {
|
|
191
|
+
...(!nullable ? { notNull: true } : {}),
|
|
192
|
+
...ifDefined('default', ddlDefault),
|
|
193
|
+
...ifDefined('codecRef', codecRef),
|
|
194
|
+
});
|
|
262
195
|
}
|
|
263
196
|
|
|
264
197
|
function buildAlterTypeOptions(
|
|
@@ -271,10 +204,7 @@ function buildAlterTypeOptions(
|
|
|
271
204
|
const col = tableAt(ctx.toContract.storage, namespaceId, table)?.columns[column];
|
|
272
205
|
if (!col) throw new Error(`Column "${table}"."${column}" not found in destination contract`);
|
|
273
206
|
const mutableHooks = ctx.codecHooks as Map<string, CodecControlHooks>;
|
|
274
|
-
const mutableTypes = ctx.storageTypes as Record<
|
|
275
|
-
string,
|
|
276
|
-
StorageTypeInstance | PostgresEnumStorageEntry
|
|
277
|
-
>;
|
|
207
|
+
const mutableTypes = ctx.storageTypes as Record<string, StorageTypeInstance>;
|
|
278
208
|
const qualifiedTargetType = buildColumnTypeSql(col, mutableHooks, mutableTypes, false);
|
|
279
209
|
const formatTypeExpected = buildExpectedFormatType(col, mutableHooks, mutableTypes);
|
|
280
210
|
return {
|
|
@@ -417,249 +347,6 @@ export const nullableTighteningCallStrategy: CallMigrationStrategy = (issues, ct
|
|
|
417
347
|
};
|
|
418
348
|
};
|
|
419
349
|
|
|
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
350
|
/**
|
|
664
351
|
* Collects every check constraint from a table in the contract storage.
|
|
665
352
|
* Returns an empty array when the table has no checks or the table is absent.
|
|
@@ -670,7 +357,7 @@ function collectContractChecks(
|
|
|
670
357
|
tableName: string,
|
|
671
358
|
): ReadonlyArray<{ name: string; column: string; permittedValues: readonly string[] }> {
|
|
672
359
|
const ns = storage.namespaces[namespaceId];
|
|
673
|
-
const tableRaw = ns
|
|
360
|
+
const tableRaw = ns !== undefined ? ns.entries.table?.[tableName] : undefined;
|
|
674
361
|
if (!(tableRaw instanceof StorageTable)) return [];
|
|
675
362
|
const checks = tableRaw.checks;
|
|
676
363
|
if (!checks || checks.length === 0) return [];
|
|
@@ -708,15 +395,13 @@ function checkValueSetsEqual(a: readonly string[], b: readonly string[]): boolea
|
|
|
708
395
|
* be altered in place).
|
|
709
396
|
*
|
|
710
397
|
* Consumes `check_missing`, `check_removed`, and `check_mismatch` issues.
|
|
711
|
-
* Does not touch the native enum path (`nativeEnumPlanCallStrategy` is
|
|
712
|
-
* unchanged).
|
|
713
398
|
*/
|
|
714
399
|
export const checkConstraintPlanCallStrategy: CallMigrationStrategy = (issues, ctx) => {
|
|
715
400
|
const calls: PostgresOpFactoryCall[] = [];
|
|
716
401
|
const handledIssueKeys = new Set<string>();
|
|
717
402
|
|
|
718
403
|
for (const [namespaceId, ns] of Object.entries(ctx.toContract.storage.namespaces)) {
|
|
719
|
-
for (const tableName of Object.keys(ns.entries.table)) {
|
|
404
|
+
for (const tableName of Object.keys(ns.entries.table ?? {})) {
|
|
720
405
|
const contractChecks = collectContractChecks(ctx.toContract.storage, namespaceId, tableName);
|
|
721
406
|
if (contractChecks.length === 0) continue;
|
|
722
407
|
|
|
@@ -788,10 +473,9 @@ export const checkConstraintPlanCallStrategy: CallMigrationStrategy = (issues, c
|
|
|
788
473
|
};
|
|
789
474
|
|
|
790
475
|
/**
|
|
791
|
-
* Dispatches
|
|
476
|
+
* Dispatches codec-typed storage types through their codec's
|
|
792
477
|
* `planTypeOperations` hook (the authoritative source for codec-driven DDL
|
|
793
|
-
* such as custom type creation).
|
|
794
|
-
* `nativeEnumPlanCallStrategy` and no longer relies on codec hooks.
|
|
478
|
+
* such as custom type creation).
|
|
795
479
|
*/
|
|
796
480
|
export const storageTypePlanCallStrategy: CallMigrationStrategy = (issues, ctx) => {
|
|
797
481
|
const storageTypes = ctx.toContract.storage.types ?? {};
|
|
@@ -803,10 +487,6 @@ export const storageTypePlanCallStrategy: CallMigrationStrategy = (issues, ctx)
|
|
|
803
487
|
for (const [typeName, typeInstance] of Object.entries(storageTypes).sort(([a], [b]) =>
|
|
804
488
|
a.localeCompare(b),
|
|
805
489
|
)) {
|
|
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
490
|
const codecInstance = typeInstance as StorageTypeInstance;
|
|
811
491
|
const hook = ctx.codecHooks.get(codecInstance.codecId);
|
|
812
492
|
if (!hook?.planTypeOperations) continue;
|
|
@@ -874,10 +554,7 @@ export const notNullAddColumnCallStrategy: CallMigrationStrategy = (issues, ctx)
|
|
|
874
554
|
const schemaLookups = buildSchemaLookupMap(ctx.schema);
|
|
875
555
|
|
|
876
556
|
const mutableCodecHooks = ctx.codecHooks as Map<string, CodecControlHooks>;
|
|
877
|
-
const mutableStorageTypes = ctx.storageTypes as Record<
|
|
878
|
-
string,
|
|
879
|
-
StorageTypeInstance | PostgresEnumType
|
|
880
|
-
>;
|
|
557
|
+
const mutableStorageTypes = ctx.storageTypes as Record<string, StorageTypeInstance>;
|
|
881
558
|
|
|
882
559
|
for (const issue of issues) {
|
|
883
560
|
if (issue.kind !== 'missing_column' || !issue.table || !issue.column) continue;
|
|
@@ -910,74 +587,34 @@ export const notNullAddColumnCallStrategy: CallMigrationStrategy = (issues, ctx)
|
|
|
910
587
|
|
|
911
588
|
if (canUseSharedTempDefault && temporaryDefault !== null) {
|
|
912
589
|
calls.push(
|
|
913
|
-
new
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
}),
|
|
923
|
-
),
|
|
590
|
+
new AddNotNullColumnWithTempDefaultCall({
|
|
591
|
+
schemaName: schemaForTable,
|
|
592
|
+
tableName: issue.table,
|
|
593
|
+
columnName: issue.column,
|
|
594
|
+
column,
|
|
595
|
+
codecHooks: mutableCodecHooks,
|
|
596
|
+
storageTypes: mutableStorageTypes,
|
|
597
|
+
temporaryDefault,
|
|
598
|
+
}),
|
|
924
599
|
);
|
|
925
600
|
continue;
|
|
926
601
|
}
|
|
927
602
|
|
|
928
603
|
const qualified = qualifyTableName(schemaForTable, issue.table);
|
|
929
604
|
calls.push(
|
|
930
|
-
new
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
{
|
|
944
|
-
description: `ensure table "${issue.table}" is empty before adding NOT NULL column without default`,
|
|
945
|
-
sql: tableIsEmptyCheck(qualified),
|
|
946
|
-
},
|
|
947
|
-
],
|
|
948
|
-
execute: [
|
|
949
|
-
{
|
|
950
|
-
description: `add column "${issue.column}"`,
|
|
951
|
-
sql: buildAddColumnSql(
|
|
952
|
-
qualified,
|
|
953
|
-
issue.column,
|
|
954
|
-
column,
|
|
955
|
-
mutableCodecHooks,
|
|
956
|
-
undefined,
|
|
957
|
-
mutableStorageTypes,
|
|
958
|
-
),
|
|
959
|
-
},
|
|
960
|
-
],
|
|
961
|
-
postcheck: [
|
|
962
|
-
{
|
|
963
|
-
description: `verify column "${issue.column}" exists`,
|
|
964
|
-
sql: columnExistsCheck({
|
|
965
|
-
schema: schemaForTable,
|
|
966
|
-
table: issue.table,
|
|
967
|
-
column: issue.column,
|
|
968
|
-
}),
|
|
969
|
-
},
|
|
970
|
-
{
|
|
971
|
-
description: `verify column "${issue.column}" is NOT NULL`,
|
|
972
|
-
sql: columnNullabilityCheck({
|
|
973
|
-
schema: schemaForTable,
|
|
974
|
-
table: issue.table,
|
|
975
|
-
column: issue.column,
|
|
976
|
-
nullable: false,
|
|
977
|
-
}),
|
|
978
|
-
},
|
|
979
|
-
],
|
|
980
|
-
}),
|
|
605
|
+
new AddNotNullColumnDirectCall(
|
|
606
|
+
schemaForTable,
|
|
607
|
+
issue.table,
|
|
608
|
+
issue.column,
|
|
609
|
+
buildAddColumnSql(
|
|
610
|
+
qualified,
|
|
611
|
+
issue.column,
|
|
612
|
+
column,
|
|
613
|
+
mutableCodecHooks,
|
|
614
|
+
undefined,
|
|
615
|
+
mutableStorageTypes,
|
|
616
|
+
),
|
|
617
|
+
),
|
|
981
618
|
);
|
|
982
619
|
}
|
|
983
620
|
|
|
@@ -1033,8 +670,7 @@ function canUseSharedTemporaryDefaultStrategy(options: {
|
|
|
1033
670
|
*
|
|
1034
671
|
* - When `'data'` is allowed (`migration plan`), the data-safe strategies
|
|
1035
672
|
* (`notNullBackfillCallStrategy`, `typeChangeCallStrategy`,
|
|
1036
|
-
* `nullableTighteningCallStrategy`)
|
|
1037
|
-
* (`nativeEnumPlanCallStrategy`) consume their matching issues and emit
|
|
673
|
+
* `nullableTighteningCallStrategy`) consume their matching issues and emit
|
|
1038
674
|
* `DataTransformCall` placeholders or recipe ops.
|
|
1039
675
|
*
|
|
1040
676
|
* - When `'data'` is not allowed (`db update` / `db init`), the
|
|
@@ -1042,23 +678,14 @@ function canUseSharedTemporaryDefaultStrategy(options: {
|
|
|
1042
678
|
* the issue for the downstream walk-schema strategies
|
|
1043
679
|
* (`storageTypePlanCallStrategy`, `notNullAddColumnCallStrategy`) or the
|
|
1044
680
|
* `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
681
|
*
|
|
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`.
|
|
682
|
+
* Codec-typed storage type entries are dispatched through
|
|
683
|
+
* `storageTypePlanCallStrategy`.
|
|
1056
684
|
*/
|
|
1057
685
|
export const postgresPlannerStrategies: readonly CallMigrationStrategy[] = [
|
|
1058
686
|
notNullBackfillCallStrategy,
|
|
1059
687
|
typeChangeCallStrategy,
|
|
1060
688
|
nullableTighteningCallStrategy,
|
|
1061
|
-
nativeEnumPlanCallStrategy,
|
|
1062
689
|
checkConstraintPlanCallStrategy,
|
|
1063
690
|
storageTypePlanCallStrategy,
|
|
1064
691
|
notNullAddColumnCallStrategy,
|