@prisma-next/target-postgres 0.14.0-dev.49 → 0.14.0-dev.50
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/control-policy-DUFgy9jO.mjs +1103 -0
- package/dist/control-policy-DUFgy9jO.mjs.map +1 -0
- package/dist/control.d.mts.map +1 -1
- package/dist/control.mjs +19 -40
- package/dist/control.mjs.map +1 -1
- package/dist/default-normalizer.mjs +247 -1
- package/dist/default-normalizer.mjs.map +1 -0
- package/dist/diff-database-schema-BmSmWcaY.mjs +313 -0
- package/dist/diff-database-schema-BmSmWcaY.mjs.map +1 -0
- package/dist/diff-database-schema.d.mts +46 -0
- package/dist/diff-database-schema.d.mts.map +1 -0
- package/dist/diff-database-schema.mjs +2 -0
- package/dist/issue-planner.d.mts +34 -10
- package/dist/issue-planner.d.mts.map +1 -1
- package/dist/issue-planner.mjs +2 -2
- package/dist/migration.d.mts +2 -2
- package/dist/migration.mjs +2 -2
- package/dist/native-type-normalizer.mjs +37 -1
- package/dist/native-type-normalizer.mjs.map +1 -0
- package/dist/{op-factory-call-BuoGT5UI.mjs → op-factory-call-BKRnMgrB.mjs} +2 -2
- package/dist/op-factory-call-BKRnMgrB.mjs.map +1 -0
- package/dist/{op-factory-call-Caqus-Qg.d.mts → op-factory-call-Dt-Jx5JS.d.mts} +2 -2
- package/dist/{op-factory-call-Caqus-Qg.d.mts.map → op-factory-call-Dt-Jx5JS.d.mts.map} +1 -1
- package/dist/op-factory-call.d.mts +2 -2
- package/dist/op-factory-call.mjs +2 -2
- package/dist/planner-BMBRPzsv.mjs +367 -0
- package/dist/planner-BMBRPzsv.mjs.map +1 -0
- package/dist/planner-ddl-builders-B8Nn6nHN.mjs.map +1 -1
- package/dist/planner-ddl-builders.d.mts +2 -2
- package/dist/planner-ddl-builders.d.mts.map +1 -1
- package/dist/planner-identity-values-CJPha2Sz.mjs.map +1 -1
- package/dist/planner-identity-values.d.mts +7 -1
- package/dist/planner-identity-values.d.mts.map +1 -1
- package/dist/planner-identity-values.mjs +2 -2
- package/dist/{planner-produced-postgres-migration-CtJi4sgS.d.mts → planner-produced-postgres-migration-C0a7I1-e.d.mts} +2 -2
- package/dist/{planner-produced-postgres-migration-CtJi4sgS.d.mts.map → planner-produced-postgres-migration-C0a7I1-e.d.mts.map} +1 -1
- package/dist/{planner-produced-postgres-migration-C2_H6Vvl.mjs → planner-produced-postgres-migration-Dp6IO_gj.mjs} +2 -2
- package/dist/{planner-produced-postgres-migration-C2_H6Vvl.mjs.map → planner-produced-postgres-migration-Dp6IO_gj.mjs.map} +1 -1
- package/dist/planner-produced-postgres-migration.d.mts +1 -1
- package/dist/planner-produced-postgres-migration.mjs +1 -1
- package/dist/planner-sql-checks-Cze3vMfe.mjs.map +1 -1
- package/dist/planner-sql-checks.d.mts +1 -1
- package/dist/planner-sql-checks.d.mts.map +1 -1
- package/dist/planner.d.mts +27 -67
- package/dist/planner.d.mts.map +1 -1
- package/dist/planner.mjs +3 -2
- package/dist/{postgres-database-schema-node-os8ogEap.d.mts → postgres-database-schema-node-C9bTuMrd.d.mts} +14 -7
- package/dist/postgres-database-schema-node-C9bTuMrd.d.mts.map +1 -0
- package/dist/{postgres-migration-D0QIVE-p.mjs → postgres-migration-D0jwzYDT.mjs} +2 -2
- package/dist/{postgres-migration-D0QIVE-p.mjs.map → postgres-migration-D0jwzYDT.mjs.map} +1 -1
- package/dist/{postgres-migration-CfyKDT7b.d.mts → postgres-migration-DFaxuRyI.d.mts} +2 -2
- package/dist/{postgres-migration-CfyKDT7b.d.mts.map → postgres-migration-DFaxuRyI.d.mts.map} +1 -1
- package/dist/{postgres-table-schema-node-Clei_xel.mjs → postgres-table-schema-node-BgaSrxYN.mjs} +30 -29
- package/dist/postgres-table-schema-node-BgaSrxYN.mjs.map +1 -0
- package/dist/schema-node-kinds-ClScchhi.mjs +76 -0
- package/dist/schema-node-kinds-ClScchhi.mjs.map +1 -0
- package/dist/types.d.mts +1 -1
- package/dist/types.mjs +1 -1
- package/package.json +21 -20
- package/src/core/migrations/column-ddl-rendering.ts +117 -0
- package/src/core/migrations/contract-to-postgres-database-schema-node.ts +25 -1
- package/src/core/migrations/control-policy.ts +68 -48
- package/src/core/migrations/diff-database-schema.ts +242 -153
- package/src/core/migrations/issue-planner.ts +621 -667
- package/src/core/migrations/operations/constraints.ts +1 -1
- package/src/core/migrations/planner-ddl-builders.ts +5 -2
- package/src/core/migrations/planner-identity-values.ts +1 -1
- package/src/core/migrations/planner-sql-checks.ts +1 -1
- package/src/core/migrations/planner-strategies.ts +248 -208
- package/src/core/migrations/planner.ts +180 -74
- package/src/core/migrations/runner.ts +1 -4
- package/src/core/migrations/verify-postgres-namespaces.ts +26 -15
- package/src/core/postgres-schema-verifier.ts +10 -7
- package/src/core/psl-infer/infer-psl-contract.ts +5 -10
- package/src/core/schema-ir/postgres-database-schema-node.ts +3 -6
- package/src/core/schema-ir/postgres-namespace-schema-node.ts +8 -9
- package/src/core/schema-ir/postgres-policy-schema-node.ts +3 -6
- package/src/core/schema-ir/postgres-role-schema-node.ts +3 -6
- package/src/core/schema-ir/postgres-table-schema-node.ts +20 -9
- package/src/core/schema-ir/schema-node-kinds.ts +80 -2
- package/src/exports/control.ts +10 -43
- package/src/exports/diff-database-schema.ts +7 -0
- package/src/exports/issue-planner.ts +1 -1
- package/src/exports/op-factory-call.ts +1 -0
- package/src/exports/planner-identity-values.ts +4 -1
- package/src/exports/planner.ts +0 -1
- package/dist/default-normalizer-Dug8Fez9.mjs +0 -248
- package/dist/default-normalizer-Dug8Fez9.mjs.map +0 -1
- package/dist/issue-planner-Cbh-xTEr.mjs +0 -849
- package/dist/issue-planner-Cbh-xTEr.mjs.map +0 -1
- package/dist/native-type-normalizer-Bc9XJzWC.mjs +0 -38
- package/dist/native-type-normalizer-Bc9XJzWC.mjs.map +0 -1
- package/dist/op-factory-call-BuoGT5UI.mjs.map +0 -1
- package/dist/planner-Bq0Z2sVO.mjs +0 -641
- package/dist/planner-Bq0Z2sVO.mjs.map +0 -1
- package/dist/postgres-database-schema-node-os8ogEap.d.mts.map +0 -1
- package/dist/postgres-table-schema-node-Clei_xel.mjs.map +0 -1
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Migration strategies.
|
|
3
3
|
*
|
|
4
|
-
* Each strategy examines the issue list, consumes issues it
|
|
5
|
-
* returns the `PostgresOpFactoryCall[]` to address them. The
|
|
6
|
-
* runs each strategy in order and routes whatever's left
|
|
7
|
-
* `
|
|
4
|
+
* Each strategy examines the node-typed diff-issue list, consumes issues it
|
|
5
|
+
* handles, and returns the `PostgresOpFactoryCall[]` to address them. The
|
|
6
|
+
* issue planner runs each strategy in order and routes whatever's left
|
|
7
|
+
* through `mapNodeIssueToCall`.
|
|
8
8
|
*
|
|
9
9
|
* The full ordered list is exported as `postgresPlannerStrategies` and is
|
|
10
10
|
* used unchanged by both `migration plan` and `db update` / `db init`. The
|
|
@@ -14,8 +14,16 @@
|
|
|
14
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
|
-
* downstream
|
|
18
|
-
*
|
|
17
|
+
* downstream strategies (codec-hook type ops and temp-default backfill)
|
|
18
|
+
* and `mapNodeIssueToCall` defaults emit direct DDL instead.
|
|
19
|
+
*
|
|
20
|
+
* Structural decisions (type/nullability drift, DDL fragments) read the diff
|
|
21
|
+
* node pair and resolve column DDL from the node's `codecRef` (`column-ddl-
|
|
22
|
+
* rendering.ts`). The retained subsystems — codec type-operations
|
|
23
|
+
* (`storageTypePlanCallStrategy`) and the NOT-NULL temp-default deferred DDL
|
|
24
|
+
* (`notNullAddColumnCallStrategy`) — keep reading the contract + codec hooks
|
|
25
|
+
* via `StrategyContext`, per the slice's scope (see `diff-database-schema.ts`
|
|
26
|
+
* / the slice spec).
|
|
19
27
|
*/
|
|
20
28
|
|
|
21
29
|
import type { Contract } from '@prisma-next/contract/types';
|
|
@@ -26,20 +34,28 @@ import type {
|
|
|
26
34
|
} from '@prisma-next/family-sql/control';
|
|
27
35
|
import { resolveValueSetValues } from '@prisma-next/family-sql/control';
|
|
28
36
|
import type { TargetBoundComponentDescriptor } from '@prisma-next/framework-components/components';
|
|
29
|
-
import type {
|
|
37
|
+
import type { SchemaDiffIssue } from '@prisma-next/framework-components/control';
|
|
30
38
|
import { UNBOUND_NAMESPACE_ID } from '@prisma-next/framework-components/ir';
|
|
31
39
|
import {
|
|
32
40
|
type SqlStorage,
|
|
33
41
|
StorageTable,
|
|
34
42
|
type StorageTypeInstance,
|
|
35
43
|
} from '@prisma-next/sql-contract/types';
|
|
36
|
-
import
|
|
37
|
-
import {
|
|
38
|
-
|
|
44
|
+
import * as contractFree from '@prisma-next/sql-relational-core/contract-free';
|
|
45
|
+
import {
|
|
46
|
+
RelationalSchemaNodeKind,
|
|
47
|
+
type SqlColumnIR,
|
|
48
|
+
type SqlSchemaIR,
|
|
49
|
+
} from '@prisma-next/sql-schema-ir/types';
|
|
39
50
|
import { blindCast } from '@prisma-next/utils/casts';
|
|
40
|
-
import { ifDefined } from '@prisma-next/utils/defined';
|
|
41
|
-
import type { JsonValue } from '@prisma-next/utils/json';
|
|
42
51
|
import { isPostgresSchema } from '../postgres-schema';
|
|
52
|
+
import {
|
|
53
|
+
renderColumnAlterType,
|
|
54
|
+
renderColumnDdl,
|
|
55
|
+
resolveColumnTemporaryDefault,
|
|
56
|
+
} from './column-ddl-rendering';
|
|
57
|
+
import { resolveNamespaceIdForDdlSchema } from './control-policy';
|
|
58
|
+
import { emissionSchemaName, issueNode, issueSchemaName, issueTableName } from './issue-planner';
|
|
43
59
|
import {
|
|
44
60
|
AddCheckConstraintCall,
|
|
45
61
|
AddColumnCall,
|
|
@@ -48,17 +64,13 @@ import {
|
|
|
48
64
|
AlterColumnTypeCall,
|
|
49
65
|
DataTransformCall,
|
|
50
66
|
DropCheckConstraintCall,
|
|
67
|
+
DropNotNullCall,
|
|
51
68
|
type PostgresOpFactoryCall,
|
|
52
|
-
postgresDefaultToDdlColumnDefault,
|
|
53
69
|
RawSqlCall,
|
|
54
70
|
SetNotNullCall,
|
|
55
71
|
} from './op-factory-call';
|
|
56
|
-
import { buildColumnTypeSql } from './planner-ddl-builders';
|
|
57
|
-
import { resolveIdentityValue } from './planner-identity-values';
|
|
58
72
|
import { buildSchemaLookupMap, hasForeignKey, hasUniqueConstraint } from './planner-schema-lookup';
|
|
59
|
-
import { buildExpectedFormatType } from './planner-sql-checks';
|
|
60
73
|
import { buildTargetDetails, type PostgresPlanTargetDetails } from './planner-target-details';
|
|
61
|
-
import { resolveColumnTypeMetadata } from './planner-type-resolution';
|
|
62
74
|
|
|
63
75
|
/**
|
|
64
76
|
* Look up a storage table by its explicit namespace coordinate. Returns
|
|
@@ -80,19 +92,6 @@ export function tableAt(
|
|
|
80
92
|
return ns.entries.table?.[tableName];
|
|
81
93
|
}
|
|
82
94
|
|
|
83
|
-
/**
|
|
84
|
-
* Default namespace coordinate for an issue that does not carry one
|
|
85
|
-
* explicitly. Hand-crafted unit-test issues and `extra_table` issues
|
|
86
|
-
* fall back to `__unbound__`, the only namespace any single-namespace
|
|
87
|
-
* contract carries — verifier-emitted issues for legacy
|
|
88
|
-
* single-namespace contracts already stamp this id explicitly. Typed
|
|
89
|
-
* structurally so issue variants without a `namespaceId` slot
|
|
90
|
-
* (e.g. `EnumValuesChangedIssue`) flow through to the same fallback.
|
|
91
|
-
*/
|
|
92
|
-
export function resolveNamespaceIdForIssue(issue: { readonly namespaceId?: string }): string {
|
|
93
|
-
return issue.namespaceId ?? UNBOUND_NAMESPACE_ID;
|
|
94
|
-
}
|
|
95
|
-
|
|
96
95
|
/**
|
|
97
96
|
* Resolve the DDL schema name for a namespace coordinate. Postgres-aware
|
|
98
97
|
* namespaces dispatch to their polymorphic `ddlSchemaName` override —
|
|
@@ -111,6 +110,17 @@ export function resolveDdlSchemaForNamespace(ctx: StrategyContext, namespaceId:
|
|
|
111
110
|
return namespaceId;
|
|
112
111
|
}
|
|
113
112
|
|
|
113
|
+
/**
|
|
114
|
+
* Recovers the contract namespace id for a DDL schema name embedded in a
|
|
115
|
+
* diff-issue path (`path[1]`). The strategies need the CONTRACT column/table
|
|
116
|
+
* (for codec-hook reads and eligibility probes the retained subsystems still
|
|
117
|
+
* run) even though the issue itself only carries the resolved DDL schema —
|
|
118
|
+
* this is the reverse of `resolveDdlSchemaForNamespace`.
|
|
119
|
+
*/
|
|
120
|
+
function namespaceIdForDdlSchema(ctx: StrategyContext, ddlSchemaName: string): string {
|
|
121
|
+
return resolveNamespaceIdForDdlSchema(ctx.toContract, ddlSchemaName);
|
|
122
|
+
}
|
|
123
|
+
|
|
114
124
|
// ============================================================================
|
|
115
125
|
// Strategy types
|
|
116
126
|
// ============================================================================
|
|
@@ -139,13 +149,20 @@ export interface StrategyContext {
|
|
|
139
149
|
// Call strategies (for issue planner)
|
|
140
150
|
// ============================================================================
|
|
141
151
|
|
|
152
|
+
/**
|
|
153
|
+
* Consumes node-typed diff issues (`SchemaDiffIssue`, from the one differ —
|
|
154
|
+
* `buildPostgresPlanDiff`), reading the issue's node pair for structural
|
|
155
|
+
* decisions and resolving column DDL from the node's `codecRef`. The
|
|
156
|
+
* retained subsystems (codec type-operations, the NOT-NULL temp-default
|
|
157
|
+
* backfill) keep reading the contract via `StrategyContext`.
|
|
158
|
+
*/
|
|
142
159
|
export type CallMigrationStrategy = (
|
|
143
|
-
issues: readonly
|
|
160
|
+
issues: readonly SchemaDiffIssue[],
|
|
144
161
|
context: StrategyContext,
|
|
145
162
|
) =>
|
|
146
163
|
| {
|
|
147
164
|
kind: 'match';
|
|
148
|
-
issues: readonly
|
|
165
|
+
issues: readonly SchemaDiffIssue[];
|
|
149
166
|
calls: readonly PostgresOpFactoryCall[];
|
|
150
167
|
/**
|
|
151
168
|
* `true` for strategies that emit cohesive sequential recipes whose
|
|
@@ -158,91 +175,56 @@ export type CallMigrationStrategy = (
|
|
|
158
175
|
}
|
|
159
176
|
| { kind: 'no_match' };
|
|
160
177
|
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
)
|
|
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`);
|
|
171
|
-
const mutableHooks = ctx.codecHooks as Map<string, CodecControlHooks>;
|
|
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
|
-
});
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
function buildAlterTypeOptions(
|
|
198
|
-
namespaceId: string,
|
|
199
|
-
table: string,
|
|
200
|
-
column: string,
|
|
201
|
-
ctx: StrategyContext,
|
|
202
|
-
using?: string,
|
|
203
|
-
) {
|
|
204
|
-
const col = tableAt(ctx.toContract.storage, namespaceId, table)?.columns[column];
|
|
205
|
-
if (!col) throw new Error(`Column "${table}"."${column}" not found in destination contract`);
|
|
206
|
-
const mutableHooks = ctx.codecHooks as Map<string, CodecControlHooks>;
|
|
207
|
-
const mutableTypes = ctx.storageTypes as Record<string, StorageTypeInstance>;
|
|
208
|
-
const qualifiedTargetType = buildColumnTypeSql(col, mutableHooks, mutableTypes, false);
|
|
209
|
-
const formatTypeExpected = buildExpectedFormatType(col, mutableHooks, mutableTypes);
|
|
178
|
+
/** A `not-equal` column issue whose node pair is narrowed to `SqlColumnIR`. */
|
|
179
|
+
function columnNodePair(
|
|
180
|
+
issue: SchemaDiffIssue,
|
|
181
|
+
): { readonly expected: SqlColumnIR; readonly actual: SqlColumnIR } | undefined {
|
|
182
|
+
const node = issueNode(issue);
|
|
183
|
+
if (node === undefined || node.nodeKind !== RelationalSchemaNodeKind.column) return undefined;
|
|
184
|
+
if (issue.expected === undefined || issue.actual === undefined) return undefined;
|
|
210
185
|
return {
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
rawTargetTypeForLabel: qualifiedTargetType,
|
|
214
|
-
...(using !== undefined ? { using } : {}),
|
|
186
|
+
expected: blindCast<SqlColumnIR, 'a column diff node is always a SqlColumnIR'>(issue.expected),
|
|
187
|
+
actual: blindCast<SqlColumnIR, 'a column diff node is always a SqlColumnIR'>(issue.actual),
|
|
215
188
|
};
|
|
216
189
|
}
|
|
217
190
|
|
|
218
191
|
export const notNullBackfillCallStrategy: CallMigrationStrategy = (issues, ctx) => {
|
|
219
192
|
// `DataTransformCall` is operation class `'data'`. When the policy excludes
|
|
220
193
|
// it (`db update` / `db init`), skip so `notNullAddColumnCallStrategy`
|
|
221
|
-
// (temp-default backfill) or `
|
|
194
|
+
// (temp-default backfill) or `mapNodeIssueToCall` can take the issue.
|
|
222
195
|
if (!ctx.policy.allowedOperationClasses.includes('data')) return { kind: 'no_match' };
|
|
223
196
|
|
|
224
|
-
const matched:
|
|
197
|
+
const matched: SchemaDiffIssue[] = [];
|
|
225
198
|
const calls: PostgresOpFactoryCall[] = [];
|
|
226
199
|
|
|
227
200
|
for (const issue of issues) {
|
|
228
|
-
if (issue.
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
const
|
|
232
|
-
|
|
233
|
-
|
|
201
|
+
if (issue.reason !== 'not-found') continue;
|
|
202
|
+
const node = issueNode(issue);
|
|
203
|
+
if (node === undefined || node.nodeKind !== RelationalSchemaNodeKind.column) continue;
|
|
204
|
+
const expected = blindCast<
|
|
205
|
+
SqlColumnIR,
|
|
206
|
+
'a not-found column issue always carries the expected column node'
|
|
207
|
+
>(issue.expected);
|
|
208
|
+
if (expected.nullable !== false || expected.resolvedDefault !== undefined) continue;
|
|
209
|
+
|
|
210
|
+
const ddlSchemaName = issueSchemaName(issue);
|
|
211
|
+
const tableName = issueTableName(issue);
|
|
212
|
+
if (ddlSchemaName === undefined || tableName === undefined) continue;
|
|
213
|
+
const schemaName = emissionSchemaName(ctx, ddlSchemaName);
|
|
234
214
|
|
|
235
215
|
matched.push(issue);
|
|
236
|
-
const
|
|
237
|
-
const
|
|
216
|
+
const ddl = renderColumnDdl(expected.name, expected, ctx.codecHooks);
|
|
217
|
+
const nullableSpec = contractFree.col(ddl.name, ddl.type, {
|
|
218
|
+
...(ddl.codecRef !== undefined ? { codecRef: ddl.codecRef } : {}),
|
|
219
|
+
});
|
|
238
220
|
calls.push(
|
|
239
|
-
new AddColumnCall(
|
|
221
|
+
new AddColumnCall(schemaName, tableName, nullableSpec),
|
|
240
222
|
new DataTransformCall(
|
|
241
|
-
`backfill-${
|
|
242
|
-
`backfill-${
|
|
243
|
-
`backfill-${
|
|
223
|
+
`backfill-${tableName}-${expected.name}`,
|
|
224
|
+
`backfill-${tableName}-${expected.name}:check`,
|
|
225
|
+
`backfill-${tableName}-${expected.name}:run`,
|
|
244
226
|
),
|
|
245
|
-
new SetNotNullCall(
|
|
227
|
+
new SetNotNullCall(schemaName, tableName, expected.name),
|
|
246
228
|
);
|
|
247
229
|
}
|
|
248
230
|
|
|
@@ -257,49 +239,87 @@ export const notNullBackfillCallStrategy: CallMigrationStrategy = (issues, ctx)
|
|
|
257
239
|
|
|
258
240
|
const SAFE_WIDENINGS = new Set(['int2→int4', 'int2→int8', 'int4→int8', 'float4→float8']);
|
|
259
241
|
|
|
242
|
+
/**
|
|
243
|
+
* Handles `not-equal` column issues whose TYPE differs. `fromContract` is
|
|
244
|
+
* only supplied by `migration plan` — for reconciliation (`db update` /
|
|
245
|
+
* `db init`, `fromContract === null`) this strategy never fires, mirroring
|
|
246
|
+
* the legacy `typeChangeCallStrategy`'s requirement of a prior contract:
|
|
247
|
+
* `mapNodeIssueToCall`'s in-place ALTER covers reconciliation directly.
|
|
248
|
+
*
|
|
249
|
+
* A single node issue can carry BOTH type and nullability drift (Postgres
|
|
250
|
+
* alters in place, so the differ emits one `not-equal` column issue where
|
|
251
|
+
* the legacy coordinate walk emitted two: `type_mismatch` +
|
|
252
|
+
* `nullability_mismatch`). When this strategy consumes the issue for its
|
|
253
|
+
* type portion, it also emits whatever nullability delta the same node pair
|
|
254
|
+
* carries — using the same construction `nullableTighteningCallStrategy` /
|
|
255
|
+
* the mapper's direct dispatch would have used — so the issue is never
|
|
256
|
+
* partially handled.
|
|
257
|
+
*/
|
|
260
258
|
export const typeChangeCallStrategy: CallMigrationStrategy = (issues, ctx) => {
|
|
261
|
-
|
|
262
|
-
// (operation class `'data'`); when the policy excludes `'data'`
|
|
263
|
-
// (`db update` / `db init`), skip those issues so `mapIssueToCall` can
|
|
264
|
-
// emit a direct `ALTER COLUMN TYPE`. Safe widenings still flow through
|
|
265
|
-
// here because the resulting `AlterColumnTypeCall` is `widening`-class.
|
|
259
|
+
if (ctx.fromContract === null) return { kind: 'no_match' };
|
|
266
260
|
const dataAllowed = ctx.policy.allowedOperationClasses.includes('data');
|
|
267
261
|
|
|
268
|
-
const matched:
|
|
262
|
+
const matched: SchemaDiffIssue[] = [];
|
|
269
263
|
const calls: PostgresOpFactoryCall[] = [];
|
|
270
264
|
|
|
271
265
|
for (const issue of issues) {
|
|
272
|
-
if (issue.
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
const
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
const
|
|
279
|
-
|
|
280
|
-
];
|
|
281
|
-
if (!fromColumn || !toColumn) continue;
|
|
282
|
-
const fromType = fromColumn.nativeType;
|
|
283
|
-
const toType = toColumn.nativeType;
|
|
284
|
-
if (fromType === toType) continue;
|
|
266
|
+
if (issue.reason !== 'not-equal') continue;
|
|
267
|
+
const pair = columnNodePair(issue);
|
|
268
|
+
if (pair === undefined) continue;
|
|
269
|
+
const { expected, actual } = pair;
|
|
270
|
+
if (!columnTypeChangedNativeOnly(expected, actual)) continue;
|
|
271
|
+
|
|
272
|
+
const fromType = actual.nativeType;
|
|
273
|
+
const toType = expected.nativeType;
|
|
285
274
|
const isSafeWidening = SAFE_WIDENINGS.has(`${fromType}→${toType}`);
|
|
286
275
|
if (!isSafeWidening && !dataAllowed) continue;
|
|
276
|
+
|
|
277
|
+
const ddlSchemaName = issueSchemaName(issue);
|
|
278
|
+
const tableName = issueTableName(issue);
|
|
279
|
+
if (ddlSchemaName === undefined || tableName === undefined) continue;
|
|
280
|
+
const schemaName = emissionSchemaName(ctx, ddlSchemaName);
|
|
281
|
+
|
|
287
282
|
matched.push(issue);
|
|
288
|
-
const
|
|
289
|
-
|
|
283
|
+
const { qualifiedTargetType, formatTypeExpected } = renderColumnAlterType(
|
|
284
|
+
expected,
|
|
285
|
+
ctx.codecHooks,
|
|
286
|
+
);
|
|
287
|
+
const alterOpts = {
|
|
288
|
+
qualifiedTargetType,
|
|
289
|
+
formatTypeExpected,
|
|
290
|
+
rawTargetTypeForLabel: qualifiedTargetType,
|
|
291
|
+
};
|
|
290
292
|
if (isSafeWidening) {
|
|
291
|
-
calls.push(new AlterColumnTypeCall(
|
|
293
|
+
calls.push(new AlterColumnTypeCall(schemaName, tableName, expected.name, alterOpts));
|
|
292
294
|
} else {
|
|
293
295
|
calls.push(
|
|
294
296
|
new DataTransformCall(
|
|
295
|
-
`typechange-${
|
|
296
|
-
`typechange-${
|
|
297
|
-
`typechange-${
|
|
297
|
+
`typechange-${tableName}-${expected.name}`,
|
|
298
|
+
`typechange-${tableName}-${expected.name}:check`,
|
|
299
|
+
`typechange-${tableName}-${expected.name}:run`,
|
|
298
300
|
),
|
|
299
|
-
new AlterColumnTypeCall(
|
|
301
|
+
new AlterColumnTypeCall(schemaName, tableName, expected.name, alterOpts),
|
|
300
302
|
);
|
|
301
303
|
}
|
|
304
|
+
|
|
305
|
+
if (expected.nullable !== actual.nullable) {
|
|
306
|
+
if (expected.nullable) {
|
|
307
|
+
calls.push(new DropNotNullCall(schemaName, tableName, expected.name));
|
|
308
|
+
} else if (dataAllowed) {
|
|
309
|
+
calls.push(
|
|
310
|
+
new DataTransformCall(
|
|
311
|
+
`handle-nulls-${tableName}-${expected.name}`,
|
|
312
|
+
`handle-nulls-${tableName}-${expected.name}:check`,
|
|
313
|
+
`handle-nulls-${tableName}-${expected.name}:run`,
|
|
314
|
+
),
|
|
315
|
+
new SetNotNullCall(schemaName, tableName, expected.name),
|
|
316
|
+
);
|
|
317
|
+
} else {
|
|
318
|
+
calls.push(new SetNotNullCall(schemaName, tableName, expected.name));
|
|
319
|
+
}
|
|
320
|
+
}
|
|
302
321
|
}
|
|
322
|
+
|
|
303
323
|
if (matched.length === 0) return { kind: 'no_match' };
|
|
304
324
|
return {
|
|
305
325
|
kind: 'match',
|
|
@@ -309,32 +329,54 @@ export const typeChangeCallStrategy: CallMigrationStrategy = (issues, ctx) => {
|
|
|
309
329
|
};
|
|
310
330
|
};
|
|
311
331
|
|
|
332
|
+
/**
|
|
333
|
+
* Whether the raw (unresolved) native type differs — the SAME comparison
|
|
334
|
+
* `typeChangeCallStrategy` always used (`fromColumn.nativeType !==
|
|
335
|
+
* toColumn.nativeType`), which for the widenable numeric/float types
|
|
336
|
+
* `SAFE_WIDENINGS` lists is identical to the resolved comparison
|
|
337
|
+
* `columnTypeChanged` (in `issue-planner.ts`) performs.
|
|
338
|
+
*/
|
|
339
|
+
function columnTypeChangedNativeOnly(expected: SqlColumnIR, actual: SqlColumnIR): boolean {
|
|
340
|
+
return expected.nativeType !== actual.nativeType;
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
/**
|
|
344
|
+
* Handles `not-equal` column issues whose type did NOT change but
|
|
345
|
+
* nullability tightened (contract requires NOT NULL, live column is
|
|
346
|
+
* nullable). A type-changed issue's nullability delta (if any) is already
|
|
347
|
+
* handled by `typeChangeCallStrategy`, which runs first — this strategy
|
|
348
|
+
* only ever sees issues that strategy left behind.
|
|
349
|
+
*/
|
|
312
350
|
export const nullableTighteningCallStrategy: CallMigrationStrategy = (issues, ctx) => {
|
|
313
351
|
// `DataTransformCall` is operation class `'data'`. When the policy excludes
|
|
314
|
-
// it (`db update` / `db init`), skip so `
|
|
352
|
+
// it (`db update` / `db init`), skip so `mapNodeIssueToCall` emits a direct
|
|
315
353
|
// `SET NOT NULL` instead.
|
|
316
354
|
if (!ctx.policy.allowedOperationClasses.includes('data')) return { kind: 'no_match' };
|
|
317
355
|
|
|
318
|
-
const matched:
|
|
356
|
+
const matched: SchemaDiffIssue[] = [];
|
|
319
357
|
const calls: PostgresOpFactoryCall[] = [];
|
|
320
358
|
|
|
321
359
|
for (const issue of issues) {
|
|
322
|
-
if (issue.
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
const
|
|
326
|
-
if (
|
|
327
|
-
if (
|
|
360
|
+
if (issue.reason !== 'not-equal') continue;
|
|
361
|
+
const pair = columnNodePair(issue);
|
|
362
|
+
if (pair === undefined) continue;
|
|
363
|
+
const { expected, actual } = pair;
|
|
364
|
+
if (columnTypeChangedNativeOnly(expected, actual)) continue; // typeChangeCallStrategy's concern
|
|
365
|
+
if (expected.nullable !== false || actual.nullable !== true) continue;
|
|
366
|
+
|
|
367
|
+
const ddlSchemaName = issueSchemaName(issue);
|
|
368
|
+
const tableName = issueTableName(issue);
|
|
369
|
+
if (ddlSchemaName === undefined || tableName === undefined) continue;
|
|
370
|
+
const schemaName = emissionSchemaName(ctx, ddlSchemaName);
|
|
328
371
|
|
|
329
372
|
matched.push(issue);
|
|
330
|
-
const schemaForTable = resolveDdlSchemaForNamespace(ctx, namespaceId);
|
|
331
373
|
calls.push(
|
|
332
374
|
new DataTransformCall(
|
|
333
|
-
`handle-nulls-${
|
|
334
|
-
`handle-nulls-${
|
|
335
|
-
`handle-nulls-${
|
|
375
|
+
`handle-nulls-${tableName}-${expected.name}`,
|
|
376
|
+
`handle-nulls-${tableName}-${expected.name}:check`,
|
|
377
|
+
`handle-nulls-${tableName}-${expected.name}:run`,
|
|
336
378
|
),
|
|
337
|
-
new SetNotNullCall(
|
|
379
|
+
new SetNotNullCall(schemaName, tableName, expected.name),
|
|
338
380
|
);
|
|
339
381
|
}
|
|
340
382
|
|
|
@@ -384,9 +426,13 @@ function checkValueSetsEqual(a: readonly string[], b: readonly string[]): boolea
|
|
|
384
426
|
/**
|
|
385
427
|
* Plans check-constraint migrations for `enumType`-authored columns.
|
|
386
428
|
*
|
|
387
|
-
* Walks every namespace's tables in the target contract
|
|
388
|
-
*
|
|
389
|
-
*
|
|
429
|
+
* Walks every namespace's tables in the target contract (the check nodes'
|
|
430
|
+
* resolved `permittedValues` are ultimately sourced from the same
|
|
431
|
+
* contract-declared value sets, so walking the contract directly is the
|
|
432
|
+
* simplest faithful port — the strategy's decisions never depend on which
|
|
433
|
+
* ISSUES happen to be in the input list, only on the contract + live schema
|
|
434
|
+
* shapes). For each table that carries `checks`, diffs the contract-expected
|
|
435
|
+
* checks against the live schema's checks:
|
|
390
436
|
*
|
|
391
437
|
* - Check in contract, absent from live DB → `AddCheckConstraintCall`.
|
|
392
438
|
* - Check in live DB, absent from contract → `DropCheckConstraintCall`.
|
|
@@ -394,7 +440,9 @@ function checkValueSetsEqual(a: readonly string[], b: readonly string[]): boolea
|
|
|
394
440
|
* then `AddCheckConstraintCall` (drop + recreate; a check predicate cannot
|
|
395
441
|
* be altered in place).
|
|
396
442
|
*
|
|
397
|
-
* Consumes
|
|
443
|
+
* Consumes every `sql-check-constraint` issue on a table this walk handles
|
|
444
|
+
* (not-found/not-expected/not-equal), leaving check issues on tables with
|
|
445
|
+
* NO contract checks to `mapNodeIssueToCall`'s `not-expected` fallback.
|
|
398
446
|
*/
|
|
399
447
|
export const checkConstraintPlanCallStrategy: CallMigrationStrategy = (issues, ctx) => {
|
|
400
448
|
const calls: PostgresOpFactoryCall[] = [];
|
|
@@ -411,7 +459,7 @@ export const checkConstraintPlanCallStrategy: CallMigrationStrategy = (issues, c
|
|
|
411
459
|
|
|
412
460
|
for (const contractCheck of contractChecks) {
|
|
413
461
|
const liveCheck = liveChecks.find((c) => c.name === contractCheck.name);
|
|
414
|
-
const issueKey = `${tableName}
|
|
462
|
+
const issueKey = `${tableName} ${contractCheck.name}`;
|
|
415
463
|
if (!liveCheck) {
|
|
416
464
|
calls.push(
|
|
417
465
|
new AddCheckConstraintCall(
|
|
@@ -446,7 +494,7 @@ export const checkConstraintPlanCallStrategy: CallMigrationStrategy = (issues, c
|
|
|
446
494
|
for (const liveCheck of liveChecks) {
|
|
447
495
|
const inContract = contractChecks.some((c) => c.name === liveCheck.name);
|
|
448
496
|
if (!inContract) {
|
|
449
|
-
const issueKey = `${tableName}
|
|
497
|
+
const issueKey = `${tableName} ${liveCheck.name}`;
|
|
450
498
|
calls.push(new DropCheckConstraintCall(ddlSchema, tableName, liveCheck.name));
|
|
451
499
|
handledIssueKeys.add(issueKey);
|
|
452
500
|
}
|
|
@@ -457,16 +505,14 @@ export const checkConstraintPlanCallStrategy: CallMigrationStrategy = (issues, c
|
|
|
457
505
|
if (calls.length === 0 && handledIssueKeys.size === 0) return { kind: 'no_match' };
|
|
458
506
|
|
|
459
507
|
const remaining = issues.filter((issue) => {
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
const key = `${issue.table}${issue.indexOrConstraint}`;
|
|
469
|
-
return !handledIssueKeys.has(key);
|
|
508
|
+
const node = issueNode(issue);
|
|
509
|
+
if (node === undefined || node.nodeKind !== RelationalSchemaNodeKind.check) return true;
|
|
510
|
+
const tableName = issueTableName(issue);
|
|
511
|
+
if (tableName === undefined) return true;
|
|
512
|
+
const checkName = blindCast<{ readonly name: string }, 'a check node always carries a name'>(
|
|
513
|
+
node,
|
|
514
|
+
).name;
|
|
515
|
+
return !handledIssueKeys.has(`${tableName} ${checkName}`);
|
|
470
516
|
});
|
|
471
517
|
|
|
472
518
|
return { kind: 'match', issues: remaining, calls };
|
|
@@ -475,14 +521,16 @@ export const checkConstraintPlanCallStrategy: CallMigrationStrategy = (issues, c
|
|
|
475
521
|
/**
|
|
476
522
|
* Dispatches codec-typed storage types through their codec's
|
|
477
523
|
* `planTypeOperations` hook (the authoritative source for codec-driven DDL
|
|
478
|
-
* such as custom type creation).
|
|
524
|
+
* such as custom type creation). Codec extension/type ops are not modeled as
|
|
525
|
+
* diff nodes — this strategy drives entirely off `ctx.toContract.storage.types`
|
|
526
|
+
* + codec hooks, consuming nothing from the node issue list (there is no
|
|
527
|
+
* node-vocabulary equivalent of `type_missing` / `enum_values_changed`).
|
|
479
528
|
*/
|
|
480
529
|
export const storageTypePlanCallStrategy: CallMigrationStrategy = (issues, ctx) => {
|
|
481
530
|
const storageTypes = ctx.toContract.storage.types ?? {};
|
|
482
531
|
if (Object.keys(storageTypes).length === 0) return { kind: 'no_match' };
|
|
483
532
|
|
|
484
533
|
const calls: PostgresOpFactoryCall[] = [];
|
|
485
|
-
const handledTypeNames = new Set<string>();
|
|
486
534
|
|
|
487
535
|
for (const [typeName, typeInstance] of Object.entries(storageTypes).sort(([a], [b]) =>
|
|
488
536
|
a.localeCompare(b),
|
|
@@ -499,11 +547,6 @@ export const storageTypePlanCallStrategy: CallMigrationStrategy = (issues, ctx)
|
|
|
499
547
|
policy: ctx.policy,
|
|
500
548
|
});
|
|
501
549
|
if (!planResult) continue;
|
|
502
|
-
if (planResult.operations.length === 0) {
|
|
503
|
-
handledTypeNames.add(typeName);
|
|
504
|
-
continue;
|
|
505
|
-
}
|
|
506
|
-
handledTypeNames.add(typeName);
|
|
507
550
|
for (const op of planResult.operations) {
|
|
508
551
|
calls.push(
|
|
509
552
|
new RawSqlCall({
|
|
@@ -517,38 +560,28 @@ export const storageTypePlanCallStrategy: CallMigrationStrategy = (issues, ctx)
|
|
|
517
560
|
}
|
|
518
561
|
}
|
|
519
562
|
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
!(
|
|
523
|
-
(issue.kind === 'type_missing' || issue.kind === 'enum_values_changed') &&
|
|
524
|
-
issue.typeName &&
|
|
525
|
-
handledTypeNames.has(issue.typeName)
|
|
526
|
-
),
|
|
527
|
-
);
|
|
528
|
-
|
|
529
|
-
if (calls.length === 0 && remaining.length === issues.length) {
|
|
530
|
-
return { kind: 'no_match' };
|
|
531
|
-
}
|
|
532
|
-
|
|
533
|
-
return { kind: 'match', issues: remaining, calls };
|
|
563
|
+
if (calls.length === 0) return { kind: 'no_match' };
|
|
564
|
+
return { kind: 'match', issues, calls };
|
|
534
565
|
};
|
|
535
566
|
|
|
536
567
|
/**
|
|
537
|
-
* Handles `
|
|
538
|
-
* default. Replaces the
|
|
568
|
+
* Handles `not-found` column issues for NOT NULL columns without a contract
|
|
569
|
+
* default. Replaces the legacy `buildAddColumnItem` non-default branches.
|
|
539
570
|
*
|
|
540
571
|
* Two shapes:
|
|
541
572
|
* - Shared-temp-default safe: emit a single atomic composite op (add
|
|
542
573
|
* nullable → backfill identity value → `SET NOT NULL` → `DROP DEFAULT`).
|
|
574
|
+
* The temp-default value is resolved from the column node's `codecRef`
|
|
575
|
+
* (`resolveColumnTemporaryDefault`, wrapping `resolveIdentityValue`).
|
|
543
576
|
* - Empty-table guarded: emit a hand-built op with a `tableIsEmptyCheck`
|
|
544
577
|
* precheck so the failure message is "table is not empty" rather than the
|
|
545
578
|
* raw PG NOT NULL violation.
|
|
546
579
|
*
|
|
547
|
-
* "Normal"
|
|
548
|
-
* for `
|
|
580
|
+
* "Normal" not-found column cases (nullable or has a contract default) are
|
|
581
|
+
* left for `mapNodeIssueToCall`'s default `AddColumnCall` emission.
|
|
549
582
|
*/
|
|
550
583
|
export const notNullAddColumnCallStrategy: CallMigrationStrategy = (issues, ctx) => {
|
|
551
|
-
const matched:
|
|
584
|
+
const matched: SchemaDiffIssue[] = [];
|
|
552
585
|
const calls: PostgresOpFactoryCall[] = [];
|
|
553
586
|
|
|
554
587
|
const schemaLookups = buildSchemaLookupMap(ctx.schema);
|
|
@@ -557,40 +590,47 @@ export const notNullAddColumnCallStrategy: CallMigrationStrategy = (issues, ctx)
|
|
|
557
590
|
const mutableStorageTypes = ctx.storageTypes as Record<string, StorageTypeInstance>;
|
|
558
591
|
|
|
559
592
|
for (const issue of issues) {
|
|
560
|
-
if (issue.
|
|
561
|
-
const
|
|
562
|
-
|
|
563
|
-
const
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
const
|
|
593
|
+
if (issue.reason !== 'not-found') continue;
|
|
594
|
+
const node = issueNode(issue);
|
|
595
|
+
if (node === undefined || node.nodeKind !== RelationalSchemaNodeKind.column) continue;
|
|
596
|
+
const expected = blindCast<
|
|
597
|
+
SqlColumnIR,
|
|
598
|
+
'a not-found column issue always carries the expected column node'
|
|
599
|
+
>(issue.expected);
|
|
600
|
+
if (expected.nullable !== false || expected.resolvedDefault !== undefined) continue;
|
|
601
|
+
|
|
602
|
+
const ddlSchemaName = issueSchemaName(issue);
|
|
603
|
+
const tableName = issueTableName(issue);
|
|
604
|
+
if (ddlSchemaName === undefined || tableName === undefined) continue;
|
|
605
|
+
|
|
606
|
+
const namespaceId = namespaceIdForDdlSchema(ctx, ddlSchemaName);
|
|
607
|
+
const schemaName = namespaceId === UNBOUND_NAMESPACE_ID ? UNBOUND_NAMESPACE_ID : ddlSchemaName;
|
|
608
|
+
const contractTable = tableAt(ctx.toContract.storage, namespaceId, tableName);
|
|
609
|
+
const column = contractTable?.columns[expected.name];
|
|
610
|
+
if (!contractTable || !column) continue;
|
|
611
|
+
|
|
612
|
+
const schemaTable = ctx.schema.tables[tableName];
|
|
571
613
|
if (!schemaTable) continue;
|
|
572
614
|
|
|
573
|
-
const temporaryDefault =
|
|
574
|
-
const schemaLookup = schemaLookups.get(
|
|
615
|
+
const temporaryDefault = resolveColumnTemporaryDefault(expected, ctx.codecHooks);
|
|
616
|
+
const schemaLookup = schemaLookups.get(tableName);
|
|
575
617
|
const canUseSharedTempDefault =
|
|
576
618
|
temporaryDefault !== null &&
|
|
577
619
|
canUseSharedTemporaryDefaultStrategy({
|
|
578
620
|
table: contractTable,
|
|
579
621
|
schemaTable,
|
|
580
622
|
schemaLookup,
|
|
581
|
-
columnName:
|
|
623
|
+
columnName: expected.name,
|
|
582
624
|
});
|
|
583
625
|
|
|
584
626
|
matched.push(issue);
|
|
585
627
|
|
|
586
|
-
const schemaForTable = resolveDdlSchemaForNamespace(ctx, namespaceId);
|
|
587
|
-
|
|
588
628
|
if (canUseSharedTempDefault && temporaryDefault !== null) {
|
|
589
629
|
calls.push(
|
|
590
630
|
new AddNotNullColumnWithTempDefaultCall({
|
|
591
|
-
schemaName
|
|
592
|
-
tableName
|
|
593
|
-
columnName:
|
|
631
|
+
schemaName,
|
|
632
|
+
tableName,
|
|
633
|
+
columnName: expected.name,
|
|
594
634
|
column,
|
|
595
635
|
codecHooks: mutableCodecHooks,
|
|
596
636
|
storageTypes: mutableStorageTypes,
|
|
@@ -602,10 +642,10 @@ export const notNullAddColumnCallStrategy: CallMigrationStrategy = (issues, ctx)
|
|
|
602
642
|
|
|
603
643
|
calls.push(
|
|
604
644
|
new AddNotNullColumnDirectCall(
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
645
|
+
schemaName,
|
|
646
|
+
tableName,
|
|
647
|
+
expected.name,
|
|
648
|
+
renderColumnDdl(expected.name, expected, ctx.codecHooks),
|
|
609
649
|
),
|
|
610
650
|
);
|
|
611
651
|
}
|
|
@@ -657,7 +697,7 @@ function canUseSharedTemporaryDefaultStrategy(options: {
|
|
|
657
697
|
* Ordered list of Postgres planner strategies, shared by `migration plan`
|
|
658
698
|
* and `db update` / `db init`. The issue planner runs each strategy in
|
|
659
699
|
* order, letting it consume any issues it handles, and routes whatever's
|
|
660
|
-
* left through `
|
|
700
|
+
* left through `mapNodeIssueToCall`. Behavior diverges purely on
|
|
661
701
|
* `policy.allowedOperationClasses`:
|
|
662
702
|
*
|
|
663
703
|
* - When `'data'` is allowed (`migration plan`), the data-safe strategies
|
|
@@ -667,9 +707,9 @@ function canUseSharedTemporaryDefaultStrategy(options: {
|
|
|
667
707
|
*
|
|
668
708
|
* - When `'data'` is not allowed (`db update` / `db init`), the
|
|
669
709
|
* placeholder-emitting strategies short-circuit to `no_match`, leaving
|
|
670
|
-
* the issue for the downstream
|
|
671
|
-
*
|
|
672
|
-
*
|
|
710
|
+
* the issue for the downstream strategies (`storageTypePlanCallStrategy`,
|
|
711
|
+
* `notNullAddColumnCallStrategy`) or the `mapNodeIssueToCall` default to
|
|
712
|
+
* handle with direct DDL.
|
|
673
713
|
*
|
|
674
714
|
* Codec-typed storage type entries are dispatched through
|
|
675
715
|
* `storageTypePlanCallStrategy`.
|