@prisma-next/target-postgres 0.14.0-dev.60 → 0.14.0-dev.62
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-WkqG_MkU.mjs → authoring-CdIZ0yvD.mjs} +147 -38
- package/dist/authoring-CdIZ0yvD.mjs.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 +2 -2
- package/dist/{control-policy-Co_13Hxa.mjs → control-policy-DKp0HKpY.mjs} +203 -45
- package/dist/control-policy-DKp0HKpY.mjs.map +1 -0
- package/dist/control.mjs +22 -25
- package/dist/control.mjs.map +1 -1
- package/dist/{ddl-B-krEXY1.mjs → ddl-gmxHf68z.mjs} +20 -4
- package/dist/{ddl-B-krEXY1.mjs.map → ddl-gmxHf68z.mjs.map} +1 -1
- package/dist/ddl.d.mts +2 -2
- package/dist/ddl.mjs +2 -2
- package/dist/{descriptor-meta-BOcKPaLE.mjs → descriptor-meta-DEuXJfpR.mjs} +3 -3
- package/dist/{descriptor-meta-BOcKPaLE.mjs.map → descriptor-meta-DEuXJfpR.mjs.map} +1 -1
- package/dist/{diff-database-schema-BOsDnH64.mjs → diff-database-schema-D2sjvMw9.mjs} +55 -19
- package/dist/diff-database-schema-D2sjvMw9.mjs.map +1 -0
- package/dist/diff-database-schema.d.mts +4 -4
- package/dist/diff-database-schema.d.mts.map +1 -1
- package/dist/diff-database-schema.mjs +1 -1
- package/dist/issue-planner.d.mts +1 -1
- 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-BtQyZha6.d.mts → nodes-Bc2N3tMy.d.mts} +27 -3
- package/dist/nodes-Bc2N3tMy.d.mts.map +1 -0
- package/dist/{nodes-B815pj_X.mjs → nodes-DJC5B8Id.mjs} +32 -2
- package/dist/nodes-DJC5B8Id.mjs.map +1 -0
- package/dist/{op-factory-call-DxNNKUeZ.mjs → op-factory-call-C4flTtTG.mjs} +105 -6
- package/dist/op-factory-call-C4flTtTG.mjs.map +1 -0
- package/dist/{op-factory-call-D6_7-7Vc.d.mts → op-factory-call-CSIg5XDu.d.mts} +26 -3
- package/dist/{op-factory-call-D6_7-7Vc.d.mts.map → op-factory-call-CSIg5XDu.d.mts.map} +1 -1
- package/dist/op-factory-call.d.mts +2 -2
- package/dist/op-factory-call.mjs +2 -2
- package/dist/pack.d.mts +146 -3
- package/dist/pack.d.mts.map +1 -1
- package/dist/pack.mjs +1 -1
- package/dist/{planner-CNrc1Odu.mjs → planner-BtEd5LMr.mjs} +32 -64
- package/dist/planner-BtEd5LMr.mjs.map +1 -0
- package/dist/{planner-ddl-builders-B8Nn6nHN.mjs → planner-ddl-builders-BKlmGDJl.mjs} +7 -3
- package/dist/planner-ddl-builders-BKlmGDJl.mjs.map +1 -0
- package/dist/planner-ddl-builders.d.mts.map +1 -1
- package/dist/planner-ddl-builders.mjs +1 -1
- package/dist/{planner-produced-postgres-migration-a-tjfKsA.d.mts → planner-produced-postgres-migration-CXue-6M9.d.mts} +2 -2
- package/dist/{planner-produced-postgres-migration-a-tjfKsA.d.mts.map → planner-produced-postgres-migration-CXue-6M9.d.mts.map} +1 -1
- package/dist/{planner-produced-postgres-migration-NI-oGKcw.mjs → planner-produced-postgres-migration-Jat3hg4u.mjs} +2 -2
- package/dist/{planner-produced-postgres-migration-NI-oGKcw.mjs.map → planner-produced-postgres-migration-Jat3hg4u.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-AXoxB1Jl.mjs → planner-sql-checks-Br1c_QsH.mjs} +3 -3
- package/dist/{planner-sql-checks-AXoxB1Jl.mjs.map → planner-sql-checks-Br1c_QsH.mjs.map} +1 -1
- package/dist/planner-sql-checks.mjs +1 -1
- package/dist/planner.d.mts +3 -3
- package/dist/planner.d.mts.map +1 -1
- package/dist/planner.mjs +2 -2
- package/dist/{postgres-contract-view-Dg-N7KA7.mjs → postgres-contract-view-BAPW0ec1.mjs} +3 -3
- package/dist/{postgres-contract-view-Dg-N7KA7.mjs.map → postgres-contract-view-BAPW0ec1.mjs.map} +1 -1
- package/dist/{postgres-database-schema-node-CfdTY-Qs.d.mts → postgres-database-schema-node-CJv5bfbs.d.mts} +70 -26
- package/dist/postgres-database-schema-node-CJv5bfbs.d.mts.map +1 -0
- package/dist/{postgres-migration-ChyRsWvE.mjs → postgres-migration-ChLMQRd-.mjs} +19 -3
- package/dist/postgres-migration-ChLMQRd-.mjs.map +1 -0
- package/dist/{postgres-migration-Bzzi0Asr.d.mts → postgres-migration-DkmgGNcv.d.mts} +21 -2
- package/dist/postgres-migration-DkmgGNcv.d.mts.map +1 -0
- package/dist/{postgres-role-schema-node-BiSTCTNg.mjs → postgres-role-schema-node-Cm-j0ZGM.mjs} +20 -25
- package/dist/postgres-role-schema-node-Cm-j0ZGM.mjs.map +1 -0
- package/dist/{postgres-schema-DTLA_aRi.mjs → postgres-schema-DWQ0-CPb.mjs} +32 -4
- package/dist/postgres-schema-DWQ0-CPb.mjs.map +1 -0
- package/dist/{postgres-schema-CU4gF6QZ.d.mts → postgres-schema-DsrtYp2v.d.mts} +13 -1
- package/dist/postgres-schema-DsrtYp2v.d.mts.map +1 -0
- package/dist/{postgres-table-schema-node-9FCtCUz2.mjs → postgres-table-schema-node-D6LvInCe.mjs} +71 -8
- package/dist/postgres-table-schema-node-D6LvInCe.mjs.map +1 -0
- package/dist/{resolve-ddl-schema-Ca88Tg6W.mjs → resolve-ddl-schema-CAza65Iy.mjs} +2 -2
- package/dist/{resolve-ddl-schema-Ca88Tg6W.mjs.map → resolve-ddl-schema-CAza65Iy.mjs.map} +1 -1
- package/dist/runtime.mjs +1 -1
- package/dist/schema-ir-annotations.mjs +1 -1
- package/dist/{sql-utils-DcfMz4MQ.mjs → sql-utils-SU4FDvIV.mjs} +12 -3
- package/dist/{sql-utils-DcfMz4MQ.mjs.map → sql-utils-SU4FDvIV.mjs.map} +1 -1
- package/dist/sql-utils.d.mts +8 -1
- package/dist/sql-utils.d.mts.map +1 -1
- package/dist/sql-utils.mjs +2 -2
- package/dist/types.d.mts +3 -3
- package/dist/types.mjs +4 -4
- package/package.json +20 -20
- package/src/contract-free/ddl.ts +27 -0
- package/src/core/authoring.ts +136 -25
- package/src/core/ddl/nodes.ts +44 -0
- package/src/core/migrations/contract-to-postgres-database-schema-node.ts +12 -1
- package/src/core/migrations/control-policy.ts +172 -45
- package/src/core/migrations/diff-database-schema.ts +48 -13
- package/src/core/migrations/issue-planner.ts +77 -4
- package/src/core/migrations/op-factory-call.ts +84 -0
- package/src/core/migrations/operations/native-enum-types.ts +62 -0
- package/src/core/migrations/planner-ddl-builders.ts +12 -2
- package/src/core/migrations/planner.ts +30 -62
- package/src/core/migrations/postgres-migration.ts +31 -0
- package/src/core/migrations/verify-postgres-namespaces.ts +0 -1
- package/src/core/postgres-role.ts +22 -0
- package/src/core/postgres-schema.ts +22 -1
- package/src/core/postgres-validators.ts +1 -0
- package/src/core/psl-infer/infer-psl-contract.ts +7 -16
- package/src/core/schema-ir/node-storage-coordinate.ts +26 -0
- package/src/core/schema-ir/postgres-database-schema-node.ts +5 -4
- package/src/core/schema-ir/postgres-namespace-schema-node.ts +18 -27
- package/src/core/schema-ir/postgres-native-enum-schema-node.ts +83 -0
- package/src/core/schema-ir/postgres-role-schema-node.ts +4 -2
- package/src/core/schema-ir/schema-node-kinds.ts +19 -3
- package/src/core/sql-utils.ts +15 -2
- package/src/exports/ddl.ts +2 -0
- package/src/exports/op-factory-call.ts +2 -0
- package/src/exports/sql-utils.ts +1 -0
- package/src/exports/types.ts +4 -1
- package/dist/authoring-WkqG_MkU.mjs.map +0 -1
- package/dist/control-policy-Co_13Hxa.mjs.map +0 -1
- package/dist/diff-database-schema-BOsDnH64.mjs.map +0 -1
- package/dist/nodes-B815pj_X.mjs.map +0 -1
- package/dist/nodes-BtQyZha6.d.mts.map +0 -1
- package/dist/op-factory-call-DxNNKUeZ.mjs.map +0 -1
- package/dist/planner-CNrc1Odu.mjs.map +0 -1
- package/dist/planner-ddl-builders-B8Nn6nHN.mjs.map +0 -1
- package/dist/postgres-database-schema-node-CfdTY-Qs.d.mts.map +0 -1
- package/dist/postgres-migration-Bzzi0Asr.d.mts.map +0 -1
- package/dist/postgres-migration-ChyRsWvE.mjs.map +0 -1
- package/dist/postgres-role-schema-node-BiSTCTNg.mjs.map +0 -1
- package/dist/postgres-schema-CU4gF6QZ.d.mts.map +0 -1
- package/dist/postgres-schema-DTLA_aRi.mjs.map +0 -1
- package/dist/postgres-table-schema-node-9FCtCUz2.mjs.map +0 -1
|
@@ -63,6 +63,7 @@ import {
|
|
|
63
63
|
} from './operations/constraints';
|
|
64
64
|
import { createExtension } from './operations/dependencies';
|
|
65
65
|
import { createIndex, dropIndex } from './operations/indexes';
|
|
66
|
+
import { createNativeEnumType, dropNativeEnumType } from './operations/native-enum-types';
|
|
66
67
|
import {
|
|
67
68
|
createRlsPolicy,
|
|
68
69
|
disableRowLevelSecurity,
|
|
@@ -1318,6 +1319,87 @@ export class CreateSchemaCall extends PostgresOpFactoryCallNode {
|
|
|
1318
1319
|
}
|
|
1319
1320
|
}
|
|
1320
1321
|
|
|
1322
|
+
// ============================================================================
|
|
1323
|
+
// Native enum types
|
|
1324
|
+
// ============================================================================
|
|
1325
|
+
|
|
1326
|
+
export class CreateNativeEnumTypeCall extends PostgresOpFactoryCallNode {
|
|
1327
|
+
readonly factoryName = 'createNativeEnumType' as const;
|
|
1328
|
+
readonly operationClass = 'additive' as const;
|
|
1329
|
+
readonly schemaName: string;
|
|
1330
|
+
readonly typeName: string;
|
|
1331
|
+
readonly members: readonly string[];
|
|
1332
|
+
readonly label: string;
|
|
1333
|
+
|
|
1334
|
+
constructor(schemaName: string, typeName: string, members: readonly string[]) {
|
|
1335
|
+
super();
|
|
1336
|
+
this.schemaName = schemaName;
|
|
1337
|
+
this.typeName = typeName;
|
|
1338
|
+
this.members = Object.freeze([...members]);
|
|
1339
|
+
this.label = `Create enum type "${typeName}"`;
|
|
1340
|
+
this.freeze();
|
|
1341
|
+
}
|
|
1342
|
+
|
|
1343
|
+
async toOp(lowerer?: ExecuteRequestLowerer): Promise<Op> {
|
|
1344
|
+
if (lowerer === undefined) {
|
|
1345
|
+
throw new Error(
|
|
1346
|
+
`CreateNativeEnumTypeCall.toOp: a DDL lowerer is required on the Postgres planner path (type "${this.typeName}"). Pass the control adapter to createPostgresMigrationPlanner.`,
|
|
1347
|
+
);
|
|
1348
|
+
}
|
|
1349
|
+
return createNativeEnumType(this.schemaName, this.typeName, this.members, lowerer);
|
|
1350
|
+
}
|
|
1351
|
+
|
|
1352
|
+
renderTypeScript(): string {
|
|
1353
|
+
const opts = [
|
|
1354
|
+
`schema: ${jsonToTsSource(this.schemaName)}`,
|
|
1355
|
+
`typeName: ${jsonToTsSource(this.typeName)}`,
|
|
1356
|
+
`members: ${jsonToTsSource(this.members)}`,
|
|
1357
|
+
];
|
|
1358
|
+
return `this.createNativeEnumType({ ${opts.join(', ')} })`;
|
|
1359
|
+
}
|
|
1360
|
+
|
|
1361
|
+
override importRequirements(): readonly ImportRequirement[] {
|
|
1362
|
+
return [];
|
|
1363
|
+
}
|
|
1364
|
+
}
|
|
1365
|
+
|
|
1366
|
+
export class DropNativeEnumTypeCall extends PostgresOpFactoryCallNode {
|
|
1367
|
+
readonly factoryName = 'dropNativeEnumType' as const;
|
|
1368
|
+
readonly operationClass = 'destructive' as const;
|
|
1369
|
+
readonly schemaName: string;
|
|
1370
|
+
readonly typeName: string;
|
|
1371
|
+
readonly label: string;
|
|
1372
|
+
|
|
1373
|
+
constructor(schemaName: string, typeName: string) {
|
|
1374
|
+
super();
|
|
1375
|
+
this.schemaName = schemaName;
|
|
1376
|
+
this.typeName = typeName;
|
|
1377
|
+
this.label = `Drop enum type "${typeName}"`;
|
|
1378
|
+
this.freeze();
|
|
1379
|
+
}
|
|
1380
|
+
|
|
1381
|
+
async toOp(lowerer?: ExecuteRequestLowerer): Promise<Op> {
|
|
1382
|
+
if (lowerer === undefined) {
|
|
1383
|
+
throw new Error(
|
|
1384
|
+
`DropNativeEnumTypeCall.toOp: a DDL lowerer is required on the Postgres planner path (type "${this.typeName}"). Pass the control adapter to createPostgresMigrationPlanner.`,
|
|
1385
|
+
);
|
|
1386
|
+
}
|
|
1387
|
+
return dropNativeEnumType(this.schemaName, this.typeName, lowerer);
|
|
1388
|
+
}
|
|
1389
|
+
|
|
1390
|
+
renderTypeScript(): string {
|
|
1391
|
+
const opts = [
|
|
1392
|
+
`schema: ${jsonToTsSource(this.schemaName)}`,
|
|
1393
|
+
`typeName: ${jsonToTsSource(this.typeName)}`,
|
|
1394
|
+
];
|
|
1395
|
+
return `this.dropNativeEnumType({ ${opts.join(', ')} })`;
|
|
1396
|
+
}
|
|
1397
|
+
|
|
1398
|
+
override importRequirements(): readonly ImportRequirement[] {
|
|
1399
|
+
return [];
|
|
1400
|
+
}
|
|
1401
|
+
}
|
|
1402
|
+
|
|
1321
1403
|
// ============================================================================
|
|
1322
1404
|
// Data transform
|
|
1323
1405
|
// ============================================================================
|
|
@@ -1562,6 +1644,8 @@ export type PostgresOpFactoryCall =
|
|
|
1562
1644
|
| RawSqlCall
|
|
1563
1645
|
| CreateExtensionCall
|
|
1564
1646
|
| CreateSchemaCall
|
|
1647
|
+
| CreateNativeEnumTypeCall
|
|
1648
|
+
| DropNativeEnumTypeCall
|
|
1565
1649
|
| CreatePostgresRlsPolicyCall
|
|
1566
1650
|
| DropPostgresRlsPolicyCall
|
|
1567
1651
|
| EnableRowLevelSecurityCall
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import type { ExecuteRequestLowerer } from '@prisma-next/family-sql/control-adapter';
|
|
2
|
+
import { ifDefined } from '@prisma-next/utils/defined';
|
|
3
|
+
import { createType, dropType } from '../../../contract-free/ddl';
|
|
4
|
+
import { validateEnumValueLength } from '../../sql-utils';
|
|
5
|
+
import { boundSchema } from '../bound-schema';
|
|
6
|
+
import { type Op, step, targetDetails } from './shared';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* `CREATE TYPE <qualified> AS ENUM (…)` for a managed native enum, built as a
|
|
10
|
+
* typed DDL node and lowered by the control adapter (the type name is quoted,
|
|
11
|
+
* member values render as escaped string literals in declaration order —
|
|
12
|
+
* Postgres enum sort order is semantic). `boundSchema` maps the unbound sentinel
|
|
13
|
+
* to an absent schema so the type name renders unqualified and the connection's
|
|
14
|
+
* `search_path` resolves the schema at runtime.
|
|
15
|
+
*/
|
|
16
|
+
export async function createNativeEnumType(
|
|
17
|
+
schemaName: string,
|
|
18
|
+
typeName: string,
|
|
19
|
+
members: readonly string[],
|
|
20
|
+
lowerer: ExecuteRequestLowerer,
|
|
21
|
+
): Promise<Op> {
|
|
22
|
+
for (const member of members) {
|
|
23
|
+
validateEnumValueLength(member, typeName);
|
|
24
|
+
}
|
|
25
|
+
const ddlNode = createType({
|
|
26
|
+
...ifDefined('schema', boundSchema(schemaName)),
|
|
27
|
+
name: typeName,
|
|
28
|
+
values: members,
|
|
29
|
+
});
|
|
30
|
+
const statement = await lowerer.lowerToExecuteRequest(ddlNode);
|
|
31
|
+
return {
|
|
32
|
+
id: `createNativeEnumType.${typeName}`,
|
|
33
|
+
label: `Create enum type "${typeName}"`,
|
|
34
|
+
operationClass: 'additive',
|
|
35
|
+
target: targetDetails('type', typeName, schemaName),
|
|
36
|
+
precheck: [],
|
|
37
|
+
execute: [step(`create enum type "${typeName}"`, statement.sql, statement.params)],
|
|
38
|
+
postcheck: [],
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/** `DROP TYPE <qualified>` for an unclaimed managed native enum, via typed DDL. */
|
|
43
|
+
export async function dropNativeEnumType(
|
|
44
|
+
schemaName: string,
|
|
45
|
+
typeName: string,
|
|
46
|
+
lowerer: ExecuteRequestLowerer,
|
|
47
|
+
): Promise<Op> {
|
|
48
|
+
const ddlNode = dropType({
|
|
49
|
+
...ifDefined('schema', boundSchema(schemaName)),
|
|
50
|
+
name: typeName,
|
|
51
|
+
});
|
|
52
|
+
const statement = await lowerer.lowerToExecuteRequest(ddlNode);
|
|
53
|
+
return {
|
|
54
|
+
id: `dropNativeEnumType.${typeName}`,
|
|
55
|
+
label: `Drop enum type "${typeName}"`,
|
|
56
|
+
operationClass: 'destructive',
|
|
57
|
+
target: targetDetails('type', typeName, schemaName),
|
|
58
|
+
precheck: [],
|
|
59
|
+
execute: [step(`drop enum type "${typeName}"`, statement.sql, statement.params)],
|
|
60
|
+
postcheck: [],
|
|
61
|
+
};
|
|
62
|
+
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { CodecControlHooks } from '@prisma-next/family-sql/control';
|
|
2
2
|
import type { StorageColumn, StorageTypeInstance } from '@prisma-next/sql-contract/types';
|
|
3
3
|
import { ifDefined } from '@prisma-next/utils/defined';
|
|
4
|
-
import { escapeLiteral, quoteIdentifier } from '../sql-utils';
|
|
4
|
+
import { escapeLiteral, quoteIdentifier, quoteQualifiedName } from '../sql-utils';
|
|
5
5
|
import type { PostgresColumnDefault } from '../types';
|
|
6
6
|
import { resolveColumnTypeMetadata } from './planner-type-resolution';
|
|
7
7
|
|
|
@@ -65,13 +65,23 @@ export function buildColumnTypeSql(
|
|
|
65
65
|
}
|
|
66
66
|
}
|
|
67
67
|
|
|
68
|
+
// A column whose codec supplied a `typeParams.typeName` references a named
|
|
69
|
+
// database type (e.g. a native enum), not a parameterized builtin: render it
|
|
70
|
+
// as its quoted, schema-qualified type-name identifier. DDL-render only — the
|
|
71
|
+
// verify comparison value (`resolvedNativeType`) stays the bare name the
|
|
72
|
+
// family expander produces, matching introspection.
|
|
73
|
+
if (typeof resolved.typeParams?.['typeName'] === 'string') {
|
|
74
|
+
const quoted = quoteQualifiedName(resolved.nativeType);
|
|
75
|
+
return column.many ? `${quoted}[]` : quoted;
|
|
76
|
+
}
|
|
77
|
+
|
|
68
78
|
const expanded = expandParameterizedTypeSql(resolved, codecHooks);
|
|
69
79
|
if (expanded !== null) {
|
|
70
80
|
return column.many ? `${expanded}[]` : expanded;
|
|
71
81
|
}
|
|
72
82
|
|
|
73
83
|
if (column.typeRef) {
|
|
74
|
-
const base =
|
|
84
|
+
const base = quoteQualifiedName(resolved.nativeType);
|
|
75
85
|
return column.many ? `${base}[]` : base;
|
|
76
86
|
}
|
|
77
87
|
|
|
@@ -28,24 +28,19 @@ import { blindCast } from '@prisma-next/utils/casts';
|
|
|
28
28
|
import { ifDefined } from '@prisma-next/utils/defined';
|
|
29
29
|
import { PostgresRlsPolicy } from '../postgres-rls-policy';
|
|
30
30
|
import { parseRlsPolicyWireName } from '../rls/wire-name';
|
|
31
|
+
import { postgresNodeStorageCoordinate } from '../schema-ir/node-storage-coordinate';
|
|
31
32
|
import { PostgresDatabaseSchemaNode } from '../schema-ir/postgres-database-schema-node';
|
|
32
33
|
import { PostgresPolicySchemaNode } from '../schema-ir/postgres-policy-schema-node';
|
|
33
|
-
import {
|
|
34
|
+
import type { SqlSchemaDiffNode } from '../schema-ir/schema-node-kinds';
|
|
34
35
|
import {
|
|
35
|
-
|
|
36
|
+
renderPostgresSuppression,
|
|
36
37
|
resolveNamespaceIdForDdlSchema,
|
|
37
38
|
resolvePostgresCallControlPolicySubject,
|
|
38
39
|
resolvePostgresNodeIssueControlPolicySubject,
|
|
39
40
|
resolvePostgresNodeIssueCreationFactoryName,
|
|
40
41
|
} from './control-policy';
|
|
41
42
|
import { buildPostgresPlanDiff } from './diff-database-schema';
|
|
42
|
-
import {
|
|
43
|
-
coalesceSubtreeIssues,
|
|
44
|
-
issueNode,
|
|
45
|
-
issueSchemaName,
|
|
46
|
-
issueTableName,
|
|
47
|
-
planIssues,
|
|
48
|
-
} from './issue-planner';
|
|
43
|
+
import { coalesceSubtreeIssues, issueNode, issueSchemaName, planIssues } from './issue-planner';
|
|
49
44
|
import type { PostgresOpFactoryCall } from './op-factory-call';
|
|
50
45
|
import {
|
|
51
46
|
CreatePostgresRlsPolicyCall,
|
|
@@ -186,6 +181,11 @@ export class PostgresMigrationPlanner implements MigrationPlanner<'sql', 'postgr
|
|
|
186
181
|
frameworkComponents: options.frameworkComponents,
|
|
187
182
|
});
|
|
188
183
|
const policyDiffIssues = rawIssues.filter((issue) => isPolicyDiffIssue(issue));
|
|
184
|
+
// Role diff issues resolve to the `external` control policy (see
|
|
185
|
+
// `resolvePostgresNodeIssueControlPolicySubject`'s role branch), so the
|
|
186
|
+
// control-policy partition below suppresses them to zero ops on its own,
|
|
187
|
+
// before `mapNodeIssueToCall` ever sees them — no separate exclusion
|
|
188
|
+
// needed here.
|
|
189
189
|
const relationalDiffIssues = rawIssues.filter((issue) => !isPolicyDiffIssue(issue));
|
|
190
190
|
|
|
191
191
|
// The generic differ is total and un-gated: strict-mode extras filtering
|
|
@@ -250,8 +250,6 @@ export class PostgresMigrationPlanner implements MigrationPlanner<'sql', 'postgr
|
|
|
250
250
|
resolveControlPolicySubject: (issue) =>
|
|
251
251
|
resolvePostgresNodeIssueControlPolicySubject(issue, options.contract),
|
|
252
252
|
resolveCreationFactoryName: resolvePostgresNodeIssueCreationFactoryName,
|
|
253
|
-
formatSubjectLabel: (factoryName, subject) =>
|
|
254
|
-
formatPostgresControlPolicySubjectLabel(factoryName, subject, options.contract),
|
|
255
253
|
});
|
|
256
254
|
|
|
257
255
|
const result = planIssues({
|
|
@@ -283,8 +281,6 @@ export class PostgresMigrationPlanner implements MigrationPlanner<'sql', 'postgr
|
|
|
283
281
|
resolveControlPolicySubject: (call) =>
|
|
284
282
|
resolvePostgresCallControlPolicySubject(call, options.contract),
|
|
285
283
|
resolveFactoryName: (call) => call.factoryName,
|
|
286
|
-
formatSubjectLabel: (factoryName, subject) =>
|
|
287
|
-
formatPostgresControlPolicySubjectLabel(factoryName, subject, options.contract),
|
|
288
284
|
});
|
|
289
285
|
|
|
290
286
|
// Inline `onFieldEvent`-emitted ops after structural DDL. The fixed
|
|
@@ -309,15 +305,13 @@ export class PostgresMigrationPlanner implements MigrationPlanner<'sql', 'postgr
|
|
|
309
305
|
resolveControlPolicySubject: (call) =>
|
|
310
306
|
resolvePostgresCallControlPolicySubject(call, options.contract),
|
|
311
307
|
resolveFactoryName: (call) => call.factoryName,
|
|
312
|
-
formatSubjectLabel: (factoryName, subject) =>
|
|
313
|
-
formatPostgresControlPolicySubjectLabel(factoryName, subject, options.contract),
|
|
314
308
|
});
|
|
315
309
|
const calls = [...result.value.calls, ...schemaDiffPartition.kept, ...fieldEventPartition.kept];
|
|
316
310
|
const warnings: SqlPlannerConflict[] = [
|
|
317
|
-
...issuePartition.
|
|
318
|
-
...schemaDiffPartition.
|
|
319
|
-
...fieldEventPartition.
|
|
320
|
-
];
|
|
311
|
+
...issuePartition.suppressions,
|
|
312
|
+
...schemaDiffPartition.suppressions,
|
|
313
|
+
...fieldEventPartition.suppressions,
|
|
314
|
+
].map((record) => renderPostgresSuppression(record, options.contract));
|
|
321
315
|
|
|
322
316
|
return Object.freeze({
|
|
323
317
|
kind: 'success' as const,
|
|
@@ -495,41 +489,14 @@ function isPolicyDiffIssue(issue: SchemaDiffIssue<SqlSchemaDiffNode>): boolean {
|
|
|
495
489
|
}
|
|
496
490
|
|
|
497
491
|
/**
|
|
498
|
-
* Drops a `not-expected` issue when it is a whole extra
|
|
499
|
-
*
|
|
500
|
-
*
|
|
501
|
-
*
|
|
502
|
-
*
|
|
503
|
-
*
|
|
504
|
-
*
|
|
505
|
-
*
|
|
506
|
-
* with a same-named table a sibling space declares in another, or with a
|
|
507
|
-
* same-named non-table entity (an enum, an RLS policy) a sibling declares in
|
|
508
|
-
* the SAME namespace: a positive answer means another space owns THIS table
|
|
509
|
-
* in THIS namespace (a table this space owned would be in its expected tree,
|
|
510
|
-
* never an extra) — leave it. A negative answer means no space owns it — a
|
|
511
|
-
* genuine orphan to drop. `entityKind` is the literal `'table'` here: this
|
|
512
|
-
* function only ever asks about a node already confirmed to be
|
|
513
|
-
* `PostgresSchemaNodeKind.table` (checked just above), and the diff tree's
|
|
514
|
-
* `nodeKind` vocabulary (`'postgres-table'`) is distinct from the storage
|
|
515
|
-
* `entries` vocabulary `elementCoordinates` walks (`'table'`) — the literal
|
|
516
|
-
* is that storage-entries spelling, not the node kind.
|
|
517
|
-
*
|
|
518
|
-
* The issue path carries the *resolved DDL schema* (e.g. `public`), but a
|
|
519
|
-
* contract space's own declared namespace id can be the unbound sentinel
|
|
520
|
-
* (`__unbound__`) that resolves to that schema — the two spellings would
|
|
521
|
-
* never string-match. `resolveNamespaceIdForDdlSchema` recovers the raw
|
|
522
|
-
* namespace id THIS space's own contract would use for that DDL schema, so
|
|
523
|
-
* a table this space's own unbound namespace declares (and any sibling
|
|
524
|
-
* whose own unbound namespace resolves the same way) is compared on the
|
|
525
|
-
* same coordinate the aggregate's `elementCoordinates` walk yields.
|
|
526
|
-
*
|
|
527
|
-
* A DEEPER extra (an extra column/constraint on a table this space DOES own —
|
|
528
|
-
* only the child drifted, so the table is in the expected tree) is this
|
|
529
|
-
* space's own drift and is always kept for dropping; asking the oracle there
|
|
530
|
-
* would wrongly suppress it, because the owned table answers `true`. Shallower
|
|
531
|
-
* issues (namespace/root) are never ownership-scoped here. No oracle ⇒ every
|
|
532
|
-
* extra is kept (single-space plan).
|
|
492
|
+
* Drops a `not-expected` issue when it is a whole extra storage entity (a table
|
|
493
|
+
* or a native enum) that some space in the composition owns. Each such node
|
|
494
|
+
* yields its own storage coordinate (see {@link postgresNodeStorageCoordinate}),
|
|
495
|
+
* so `declaresEntity` answers over the whole composition on one uniform
|
|
496
|
+
* coordinate: a positive answer means a sibling owns this entity here — leave
|
|
497
|
+
* it; a negative answer means a genuine orphan — drop it. A node with no storage
|
|
498
|
+
* coordinate (a namespace, or a deeper column/constraint drift on an owned
|
|
499
|
+
* table) is this space's own and is always kept. No oracle ⇒ keep everything.
|
|
533
500
|
*/
|
|
534
501
|
function retainUnownedExtras(
|
|
535
502
|
issues: readonly SchemaDiffIssue<SqlSchemaDiffNode>[],
|
|
@@ -540,12 +507,13 @@ function retainUnownedExtras(
|
|
|
540
507
|
return issues.filter((issue) => {
|
|
541
508
|
if (issue.reason !== 'not-expected') return true;
|
|
542
509
|
const node = issueNode(issue);
|
|
543
|
-
if (node === undefined
|
|
510
|
+
if (node === undefined) return true;
|
|
511
|
+
const coordinate = postgresNodeStorageCoordinate(node);
|
|
512
|
+
if (coordinate === undefined) return true;
|
|
544
513
|
const ddlSchemaName = issueSchemaName(issue);
|
|
545
|
-
|
|
546
|
-
if (ddlSchemaName === undefined || tableName === undefined) return true;
|
|
514
|
+
if (ddlSchemaName === undefined) return true;
|
|
547
515
|
const namespaceId = resolveNamespaceIdForDdlSchema(contract, ddlSchemaName);
|
|
548
|
-
return !ownership.declaresEntity({ namespaceId,
|
|
516
|
+
return !ownership.declaresEntity({ namespaceId, ...coordinate });
|
|
549
517
|
});
|
|
550
518
|
}
|
|
551
519
|
|
|
@@ -562,9 +530,9 @@ function retainUnownedExtras(
|
|
|
562
530
|
* Returns the real `PostgresNamespaceSchemaNode` reference rather than a
|
|
563
531
|
* projection: `storageTypePlanCallStrategy` hands this same value to codec
|
|
564
532
|
* `planTypeOperations` hooks as `schema`, and hooks read the Postgres-specific
|
|
565
|
-
* `
|
|
566
|
-
*
|
|
567
|
-
*
|
|
533
|
+
* `nativeEnums` field off it (via `PostgresNamespaceSchemaNode.is`) to decide
|
|
534
|
+
* whether a native enum type already exists — a projection that only copies
|
|
535
|
+
* `tables` would silently drop that signal. `StrategyContext.schema`'s
|
|
568
536
|
* declared type (`SqlSchemaIR`, shared with SQLite's flat shape) doesn't
|
|
569
537
|
* capture this, so the return is `blindCast` the same way `namespaceSchemaNodes`
|
|
570
538
|
* in the family's relational walk already treats a namespace node as a
|
|
@@ -580,7 +548,7 @@ function relationalNamespaceNode(
|
|
|
580
548
|
if (namespaceNode === undefined) return undefined;
|
|
581
549
|
return blindCast<
|
|
582
550
|
SqlSchemaIR,
|
|
583
|
-
'PostgresNamespaceSchemaNode carries tables (+
|
|
551
|
+
'PostgresNamespaceSchemaNode carries tables (+ nativeEnums, read by codec hooks via PostgresNamespaceSchemaNode.is) structurally compatible with the SqlSchemaIR shape the strategy layer declares'
|
|
584
552
|
>(namespaceNode);
|
|
585
553
|
}
|
|
586
554
|
|
|
@@ -17,6 +17,7 @@ import {
|
|
|
17
17
|
AlterColumnTypeCall,
|
|
18
18
|
type AlterColumnTypeOptions,
|
|
19
19
|
CreateIndexCall,
|
|
20
|
+
CreateNativeEnumTypeCall,
|
|
20
21
|
CreateSchemaCall,
|
|
21
22
|
CreateTableCall,
|
|
22
23
|
DropCheckConstraintCall,
|
|
@@ -24,6 +25,7 @@ import {
|
|
|
24
25
|
DropConstraintCall,
|
|
25
26
|
DropDefaultCall,
|
|
26
27
|
DropIndexCall,
|
|
28
|
+
DropNativeEnumTypeCall,
|
|
27
29
|
DropNotNullCall,
|
|
28
30
|
DropTableCall,
|
|
29
31
|
SetDefaultCall,
|
|
@@ -171,6 +173,35 @@ export abstract class PostgresMigration<
|
|
|
171
173
|
return new CreateSchemaCall(options.schema).toOp(this.controlAdapterFor('createSchema'));
|
|
172
174
|
}
|
|
173
175
|
|
|
176
|
+
/**
|
|
177
|
+
* Emit a `CREATE TYPE ... AS ENUM (...)` migration operation for a managed
|
|
178
|
+
* native enum. Builds a typed DDL node and lowers it through the stored
|
|
179
|
+
* control adapter (members render in declaration order). Throws if no adapter
|
|
180
|
+
* is present.
|
|
181
|
+
*/
|
|
182
|
+
protected createNativeEnumType(options: {
|
|
183
|
+
readonly schema: string;
|
|
184
|
+
readonly typeName: string;
|
|
185
|
+
readonly members: readonly string[];
|
|
186
|
+
}): Promise<SqlMigrationPlanOperation<PostgresPlanTargetDetails>> {
|
|
187
|
+
return new CreateNativeEnumTypeCall(options.schema, options.typeName, options.members).toOp(
|
|
188
|
+
this.controlAdapterFor('createNativeEnumType'),
|
|
189
|
+
);
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* Emit a `DROP TYPE` migration operation for a managed native enum, lowered
|
|
194
|
+
* through the stored control adapter. Throws if no adapter is present.
|
|
195
|
+
*/
|
|
196
|
+
protected dropNativeEnumType(options: {
|
|
197
|
+
readonly schema: string;
|
|
198
|
+
readonly typeName: string;
|
|
199
|
+
}): Promise<SqlMigrationPlanOperation<PostgresPlanTargetDetails>> {
|
|
200
|
+
return new DropNativeEnumTypeCall(options.schema, options.typeName).toOp(
|
|
201
|
+
this.controlAdapterFor('dropNativeEnumType'),
|
|
202
|
+
);
|
|
203
|
+
}
|
|
204
|
+
|
|
174
205
|
protected addColumn(options: {
|
|
175
206
|
readonly schema: string;
|
|
176
207
|
readonly table: string;
|
|
@@ -1,6 +1,15 @@
|
|
|
1
1
|
import { freezeNode } from '@prisma-next/framework-components/ir';
|
|
2
2
|
import { SqlNode } from '@prisma-next/sql-contract/types';
|
|
3
3
|
|
|
4
|
+
/**
|
|
5
|
+
* Roles are referenced by the contract but never owned — the framework
|
|
6
|
+
* issues no `CREATE`/`DROP ROLE`, so a role's effective governance is always
|
|
7
|
+
* `external`: a missing declared role fails verify, an extra live role is
|
|
8
|
+
* tolerated, and the planner emits zero role DDL. `external` is the only
|
|
9
|
+
* value a role's control can ever carry.
|
|
10
|
+
*/
|
|
11
|
+
export const ROLE_DEFAULT_CONTROL_POLICY = 'external';
|
|
12
|
+
|
|
4
13
|
export interface PostgresRoleInput {
|
|
5
14
|
readonly name: string;
|
|
6
15
|
/**
|
|
@@ -8,6 +17,11 @@ export interface PostgresRoleInput {
|
|
|
8
17
|
* from `@prisma-next/framework-components/ir`.
|
|
9
18
|
*/
|
|
10
19
|
readonly namespaceId: string;
|
|
20
|
+
/**
|
|
21
|
+
* Defaults to {@link ROLE_DEFAULT_CONTROL_POLICY} when omitted. Roles are
|
|
22
|
+
* referenced but never owned, so `'external'` is the only allowed value.
|
|
23
|
+
*/
|
|
24
|
+
readonly control?: 'external';
|
|
11
25
|
}
|
|
12
26
|
|
|
13
27
|
/**
|
|
@@ -22,16 +36,24 @@ export interface PostgresRoleInput {
|
|
|
22
36
|
* Extends `SqlNode` directly, frozen at construction via `freezeNode(this)`.
|
|
23
37
|
* The `kind: 'role'` discriminant is enumerable so it survives JSON.
|
|
24
38
|
* Matches the entries key (one-string rule).
|
|
39
|
+
*
|
|
40
|
+
* `control` always carries a value (never omitted): it defaults to
|
|
41
|
+
* {@link ROLE_DEFAULT_CONTROL_POLICY} in the constructor, so both a
|
|
42
|
+
* freshly-authored role and one hydrated from an older `contract.json`
|
|
43
|
+
* lacking the field resolve the same way — the control-policy resolver
|
|
44
|
+
* reads it directly off the entity instead of special-casing role issues.
|
|
25
45
|
*/
|
|
26
46
|
export class PostgresRole extends SqlNode {
|
|
27
47
|
override readonly kind = 'role' as const;
|
|
28
48
|
readonly name: string;
|
|
29
49
|
readonly namespaceId: string;
|
|
50
|
+
readonly control: 'external';
|
|
30
51
|
|
|
31
52
|
constructor(input: PostgresRoleInput) {
|
|
32
53
|
super();
|
|
33
54
|
this.name = input.name;
|
|
34
55
|
this.namespaceId = input.namespaceId;
|
|
56
|
+
this.control = input.control ?? ROLE_DEFAULT_CONTROL_POLICY;
|
|
35
57
|
freezeNode(this);
|
|
36
58
|
}
|
|
37
59
|
}
|
|
@@ -15,6 +15,7 @@ import {
|
|
|
15
15
|
} from '@prisma-next/sql-contract/types';
|
|
16
16
|
|
|
17
17
|
import { type CfExpr, cfExpr } from '@prisma-next/sql-relational-core/contract-free';
|
|
18
|
+
import { invariant } from '@prisma-next/utils/assertions';
|
|
18
19
|
import { blindCast } from '@prisma-next/utils/casts';
|
|
19
20
|
import { ifDefined } from '@prisma-next/utils/defined';
|
|
20
21
|
import { PostgresTableSource } from './ast/table-source';
|
|
@@ -25,7 +26,7 @@ import {
|
|
|
25
26
|
rlsEnablementEntityKind,
|
|
26
27
|
roleEntityKind,
|
|
27
28
|
} from './entity-kinds';
|
|
28
|
-
import
|
|
29
|
+
import { PostgresNativeEnum } from './postgres-native-enum';
|
|
29
30
|
import type { PostgresRlsEnablement } from './postgres-rls-enablement';
|
|
30
31
|
import type { PostgresRlsPolicy } from './postgres-rls-policy';
|
|
31
32
|
import type { PostgresRole } from './postgres-role';
|
|
@@ -88,6 +89,26 @@ export class PostgresSchema extends SqlNamespaceBase {
|
|
|
88
89
|
'carry',
|
|
89
90
|
);
|
|
90
91
|
|
|
92
|
+
// A native enum keys under its PHYSICAL type name in storage `entries`
|
|
93
|
+
// (ADR 221 coordinate `entityName`). The family/authoring layer keys every
|
|
94
|
+
// block by its author handle, so re-key the `native_enum` slot by each
|
|
95
|
+
// entity's `typeName` here — target-local Postgres code that already knows
|
|
96
|
+
// this entity kind. This runs at both authoring and hydration; a serialized
|
|
97
|
+
// contract's key already equals the `typeName`, so the re-key is idempotent.
|
|
98
|
+
const nativeEnumSlot = dispatched['native_enum'];
|
|
99
|
+
if (nativeEnumSlot !== undefined) {
|
|
100
|
+
const rekeyed: Record<string, unknown> = {};
|
|
101
|
+
for (const [handle, entity] of Object.entries(nativeEnumSlot)) {
|
|
102
|
+
const physicalName = PostgresNativeEnum.is(entity) ? entity.typeName : handle;
|
|
103
|
+
invariant(
|
|
104
|
+
!Object.hasOwn(rekeyed, physicalName),
|
|
105
|
+
`PostgresSchema "${input.id}": two native_enum entities resolve to the same physical type name "${physicalName}". Give them distinct @@map type names.`,
|
|
106
|
+
);
|
|
107
|
+
rekeyed[physicalName] = entity;
|
|
108
|
+
}
|
|
109
|
+
dispatched['native_enum'] = Object.freeze(rekeyed);
|
|
110
|
+
}
|
|
111
|
+
|
|
91
112
|
// Drop an empty valueSet so presence signals non-emptiness.
|
|
92
113
|
const valueSetRaw = dispatched['valueSet'];
|
|
93
114
|
const withPresence =
|
|
@@ -300,10 +300,10 @@ export function inferPostgresPslContract(
|
|
|
300
300
|
const owners = describedContractOwners(describedContracts ?? []);
|
|
301
301
|
const enumOwners = describedNativeEnumOwnersByTypeName(describedContracts ?? []);
|
|
302
302
|
|
|
303
|
-
// Native enum adoption: each namespace's introspected `
|
|
304
|
-
//
|
|
305
|
-
//
|
|
306
|
-
//
|
|
303
|
+
// Native enum adoption: each namespace's introspected `enums` nodes become
|
|
304
|
+
// `native_enum` blocks + `pg.enum(<Name>)` columns, minus the types a
|
|
305
|
+
// described pack contract already declares (matched by TYPE NAME — see
|
|
306
|
+
// describedNativeEnumOwnersByTypeName). Transitional grade gap: an
|
|
307
307
|
// inferred block carries no explicit `control` and inherits the contract's
|
|
308
308
|
// `defaultControl`; under `defaultControl: 'managed'` the planner does not
|
|
309
309
|
// yet enforce the native enum lifecycle (CREATE TYPE / DROP TYPE / ADD
|
|
@@ -313,17 +313,7 @@ export function inferPostgresPslContract(
|
|
|
313
313
|
const packOwnedEnumTypesByNamespace = new Map<string, Map<string, string>>();
|
|
314
314
|
const enumNamespaceNames = new Set<string>();
|
|
315
315
|
for (const namespace of namespaces) {
|
|
316
|
-
const
|
|
317
|
-
for (const typeName of namespace.nativeEnumTypeNames) {
|
|
318
|
-
if (!definitionsForNamespace.has(typeName)) {
|
|
319
|
-
throw new Error(
|
|
320
|
-
`contract infer: introspection reported native enum type "${typeName}" in schema ` +
|
|
321
|
-
`"${namespace.schemaName}" without member values — the introspected tree's ` +
|
|
322
|
-
'`nativeEnumTypeNames` and `nativeEnums` are out of sync (internal invariant).',
|
|
323
|
-
);
|
|
324
|
-
}
|
|
325
|
-
}
|
|
326
|
-
for (const { typeName, values } of namespace.nativeEnums) {
|
|
316
|
+
for (const { typeName, members } of namespace.nativeEnums) {
|
|
327
317
|
const owner = enumOwners.get(
|
|
328
318
|
coordinateKey({
|
|
329
319
|
namespaceId: namespace.schemaName,
|
|
@@ -341,7 +331,7 @@ export function inferPostgresPslContract(
|
|
|
341
331
|
packOwnedEnumTypesByNamespace.set(namespace.schemaName, owned);
|
|
342
332
|
continue;
|
|
343
333
|
}
|
|
344
|
-
enumDefinitions.set(typeName,
|
|
334
|
+
enumDefinitions.set(typeName, members);
|
|
345
335
|
enumNamespaceNames.add(namespace.schemaName);
|
|
346
336
|
}
|
|
347
337
|
}
|
|
@@ -663,6 +653,7 @@ function buildNativeEnumBlock(
|
|
|
663
653
|
|
|
664
654
|
return {
|
|
665
655
|
kind: 'native_enum',
|
|
656
|
+
keyword: 'native_enum',
|
|
666
657
|
name,
|
|
667
658
|
parameters,
|
|
668
659
|
blockAttributes:
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { EntityCoordinate } from '@prisma-next/framework-components/ir';
|
|
2
|
+
import type { SqlSchemaIRNode } from '@prisma-next/sql-schema-ir/types';
|
|
3
|
+
import { PostgresNativeEnumSchemaNode } from './postgres-native-enum-schema-node';
|
|
4
|
+
import { PostgresTableSchemaNode } from './postgres-table-schema-node';
|
|
5
|
+
import { postgresNodeEntityKind } from './schema-node-kinds';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* The storage-`entries` coordinate `(entityKind, entityName)` a Postgres diff
|
|
9
|
+
* node addresses — a table by its name, a native enum by its physical type
|
|
10
|
+
* name. `undefined` for a node that is not a whole storage entity (a namespace,
|
|
11
|
+
* a column, a policy). Lets ownership/subject resolution treat every entity kind
|
|
12
|
+
* uniformly, the node self-describing its storage identity.
|
|
13
|
+
*/
|
|
14
|
+
export function postgresNodeStorageCoordinate(
|
|
15
|
+
node: SqlSchemaIRNode,
|
|
16
|
+
): Pick<EntityCoordinate, 'entityKind' | 'entityName'> | undefined {
|
|
17
|
+
if (PostgresTableSchemaNode.is(node)) {
|
|
18
|
+
const entityKind = postgresNodeEntityKind(node.nodeKind);
|
|
19
|
+
return entityKind === undefined ? undefined : { entityKind, entityName: node.name };
|
|
20
|
+
}
|
|
21
|
+
if (PostgresNativeEnumSchemaNode.is(node)) {
|
|
22
|
+
const entityKind = postgresNodeEntityKind(node.nodeKind);
|
|
23
|
+
return entityKind === undefined ? undefined : { entityKind, entityName: node.typeName };
|
|
24
|
+
}
|
|
25
|
+
return undefined;
|
|
26
|
+
}
|
|
@@ -17,9 +17,10 @@ export interface PostgresDatabaseSchemaNodeInput {
|
|
|
17
17
|
*
|
|
18
18
|
* `id` is the fixed sentinel `'database'` — the root has no siblings and
|
|
19
19
|
* the value is never emitted into migration paths. `isEqualTo` is identity
|
|
20
|
-
* (roots always share the `'database'` id). `children()`
|
|
21
|
-
* nodes
|
|
22
|
-
*
|
|
20
|
+
* (roots always share the `'database'` id). `children()` yields the namespace
|
|
21
|
+
* nodes followed by the role nodes — both are root-level diff subjects. The
|
|
22
|
+
* differ pairs siblings by `(nodeKind, id)`, so a role and a namespace may
|
|
23
|
+
* share a name without colliding.
|
|
23
24
|
*/
|
|
24
25
|
export class PostgresDatabaseSchemaNode extends SqlSchemaIRNode implements DiffableNode {
|
|
25
26
|
override readonly nodeKind = PostgresSchemaNodeKind.database;
|
|
@@ -47,7 +48,7 @@ export class PostgresDatabaseSchemaNode extends SqlSchemaIRNode implements Diffa
|
|
|
47
48
|
}
|
|
48
49
|
|
|
49
50
|
children(): readonly DiffableNode[] {
|
|
50
|
-
return Object.values(this.namespaces);
|
|
51
|
+
return [...Object.values(this.namespaces), ...this.roles];
|
|
51
52
|
}
|
|
52
53
|
|
|
53
54
|
static is(node: SqlSchemaIRNode): node is PostgresDatabaseSchemaNode {
|