@prisma-next/target-postgres 0.16.0-dev.25 → 0.16.0-dev.27
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/{authoring-BCzcFRay.mjs → authoring-DnO1B0N9.mjs} +7 -14
- package/dist/authoring-DnO1B0N9.mjs.map +1 -0
- package/dist/canonicalize-C7zKHw8m.mjs +59 -0
- package/dist/canonicalize-C7zKHw8m.mjs.map +1 -0
- package/dist/{canonicalize-D1F4vUnP.d.mts → canonicalize-CQlxSR4D.d.mts} +3 -18
- package/dist/canonicalize-CQlxSR4D.d.mts.map +1 -0
- package/dist/contract-free.d.mts +1 -1
- package/dist/contract-free.d.mts.map +1 -1
- package/dist/contract-free.mjs +1 -1
- package/dist/{control-policy-B-_34g05.mjs → control-policy-DEwdS3oK.mjs} +23 -24
- package/dist/control-policy-DEwdS3oK.mjs.map +1 -0
- package/dist/control.mjs +10 -7
- package/dist/control.mjs.map +1 -1
- package/dist/{ddl-DYEob9xQ.mjs → ddl-BQC6u-L9.mjs} +27 -3
- package/dist/{ddl-DYEob9xQ.mjs.map → ddl-BQC6u-L9.mjs.map} +1 -1
- package/dist/ddl.d.mts +3 -3
- package/dist/ddl.mjs +2 -2
- package/dist/{descriptor-meta-D_0ZKcj-.mjs → descriptor-meta-DGlJurgA.mjs} +2 -2
- package/dist/{descriptor-meta-D_0ZKcj-.mjs.map → descriptor-meta-DGlJurgA.mjs.map} +1 -1
- package/dist/{diff-database-schema-C9H09OD3.mjs → diff-database-schema-qRPwuYBH.mjs} +22 -12
- package/dist/diff-database-schema-qRPwuYBH.mjs.map +1 -0
- package/dist/diff-database-schema.mjs +1 -1
- package/dist/errors.d.mts +4 -2
- package/dist/errors.d.mts.map +1 -1
- package/dist/errors.mjs +2 -2
- package/dist/issue-planner.d.mts +2 -2
- package/dist/issue-planner.d.mts.map +1 -1
- package/dist/issue-planner.mjs +1 -1
- package/dist/migration.d.mts +2 -2
- package/dist/migration.mjs +2 -2
- package/dist/{nodes-DJC5B8Id.mjs → nodes-04EILM-z.mjs} +65 -2
- package/dist/nodes-04EILM-z.mjs.map +1 -0
- package/dist/{nodes-CQzM2hR0.d.mts → nodes-DjCsN-uv.d.mts} +69 -4
- package/dist/nodes-DjCsN-uv.d.mts.map +1 -0
- package/dist/{op-factory-call-DT7kdjVP.d.mts → op-factory-call-0TB5XNij.d.mts} +46 -8
- package/dist/op-factory-call-0TB5XNij.d.mts.map +1 -0
- package/dist/{op-factory-call-BVYLIojV.mjs → op-factory-call-CIaPDDUc.mjs} +104 -27
- package/dist/op-factory-call-CIaPDDUc.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.map +1 -1
- package/dist/pack.mjs +1 -1
- package/dist/{planner-4sj5cJCY.mjs → planner-BhnkIJX-.mjs} +117 -9
- package/dist/planner-BhnkIJX-.mjs.map +1 -0
- package/dist/{planner-produced-postgres-migration-CcQPDWmX.mjs → planner-produced-postgres-migration-BE3EJIRK.mjs} +2 -2
- package/dist/{planner-produced-postgres-migration-CcQPDWmX.mjs.map → planner-produced-postgres-migration-BE3EJIRK.mjs.map} +1 -1
- package/dist/{planner-produced-postgres-migration-8QP8eMph.d.mts → planner-produced-postgres-migration-CRAHl6ak.d.mts} +2 -2
- package/dist/planner-produced-postgres-migration-CRAHl6ak.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-schema-lookup-CiVaAQP-.mjs → planner-schema-lookup-D3Y_OL-C.mjs} +3 -3
- package/dist/planner-schema-lookup-D3Y_OL-C.mjs.map +1 -0
- package/dist/planner-schema-lookup.mjs +1 -1
- package/dist/planner.d.mts +21 -1
- package/dist/planner.d.mts.map +1 -1
- package/dist/planner.mjs +2 -2
- package/dist/{postgres-contract-view-B8yXVWYe.mjs → postgres-contract-view-BeIEyZfD.mjs} +2 -2
- package/dist/{postgres-contract-view-B8yXVWYe.mjs.map → postgres-contract-view-BeIEyZfD.mjs.map} +1 -1
- package/dist/{postgres-migration-GIOABsAa.mjs → postgres-migration--BzvpA92.mjs} +7 -4
- package/dist/postgres-migration--BzvpA92.mjs.map +1 -0
- package/dist/{postgres-migration-DLIvzHjX.d.mts → postgres-migration-BXelnqHO.d.mts} +15 -10
- package/dist/postgres-migration-BXelnqHO.d.mts.map +1 -0
- package/dist/rls-canonicalize.d.mts +2 -26
- package/dist/rls-canonicalize.mjs +2 -2
- package/dist/runtime.mjs +1 -1
- package/package.json +20 -20
- package/src/contract-free/ddl.ts +47 -0
- package/src/core/authoring.ts +10 -22
- package/src/core/ddl/nodes.ts +104 -0
- package/src/core/migrations/contract-to-postgres-database-schema-node.ts +24 -13
- package/src/core/migrations/issue-planner.ts +40 -13
- package/src/core/migrations/op-factory-call.ts +100 -9
- package/src/core/migrations/operations/indexes.ts +80 -35
- package/src/core/migrations/planner-schema-lookup.ts +2 -2
- package/src/core/migrations/planner.ts +155 -5
- package/src/core/migrations/postgres-migration.ts +26 -8
- package/src/core/psl-infer/infer-psl-contract.ts +8 -1
- package/src/core/rls/canonicalize.ts +4 -21
- package/src/exports/ddl.ts +4 -0
- package/src/exports/errors.ts +1 -1
- package/src/exports/op-factory-call.ts +1 -0
- package/src/exports/rls-canonicalize.ts +0 -6
- package/dist/authoring-BCzcFRay.mjs.map +0 -1
- package/dist/canonicalize-D1F4vUnP.d.mts.map +0 -1
- package/dist/control-policy-B-_34g05.mjs.map +0 -1
- package/dist/diff-database-schema-C9H09OD3.mjs.map +0 -1
- package/dist/nodes-CQzM2hR0.d.mts.map +0 -1
- package/dist/nodes-DJC5B8Id.mjs.map +0 -1
- package/dist/op-factory-call-BVYLIojV.mjs.map +0 -1
- package/dist/op-factory-call-DT7kdjVP.d.mts.map +0 -1
- package/dist/planner-4sj5cJCY.mjs.map +0 -1
- package/dist/planner-produced-postgres-migration-8QP8eMph.d.mts.map +0 -1
- package/dist/planner-schema-lookup-CiVaAQP-.mjs.map +0 -1
- package/dist/postgres-migration-DLIvzHjX.d.mts.map +0 -1
- package/dist/postgres-migration-GIOABsAa.mjs.map +0 -1
- package/dist/rls-canonicalize.d.mts.map +0 -1
- package/dist/wire-name-DbQ9-2RC.mjs +0 -104
- package/dist/wire-name-DbQ9-2RC.mjs.map +0 -1
- package/src/core/rls/wire-name.ts +0 -33
|
@@ -1,16 +1,17 @@
|
|
|
1
1
|
import { t as DEFAULT_NAMESPACE_ID } from "./namespace-ids-xtp_ZY86.mjs";
|
|
2
2
|
import { g as PostgresRlsPolicy, r as isPostgresSchema } from "./postgres-schema-CTplQZDK.mjs";
|
|
3
|
-
import { n as parseRlsPolicyWireName } from "./wire-name-DbQ9-2RC.mjs";
|
|
4
3
|
import { i as PostgresDatabaseSchemaNode, n as PostgresPolicySchemaNode, r as PostgresNamespaceSchemaNode } from "./postgres-role-schema-node-bg32e7I-.mjs";
|
|
5
4
|
import { t as resolveDdlSchemaForNamespaceStorage } from "./resolve-ddl-schema-BAsJEKW8.mjs";
|
|
6
|
-
import { t as buildPostgresPlanDiff } from "./diff-database-schema-
|
|
7
|
-
import { a as resolvePostgresNodeIssueCreationFactoryName, c as issueSchemaName, d as postgresNodeStorageCoordinate, i as resolvePostgresNodeIssueControlPolicySubject, l as planIssues, n as resolveNamespaceIdForDdlSchema, o as coalesceSubtreeIssues, r as resolvePostgresCallControlPolicySubject, s as issueNode, t as renderPostgresSuppression, u as postgresPlannerStrategies } from "./control-policy-
|
|
8
|
-
import { T as DropPostgresRlsPolicyCall, k as
|
|
9
|
-
import { t as TypeScriptRenderablePostgresMigration } from "./planner-produced-postgres-migration-
|
|
5
|
+
import { t as buildPostgresPlanDiff } from "./diff-database-schema-qRPwuYBH.mjs";
|
|
6
|
+
import { a as resolvePostgresNodeIssueCreationFactoryName, c as issueSchemaName, d as postgresNodeStorageCoordinate, i as resolvePostgresNodeIssueControlPolicySubject, l as planIssues, n as resolveNamespaceIdForDdlSchema, o as coalesceSubtreeIssues, r as resolvePostgresCallControlPolicySubject, s as issueNode, t as renderPostgresSuppression, u as postgresPlannerStrategies } from "./control-policy-DEwdS3oK.mjs";
|
|
7
|
+
import { A as RenamePostgresRlsPolicyCall, T as DropPostgresRlsPolicyCall, k as RenameIndexCall, p as CreatePostgresRlsPolicyCall } from "./op-factory-call-CIaPDDUc.mjs";
|
|
8
|
+
import { t as TypeScriptRenderablePostgresMigration } from "./planner-produced-postgres-migration-BE3EJIRK.mjs";
|
|
10
9
|
import { UNBOUND_NAMESPACE_ID } from "@prisma-next/framework-components/ir";
|
|
11
10
|
import { blindCast } from "@prisma-next/utils/casts";
|
|
12
11
|
import { extractCodecControlHooks, partitionCallsByControlPolicy, partitionIssuesByControlPolicy, planFieldEventOperations, plannerFailure } from "@prisma-next/family-sql/control";
|
|
13
12
|
import { ifDefined } from "@prisma-next/utils/defined";
|
|
13
|
+
import { parseWireName } from "@prisma-next/sql-schema-ir/naming";
|
|
14
|
+
import { SqlIndexIR } from "@prisma-next/sql-schema-ir/types";
|
|
14
15
|
import { issueOutcome } from "@prisma-next/framework-components/control";
|
|
15
16
|
//#region src/core/migrations/verify-postgres-namespaces.ts
|
|
16
17
|
/**
|
|
@@ -150,11 +151,13 @@ var PostgresMigrationPlanner = class {
|
|
|
150
151
|
contract: options.contract,
|
|
151
152
|
schema: options.schema
|
|
152
153
|
}), ...gated];
|
|
154
|
+
const indexRenames = this.pairIndexRenames(options, schemaIssues);
|
|
155
|
+
const plannableIssues = indexRenames.consumed.size === 0 ? schemaIssues : schemaIssues.filter((issue) => !indexRenames.consumed.has(issue));
|
|
153
156
|
const codecHooks = extractCodecControlHooks(options.frameworkComponents);
|
|
154
157
|
const storageTypes = options.contract.storage.types ?? {};
|
|
155
158
|
const relationalSchema = relationalNamespaceNode(options.schema, schemaName);
|
|
156
159
|
const issuePartition = partitionIssuesByControlPolicy({
|
|
157
|
-
issues:
|
|
160
|
+
issues: plannableIssues,
|
|
158
161
|
contract: options.contract,
|
|
159
162
|
resolveControlPolicySubject: (issue) => resolvePostgresNodeIssueControlPolicySubject(issue, options.contract),
|
|
160
163
|
resolveCreationFactoryName: resolvePostgresNodeIssueCreationFactoryName
|
|
@@ -172,6 +175,12 @@ var PostgresMigrationPlanner = class {
|
|
|
172
175
|
strategies: postgresPlannerStrategies
|
|
173
176
|
});
|
|
174
177
|
if (!result.ok) return plannerFailure(result.failure);
|
|
178
|
+
const indexRenamePartition = partitionCallsByControlPolicy({
|
|
179
|
+
calls: indexRenames.calls,
|
|
180
|
+
contract: options.contract,
|
|
181
|
+
resolveControlPolicySubject: (call) => resolvePostgresCallControlPolicySubject(call, options.contract),
|
|
182
|
+
resolveFactoryName: (call) => call.factoryName
|
|
183
|
+
});
|
|
175
184
|
const schemaDiffPartition = partitionCallsByControlPolicy({
|
|
176
185
|
calls: this.planPostgresSchemaDiff(options, policyDiffIssues),
|
|
177
186
|
contract: options.contract,
|
|
@@ -190,11 +199,13 @@ var PostgresMigrationPlanner = class {
|
|
|
190
199
|
});
|
|
191
200
|
const calls = [
|
|
192
201
|
...result.value.calls,
|
|
202
|
+
...indexRenamePartition.kept,
|
|
193
203
|
...schemaDiffPartition.kept,
|
|
194
204
|
...fieldEventPartition.kept
|
|
195
205
|
];
|
|
196
206
|
const warnings = [
|
|
197
207
|
...issuePartition.suppressions,
|
|
208
|
+
...indexRenamePartition.suppressions,
|
|
198
209
|
...schemaDiffPartition.suppressions,
|
|
199
210
|
...fieldEventPartition.suppressions
|
|
200
211
|
].map((record) => renderPostgresSuppression(record, options.contract));
|
|
@@ -208,6 +219,103 @@ var PostgresMigrationPlanner = class {
|
|
|
208
219
|
});
|
|
209
220
|
}
|
|
210
221
|
/**
|
|
222
|
+
* Rename post-pass for indexes, per `(schema, table)`, widening-only,
|
|
223
|
+
* deterministic by sorted names — the same structure as the policy pass
|
|
224
|
+
* below (which stays untouched: policies pair by hash only).
|
|
225
|
+
*
|
|
226
|
+
* Phase 1 — hash pairing: extras whose live names parse as wire names,
|
|
227
|
+
* grouped by `(schema, table, hash)`; missing nodes iterated in sorted-name
|
|
228
|
+
* order consume the sorted-name-first candidate — a prefix-only rename.
|
|
229
|
+
*
|
|
230
|
+
* Phase 2 — content pairing: the remaining managed-missing nodes
|
|
231
|
+
* (`prefix` defined) against the remaining extras of any name shape,
|
|
232
|
+
* paired iff content-equal (columns ordered-strict both-defined-or-
|
|
233
|
+
* both-undefined, `unique`/`type` strict, `options` loose, bodies
|
|
234
|
+
* byte-equal) — exact→managed adoption convergence.
|
|
235
|
+
*
|
|
236
|
+
* Leftovers proceed as create/drop exactly as before; without the
|
|
237
|
+
* widening allowance the pass is skipped and pairing degrades to the
|
|
238
|
+
* additive half, like the policy pass.
|
|
239
|
+
*/
|
|
240
|
+
pairIndexRenames(options, issues) {
|
|
241
|
+
const consumed = /* @__PURE__ */ new Set();
|
|
242
|
+
const calls = [];
|
|
243
|
+
if (!options.policy.allowedOperationClasses.includes("widening")) return {
|
|
244
|
+
calls,
|
|
245
|
+
consumed
|
|
246
|
+
};
|
|
247
|
+
const missing = [];
|
|
248
|
+
const extra = [];
|
|
249
|
+
for (const issue of issues) {
|
|
250
|
+
const node = issueNode(issue);
|
|
251
|
+
if (node === void 0 || !SqlIndexIR.is(node)) continue;
|
|
252
|
+
const ddlSchema = issue.path[1];
|
|
253
|
+
const tableName = issue.path[2];
|
|
254
|
+
if (ddlSchema === void 0 || tableName === void 0) continue;
|
|
255
|
+
if (issueOutcome(issue) === "not-found") missing.push({
|
|
256
|
+
issue,
|
|
257
|
+
node,
|
|
258
|
+
ddlSchema,
|
|
259
|
+
tableName
|
|
260
|
+
});
|
|
261
|
+
else if (issueOutcome(issue) === "not-expected") extra.push({
|
|
262
|
+
issue,
|
|
263
|
+
node,
|
|
264
|
+
ddlSchema,
|
|
265
|
+
tableName
|
|
266
|
+
});
|
|
267
|
+
}
|
|
268
|
+
if (missing.length === 0 || extra.length === 0) return {
|
|
269
|
+
calls,
|
|
270
|
+
consumed
|
|
271
|
+
};
|
|
272
|
+
const byName = (a, b) => a.node.name < b.node.name ? -1 : a.node.name > b.node.name ? 1 : 0;
|
|
273
|
+
const emissionSchema = (ddlSchema) => resolveNamespaceIdForDdlSchema(options.contract, ddlSchema) === UNBOUND_NAMESPACE_ID ? UNBOUND_NAMESPACE_ID : ddlSchema;
|
|
274
|
+
const pairingKey = (finding, hash) => JSON.stringify([
|
|
275
|
+
finding.ddlSchema,
|
|
276
|
+
finding.tableName,
|
|
277
|
+
hash
|
|
278
|
+
]);
|
|
279
|
+
const rename = (missingFinding, candidate) => {
|
|
280
|
+
consumed.add(missingFinding.issue);
|
|
281
|
+
consumed.add(candidate.issue);
|
|
282
|
+
calls.push(new RenameIndexCall(emissionSchema(missingFinding.ddlSchema), missingFinding.tableName, candidate.node.name, missingFinding.node.name));
|
|
283
|
+
};
|
|
284
|
+
const sortedMissing = [...missing].sort(byName);
|
|
285
|
+
const extrasByHash = /* @__PURE__ */ new Map();
|
|
286
|
+
for (const finding of extra) {
|
|
287
|
+
const parsed = parseWireName(finding.node.name);
|
|
288
|
+
if (parsed === void 0) continue;
|
|
289
|
+
const key = pairingKey(finding, parsed.hash);
|
|
290
|
+
const group = extrasByHash.get(key) ?? [];
|
|
291
|
+
group.push(finding);
|
|
292
|
+
extrasByHash.set(key, group);
|
|
293
|
+
}
|
|
294
|
+
for (const group of extrasByHash.values()) group.sort(byName);
|
|
295
|
+
for (const missingFinding of sortedMissing) {
|
|
296
|
+
const parsed = parseWireName(missingFinding.node.name);
|
|
297
|
+
if (parsed === void 0) continue;
|
|
298
|
+
const candidate = extrasByHash.get(pairingKey(missingFinding, parsed.hash))?.shift();
|
|
299
|
+
if (candidate === void 0) continue;
|
|
300
|
+
rename(missingFinding, candidate);
|
|
301
|
+
}
|
|
302
|
+
const sortedExtras = [...extra].sort(byName);
|
|
303
|
+
for (const missingFinding of sortedMissing) {
|
|
304
|
+
if (consumed.has(missingFinding.issue)) continue;
|
|
305
|
+
if (missingFinding.node.prefix === void 0) continue;
|
|
306
|
+
const candidate = sortedExtras.find((extraFinding) => !consumed.has(extraFinding.issue) && extraFinding.ddlSchema === missingFinding.ddlSchema && extraFinding.tableName === missingFinding.tableName && missingFinding.node.contentEquals(extraFinding.node, {
|
|
307
|
+
columnPresence: "matching",
|
|
308
|
+
bodies: "verbatim"
|
|
309
|
+
}));
|
|
310
|
+
if (candidate === void 0) continue;
|
|
311
|
+
rename(missingFinding, candidate);
|
|
312
|
+
}
|
|
313
|
+
return {
|
|
314
|
+
calls,
|
|
315
|
+
consumed
|
|
316
|
+
};
|
|
317
|
+
}
|
|
318
|
+
/**
|
|
211
319
|
* Maps the RLS policy presence findings of the one combined tree diff
|
|
212
320
|
* (`buildPostgresPlanDiff`, already ownership-filtered) into
|
|
213
321
|
* `CREATE POLICY` / `DROP POLICY` / `ALTER POLICY … RENAME TO` ops. It
|
|
@@ -261,7 +369,7 @@ var PostgresMigrationPlanner = class {
|
|
|
261
369
|
if (allowsWidening) {
|
|
262
370
|
const extrasByGroup = /* @__PURE__ */ new Map();
|
|
263
371
|
for (const finding of extra) {
|
|
264
|
-
const parsed =
|
|
372
|
+
const parsed = parseWireName(finding.node.name);
|
|
265
373
|
if (parsed === void 0) continue;
|
|
266
374
|
const key = pairingKey(finding, parsed.hash);
|
|
267
375
|
const group = extrasByGroup.get(key) ?? [];
|
|
@@ -271,7 +379,7 @@ var PostgresMigrationPlanner = class {
|
|
|
271
379
|
for (const group of extrasByGroup.values()) group.sort((a, b) => a.node.name < b.node.name ? -1 : a.node.name > b.node.name ? 1 : 0);
|
|
272
380
|
const sortedMissing = [...missing].sort((a, b) => a.node.name < b.node.name ? -1 : a.node.name > b.node.name ? 1 : 0);
|
|
273
381
|
for (const missingFinding of sortedMissing) {
|
|
274
|
-
const parsed =
|
|
382
|
+
const parsed = parseWireName(missingFinding.node.name);
|
|
275
383
|
if (parsed === void 0) continue;
|
|
276
384
|
const candidate = extrasByGroup.get(pairingKey(missingFinding, parsed.hash))?.shift();
|
|
277
385
|
if (candidate === void 0) continue;
|
|
@@ -387,4 +495,4 @@ function policyNodeToContractPolicy(node) {
|
|
|
387
495
|
//#endregion
|
|
388
496
|
export { createPostgresMigrationPlanner as t };
|
|
389
497
|
|
|
390
|
-
//# sourceMappingURL=planner-
|
|
498
|
+
//# sourceMappingURL=planner-BhnkIJX-.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"planner-BhnkIJX-.mjs","names":["#lowerer"],"sources":["../src/core/migrations/verify-postgres-namespaces.ts","../src/core/migrations/planner.ts"],"sourcesContent":["import type { Contract } from '@prisma-next/contract/types';\nimport type { SchemaDiffIssue } from '@prisma-next/framework-components/control';\nimport { UNBOUND_NAMESPACE_ID } from '@prisma-next/framework-components/ir';\nimport type { SqlStorage } from '@prisma-next/sql-contract/types';\nimport type { SqlSchemaIRNode } from '@prisma-next/sql-schema-ir/types';\nimport { DEFAULT_NAMESPACE_ID } from '../namespace-ids';\nimport { isPostgresSchema } from '../postgres-schema';\nimport { PostgresDatabaseSchemaNode } from '../schema-ir/postgres-database-schema-node';\nimport { PostgresNamespaceSchemaNode } from '../schema-ir/postgres-namespace-schema-node';\nimport type { SqlSchemaDiffNode } from '../schema-ir/schema-node-kinds';\n\n/**\n * Resolves the live-database schema name for a given namespace\n * coordinate. Mirrors `resolveDdlSchemaForNamespace` in\n * `planner-strategies.ts` so the verifier's projection and the\n * planner's projection always agree — Postgres-aware namespaces (the\n * production path) dispatch to `ddlSchemaName(storage)`, and bare\n * object payloads (used by some tests) fall back to the coordinate\n * itself.\n */\nfunction resolveDdlSchemaName(storage: SqlStorage, namespaceId: string): string {\n const namespace = storage.namespaces[namespaceId];\n if (isPostgresSchema(namespace)) {\n return namespace.ddlSchemaName(storage);\n }\n return namespaceId;\n}\n\n/**\n * Reads the introspected list of schema names from the database-root schema\n * node. `existingSchemas` is database-level, so it lives on the\n * `PostgresDatabaseSchemaNode` root — not on the per-schema namespace nodes.\n *\n * Defaults to the always-present `public` schema when the node is not the\n * database root — a fresh Postgres database always carries `public` (unless an\n * operator dropped it manually), so any verifier path that runs without an\n * enriched introspection still suppresses the redundant `CREATE SCHEMA\n * \"public\"`.\n *\n * Production introspection (`PostgresControlAdapter.introspect`) is the\n * authoritative source: it queries `pg_namespace` and sets `existingSchemas`\n * on the returned root. Tests that want to assert against a richer initial\n * state construct a `PostgresDatabaseSchemaNode` explicitly.\n */\nfunction existingSchemasFromSchema(schema: SqlSchemaIRNode): readonly string[] {\n if (PostgresDatabaseSchemaNode.is(schema)) {\n return schema.existingSchemas;\n }\n return [DEFAULT_NAMESPACE_ID];\n}\n\n/**\n * Emits a `postgres-namespace` `not-found` diff issue for every\n * contract-declared Postgres namespace whose live container does not yet\n * exist. The planner prepends these (node-typed, synthesized) to the\n * relational diff issues so a multi-schema plan emits `CREATE SCHEMA`\n * before the tables that need it — a planner-only concern (verify already\n * rejects via the `not-found` table issues a missing schema already\n * produces), so this is not part of the shared diff.\n *\n * A namespace's live container is the schema returned by its\n * polymorphic `ddlSchemaName(storage)` method — named schemas resolve\n * to their own id; the unbound singleton returns `UNBOUND_NAMESPACE_ID`\n * and is skipped explicitly (late-bound namespaces have no fixed DDL\n * schema). Issues are emitted only when the resolved name is a real,\n * creatable schema (not the unbound sentinel) and is missing from the\n * introspected list. `public` is suppressed implicitly because the\n * introspection (or its sensible default) always carries it.\n *\n * Each emitted issue's path is `['database', ddlName]` — an ancestor of\n * every table path under that schema, so it must never be run through\n * `coalesceSubtreeIssues` alongside the table diff (it would swallow the\n * table-level `not-found` issues that drive `CREATE TABLE`); the planner\n * adds these AFTER coalescing the relational issues, and they are not\n * subject to sibling-space ownership scoping (mirrors the retired\n * coordinate walk, which prepended namespace issues after that filter).\n */\nexport function verifyPostgresNamespacePresence(input: {\n readonly contract: Contract<SqlStorage>;\n readonly schema: SqlSchemaIRNode;\n}): readonly SchemaDiffIssue<SqlSchemaDiffNode>[] {\n const { contract, schema } = input;\n const existing = new Set(existingSchemasFromSchema(schema));\n const issues: SchemaDiffIssue<SqlSchemaDiffNode>[] = [];\n const namespaceIds = Object.keys(contract.storage.namespaces).sort();\n for (const namespaceId of namespaceIds) {\n if (namespaceId === UNBOUND_NAMESPACE_ID) continue;\n const ddlName = resolveDdlSchemaName(contract.storage, namespaceId);\n if (ddlName === UNBOUND_NAMESPACE_ID) continue;\n if (existing.has(ddlName)) continue;\n const namespace = new PostgresNamespaceSchemaNode({\n schemaName: ddlName,\n tables: {},\n });\n issues.push({\n path: ['database', ddlName],\n expected: namespace,\n });\n }\n return issues;\n}\n","import type { Contract } from '@prisma-next/contract/types';\nimport type {\n MigrationOperationPolicy,\n SqlMigrationPlannerPlanOptions,\n SqlPlannerConflict,\n SqlPlannerFailureResult,\n} from '@prisma-next/family-sql/control';\nimport {\n extractCodecControlHooks,\n partitionCallsByControlPolicy,\n partitionIssuesByControlPolicy,\n planFieldEventOperations,\n plannerFailure,\n} from '@prisma-next/family-sql/control';\nimport type { ExecuteRequestLowerer } from '@prisma-next/family-sql/control-adapter';\nimport type { TargetBoundComponentDescriptor } from '@prisma-next/framework-components/components';\nimport type {\n MigrationPlanner,\n MigrationPlanWithAuthoringSurface,\n MigrationScaffoldContext,\n SchemaDiffIssue,\n SchemaOwnership,\n} from '@prisma-next/framework-components/control';\nimport { issueOutcome } from '@prisma-next/framework-components/control';\nimport { UNBOUND_NAMESPACE_ID } from '@prisma-next/framework-components/ir';\nimport type { SqlStorage } from '@prisma-next/sql-contract/types';\nimport { parseWireName } from '@prisma-next/sql-schema-ir/naming';\nimport type { SqlSchemaIR } from '@prisma-next/sql-schema-ir/types';\nimport { SqlIndexIR } from '@prisma-next/sql-schema-ir/types';\nimport { blindCast } from '@prisma-next/utils/casts';\nimport { ifDefined } from '@prisma-next/utils/defined';\nimport { PostgresRlsPolicy } from '../postgres-rls-policy';\nimport { postgresNodeStorageCoordinate } from '../schema-ir/node-storage-coordinate';\nimport { PostgresDatabaseSchemaNode } from '../schema-ir/postgres-database-schema-node';\nimport { PostgresPolicySchemaNode } from '../schema-ir/postgres-policy-schema-node';\nimport type { SqlSchemaDiffNode } from '../schema-ir/schema-node-kinds';\nimport {\n renderPostgresSuppression,\n resolveNamespaceIdForDdlSchema,\n resolvePostgresCallControlPolicySubject,\n resolvePostgresNodeIssueControlPolicySubject,\n resolvePostgresNodeIssueCreationFactoryName,\n} from './control-policy';\nimport { buildPostgresPlanDiff } from './diff-database-schema';\nimport { coalesceSubtreeIssues, issueNode, issueSchemaName, planIssues } from './issue-planner';\nimport type { PostgresOpFactoryCall } from './op-factory-call';\nimport {\n CreatePostgresRlsPolicyCall,\n DropPostgresRlsPolicyCall,\n RenameIndexCall,\n RenamePostgresRlsPolicyCall,\n} from './op-factory-call';\nimport { TypeScriptRenderablePostgresMigration } from './planner-produced-postgres-migration';\nimport { postgresPlannerStrategies } from './planner-strategies';\nimport { resolveDdlSchemaForNamespaceStorage } from './resolve-ddl-schema';\nimport { verifyPostgresNamespacePresence } from './verify-postgres-namespaces';\n\ntype PlannerFrameworkComponents = SqlMigrationPlannerPlanOptions extends {\n readonly frameworkComponents: infer T;\n}\n ? T\n : ReadonlyArray<unknown>;\n\ntype PlannerOptionsWithComponents = SqlMigrationPlannerPlanOptions & {\n readonly frameworkComponents: PlannerFrameworkComponents;\n};\n\nexport function createPostgresMigrationPlanner(\n lowerer: ExecuteRequestLowerer,\n): PostgresMigrationPlanner {\n return new PostgresMigrationPlanner(lowerer);\n}\n\n/**\n * Result of `PostgresMigrationPlanner.plan()`. A discriminated union whose\n * success variant carries a `TypeScriptRenderablePostgresMigration` — a\n * migration object that both the CLI (via `renderTypeScript()`) and the\n * SQL-typed callers (via `operations`, `describe()`, etc.) consume\n * uniformly.\n */\nexport type PostgresPlanResult =\n | {\n readonly kind: 'success';\n readonly plan: TypeScriptRenderablePostgresMigration;\n readonly warnings?: readonly SqlPlannerConflict[];\n }\n | SqlPlannerFailureResult;\n\n/**\n * Postgres migration planner — a thin wrapper over `planIssues`.\n *\n * `plan()` diffs the target contract against the live schema via the one\n * differ (`buildPostgresPlanDiff`, producing node-typed `SchemaDiffIssue[]`)\n * and delegates to `planIssues` with the unified `postgresPlannerStrategies`\n * list: NOT-NULL backfill, type-change, nullable-tightening, codec-hook\n * storage types, component-declared dependency installs, and\n * shared-temp-default / empty-table-guarded NOT-NULL add-column. The same\n * strategy list runs for `migration plan`, `db update`, and `db init`;\n * behavior diverges purely on `policy.allowedOperationClasses` (the\n * data-safe strategies short-circuit when `'data'` is excluded). The issue\n * planner applies operation-class policy gates and emits a single\n * `PostgresOpFactoryCall[]` that drives both the runtime-ops view (via\n * `renderOps`) and the `renderTypeScript()` authoring surface. RLS policy\n * drift (the structural half of the same one-differ tree) is handled\n * separately via `planPostgresSchemaDiff`.\n */\nexport class PostgresMigrationPlanner implements MigrationPlanner<'sql', 'postgres'> {\n readonly #lowerer: ExecuteRequestLowerer | undefined;\n\n constructor(lowerer?: ExecuteRequestLowerer) {\n this.#lowerer = lowerer;\n }\n\n plan(options: {\n readonly contract: unknown;\n readonly schema: unknown;\n readonly policy: MigrationOperationPolicy;\n /**\n * The \"from\" contract (state the planner assumes the database starts\n * at), or `null` for reconciliation flows. Only `migration plan` ever\n * supplies a non-null value; `db update` / `db init` reconcile against\n * the live schema and pass `null`. When present alongside the\n * `'data'` operation class, strategies that need from/to column-shape\n * comparisons (unsafe type change, nullability tightening) activate.\n *\n * Typed as the framework `Contract | null` to satisfy the\n * `MigrationPlanner` interface contract; `planSql` narrows to the SQL\n * shape via `SqlMigrationPlannerPlanOptions`. Used to populate\n * `describe().from` on the produced plan as\n * `fromContract?.storage.storageHash ?? null`.\n */\n readonly fromContract: Contract | null;\n readonly schemaName?: string;\n readonly frameworkComponents: ReadonlyArray<TargetBoundComponentDescriptor<'sql', string>>;\n /**\n * Contract space this plan applies to. Stamped onto the produced\n * {@link TypeScriptRenderablePostgresMigration.spaceId} so the runner keys\n * the marker row by the right space.\n */\n readonly spaceId: string;\n /**\n * Ownership oracle over the contract-space composition — see\n * {@link SqlMigrationPlannerPlanOptions.ownership}.\n */\n readonly ownership?: SchemaOwnership;\n /**\n * POSIX-relative path from the migration package dir to\n * `migrations/snapshots` — see\n * {@link SqlMigrationPlannerPlanOptions.snapshotsImportPath}.\n */\n readonly snapshotsImportPath: string;\n }): PostgresPlanResult {\n return this.planSql(options as SqlMigrationPlannerPlanOptions);\n }\n\n emptyMigration(\n context: MigrationScaffoldContext,\n spaceId: string,\n ): MigrationPlanWithAuthoringSurface {\n return new TypeScriptRenderablePostgresMigration(\n [],\n {\n from: context.fromHash,\n to: context.toHash,\n },\n spaceId,\n context.snapshotsImportPath,\n this.#lowerer,\n );\n }\n\n private planSql(options: SqlMigrationPlannerPlanOptions): PostgresPlanResult {\n const schemaName =\n options.schemaName ??\n Object.keys(options.contract.storage.namespaces).find((id) => id !== UNBOUND_NAMESPACE_ID) ??\n UNBOUND_NAMESPACE_ID;\n const policyResult = this.ensureAdditivePolicy(options.policy);\n if (policyResult) {\n return policyResult;\n }\n\n // The one combined tree diff drives the whole plan: relational findings\n // become structural DDL via `planIssues`, policy findings become RLS ops\n // via `planPostgresSchemaDiff`. Verify runs its own full-tree node diff\n // (`diffSchema`) over the same schema and rejects on a\n // surviving failure.\n PostgresDatabaseSchemaNode.assert(options.schema);\n const { issues: rawIssues } = buildPostgresPlanDiff({\n contract: options.contract,\n actualSchema: options.schema,\n frameworkComponents: options.frameworkComponents,\n });\n const policyDiffIssues = rawIssues.filter((issue) => isPolicyDiffIssue(issue));\n // Role diff issues resolve to the `external` control policy (see\n // `resolvePostgresNodeIssueControlPolicySubject`'s role branch), so the\n // control-policy partition below suppresses them to zero ops on its own,\n // before `mapNodeIssueToCall` ever sees them — no separate exclusion\n // needed here.\n const relationalDiffIssues = rawIssues.filter((issue) => !isPolicyDiffIssue(issue));\n\n // The generic differ is total and un-gated: strict-mode extras filtering\n // (dropping `not-expected` findings outside strict mode, mirroring the\n // retired coordinate walk's `if (strict) { ...extra_* } }` guards),\n // subtree coalescing (a missing/extra table also emits an issue for\n // every child under it — redundant once the table-level Create/Drop call\n // already accounts for the whole subtree), and ownership are all post-diff\n // planner steps.\n //\n // Ownership: a live extra is only this plan's to drop when no contract\n // space owns it. The differ ran against THIS space's contract, so a table\n // a sibling space owns surfaces here as `not-expected`; the planner asks\n // the ownership oracle (the passive aggregate) whether any space declares\n // it and, if so, leaves it alone — it is a sibling's table, not an orphan.\n // A table no space owns stays a genuine extra to drop under a destructive\n // policy. Ownership lives in the aggregate; the planner only asks. Absent\n // oracle (single-space, none handed) keeps every extra. Coalescing MUST\n // run before this so a sibling-owned table's child issues have already\n // collapsed into the one table-level issue that carries the table name.\n const strict =\n options.policy.allowedOperationClasses.includes('widening') ||\n options.policy.allowedOperationClasses.includes('destructive');\n const coalesced = coalesceSubtreeIssues(relationalDiffIssues);\n const owned = retainUnownedExtras(coalesced, options.ownership, options.contract);\n const gated = strict ? owned : owned.filter((issue) => issueOutcome(issue) !== 'not-expected');\n\n // Namespace presence (`CREATE SCHEMA`) is a planner-only op-generation\n // concern stitched in here rather than inside the shared diff — verify\n // never needs it (a missing schema already surfaces as a `not-found`\n // table in the relational findings). These synthesized issues are added\n // AFTER coalescing/scoping (never coalesced against the table diff —\n // their path is an ancestor of every table path under that schema, so\n // running them through the same coalesce would swallow the table-level\n // `not-found` issues that drive `CREATE TABLE`) and are NOT subject to\n // sibling-space scoping, matching the retired coordinate walk exactly.\n const namespaceIssues = verifyPostgresNamespacePresence({\n contract: options.contract,\n schema: options.schema,\n });\n const schemaIssues = [...namespaceIssues, ...gated];\n\n // Index rename post-pass (the policy pass's structure, generalized): a\n // `not-found` and a `not-expected` index that are one rename collapse\n // into a single widening `ALTER INDEX … RENAME TO` before the per-issue\n // mapping turns them into create + drop. Consumed issues never reach\n // `planIssues`; the rename calls go through the same call-side\n // control-policy partition the policy ops use.\n const indexRenames = this.pairIndexRenames(options, schemaIssues);\n const plannableIssues =\n indexRenames.consumed.size === 0\n ? schemaIssues\n : schemaIssues.filter((issue) => !indexRenames.consumed.has(issue));\n\n const codecHooks = extractCodecControlHooks(options.frameworkComponents);\n const storageTypes = options.contract.storage.types ?? {};\n // The strategy layer reads the live schema by bare table name for existence\n // checks (shared-temp-default safety, FK/unique probes), so it takes one\n // per-schema namespace node — never the whole tree root, and never a flat\n // merge of every namespace (which would collide same-named tables across\n // schemas). Probing more than one namespace at once is future work.\n const relationalSchema = relationalNamespaceNode(options.schema, schemaName);\n\n // Input-side control-policy partition. `external` / `observed` subjects\n // — and non-creation issues for `tolerated` subjects — are dropped from\n // the planner's input entirely; the planner never observes them, never\n // diffs them, never generates DDL for them. Suppression warnings are\n // built directly from the suppressed partition (one per subject), so the\n // user-visible message survives even when the planner would have failed\n // to model the subject's live shape.\n const issuePartition = partitionIssuesByControlPolicy({\n issues: plannableIssues,\n contract: options.contract,\n resolveControlPolicySubject: (issue) =>\n resolvePostgresNodeIssueControlPolicySubject(issue, options.contract),\n resolveCreationFactoryName: resolvePostgresNodeIssueCreationFactoryName,\n });\n\n const result = planIssues({\n issues: issuePartition.plannable,\n toContract: options.contract,\n // `fromContract` is only supplied by `migration plan`. It is `null` for\n // `db update` / `db init`, which means data-safety strategies needing\n // from/to comparisons (unsafe type change, nullable tightening) are\n // inapplicable there — reconciliation falls through to\n // `mapNodeIssueToCall`'s direct destructive handlers.\n fromContract: options.fromContract,\n schemaName,\n codecHooks,\n storageTypes,\n ...ifDefined('schema', relationalSchema),\n policy: options.policy,\n frameworkComponents: options.frameworkComponents,\n strategies: postgresPlannerStrategies,\n });\n\n if (!result.ok) {\n return plannerFailure(result.failure);\n }\n\n const indexRenamePartition = partitionCallsByControlPolicy({\n calls: indexRenames.calls,\n contract: options.contract,\n resolveControlPolicySubject: (call) =>\n resolvePostgresCallControlPolicySubject(call, options.contract),\n resolveFactoryName: (call) => call.factoryName,\n });\n\n const schemaDiffCalls = this.planPostgresSchemaDiff(options, policyDiffIssues);\n const schemaDiffPartition = partitionCallsByControlPolicy({\n calls: schemaDiffCalls,\n contract: options.contract,\n resolveControlPolicySubject: (call) =>\n resolvePostgresCallControlPolicySubject(call, options.contract),\n resolveFactoryName: (call) => call.factoryName,\n });\n\n // Inline `onFieldEvent`-emitted ops after structural DDL. The fixed\n // ordering is `structural → added → dropped → altered`, with\n // within-group sorting by `(tableName, fieldName)` so re-emits are\n // byte-stable. The hook fires only at the application emitter —\n // extension-space planning never reaches this helper.\n const fieldEventOps = planFieldEventOperations({\n priorContract: options.fromContract,\n newContract: options.contract,\n codecHooks,\n });\n // Codec hook ops are target-agnostic `OpFactoryCall`; Postgres planning\n // lifts them at this integration boundary (see field-event-planner JSDoc).\n const fieldEventPostgresCalls = blindCast<\n readonly PostgresOpFactoryCall[],\n 'Codec hook ops conform to PostgresOpFactoryCall at the app emitter boundary'\n >(fieldEventOps);\n const fieldEventPartition = partitionCallsByControlPolicy({\n calls: fieldEventPostgresCalls,\n contract: options.contract,\n resolveControlPolicySubject: (call) =>\n resolvePostgresCallControlPolicySubject(call, options.contract),\n resolveFactoryName: (call) => call.factoryName,\n });\n const calls = [\n ...result.value.calls,\n ...indexRenamePartition.kept,\n ...schemaDiffPartition.kept,\n ...fieldEventPartition.kept,\n ];\n const warnings: SqlPlannerConflict[] = [\n ...issuePartition.suppressions,\n ...indexRenamePartition.suppressions,\n ...schemaDiffPartition.suppressions,\n ...fieldEventPartition.suppressions,\n ].map((record) => renderPostgresSuppression(record, options.contract));\n\n return Object.freeze({\n kind: 'success' as const,\n plan: new TypeScriptRenderablePostgresMigration(\n calls,\n {\n from: options.fromContract?.storage.storageHash ?? null,\n to: options.contract.storage.storageHash,\n },\n options.spaceId,\n options.snapshotsImportPath,\n this.#lowerer,\n ),\n ...(warnings.length > 0 ? { warnings: Object.freeze(warnings) } : {}),\n });\n }\n\n /**\n * Rename post-pass for indexes, per `(schema, table)`, widening-only,\n * deterministic by sorted names — the same structure as the policy pass\n * below (which stays untouched: policies pair by hash only).\n *\n * Phase 1 — hash pairing: extras whose live names parse as wire names,\n * grouped by `(schema, table, hash)`; missing nodes iterated in sorted-name\n * order consume the sorted-name-first candidate — a prefix-only rename.\n *\n * Phase 2 — content pairing: the remaining managed-missing nodes\n * (`prefix` defined) against the remaining extras of any name shape,\n * paired iff content-equal (columns ordered-strict both-defined-or-\n * both-undefined, `unique`/`type` strict, `options` loose, bodies\n * byte-equal) — exact→managed adoption convergence.\n *\n * Leftovers proceed as create/drop exactly as before; without the\n * widening allowance the pass is skipped and pairing degrades to the\n * additive half, like the policy pass.\n */\n private pairIndexRenames(\n options: PlannerOptionsWithComponents,\n issues: readonly SchemaDiffIssue<SqlSchemaDiffNode>[],\n ): {\n readonly calls: readonly RenameIndexCall[];\n readonly consumed: ReadonlySet<SchemaDiffIssue<SqlSchemaDiffNode>>;\n } {\n const consumed = new Set<SchemaDiffIssue<SqlSchemaDiffNode>>();\n const calls: RenameIndexCall[] = [];\n if (!options.policy.allowedOperationClasses.includes('widening')) {\n return { calls, consumed };\n }\n\n interface IndexFinding {\n readonly issue: SchemaDiffIssue<SqlSchemaDiffNode>;\n readonly node: SqlIndexIR;\n readonly ddlSchema: string;\n readonly tableName: string;\n }\n const missing: IndexFinding[] = [];\n const extra: IndexFinding[] = [];\n for (const issue of issues) {\n const node = issueNode(issue);\n if (node === undefined || !SqlIndexIR.is(node)) continue;\n const ddlSchema = issue.path[1];\n const tableName = issue.path[2];\n if (ddlSchema === undefined || tableName === undefined) continue;\n if (issueOutcome(issue) === 'not-found') {\n missing.push({ issue, node, ddlSchema, tableName });\n } else if (issueOutcome(issue) === 'not-expected') {\n extra.push({ issue, node, ddlSchema, tableName });\n }\n }\n if (missing.length === 0 || extra.length === 0) {\n return { calls, consumed };\n }\n\n const byName = (a: IndexFinding, b: IndexFinding): number =>\n a.node.name < b.node.name ? -1 : a.node.name > b.node.name ? 1 : 0;\n // DDL emission must stay unqualified for the unbound namespace, exactly\n // like the per-issue mapping's `emissionSchemaName`.\n const emissionSchema = (ddlSchema: string): string =>\n resolveNamespaceIdForDdlSchema(options.contract, ddlSchema) === UNBOUND_NAMESPACE_ID\n ? UNBOUND_NAMESPACE_ID\n : ddlSchema;\n const pairingKey = (finding: IndexFinding, hash: string): string =>\n JSON.stringify([finding.ddlSchema, finding.tableName, hash]);\n const rename = (missingFinding: IndexFinding, candidate: IndexFinding): void => {\n consumed.add(missingFinding.issue);\n consumed.add(candidate.issue);\n calls.push(\n new RenameIndexCall(\n emissionSchema(missingFinding.ddlSchema),\n missingFinding.tableName,\n candidate.node.name,\n missingFinding.node.name,\n ),\n );\n };\n\n const sortedMissing = [...missing].sort(byName);\n\n const extrasByHash = new Map<string, IndexFinding[]>();\n for (const finding of extra) {\n const parsed = parseWireName(finding.node.name);\n if (parsed === undefined) continue;\n const key = pairingKey(finding, parsed.hash);\n const group = extrasByHash.get(key) ?? [];\n group.push(finding);\n extrasByHash.set(key, group);\n }\n for (const group of extrasByHash.values()) {\n group.sort(byName);\n }\n for (const missingFinding of sortedMissing) {\n const parsed = parseWireName(missingFinding.node.name);\n if (parsed === undefined) continue;\n const candidate = extrasByHash.get(pairingKey(missingFinding, parsed.hash))?.shift();\n if (candidate === undefined) continue;\n rename(missingFinding, candidate);\n }\n\n const sortedExtras = [...extra].sort(byName);\n for (const missingFinding of sortedMissing) {\n if (consumed.has(missingFinding.issue)) continue;\n if (missingFinding.node.prefix === undefined) continue;\n const candidate = sortedExtras.find(\n (extraFinding) =>\n !consumed.has(extraFinding.issue) &&\n extraFinding.ddlSchema === missingFinding.ddlSchema &&\n extraFinding.tableName === missingFinding.tableName &&\n missingFinding.node.contentEquals(extraFinding.node, {\n columnPresence: 'matching',\n bodies: 'verbatim',\n }),\n );\n if (candidate === undefined) continue;\n rename(missingFinding, candidate);\n }\n\n return { calls, consumed };\n }\n\n /**\n * Maps the RLS policy presence findings of the one combined tree diff\n * (`buildPostgresPlanDiff`, already ownership-filtered) into\n * `CREATE POLICY` / `DROP POLICY` / `ALTER POLICY … RENAME TO` ops. It\n * does not re-diff — it consumes exactly the policy-node subset of the\n * shared diff's issues. Enablement is NOT decided here: `ENABLE`/`DISABLE\n * ROW LEVEL SECURITY` derive from the table's marker-driven `rlsEnabled`\n * attribute diff on the relational side.\n *\n * Rename post-pass: a `not-found` and a `not-expected` policy on the SAME\n * table whose wire-name content hashes match but prefixes differ are one\n * prefix-only rename, collapsed into a single non-destructive\n * `RenamePostgresRlsPolicyCall`. Multi-candidate hash groups pair\n * deterministically by sorted wire name; leftovers proceed as\n * create/drop. Unparseable wire names never pair.\n *\n * The pairing runs only when the policy allows `widening` (rename's\n * class). Without it (db-init's additive-only set), pairing degrades\n * deliberately to the additive half: the new name is CREATEd and the old\n * policy survives live until a widening/destructive-allowed plan runs —\n * emitting an ungated widening rename would only fail at the runner's\n * class re-enforcement.\n */\n private planPostgresSchemaDiff(\n options: PlannerOptionsWithComponents,\n filteredDiffIssues: readonly SchemaDiffIssue<SqlSchemaDiffNode>[],\n ): readonly PostgresOpFactoryCall[] {\n const allowsDestructive = options.policy.allowedOperationClasses.includes('destructive');\n const allowsWidening = options.policy.allowedOperationClasses.includes('widening');\n\n interface PolicyFinding {\n readonly node: PostgresPolicySchemaNode;\n readonly schemaForTable: string;\n }\n const missing: PolicyFinding[] = [];\n const extra: PolicyFinding[] = [];\n\n for (const issue of filteredDiffIssues) {\n // 'not-equal' is unreachable for content-addressed policies: the wire name\n // encodes the body hash, so two policies sharing a local key (same name)\n // are always equal and isEqualTo never returns false.\n if (issueOutcome(issue) === 'not-found') {\n const expected = issue.expected;\n PostgresPolicySchemaNode.assert(expected);\n // expected.namespaceId is the DDL schema name (resolved during projection);\n // this re-resolution is a no-op as long as PostgresSchema.ddlSchemaName() returns this.id.\n missing.push({\n node: expected,\n schemaForTable: resolveDdlSchemaForNamespaceStorage(\n options.contract.storage,\n expected.namespaceId,\n ),\n });\n } else if (issueOutcome(issue) === 'not-expected') {\n const actual = issue.actual;\n PostgresPolicySchemaNode.assert(actual);\n extra.push({\n node: actual,\n schemaForTable: resolveDdlSchemaForNamespaceStorage(\n options.contract.storage,\n actual.namespaceId,\n ),\n });\n }\n }\n\n const calls: PostgresOpFactoryCall[] = [];\n const renamedExtras = new Set<PolicyFinding>();\n const renamedMissing = new Set<PolicyFinding>();\n const pairingKey = (finding: PolicyFinding, hash: string): string =>\n JSON.stringify([finding.schemaForTable, finding.node.tableName, hash]);\n\n if (allowsWidening) {\n const extrasByGroup = new Map<string, PolicyFinding[]>();\n for (const finding of extra) {\n const parsed = parseWireName(finding.node.name);\n if (parsed === undefined) continue;\n const key = pairingKey(finding, parsed.hash);\n const group = extrasByGroup.get(key) ?? [];\n group.push(finding);\n extrasByGroup.set(key, group);\n }\n for (const group of extrasByGroup.values()) {\n group.sort((a, b) => (a.node.name < b.node.name ? -1 : a.node.name > b.node.name ? 1 : 0));\n }\n\n const sortedMissing = [...missing].sort((a, b) =>\n a.node.name < b.node.name ? -1 : a.node.name > b.node.name ? 1 : 0,\n );\n for (const missingFinding of sortedMissing) {\n const parsed = parseWireName(missingFinding.node.name);\n if (parsed === undefined) continue;\n const candidates = extrasByGroup.get(pairingKey(missingFinding, parsed.hash));\n const candidate = candidates?.shift();\n if (candidate === undefined) continue;\n // Same name would never surface as missing+extra (the differ pairs by\n // name), so a matched candidate always differs in prefix only.\n renamedExtras.add(candidate);\n renamedMissing.add(missingFinding);\n calls.push(\n new RenamePostgresRlsPolicyCall(\n missingFinding.schemaForTable,\n missingFinding.node.tableName,\n candidate.node.name,\n missingFinding.node.name,\n ),\n );\n }\n }\n\n for (const finding of missing) {\n if (renamedMissing.has(finding)) continue;\n calls.push(\n new CreatePostgresRlsPolicyCall(\n finding.schemaForTable,\n finding.node.tableName,\n policyNodeToContractPolicy(finding.node),\n ),\n );\n }\n if (allowsDestructive) {\n for (const finding of extra) {\n if (renamedExtras.has(finding)) continue;\n calls.push(\n new DropPostgresRlsPolicyCall(\n finding.schemaForTable,\n finding.node.tableName,\n finding.node.name,\n ),\n );\n }\n }\n\n return calls;\n }\n\n private ensureAdditivePolicy(policy: MigrationOperationPolicy) {\n if (!policy.allowedOperationClasses.includes('additive')) {\n return plannerFailure([\n {\n kind: 'unsupportedOperation',\n summary: 'Migration planner requires additive operations be allowed',\n why: 'The planner requires the \"additive\" operation class to be allowed in the policy.',\n },\n ]);\n }\n return null;\n }\n}\n\n/**\n * A diff issue whose node is an RLS policy — the structural half of the one\n * combined tree diff, routed to `planPostgresSchemaDiff` instead of\n * `planIssues`.\n */\nfunction isPolicyDiffIssue(issue: SchemaDiffIssue<SqlSchemaDiffNode>): boolean {\n const node = issue.expected ?? issue.actual;\n return node !== undefined && PostgresPolicySchemaNode.is(node);\n}\n\n/**\n * Drops a `not-expected` issue when it is a whole extra storage entity (a table\n * or a native enum) that some space in the composition owns. Each such node\n * yields its own storage coordinate (see {@link postgresNodeStorageCoordinate}),\n * so `declaresEntity` answers over the whole composition on one uniform\n * coordinate: a positive answer means a sibling owns this entity here — leave\n * it; a negative answer means a genuine orphan — drop it. A node with no storage\n * coordinate (a namespace, or a deeper column/constraint drift on an owned\n * table) is this space's own and is always kept. No oracle ⇒ keep everything.\n */\nfunction retainUnownedExtras(\n issues: readonly SchemaDiffIssue<SqlSchemaDiffNode>[],\n ownership: SchemaOwnership | undefined,\n contract: Contract<SqlStorage>,\n): readonly SchemaDiffIssue<SqlSchemaDiffNode>[] {\n if (ownership === undefined) return issues;\n return issues.filter((issue) => {\n if (issueOutcome(issue) !== 'not-expected') return true;\n const node = issueNode(issue);\n if (node === undefined) return true;\n const coordinate = postgresNodeStorageCoordinate(node);\n if (coordinate === undefined) return true;\n const ddlSchemaName = issueSchemaName(issue);\n if (ddlSchemaName === undefined) return true;\n const namespaceId = resolveNamespaceIdForDdlSchema(contract, ddlSchemaName);\n return !ownership.declaresEntity({ namespaceId, ...coordinate });\n });\n}\n\n/**\n * Returns the one namespace node the relational strategy layer probes for\n * live-table existence and reads codec-hook context off — the namespace\n * matching the planner's resolved schema name, or the first namespace when\n * none matches. `undefined` when the tree has no namespaces, so the strategy\n * context uses its empty-schema default.\n *\n * The relational strategies key tables by bare name, so they can only probe one\n * namespace at a time; probing across every namespace at once is future work.\n *\n * Returns the real `PostgresNamespaceSchemaNode` reference rather than a\n * projection: `storageTypePlanCallStrategy` hands this same value to codec\n * `planTypeOperations` hooks as `schema`, and hooks read the Postgres-specific\n * `nativeEnums` field off it (via `PostgresNamespaceSchemaNode.is`) to decide\n * whether a native enum type already exists — a projection that only copies\n * `tables` would silently drop that signal. `StrategyContext.schema`'s\n * declared type (`SqlSchemaIR`, shared with SQLite's flat shape) doesn't\n * capture this, so the return is `blindCast` the same way `namespaceSchemaNodes`\n * in the family's relational walk already treats a namespace node as a\n * structurally-`SqlSchemaIR`-shaped value.\n */\nfunction relationalNamespaceNode(\n schema: PostgresDatabaseSchemaNode,\n schemaName: string,\n): SqlSchemaIR | undefined {\n const namespaceNodes = Object.values(schema.namespaces);\n const namespaceNode =\n namespaceNodes.find((node) => node.schemaName === schemaName) ?? namespaceNodes[0];\n if (namespaceNode === undefined) return undefined;\n return blindCast<\n SqlSchemaIR,\n 'PostgresNamespaceSchemaNode carries tables (+ nativeEnums, read by codec hooks via PostgresNamespaceSchemaNode.is) structurally compatible with the SqlSchemaIR shape the strategy layer declares'\n >(namespaceNode);\n}\n\n/**\n * Rebuilds the `PostgresRlsPolicy` contract entity `CreatePostgresRlsPolicyCall`\n * carries (its `renderTypeScript`/`createRlsPolicy` paths serialize the whole\n * entity, `namespaceId` included). This reconstructs rather than looking the\n * original up in the contract on purpose: the diff node's `namespaceId` is the\n * *resolved DDL schema* (set when the expected tree was built), which is the\n * value the emitted op must carry; the contract-stored entity holds the raw,\n * pre-resolution coordinate, so a lookup would change the migration output.\n */\nfunction policyNodeToContractPolicy(node: PostgresPolicySchemaNode): PostgresRlsPolicy {\n return new PostgresRlsPolicy({\n name: node.name,\n prefix: node.prefix,\n tableName: node.tableName,\n namespaceId: node.namespaceId,\n operation: node.operation,\n roles: [...node.roles],\n ...ifDefined('using', node.using),\n ...ifDefined('withCheck', node.withCheck),\n permissive: node.permissive,\n });\n}\n"],"mappings":"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"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { t as PostgresMigration } from "./postgres-migration
|
|
1
|
+
import { t as PostgresMigration } from "./postgres-migration--BzvpA92.mjs";
|
|
2
2
|
import { t as renderOps } from "./render-ops-TcY9XEkz.mjs";
|
|
3
3
|
import { t as renderCallsToTypeScript } from "./render-typescript-Ta7_6CGf.mjs";
|
|
4
4
|
//#region src/core/migrations/planner-produced-postgres-migration.ts
|
|
@@ -43,4 +43,4 @@ var TypeScriptRenderablePostgresMigration = class extends PostgresMigration {
|
|
|
43
43
|
//#endregion
|
|
44
44
|
export { TypeScriptRenderablePostgresMigration as t };
|
|
45
45
|
|
|
46
|
-
//# sourceMappingURL=planner-produced-postgres-migration-
|
|
46
|
+
//# sourceMappingURL=planner-produced-postgres-migration-BE3EJIRK.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"planner-produced-postgres-migration-
|
|
1
|
+
{"version":3,"file":"planner-produced-postgres-migration-BE3EJIRK.mjs","names":["#calls","#meta","#spaceId","#snapshotsImportPath","#lowerer","#operationsCache"],"sources":["../src/core/migrations/planner-produced-postgres-migration.ts"],"sourcesContent":["/**\n * Planner-produced Postgres migration.\n *\n * Returned by `PostgresMigrationPlanner.plan(...)` and `emptyMigration(...)`.\n * Holds the migration IR (`PostgresOpFactoryCall[]`) alongside\n * `MigrationMeta` and exposes both the runtime-ops view (`get operations`)\n * and the TypeScript authoring view (`renderTypeScript()`). Satisfies\n * `MigrationPlanWithAuthoringSurface` so the CLI can uniformly serialize any\n * planner result back to `migration.ts`.\n *\n * Extends the family-level `SqlMigration` alias rather than the target-local\n * migration base directly — mirrors Mongo's `PlannerProducedMongoMigration`\n * shape and keeps CLI wiring one step removed from target internals.\n *\n * Placeholder-bearing plans: `renderTypeScript()` always succeeds and embeds\n * `() => placeholder(\"slot\")` at each stub. `operations`, in contrast, is\n * _not safe to enumerate_ on a stub-bearing plan — `DataTransformCall.toOp()`\n * throws `MIGRATION.UNFILLED_PLACEHOLDER` because a planner-stubbed closure cannot be lowered\n * to a runtime op. Callers that know a plan may carry stubs must render to\n * `migration.ts`, let the user fill the slots, and re-load the edited\n * migration before enumerating ops. The walk-schema planner does not emit\n * `DataTransformCall`s today, so this asymmetry is invisible until the\n * issue-planner integration lands in Phase 2.\n */\n\nimport type { SqlMigrationPlanOperation } from '@prisma-next/family-sql/control';\nimport type { ExecuteRequestLowerer } from '@prisma-next/family-sql/control-adapter';\nimport type {\n MigrationPlanWithAuthoringSurface,\n OpFactoryCall,\n} from '@prisma-next/framework-components/control';\nimport type { MigrationMeta } from '@prisma-next/migration-tools/migration';\nimport type { PostgresPlanTargetDetails } from './planner-target-details';\nimport { PostgresMigration } from './postgres-migration';\nimport { renderOps } from './render-ops';\nimport { renderCallsToTypeScript } from './render-typescript';\n\nexport class TypeScriptRenderablePostgresMigration\n extends PostgresMigration\n implements MigrationPlanWithAuthoringSurface\n{\n readonly #calls: readonly OpFactoryCall[];\n readonly #meta: MigrationMeta;\n readonly #spaceId: string;\n readonly #snapshotsImportPath: string;\n readonly #lowerer: ExecuteRequestLowerer | undefined;\n #operationsCache:\n | readonly (\n | SqlMigrationPlanOperation<PostgresPlanTargetDetails>\n | Promise<SqlMigrationPlanOperation<PostgresPlanTargetDetails>>\n )[]\n | undefined;\n\n constructor(\n calls: readonly OpFactoryCall[],\n meta: MigrationMeta,\n spaceId: string,\n snapshotsImportPath: string,\n lowerer?: ExecuteRequestLowerer,\n ) {\n super();\n this.#calls = calls;\n this.#meta = meta;\n this.#spaceId = spaceId;\n this.#snapshotsImportPath = snapshotsImportPath;\n this.#lowerer = lowerer;\n }\n\n override get operations(): readonly (\n | SqlMigrationPlanOperation<PostgresPlanTargetDetails>\n | Promise<SqlMigrationPlanOperation<PostgresPlanTargetDetails>>\n )[] {\n this.#operationsCache ??= renderOps(this.#calls, this.#lowerer);\n return this.#operationsCache;\n }\n\n override describe(): MigrationMeta {\n return this.#meta;\n }\n\n /**\n * Contract space this planner-produced plan applies to. Threaded\n * from the planner options so the runner keys the marker row by\n * the right space when executing the plan.\n */\n get spaceId(): string {\n return this.#spaceId;\n }\n\n renderTypeScript(): string {\n return renderCallsToTypeScript(this.#calls, {\n from: this.#meta.from,\n to: this.#meta.to,\n snapshotsImportPath: this.#snapshotsImportPath,\n });\n }\n}\n"],"mappings":";;;;AAqCA,IAAa,wCAAb,cACU,kBAEV;CACE;CACA;CACA;CACA;CACA;CACA;CAOA,YACE,OACA,MACA,SACA,qBACA,SACA;EACA,MAAM;EACN,KAAKA,SAAS;EACd,KAAKC,QAAQ;EACb,KAAKC,WAAW;EAChB,KAAKC,uBAAuB;EAC5B,KAAKC,WAAW;CAClB;CAEA,IAAa,aAGT;EACF,KAAKC,qBAAqB,UAAU,KAAKL,QAAQ,KAAKI,QAAQ;EAC9D,OAAO,KAAKC;CACd;CAEA,WAAmC;EACjC,OAAO,KAAKJ;CACd;;;;;;CAOA,IAAI,UAAkB;EACpB,OAAO,KAAKC;CACd;CAEA,mBAA2B;EACzB,OAAO,wBAAwB,KAAKF,QAAQ;GAC1C,MAAM,KAAKC,MAAM;GACjB,IAAI,KAAKA,MAAM;GACf,qBAAqB,KAAKE;EAC5B,CAAC;CACH;AACF"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { t as PostgresPlanTargetDetails } from "./planner-target-details-HkP4RrRO.mjs";
|
|
2
|
-
import { t as PostgresMigration } from "./postgres-migration-
|
|
2
|
+
import { t as PostgresMigration } from "./postgres-migration-BXelnqHO.mjs";
|
|
3
3
|
import { SqlMigrationPlanOperation } from "@prisma-next/family-sql/control";
|
|
4
4
|
import { MigrationPlanWithAuthoringSurface, OpFactoryCall } from "@prisma-next/framework-components/control";
|
|
5
5
|
import { MigrationMeta } from "@prisma-next/migration-tools/migration";
|
|
@@ -20,4 +20,4 @@ declare class TypeScriptRenderablePostgresMigration extends PostgresMigration im
|
|
|
20
20
|
}
|
|
21
21
|
//#endregion
|
|
22
22
|
export { TypeScriptRenderablePostgresMigration as t };
|
|
23
|
-
//# sourceMappingURL=planner-produced-postgres-migration-
|
|
23
|
+
//# sourceMappingURL=planner-produced-postgres-migration-CRAHl6ak.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"planner-produced-postgres-migration-CRAHl6ak.d.mts","names":[],"sources":["../src/core/migrations/planner-produced-postgres-migration.ts"],"mappings":";;;;;;;cAqCa,8CACH,6BACG;;cAeT,gBAAgB,iBAChB,MAAM,eACN,iBACA,6BACA,UAAU;MAUC,wBACT,0BAA0B,6BAC1B,QAAQ,0BAA0B;EAM7B,YAAY;;;;;;MASjB;EAIJ"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { t as TypeScriptRenderablePostgresMigration } from "./planner-produced-postgres-migration-
|
|
1
|
+
import { t as TypeScriptRenderablePostgresMigration } from "./planner-produced-postgres-migration-CRAHl6ak.mjs";
|
|
2
2
|
export { TypeScriptRenderablePostgresMigration };
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { t as TypeScriptRenderablePostgresMigration } from "./planner-produced-postgres-migration-
|
|
1
|
+
import { t as TypeScriptRenderablePostgresMigration } from "./planner-produced-postgres-migration-BE3EJIRK.mjs";
|
|
2
2
|
export { TypeScriptRenderablePostgresMigration };
|
|
@@ -7,8 +7,8 @@ function buildSchemaLookupMap(schema) {
|
|
|
7
7
|
}
|
|
8
8
|
function buildSchemaTableLookup(table) {
|
|
9
9
|
const uniqueKeys = new Set(table.uniques.map((u) => u.columns.join(",")));
|
|
10
|
-
const indexKeys = new Set(table.indexes.map((i) => i.columns.join(",")));
|
|
11
|
-
const uniqueIndexKeys = new Set(table.indexes.filter((i) => i.unique).map((i) => i.columns.join(",")));
|
|
10
|
+
const indexKeys = new Set(table.indexes.map((i) => (i.columns ?? []).join(",")));
|
|
11
|
+
const uniqueIndexKeys = new Set(table.indexes.filter((i) => i.unique).map((i) => (i.columns ?? []).join(",")));
|
|
12
12
|
const fkKeys = /* @__PURE__ */ new Set();
|
|
13
13
|
for (const fk of table.foreignKeys) {
|
|
14
14
|
fkKeys.add(JSON.stringify([
|
|
@@ -54,4 +54,4 @@ function hasForeignKey(lookup, fk) {
|
|
|
54
54
|
//#endregion
|
|
55
55
|
export { hasUniqueConstraint as i, hasForeignKey as n, hasIndex as r, buildSchemaLookupMap as t };
|
|
56
56
|
|
|
57
|
-
//# sourceMappingURL=planner-schema-lookup-
|
|
57
|
+
//# sourceMappingURL=planner-schema-lookup-D3Y_OL-C.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"planner-schema-lookup-D3Y_OL-C.mjs","names":[],"sources":["../src/core/migrations/planner-schema-lookup.ts"],"sourcesContent":["import { UNBOUND_NAMESPACE_ID } from '@prisma-next/framework-components/ir';\nimport type { ForeignKey } from '@prisma-next/sql-contract/types';\nimport type { SqlSchemaIR } from '@prisma-next/sql-schema-ir/types';\n\n/**\n * Pre-computed lookup sets for a schema table's constraints.\n * Converts O(n*m) linear scans to O(1) Set lookups per constraint check.\n */\nexport interface SchemaTableLookup {\n readonly uniqueKeys: Set<string>;\n readonly indexKeys: Set<string>;\n readonly uniqueIndexKeys: Set<string>;\n readonly fkKeys: Set<string>;\n}\n\nexport function buildSchemaLookupMap(schema: SqlSchemaIR): ReadonlyMap<string, SchemaTableLookup> {\n const map = new Map<string, SchemaTableLookup>();\n for (const [tableName, table] of Object.entries(schema.tables)) {\n map.set(tableName, buildSchemaTableLookup(table));\n }\n return map;\n}\n\nfunction buildSchemaTableLookup(table: SqlSchemaIR['tables'][string]): SchemaTableLookup {\n const uniqueKeys = new Set(table.uniques.map((u) => u.columns.join(',')));\n const indexKeys = new Set(table.indexes.map((i) => (i.columns ?? []).join(',')));\n const uniqueIndexKeys = new Set(\n table.indexes.filter((i) => i.unique).map((i) => (i.columns ?? []).join(',')),\n );\n const fkKeys = new Set<string>();\n for (const fk of table.foreignKeys) {\n // Keys are JSON-encoded tuples so identifiers containing any character\n // (including the column-list comma or pipe characters) cannot collide\n // across structurally-distinct FKs. Unqualified keys are 3-tuples\n // (cols, table, refCols); qualified keys are 4-tuples\n // (cols, schema, table, refCols) — the arity difference makes the two\n // key shapes fundamentally non-collidable.\n fkKeys.add(JSON.stringify([fk.columns, fk.referencedTable, fk.referencedColumns]));\n if (fk.referencedSchema !== undefined) {\n fkKeys.add(\n JSON.stringify([fk.columns, fk.referencedSchema, fk.referencedTable, fk.referencedColumns]),\n );\n }\n }\n return { uniqueKeys, indexKeys, uniqueIndexKeys, fkKeys };\n}\n\nexport function hasUniqueConstraint(\n lookup: SchemaTableLookup,\n columns: readonly string[],\n): boolean {\n const key = columns.join(',');\n return lookup.uniqueKeys.has(key) || lookup.uniqueIndexKeys.has(key);\n}\n\nexport function hasIndex(lookup: SchemaTableLookup, columns: readonly string[]): boolean {\n const key = columns.join(',');\n return lookup.indexKeys.has(key) || lookup.uniqueKeys.has(key);\n}\n\nexport function hasForeignKey(lookup: SchemaTableLookup, fk: ForeignKey): boolean {\n // Mirror the encoding produced by buildSchemaTableLookup exactly:\n // unqualified 3-tuple for unbound-namespace FKs, qualified 4-tuple for\n // bound-namespace FKs.\n if (fk.target.namespaceId === UNBOUND_NAMESPACE_ID) {\n return lookup.fkKeys.has(\n JSON.stringify([fk.source.columns, fk.target.tableName, fk.target.columns]),\n );\n }\n return lookup.fkKeys.has(\n JSON.stringify([\n fk.source.columns,\n fk.target.namespaceId,\n fk.target.tableName,\n fk.target.columns,\n ]),\n );\n}\n"],"mappings":";;AAeA,SAAgB,qBAAqB,QAA6D;CAChG,MAAM,sBAAM,IAAI,IAA+B;CAC/C,KAAK,MAAM,CAAC,WAAW,UAAU,OAAO,QAAQ,OAAO,MAAM,GAC3D,IAAI,IAAI,WAAW,uBAAuB,KAAK,CAAC;CAElD,OAAO;AACT;AAEA,SAAS,uBAAuB,OAAyD;CACvF,MAAM,aAAa,IAAI,IAAI,MAAM,QAAQ,KAAK,MAAM,EAAE,QAAQ,KAAK,GAAG,CAAC,CAAC;CACxE,MAAM,YAAY,IAAI,IAAI,MAAM,QAAQ,KAAK,OAAO,EAAE,WAAW,CAAC,EAAA,CAAG,KAAK,GAAG,CAAC,CAAC;CAC/E,MAAM,kBAAkB,IAAI,IAC1B,MAAM,QAAQ,QAAQ,MAAM,EAAE,MAAM,CAAC,CAAC,KAAK,OAAO,EAAE,WAAW,CAAC,EAAA,CAAG,KAAK,GAAG,CAAC,CAC9E;CACA,MAAM,yBAAS,IAAI,IAAY;CAC/B,KAAK,MAAM,MAAM,MAAM,aAAa;EAOlC,OAAO,IAAI,KAAK,UAAU;GAAC,GAAG;GAAS,GAAG;GAAiB,GAAG;EAAiB,CAAC,CAAC;EACjF,IAAI,GAAG,qBAAqB,KAAA,GAC1B,OAAO,IACL,KAAK,UAAU;GAAC,GAAG;GAAS,GAAG;GAAkB,GAAG;GAAiB,GAAG;EAAiB,CAAC,CAC5F;CAEJ;CACA,OAAO;EAAE;EAAY;EAAW;EAAiB;CAAO;AAC1D;AAEA,SAAgB,oBACd,QACA,SACS;CACT,MAAM,MAAM,QAAQ,KAAK,GAAG;CAC5B,OAAO,OAAO,WAAW,IAAI,GAAG,KAAK,OAAO,gBAAgB,IAAI,GAAG;AACrE;AAEA,SAAgB,SAAS,QAA2B,SAAqC;CACvF,MAAM,MAAM,QAAQ,KAAK,GAAG;CAC5B,OAAO,OAAO,UAAU,IAAI,GAAG,KAAK,OAAO,WAAW,IAAI,GAAG;AAC/D;AAEA,SAAgB,cAAc,QAA2B,IAAyB;CAIhF,IAAI,GAAG,OAAO,gBAAgB,sBAC5B,OAAO,OAAO,OAAO,IACnB,KAAK,UAAU;EAAC,GAAG,OAAO;EAAS,GAAG,OAAO;EAAW,GAAG,OAAO;CAAO,CAAC,CAC5E;CAEF,OAAO,OAAO,OAAO,IACnB,KAAK,UAAU;EACb,GAAG,OAAO;EACV,GAAG,OAAO;EACV,GAAG,OAAO;EACV,GAAG,OAAO;CACZ,CAAC,CACH;AACF"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { i as hasUniqueConstraint, n as hasForeignKey, r as hasIndex, t as buildSchemaLookupMap } from "./planner-schema-lookup-
|
|
1
|
+
import { i as hasUniqueConstraint, n as hasForeignKey, r as hasIndex, t as buildSchemaLookupMap } from "./planner-schema-lookup-D3Y_OL-C.mjs";
|
|
2
2
|
export { buildSchemaLookupMap, hasForeignKey, hasIndex, hasUniqueConstraint };
|
package/dist/planner.d.mts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { t as PostgresDatabaseSchemaNode } from "./postgres-database-schema-node-DMXsIwAb.mjs";
|
|
2
2
|
import { t as PostgresContract } from "./postgres-schema-FdKRwe7X.mjs";
|
|
3
|
-
import { t as TypeScriptRenderablePostgresMigration } from "./planner-produced-postgres-migration-
|
|
3
|
+
import { t as TypeScriptRenderablePostgresMigration } from "./planner-produced-postgres-migration-CRAHl6ak.mjs";
|
|
4
4
|
import { ContractToSchemaIROptions, MigrationOperationPolicy, SqlPlannerConflict, SqlPlannerFailureResult } from "@prisma-next/family-sql/control";
|
|
5
5
|
import { MigrationPlanWithAuthoringSurface, MigrationPlanner, MigrationScaffoldContext, SchemaOwnership } from "@prisma-next/framework-components/control";
|
|
6
6
|
import { Contract } from "@prisma-next/contract/types";
|
|
@@ -105,6 +105,26 @@ declare class PostgresMigrationPlanner implements MigrationPlanner<'sql', 'postg
|
|
|
105
105
|
}): PostgresPlanResult;
|
|
106
106
|
emptyMigration(context: MigrationScaffoldContext, spaceId: string): MigrationPlanWithAuthoringSurface;
|
|
107
107
|
private planSql;
|
|
108
|
+
/**
|
|
109
|
+
* Rename post-pass for indexes, per `(schema, table)`, widening-only,
|
|
110
|
+
* deterministic by sorted names — the same structure as the policy pass
|
|
111
|
+
* below (which stays untouched: policies pair by hash only).
|
|
112
|
+
*
|
|
113
|
+
* Phase 1 — hash pairing: extras whose live names parse as wire names,
|
|
114
|
+
* grouped by `(schema, table, hash)`; missing nodes iterated in sorted-name
|
|
115
|
+
* order consume the sorted-name-first candidate — a prefix-only rename.
|
|
116
|
+
*
|
|
117
|
+
* Phase 2 — content pairing: the remaining managed-missing nodes
|
|
118
|
+
* (`prefix` defined) against the remaining extras of any name shape,
|
|
119
|
+
* paired iff content-equal (columns ordered-strict both-defined-or-
|
|
120
|
+
* both-undefined, `unique`/`type` strict, `options` loose, bodies
|
|
121
|
+
* byte-equal) — exact→managed adoption convergence.
|
|
122
|
+
*
|
|
123
|
+
* Leftovers proceed as create/drop exactly as before; without the
|
|
124
|
+
* widening allowance the pass is skipped and pairing degrades to the
|
|
125
|
+
* additive half, like the policy pass.
|
|
126
|
+
*/
|
|
127
|
+
private pairIndexRenames;
|
|
108
128
|
/**
|
|
109
129
|
* Maps the RLS policy presence findings of the one combined tree diff
|
|
110
130
|
* (`buildPostgresPlanDiff`, already ownership-filtered) into
|
package/dist/planner.d.mts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"planner.d.mts","names":[],"sources":["../src/core/migrations/contract-to-postgres-database-schema-node.ts","../src/core/migrations/planner.ts"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBA8FgB,qCACd,UAAU,yBACV,SAAS,4BACR;;;
|
|
1
|
+
{"version":3,"file":"planner.d.mts","names":[],"sources":["../src/core/migrations/contract-to-postgres-database-schema-node.ts","../src/core/migrations/planner.ts"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBA8FgB,qCACd,UAAU,yBACV,SAAS,4BACR;;;iBC9Ba,+BACd,SAAS,wBACR;;;;;;;;KAWS;WAEG;WACA,MAAM;WACN,oBAAoB;IAE/B;;;;;;;;;;;;;;;;;;;cAoBS,oCAAoC;;cAGnC,UAAU;EAItB,KAAK;aACM;aACA;aACA,QAAQ;;;;;;;;;;;;;;;aAeR,cAAc;aACd;aACA,qBAAqB,cAAc;;;;;;aAMnC;;;;;aAKA,YAAY;;;;;;aAMZ;MACP;EAIJ,eACE,SAAS,0BACT,kBACC;UAaK;;;;;;;;;;;;;;;;;;;;UAuNA;;;;;;;;;;;;;;;;;;;;;;;;UA8HA;UAiHA"}
|
package/dist/planner.mjs
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import { r as contractToPostgresDatabaseSchemaNode } from "./diff-database-schema-
|
|
2
|
-
import { t as createPostgresMigrationPlanner } from "./planner-
|
|
1
|
+
import { r as contractToPostgresDatabaseSchemaNode } from "./diff-database-schema-qRPwuYBH.mjs";
|
|
2
|
+
import { t as createPostgresMigrationPlanner } from "./planner-BhnkIJX-.mjs";
|
|
3
3
|
export { contractToPostgresDatabaseSchemaNode, createPostgresMigrationPlanner };
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { b as PG_TEXT_CODEC_ID, h as PG_INT_CODEC_ID } from "./codec-ids-D3G8K0YM.mjs";
|
|
2
2
|
import { a as nativeEnumEntityKind, c as roleEntityKind, o as policyEntityKind, s as rlsEnablementEntityKind, t as PostgresSchema } from "./postgres-schema-CTplQZDK.mjs";
|
|
3
|
-
import { t as postgresAuthoringEntityTypes } from "./authoring-
|
|
3
|
+
import { t as postgresAuthoringEntityTypes } from "./authoring-DnO1B0N9.mjs";
|
|
4
4
|
import { UNBOUND_NAMESPACE_ID } from "@prisma-next/framework-components/ir";
|
|
5
5
|
import { blindCast } from "@prisma-next/utils/casts";
|
|
6
6
|
import { buildSqlSchemaQualifiedView } from "@prisma-next/sql-contract/contract-view";
|
|
@@ -108,4 +108,4 @@ const PostgresContractView = {
|
|
|
108
108
|
//#endregion
|
|
109
109
|
export { PostgresContractSerializer as n, PostgresContractView as t };
|
|
110
110
|
|
|
111
|
-
//# sourceMappingURL=postgres-contract-view-
|
|
111
|
+
//# sourceMappingURL=postgres-contract-view-BeIEyZfD.mjs.map
|
package/dist/{postgres-contract-view-B8yXVWYe.mjs.map → postgres-contract-view-BeIEyZfD.mjs.map}
RENAMED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"postgres-contract-view-
|
|
1
|
+
{"version":3,"file":"postgres-contract-view-BeIEyZfD.mjs","names":[],"sources":["../src/core/postgres-contract-serializer.ts","../src/core/postgres-contract-view.ts"],"sourcesContent":["import type { Contract } from '@prisma-next/contract/types';\nimport {\n SqlContractSerializerBase,\n type SqlEntityHydrationFactory,\n} from '@prisma-next/family-sql/ir';\nimport {\n type AuthoringEntityContext,\n type AuthoringEntityTypeFactoryOutput,\n type AuthoringEntityTypeNamespace,\n isAuthoringEntityTypeDescriptor,\n} from '@prisma-next/framework-components/authoring';\nimport {\n type AnyEntityKindDescriptor,\n type Namespace,\n UNBOUND_NAMESPACE_ID,\n} from '@prisma-next/framework-components/ir';\nimport type { SqlNamespaceInput, SqlStorage } from '@prisma-next/sql-contract/types';\nimport { blindCast } from '@prisma-next/utils/casts';\nimport type { JsonObject } from '@prisma-next/utils/json';\nimport { postgresAuthoringEntityTypes } from './authoring';\nimport { PG_INT_CODEC_ID, PG_TEXT_CODEC_ID } from './codec-ids';\nimport {\n nativeEnumEntityKind,\n policyEntityKind,\n rlsEnablementEntityKind,\n roleEntityKind,\n} from './entity-kinds';\nimport { PostgresSchema } from './postgres-schema';\n\nconst POSTGRES_AUTHORING_CTX: AuthoringEntityContext = {\n family: 'sql',\n target: 'postgres',\n enumInferenceCodecs: { text: PG_TEXT_CODEC_ID, int: PG_INT_CODEC_ID },\n};\n\nfunction isAuthoringEntityTypeFactoryOutput(\n output: unknown,\n): output is AuthoringEntityTypeFactoryOutput<unknown, unknown> {\n return (\n typeof output === 'object' &&\n output !== null &&\n 'factory' in output &&\n typeof output.factory === 'function'\n );\n}\n\n/**\n * Walks a pack's entity-type namespace tree and emits hydration factories\n * keyed by the descriptor's `discriminator`. Used for `storage.types`\n * (codec-triple hydration). Namespace entries hydration dispatches by\n * entries key, not discriminator — handled by `hydrateNamespaceEntities`.\n */\nfunction collectStorageTypesHydrators(\n namespace: AuthoringEntityTypeNamespace,\n): ReadonlyMap<string, SqlEntityHydrationFactory> {\n const registry = new Map<string, SqlEntityHydrationFactory>();\n const walk = (node: AuthoringEntityTypeNamespace): void => {\n for (const value of Object.values(node)) {\n if (isAuthoringEntityTypeDescriptor(value)) {\n if (isAuthoringEntityTypeFactoryOutput(value.output)) {\n const { factory } = value.output;\n registry.set(value.discriminator, (raw) => factory(raw, POSTGRES_AUTHORING_CTX));\n }\n continue;\n }\n if (typeof value === 'object' && value !== null) {\n walk(value);\n }\n }\n };\n walk(namespace);\n return registry;\n}\n\nexport class PostgresContractSerializer extends SqlContractSerializerBase<Contract<SqlStorage>> {\n constructor(extraPackEntityKinds: readonly AnyEntityKindDescriptor[] = []) {\n const storageTypesHydrators = collectStorageTypesHydrators(postgresAuthoringEntityTypes);\n super(storageTypesHydrators, [\n policyEntityKind,\n roleEntityKind,\n rlsEnablementEntityKind,\n nativeEnumEntityKind,\n ...extraPackEntityKinds,\n ]);\n }\n\n protected override hydrateSqlNamespaceEntry(\n nsId: string,\n raw: Record<string, unknown>,\n ): Namespace | SqlNamespaceInput {\n const hydrated = blindCast<\n SqlNamespaceInput,\n 'raw is always plain JSON, so super.hydrateSqlNamespaceEntry returns SqlNamespaceInput'\n >(super.hydrateSqlNamespaceEntry(nsId, raw));\n const { id, entries } = hydrated;\n\n const allSlotsEmpty = Object.values(entries).every(\n (slot) => slot === undefined || Object.keys(slot).length === 0,\n );\n if (id === UNBOUND_NAMESPACE_ID && allSlotsEmpty) {\n return PostgresSchema.unbound;\n }\n const valueSetSlot = entries['valueSet'];\n const hasValueSets = valueSetSlot !== undefined && Object.keys(valueSetSlot).length > 0;\n return new PostgresSchema({\n id,\n entries: {\n ...entries,\n table: entries['table'] ?? {},\n ...(hasValueSets ? { valueSet: valueSetSlot } : {}),\n },\n });\n }\n\n override serializeContract(contract: Contract<SqlStorage>): JsonObject {\n const { storage, ...rest } = contract;\n const namespacesJson: Record<string, JsonObject> = {};\n // Each namespace serializes to its id, its schema-kind tag, and the\n // base's generic entries walk — every enumerable kind on\n // `PostgresSchema.entries`, including `native_enum`.\n for (const [nsId, ns] of Object.entries(storage.namespaces)) {\n const isUnboundSlot = ns.id === UNBOUND_NAMESPACE_ID;\n namespacesJson[nsId] = {\n id: ns.id,\n kind: isUnboundSlot ? 'postgres-unbound-schema' : 'postgres-schema',\n entries: this.serializeNamespaceEntries(ns.entries),\n };\n }\n const storageOut: Record<string, unknown> = {\n storageHash: String(storage.storageHash),\n namespaces: namespacesJson,\n };\n if (storage.types !== undefined) {\n const typesOut: Record<string, JsonObject> = {};\n for (const [name, entry] of Object.entries(storage.types)) {\n typesOut[name] = this.serializeJsonObject(entry);\n }\n storageOut['types'] = typesOut;\n }\n return blindCast<\n JsonObject,\n 'contract minus storage plus a JSON-shaped storageOut is a JsonObject'\n >({\n ...rest,\n storage: storageOut,\n });\n }\n}\n","import type { Contract } from '@prisma-next/contract/types';\nimport {\n buildSqlSchemaQualifiedView,\n type SqlSchemaQualifiedView,\n} from '@prisma-next/sql-contract/contract-view';\nimport type { SqlStorage } from '@prisma-next/sql-contract/types';\nimport { PostgresContractSerializer } from './postgres-contract-serializer';\n\n/**\n * A schema-qualified Postgres contract view: the deserialized contract\n * intersected with a single `namespace` member holding every schema by id. It is\n * substitutable for `Contract` (carries `storage`, `domain`, …) and reaches each\n * schema's entities through `view.namespace.<schema>`:\n *\n * ```ts\n * const view = PostgresContractView.fromJson<Contract>(contractJson);\n * view.namespace.public.table.users // typed table leaf in the public schema\n * view.namespace.auth.table.users // the auth schema's own users table\n * view.namespace.public.entries.policy.X // pack-contributed kind (RLS / #771 path)\n * view.namespace.__unbound__.table.X // default schema, keyed by its raw id\n * view.storage // the full contract is still present\n * ```\n *\n * This mirrors the runtime `db.enums.<ns>` keying exactly (the default schema\n * keeps its literal `__unbound__` id). Schema names are NOT promoted to the\n * contract root, so there is no collision with contract envelope fields — a\n * schema named `storage` is `view.namespace.storage`, while `view.storage`\n * stays the contract's `storage`.\n */\nexport type PostgresContractView<TContract extends Contract<SqlStorage> = Contract<SqlStorage>> =\n SqlSchemaQualifiedView<TContract>;\n\nexport const PostgresContractView = {\n /** Wrap an already-deserialized Postgres contract in a schema-qualified view. */\n from<TContract extends Contract<SqlStorage>>(\n contract: TContract,\n ): PostgresContractView<TContract> {\n return buildSqlSchemaQualifiedView(contract);\n },\n\n /** Deserialize a Postgres contract JSON envelope and wrap it in a view. */\n fromJson<TContract extends Contract<SqlStorage> = Contract<SqlStorage>>(\n json: unknown,\n ): PostgresContractView<TContract> {\n const contract = new PostgresContractSerializer().deserializeContract<TContract>(json);\n return buildSqlSchemaQualifiedView(contract);\n },\n};\n"],"mappings":";;;;;;;;;AA6BA,MAAM,yBAAiD;CACrD,QAAQ;CACR,QAAQ;CACR,qBAAqB;EAAE,MAAM;EAAkB,KAAK;CAAgB;AACtE;AAEA,SAAS,mCACP,QAC8D;CAC9D,OACE,OAAO,WAAW,YAClB,WAAW,QACX,aAAa,UACb,OAAO,OAAO,YAAY;AAE9B;;;;;;;AAQA,SAAS,6BACP,WACgD;CAChD,MAAM,2BAAW,IAAI,IAAuC;CAC5D,MAAM,QAAQ,SAA6C;EACzD,KAAK,MAAM,SAAS,OAAO,OAAO,IAAI,GAAG;GACvC,IAAI,gCAAgC,KAAK,GAAG;IAC1C,IAAI,mCAAmC,MAAM,MAAM,GAAG;KACpD,MAAM,EAAE,YAAY,MAAM;KAC1B,SAAS,IAAI,MAAM,gBAAgB,QAAQ,QAAQ,KAAK,sBAAsB,CAAC;IACjF;IACA;GACF;GACA,IAAI,OAAO,UAAU,YAAY,UAAU,MACzC,KAAK,KAAK;EAEd;CACF;CACA,KAAK,SAAS;CACd,OAAO;AACT;AAEA,IAAa,6BAAb,cAAgD,0BAAgD;CAC9F,YAAY,uBAA2D,CAAC,GAAG;EACzE,MAAM,wBAAwB,6BAA6B,4BAA4B;EACvF,MAAM,uBAAuB;GAC3B;GACA;GACA;GACA;GACA,GAAG;EACL,CAAC;CACH;CAEA,yBACE,MACA,KAC+B;EAK/B,MAAM,EAAE,IAAI,YAJK,UAGf,MAAM,yBAAyB,MAAM,GAAG,CACX;EAE/B,MAAM,gBAAgB,OAAO,OAAO,OAAO,CAAC,CAAC,OAC1C,SAAS,SAAS,KAAA,KAAa,OAAO,KAAK,IAAI,CAAC,CAAC,WAAW,CAC/D;EACA,IAAI,OAAO,wBAAwB,eACjC,OAAO,eAAe;EAExB,MAAM,eAAe,QAAQ;EAC7B,MAAM,eAAe,iBAAiB,KAAA,KAAa,OAAO,KAAK,YAAY,CAAC,CAAC,SAAS;EACtF,OAAO,IAAI,eAAe;GACxB;GACA,SAAS;IACP,GAAG;IACH,OAAO,QAAQ,YAAY,CAAC;IAC5B,GAAI,eAAe,EAAE,UAAU,aAAa,IAAI,CAAC;GACnD;EACF,CAAC;CACH;CAEA,kBAA2B,UAA4C;EACrE,MAAM,EAAE,SAAS,GAAG,SAAS;EAC7B,MAAM,iBAA6C,CAAC;EAIpD,KAAK,MAAM,CAAC,MAAM,OAAO,OAAO,QAAQ,QAAQ,UAAU,GAAG;GAC3D,MAAM,gBAAgB,GAAG,OAAO;GAChC,eAAe,QAAQ;IACrB,IAAI,GAAG;IACP,MAAM,gBAAgB,4BAA4B;IAClD,SAAS,KAAK,0BAA0B,GAAG,OAAO;GACpD;EACF;EACA,MAAM,aAAsC;GAC1C,aAAa,OAAO,QAAQ,WAAW;GACvC,YAAY;EACd;EACA,IAAI,QAAQ,UAAU,KAAA,GAAW;GAC/B,MAAM,WAAuC,CAAC;GAC9C,KAAK,MAAM,CAAC,MAAM,UAAU,OAAO,QAAQ,QAAQ,KAAK,GACtD,SAAS,QAAQ,KAAK,oBAAoB,KAAK;GAEjD,WAAW,WAAW;EACxB;EACA,OAAO,UAGL;GACA,GAAG;GACH,SAAS;EACX,CAAC;CACH;AACF;;;ACnHA,MAAa,uBAAuB;;CAElC,KACE,UACiC;EACjC,OAAO,4BAA4B,QAAQ;CAC7C;;CAGA,SACE,MACiC;EAEjC,OAAO,4BADU,IAAI,2BAA2B,CAAC,CAAC,oBAA+B,IACvC,CAAC;CAC7C;AACF"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { t as errorPostgresMigrationStackMissing } from "./errors-DBSNx7EJ.mjs";
|
|
2
2
|
import { g as PostgresRlsPolicy } from "./postgres-schema-CTplQZDK.mjs";
|
|
3
|
-
import { A as
|
|
4
|
-
import { t as PostgresContractView } from "./postgres-contract-view-
|
|
3
|
+
import { A as RenamePostgresRlsPolicyCall, C as DropNativeEnumTypeCall, D as EnableRowLevelSecurityCall, E as DropTableCall, I as installExtension, M as SetNotNullCall, S as DropIndexCall, T as DropPostgresRlsPolicyCall, _ as DisableRowLevelSecurityCall, b as DropConstraintCall, c as AddUniqueCall, d as CreateIndexCall, f as CreateNativeEnumTypeCall, h as CreateTableCall, i as AddNativeEnumValueCall, j as SetDefaultCall, k as RenameIndexCall, l as AlterColumnTypeCall, m as CreateSchemaCall, n as AddColumnCall, p as CreatePostgresRlsPolicyCall, r as AddForeignKeyCall, s as AddPrimaryKeyCall, t as AddCheckConstraintCall, v as DropCheckConstraintCall, w as DropNotNullCall, x as DropDefaultCall, y as DropColumnCall } from "./op-factory-call-CIaPDDUc.mjs";
|
|
4
|
+
import { t as PostgresContractView } from "./postgres-contract-view-BeIEyZfD.mjs";
|
|
5
5
|
import { t as dataTransform } from "./data-transform-BOWpliq8.mjs";
|
|
6
6
|
import { Migration } from "@prisma-next/family-sql/migration";
|
|
7
7
|
import { MigrationContractViews } from "@prisma-next/migration-tools/migration";
|
|
@@ -164,7 +164,10 @@ var PostgresMigration = class extends Migration {
|
|
|
164
164
|
return new DropDefaultCall(options.schema, options.table, options.column).toOp(this.controlAdapterFor("dropDefault"));
|
|
165
165
|
}
|
|
166
166
|
createIndex(options) {
|
|
167
|
-
return new CreateIndexCall(options.schema, options.table, options.index, options.columns, options.extras).toOp(this.controlAdapterFor("createIndex"));
|
|
167
|
+
return new CreateIndexCall(options.schema, options.table, options.index, options.columns !== void 0 ? { columns: options.columns } : { expression: options.expression }, options.extras).toOp(this.controlAdapterFor("createIndex"));
|
|
168
|
+
}
|
|
169
|
+
renameIndex(options) {
|
|
170
|
+
return new RenameIndexCall(options.schema, options.table, options.from, options.to).toOp(this.controlAdapterFor("renameIndex"));
|
|
168
171
|
}
|
|
169
172
|
dropIndex(options) {
|
|
170
173
|
return new DropIndexCall(options.schema, options.table, options.index).toOp(this.controlAdapterFor("dropIndex"));
|
|
@@ -191,4 +194,4 @@ var PostgresMigration = class extends Migration {
|
|
|
191
194
|
//#endregion
|
|
192
195
|
export { PostgresMigration as t };
|
|
193
196
|
|
|
194
|
-
//# sourceMappingURL=postgres-migration
|
|
197
|
+
//# sourceMappingURL=postgres-migration--BzvpA92.mjs.map
|