@prisma-next/target-postgres 0.14.0-dev.39 → 0.14.0-dev.40

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.
Files changed (106) hide show
  1. package/dist/{authoring-CZmSQriw.mjs → authoring-BX1WHIdN.mjs} +2 -2
  2. package/dist/authoring-BX1WHIdN.mjs.map +1 -0
  3. package/dist/contract-free.mjs +2 -2
  4. package/dist/control.d.mts.map +1 -1
  5. package/dist/control.mjs +633 -16
  6. package/dist/control.mjs.map +1 -1
  7. package/dist/{ddl-CGf6UABz.mjs → ddl-BmLIe1z_.mjs} +2 -2
  8. package/dist/{ddl-CGf6UABz.mjs.map → ddl-BmLIe1z_.mjs.map} +1 -1
  9. package/dist/{descriptor-meta-Du6olWQI.mjs → descriptor-meta-tjatyhtZ.mjs} +3 -3
  10. package/dist/{descriptor-meta-Du6olWQI.mjs.map → descriptor-meta-tjatyhtZ.mjs.map} +1 -1
  11. package/dist/{issue-planner-C-CwZd8D.mjs → issue-planner-DbFvaAgu.mjs} +5 -5
  12. package/dist/issue-planner-DbFvaAgu.mjs.map +1 -0
  13. package/dist/issue-planner.d.mts +1 -1
  14. package/dist/issue-planner.mjs +1 -1
  15. package/dist/migration.d.mts +2 -2
  16. package/dist/migration.mjs +2 -2
  17. package/dist/{op-factory-call-DPJR7tJc.d.mts → op-factory-call-CJWUxyUW.d.mts} +2 -2
  18. package/dist/{op-factory-call-DPJR7tJc.d.mts.map → op-factory-call-CJWUxyUW.d.mts.map} +1 -1
  19. package/dist/{op-factory-call-DArx_1HM.mjs → op-factory-call-t65c4jyY.mjs} +3 -3
  20. package/dist/op-factory-call-t65c4jyY.mjs.map +1 -0
  21. package/dist/op-factory-call.d.mts +1 -1
  22. package/dist/op-factory-call.mjs +1 -1
  23. package/dist/pack.d.mts +2 -2
  24. package/dist/pack.mjs +1 -1
  25. package/dist/{planner-BQ7lSqbm.mjs → planner-CG2pLHk-.mjs} +310 -126
  26. package/dist/planner-CG2pLHk-.mjs.map +1 -0
  27. package/dist/{planner-produced-postgres-migration-3ppG0t3i.mjs → planner-produced-postgres-migration-BolD0Mvc.mjs} +2 -2
  28. package/dist/{planner-produced-postgres-migration-3ppG0t3i.mjs.map → planner-produced-postgres-migration-BolD0Mvc.mjs.map} +1 -1
  29. package/dist/{planner-produced-postgres-migration-BauTSigX.d.mts → planner-produced-postgres-migration-Cz9--XOT.d.mts} +2 -2
  30. package/dist/{planner-produced-postgres-migration-BauTSigX.d.mts.map → planner-produced-postgres-migration-Cz9--XOT.d.mts.map} +1 -1
  31. package/dist/planner-produced-postgres-migration.d.mts +1 -1
  32. package/dist/planner-produced-postgres-migration.mjs +1 -1
  33. package/dist/{planner-sql-checks-CyjaoKhm.mjs → planner-sql-checks-DE9c4519.mjs} +2 -2
  34. package/dist/{planner-sql-checks-CyjaoKhm.mjs.map → planner-sql-checks-DE9c4519.mjs.map} +1 -1
  35. package/dist/planner-sql-checks.mjs +1 -1
  36. package/dist/planner.d.mts +73 -22
  37. package/dist/planner.d.mts.map +1 -1
  38. package/dist/planner.mjs +2 -2
  39. package/dist/{postgres-contract-view-CoRZewAp.mjs → postgres-contract-view-BegduLr6.mjs} +3 -3
  40. package/dist/{postgres-contract-view-CoRZewAp.mjs.map → postgres-contract-view-BegduLr6.mjs.map} +1 -1
  41. package/dist/postgres-database-schema-node-os8ogEap.d.mts +183 -0
  42. package/dist/postgres-database-schema-node-os8ogEap.d.mts.map +1 -0
  43. package/dist/{postgres-migration-BVjq0iFW.mjs → postgres-migration-BfMmK6tv.mjs} +3 -3
  44. package/dist/{postgres-migration-BVjq0iFW.mjs.map → postgres-migration-BfMmK6tv.mjs.map} +1 -1
  45. package/dist/{postgres-migration-UtGCjw5j.d.mts → postgres-migration-D7kNVWVG.d.mts} +2 -2
  46. package/dist/{postgres-migration-UtGCjw5j.d.mts.map → postgres-migration-D7kNVWVG.d.mts.map} +1 -1
  47. package/dist/{postgres-rls-policy-12wAEQhs.d.mts → postgres-rls-policy--CSddvMR.d.mts} +8 -16
  48. package/dist/postgres-rls-policy--CSddvMR.d.mts.map +1 -0
  49. package/dist/{postgres-schema-DLcc3W2-.mjs → postgres-schema-BLIHBEfX.mjs} +34 -64
  50. package/dist/postgres-schema-BLIHBEfX.mjs.map +1 -0
  51. package/dist/{postgres-schema-DJUEkWg-.d.mts → postgres-schema-BywoeKlt.d.mts} +9 -10
  52. package/dist/postgres-schema-BywoeKlt.d.mts.map +1 -0
  53. package/dist/postgres-table-schema-node-1nNc8XDT.mjs +248 -0
  54. package/dist/postgres-table-schema-node-1nNc8XDT.mjs.map +1 -0
  55. package/dist/resolve-ddl-schema-CL1RR1mx.mjs +20 -0
  56. package/dist/resolve-ddl-schema-CL1RR1mx.mjs.map +1 -0
  57. package/dist/runtime.mjs +1 -1
  58. package/dist/schema-ir-annotations.d.mts +9 -3
  59. package/dist/schema-ir-annotations.d.mts.map +1 -1
  60. package/dist/schema-ir-annotations.mjs +1 -1
  61. package/dist/types.d.mts +4 -4
  62. package/dist/types.mjs +3 -3
  63. package/package.json +20 -19
  64. package/src/core/authoring.ts +2 -2
  65. package/src/core/entity-kinds.ts +2 -2
  66. package/src/core/migrations/contract-to-postgres-database-schema-node.ts +125 -0
  67. package/src/core/migrations/diff-database-schema.ts +191 -0
  68. package/src/core/migrations/issue-planner.ts +1 -1
  69. package/src/core/migrations/op-factory-call.ts +1 -1
  70. package/src/core/migrations/operations/rls.ts +1 -1
  71. package/src/core/migrations/planner.ts +117 -68
  72. package/src/core/{schema-ir/postgres-schema-ir-annotations.ts → migrations/resolve-ddl-schema.ts} +8 -6
  73. package/src/core/migrations/runner.ts +13 -16
  74. package/src/core/migrations/verify-postgres-namespaces.ts +22 -14
  75. package/src/core/{schema-ir/postgres-rls-policy.ts → postgres-rls-policy.ts} +6 -43
  76. package/src/core/{schema-ir/postgres-role.ts → postgres-role.ts} +6 -21
  77. package/src/core/postgres-schema.ts +2 -2
  78. package/src/core/psl-infer/infer-psl-contract.ts +834 -0
  79. package/src/core/psl-infer/postgres-default-mapping.ts +16 -0
  80. package/src/core/psl-infer/postgres-type-map.ts +157 -0
  81. package/src/core/schema-ir/postgres-database-schema-node.ts +63 -0
  82. package/src/core/schema-ir/postgres-namespace-schema-node.ts +61 -0
  83. package/src/core/schema-ir/postgres-policy-schema-node.ts +93 -0
  84. package/src/core/schema-ir/postgres-role-schema-node.ts +66 -0
  85. package/src/core/schema-ir/{postgres-table-ir.ts → postgres-table-schema-node.ts} +29 -18
  86. package/src/core/schema-ir/schema-node-kinds.ts +28 -0
  87. package/src/exports/control.ts +30 -2
  88. package/src/exports/planner.ts +2 -5
  89. package/src/exports/schema-ir-annotations.ts +1 -1
  90. package/src/exports/types.ts +23 -15
  91. package/dist/authoring-CZmSQriw.mjs.map +0 -1
  92. package/dist/issue-planner-C-CwZd8D.mjs.map +0 -1
  93. package/dist/op-factory-call-DArx_1HM.mjs.map +0 -1
  94. package/dist/planner-BQ7lSqbm.mjs.map +0 -1
  95. package/dist/postgres-rls-policy-12wAEQhs.d.mts.map +0 -1
  96. package/dist/postgres-schema-DJUEkWg-.d.mts.map +0 -1
  97. package/dist/postgres-schema-DLcc3W2-.mjs.map +0 -1
  98. package/dist/postgres-schema-ir-BJl_a3J7.d.mts +0 -102
  99. package/dist/postgres-schema-ir-BJl_a3J7.d.mts.map +0 -1
  100. package/dist/postgres-schema-ir-I_FiUndw.mjs +0 -133
  101. package/dist/postgres-schema-ir-I_FiUndw.mjs.map +0 -1
  102. package/dist/postgres-schema-ir-annotations-BbDSiwx2.mjs +0 -14
  103. package/dist/postgres-schema-ir-annotations-BbDSiwx2.mjs.map +0 -1
  104. package/src/core/migrations/contract-to-postgres-schema-ir.ts +0 -91
  105. package/src/core/migrations/diff-postgres-schema.ts +0 -55
  106. package/src/core/schema-ir/postgres-schema-ir.ts +0 -138
@@ -0,0 +1,16 @@
1
+ import type { DefaultMappingOptions } from '@prisma-next/family-sql/psl-infer';
2
+
3
+ const POSTGRES_FUNCTION_ATTRIBUTES: Readonly<Record<string, string>> = {
4
+ 'gen_random_uuid()': '@default(dbgenerated("gen_random_uuid()"))',
5
+ };
6
+
7
+ function formatDbGeneratedAttribute(expression: string): string {
8
+ return `@default(dbgenerated(${JSON.stringify(expression)}))`;
9
+ }
10
+
11
+ export function createPostgresDefaultMapping(): DefaultMappingOptions {
12
+ return {
13
+ functionAttributes: POSTGRES_FUNCTION_ATTRIBUTES,
14
+ fallbackFunctionAttribute: formatDbGeneratedAttribute,
15
+ };
16
+ }
@@ -0,0 +1,157 @@
1
+ import type {
2
+ EnumInfo,
3
+ PslNativeTypeAttribute,
4
+ PslTypeMap,
5
+ PslTypeResolution,
6
+ } from '@prisma-next/family-sql/psl-infer';
7
+
8
+ const POSTGRES_TO_PSL: Record<string, string> = {
9
+ text: 'String',
10
+ bool: 'Boolean',
11
+ boolean: 'Boolean',
12
+ int4: 'Int',
13
+ integer: 'Int',
14
+ int8: 'BigInt',
15
+ bigint: 'BigInt',
16
+ float8: 'Float',
17
+ 'double precision': 'Float',
18
+ numeric: 'Decimal',
19
+ decimal: 'Decimal',
20
+ timestamptz: 'DateTime',
21
+ 'timestamp with time zone': 'DateTime',
22
+ jsonb: 'Json',
23
+ bytea: 'Bytes',
24
+ };
25
+
26
+ const PRESERVED_NATIVE_TYPES: Record<
27
+ string,
28
+ { readonly pslType: string; readonly attributeName: string }
29
+ > = {
30
+ 'character varying': { pslType: 'String', attributeName: 'db.VarChar' },
31
+ character: { pslType: 'String', attributeName: 'db.Char' },
32
+ char: { pslType: 'String', attributeName: 'db.Char' },
33
+ varchar: { pslType: 'String', attributeName: 'db.VarChar' },
34
+ uuid: { pslType: 'String', attributeName: 'db.Uuid' },
35
+ int2: { pslType: 'Int', attributeName: 'db.SmallInt' },
36
+ smallint: { pslType: 'Int', attributeName: 'db.SmallInt' },
37
+ float4: { pslType: 'Float', attributeName: 'db.Real' },
38
+ real: { pslType: 'Float', attributeName: 'db.Real' },
39
+ timestamp: { pslType: 'DateTime', attributeName: 'db.Timestamp' },
40
+ 'timestamp without time zone': { pslType: 'DateTime', attributeName: 'db.Timestamp' },
41
+ date: { pslType: 'DateTime', attributeName: 'db.Date' },
42
+ time: { pslType: 'DateTime', attributeName: 'db.Time' },
43
+ 'time without time zone': { pslType: 'DateTime', attributeName: 'db.Time' },
44
+ timetz: { pslType: 'DateTime', attributeName: 'db.Timetz' },
45
+ 'time with time zone': { pslType: 'DateTime', attributeName: 'db.Timetz' },
46
+ json: { pslType: 'Json', attributeName: 'db.Json' },
47
+ };
48
+
49
+ const PARAMETERIZED_NATIVE_TYPES: Record<
50
+ string,
51
+ { readonly pslType: string; readonly attributeName: string }
52
+ > = {
53
+ 'character varying': { pslType: 'String', attributeName: 'db.VarChar' },
54
+ character: { pslType: 'String', attributeName: 'db.Char' },
55
+ char: { pslType: 'String', attributeName: 'db.Char' },
56
+ varchar: { pslType: 'String', attributeName: 'db.VarChar' },
57
+ numeric: { pslType: 'Decimal', attributeName: 'db.Numeric' },
58
+ timestamp: { pslType: 'DateTime', attributeName: 'db.Timestamp' },
59
+ timestamptz: { pslType: 'DateTime', attributeName: 'db.Timestamptz' },
60
+ time: { pslType: 'DateTime', attributeName: 'db.Time' },
61
+ timetz: { pslType: 'DateTime', attributeName: 'db.Timetz' },
62
+ };
63
+
64
+ const PARAMETERIZED_TYPE_PATTERN = /^(.+?)\((.+)\)$/;
65
+
66
+ function getOwnMappingValue(map: Record<string, string>, key: string): string | undefined {
67
+ return Object.hasOwn(map, key) ? map[key] : undefined;
68
+ }
69
+
70
+ function getOwnRecordValue<T>(map: Record<string, T>, key: string): T | undefined {
71
+ return Object.hasOwn(map, key) ? map[key] : undefined;
72
+ }
73
+
74
+ function createNativeTypeAttribute(name: string, args?: readonly string[]): PslNativeTypeAttribute {
75
+ return args && args.length > 0 ? { name, args } : { name };
76
+ }
77
+
78
+ function splitTypeParameterList(params: string): readonly string[] {
79
+ return params
80
+ .split(',')
81
+ .map((part) => part.trim())
82
+ .filter((part) => part.length > 0);
83
+ }
84
+
85
+ export function createPostgresTypeMap(enumTypeNames?: ReadonlySet<string>): PslTypeMap {
86
+ return {
87
+ resolve(nativeType: string): PslTypeResolution {
88
+ if (enumTypeNames?.has(nativeType)) {
89
+ return { pslType: nativeType, nativeType };
90
+ }
91
+
92
+ const paramMatch = nativeType.match(PARAMETERIZED_TYPE_PATTERN);
93
+ if (paramMatch) {
94
+ const [, baseType = nativeType, params = ''] = paramMatch;
95
+ const template = getOwnRecordValue(PARAMETERIZED_NATIVE_TYPES, baseType);
96
+ if (template) {
97
+ return {
98
+ pslType: template.pslType,
99
+ nativeType,
100
+ typeParams: { baseType, params },
101
+ nativeTypeAttribute: createNativeTypeAttribute(
102
+ template.attributeName,
103
+ splitTypeParameterList(params),
104
+ ),
105
+ };
106
+ }
107
+ }
108
+
109
+ const preservedType = getOwnRecordValue(PRESERVED_NATIVE_TYPES, nativeType);
110
+ if (preservedType) {
111
+ return {
112
+ pslType: preservedType.pslType,
113
+ nativeType,
114
+ nativeTypeAttribute: createNativeTypeAttribute(preservedType.attributeName),
115
+ };
116
+ }
117
+
118
+ const pslType = getOwnMappingValue(POSTGRES_TO_PSL, nativeType);
119
+ if (pslType) {
120
+ return {
121
+ pslType,
122
+ nativeType,
123
+ };
124
+ }
125
+
126
+ return { unsupported: true, nativeType };
127
+ },
128
+ };
129
+ }
130
+
131
+ export function extractEnumInfo(annotations?: Record<string, unknown>): EnumInfo {
132
+ const pgAnnotations = annotations?.['pg'] as Record<string, unknown> | undefined;
133
+ const nativeEnumTypeNames = pgAnnotations?.['nativeEnumTypeNames'];
134
+
135
+ const typeNames = new Set<string>();
136
+ const definitions = new Map<string, readonly string[]>();
137
+
138
+ if (Array.isArray(nativeEnumTypeNames)) {
139
+ for (const name of nativeEnumTypeNames) {
140
+ if (typeof name === 'string') {
141
+ typeNames.add(name);
142
+ }
143
+ }
144
+ }
145
+
146
+ return { typeNames, definitions };
147
+ }
148
+
149
+ export function extractEnumTypeNames(annotations?: Record<string, unknown>): ReadonlySet<string> {
150
+ return extractEnumInfo(annotations).typeNames;
151
+ }
152
+
153
+ export function extractEnumDefinitions(
154
+ annotations?: Record<string, unknown>,
155
+ ): ReadonlyMap<string, readonly string[]> {
156
+ return extractEnumInfo(annotations).definitions;
157
+ }
@@ -0,0 +1,63 @@
1
+ import type { DiffableNode } from '@prisma-next/framework-components/control';
2
+ import { freezeNode } from '@prisma-next/framework-components/ir';
3
+ import { SqlSchemaIRNode } from '@prisma-next/sql-schema-ir/types';
4
+ import type { PostgresNamespaceSchemaNode } from './postgres-namespace-schema-node';
5
+ import type { PostgresRoleSchemaNode } from './postgres-role-schema-node';
6
+ import { PostgresSchemaNodeKind } from './schema-node-kinds';
7
+
8
+ export interface PostgresDatabaseSchemaNodeInput {
9
+ readonly namespaces: Readonly<Record<string, PostgresNamespaceSchemaNode>>;
10
+ readonly roles: readonly PostgresRoleSchemaNode[];
11
+ readonly existingSchemas: readonly string[];
12
+ readonly pgVersion: string;
13
+ }
14
+
15
+ /**
16
+ * The root of the Postgres schema-diff tree: one node per database.
17
+ *
18
+ * `id` is the fixed sentinel `'database'` — the root has no siblings and
19
+ * the value is never emitted into migration paths. `isEqualTo` is identity
20
+ * (roots always share the `'database'` id). `children()` returns namespace
21
+ * nodes only; roles are held on the root but not yielded (role diffing is a
22
+ * later slice).
23
+ */
24
+ export class PostgresDatabaseSchemaNode extends SqlSchemaIRNode implements DiffableNode {
25
+ override readonly nodeKind = PostgresSchemaNodeKind.database;
26
+ readonly namespaces: Readonly<Record<string, PostgresNamespaceSchemaNode>>;
27
+ readonly roles: readonly PostgresRoleSchemaNode[];
28
+ readonly existingSchemas: readonly string[];
29
+ readonly pgVersion: string;
30
+
31
+ constructor(input: PostgresDatabaseSchemaNodeInput) {
32
+ super();
33
+ this.namespaces = Object.freeze({ ...input.namespaces });
34
+ this.roles = Object.freeze([...input.roles]);
35
+ this.existingSchemas = Object.freeze([...input.existingSchemas]);
36
+ this.pgVersion = input.pgVersion;
37
+ freezeNode(this);
38
+ }
39
+
40
+ get id(): string {
41
+ return 'database';
42
+ }
43
+
44
+ isEqualTo(other: DiffableNode): boolean {
45
+ return this.id === other.id;
46
+ }
47
+
48
+ children(): readonly DiffableNode[] {
49
+ return Object.values(this.namespaces);
50
+ }
51
+
52
+ static is(node: SqlSchemaIRNode): node is PostgresDatabaseSchemaNode {
53
+ return node.nodeKind === PostgresSchemaNodeKind.database;
54
+ }
55
+
56
+ static assert(node: SqlSchemaIRNode): asserts node is PostgresDatabaseSchemaNode {
57
+ if (!PostgresDatabaseSchemaNode.is(node)) {
58
+ throw new Error(
59
+ `Expected a PostgresDatabaseSchemaNode but got nodeKind=${node.nodeKind ?? 'undefined'}`,
60
+ );
61
+ }
62
+ }
63
+ }
@@ -0,0 +1,61 @@
1
+ import type { DiffableNode } from '@prisma-next/framework-components/control';
2
+ import { freezeNode } from '@prisma-next/framework-components/ir';
3
+ import { SqlSchemaIRNode } from '@prisma-next/sql-schema-ir/types';
4
+ import type { PostgresTableSchemaNode } from './postgres-table-schema-node';
5
+ import { PostgresSchemaNodeKind } from './schema-node-kinds';
6
+
7
+ export interface PostgresNamespaceSchemaNodeInput {
8
+ readonly schemaName: string;
9
+ readonly tables: Readonly<Record<string, PostgresTableSchemaNode>>;
10
+ readonly nativeEnumTypeNames: readonly string[];
11
+ }
12
+
13
+ /**
14
+ * One-per-Postgres-schema diff-tree node. Groups the tables belonging to a
15
+ * single namespace and satisfies the `SqlSchemaIR` shape so legacy per-schema
16
+ * consumers (verifySqlSchema, the relational planner, toSchemaView) can
17
+ * accept it unchanged in Unit 6.
18
+ *
19
+ * `id` is the schema name. `isEqualTo` is identity — two namespace nodes are
20
+ * equal iff their ids (schema names) match. `children()` returns the table
21
+ * nodes. Per-schema metadata is carried on the typed `nativeEnumTypeNames`
22
+ * field, not an annotations bag.
23
+ */
24
+ export class PostgresNamespaceSchemaNode extends SqlSchemaIRNode implements DiffableNode {
25
+ override readonly nodeKind = PostgresSchemaNodeKind.namespace;
26
+ readonly schemaName: string;
27
+ readonly tables: Readonly<Record<string, PostgresTableSchemaNode>>;
28
+ readonly nativeEnumTypeNames: readonly string[];
29
+
30
+ constructor(input: PostgresNamespaceSchemaNodeInput) {
31
+ super();
32
+ this.schemaName = input.schemaName;
33
+ this.tables = Object.freeze({ ...input.tables });
34
+ this.nativeEnumTypeNames = Object.freeze([...input.nativeEnumTypeNames]);
35
+ freezeNode(this);
36
+ }
37
+
38
+ get id(): string {
39
+ return this.schemaName;
40
+ }
41
+
42
+ isEqualTo(other: DiffableNode): boolean {
43
+ return this.id === other.id;
44
+ }
45
+
46
+ children(): readonly DiffableNode[] {
47
+ return Object.values(this.tables);
48
+ }
49
+
50
+ static is(node: SqlSchemaIRNode): node is PostgresNamespaceSchemaNode {
51
+ return node.nodeKind === PostgresSchemaNodeKind.namespace;
52
+ }
53
+
54
+ static assert(node: SqlSchemaIRNode): asserts node is PostgresNamespaceSchemaNode {
55
+ if (!PostgresNamespaceSchemaNode.is(node)) {
56
+ throw new Error(
57
+ `Expected a PostgresNamespaceSchemaNode but got nodeKind=${node.nodeKind ?? 'undefined'}`,
58
+ );
59
+ }
60
+ }
61
+ }
@@ -0,0 +1,93 @@
1
+ import type { DiffableNode } from '@prisma-next/framework-components/control';
2
+ import { freezeNode } from '@prisma-next/framework-components/ir';
3
+ import { SqlSchemaIRNode } from '@prisma-next/sql-schema-ir/types';
4
+ import { blindCast } from '@prisma-next/utils/casts';
5
+ import type { RlsPolicyOperation } from '../postgres-rls-policy';
6
+ import { PostgresSchemaNodeKind } from './schema-node-kinds';
7
+
8
+ export interface PostgresPolicySchemaNodeInput {
9
+ /** Full wire name: `<prefix>_<8hex>`. */
10
+ readonly name: string;
11
+ /** User-supplied prefix (the part before the `_<8hex>` suffix). */
12
+ readonly prefix: string;
13
+ /** Name of the table this policy attaches to, by name within the same schema. */
14
+ readonly tableName: string;
15
+ /** Namespace coordinate (schema name). */
16
+ readonly namespaceId: string;
17
+ readonly operation: RlsPolicyOperation;
18
+ /** Sorted role names rendered in `TO <roles>`. */
19
+ readonly roles: readonly string[];
20
+ /** USING predicate SQL string, if present. */
21
+ readonly using?: string;
22
+ /** WITH CHECK predicate SQL string, if present. */
23
+ readonly withCheck?: string;
24
+ /** `true` = `AS PERMISSIVE`, `false` = `AS RESTRICTIVE`. */
25
+ readonly permissive: boolean;
26
+ }
27
+
28
+ /**
29
+ * Schema-diff leaf node for a Postgres row-level security policy.
30
+ *
31
+ * This is a derived, transient node walked by the differ — it is NEVER serialized.
32
+ * Built by project-from-contract and project-from-database from their respective
33
+ * `PostgresRlsPolicy` contract entities / introspected rows.
34
+ *
35
+ * `id` is the wire name (`<prefix>_<sha256(body)[0..8]>`), so name-equality is
36
+ * body-equality. `isEqualTo` compares names only — never byte-compare predicate
37
+ * bodies, because Postgres reprints them.
38
+ */
39
+ export class PostgresPolicySchemaNode extends SqlSchemaIRNode implements DiffableNode {
40
+ override readonly nodeKind = PostgresSchemaNodeKind.policy;
41
+ readonly name: string;
42
+ readonly prefix: string;
43
+ readonly tableName: string;
44
+ readonly namespaceId: string;
45
+ readonly operation: RlsPolicyOperation;
46
+ readonly roles: readonly string[];
47
+ declare readonly using?: string;
48
+ declare readonly withCheck?: string;
49
+ readonly permissive: boolean;
50
+
51
+ constructor(input: PostgresPolicySchemaNodeInput) {
52
+ super();
53
+ this.name = input.name;
54
+ this.prefix = input.prefix;
55
+ this.tableName = input.tableName;
56
+ this.namespaceId = input.namespaceId;
57
+ this.operation = input.operation;
58
+ this.roles = Object.freeze([...input.roles]);
59
+ if (input.using !== undefined) this.using = input.using;
60
+ if (input.withCheck !== undefined) this.withCheck = input.withCheck;
61
+ this.permissive = input.permissive;
62
+ freezeNode(this);
63
+ }
64
+
65
+ get id(): string {
66
+ return this.name;
67
+ }
68
+
69
+ children(): readonly DiffableNode[] {
70
+ return [];
71
+ }
72
+
73
+ isEqualTo(other: DiffableNode): boolean {
74
+ const node = blindCast<
75
+ SqlSchemaIRNode,
76
+ 'every diff-tree node the differ pairs is a SqlSchemaIRNode; the guard rejects non-policy kinds'
77
+ >(other);
78
+ PostgresPolicySchemaNode.assert(node);
79
+ return this.id === node.id;
80
+ }
81
+
82
+ static is(node: SqlSchemaIRNode): node is PostgresPolicySchemaNode {
83
+ return node.nodeKind === PostgresSchemaNodeKind.policy;
84
+ }
85
+
86
+ static assert(node: SqlSchemaIRNode | undefined): asserts node is PostgresPolicySchemaNode {
87
+ if (node === undefined || !PostgresPolicySchemaNode.is(node)) {
88
+ throw new Error(
89
+ `Expected a PostgresPolicySchemaNode but got nodeKind=${node?.nodeKind ?? 'undefined'}`,
90
+ );
91
+ }
92
+ }
93
+ }
@@ -0,0 +1,66 @@
1
+ import type { DiffableNode } from '@prisma-next/framework-components/control';
2
+ import { freezeNode } from '@prisma-next/framework-components/ir';
3
+ import { SqlSchemaIRNode } from '@prisma-next/sql-schema-ir/types';
4
+ import { blindCast } from '@prisma-next/utils/casts';
5
+ import { PostgresSchemaNodeKind } from './schema-node-kinds';
6
+
7
+ export interface PostgresRoleSchemaNodeInput {
8
+ readonly name: string;
9
+ /**
10
+ * Namespace coordinate. Roles are cluster-scoped; callers pass
11
+ * `UNBOUND_NAMESPACE_ID` from `@prisma-next/framework-components/ir`.
12
+ */
13
+ readonly namespaceId: string;
14
+ }
15
+
16
+ /**
17
+ * Schema-diff leaf node for a Postgres database role.
18
+ *
19
+ * This is a derived, transient node walked by the differ — it is NEVER serialized.
20
+ * Built by project-from-contract and project-from-database from their respective
21
+ * `PostgresRole` contract entities / introspected rows.
22
+ *
23
+ * Roles are cluster-scoped, so `id` is the role name alone. `isEqualTo` compares
24
+ * names — name-equality is role-equality for cluster-scoped objects.
25
+ */
26
+ export class PostgresRoleSchemaNode extends SqlSchemaIRNode implements DiffableNode {
27
+ override readonly nodeKind = PostgresSchemaNodeKind.role;
28
+ readonly name: string;
29
+ readonly namespaceId: string;
30
+
31
+ constructor(input: PostgresRoleSchemaNodeInput) {
32
+ super();
33
+ this.name = input.name;
34
+ this.namespaceId = input.namespaceId;
35
+ freezeNode(this);
36
+ }
37
+
38
+ get id(): string {
39
+ return this.name;
40
+ }
41
+
42
+ children(): readonly DiffableNode[] {
43
+ return [];
44
+ }
45
+
46
+ isEqualTo(other: DiffableNode): boolean {
47
+ const node = blindCast<
48
+ SqlSchemaIRNode,
49
+ 'every diff-tree node the differ pairs is a SqlSchemaIRNode; the guard rejects non-role kinds'
50
+ >(other);
51
+ PostgresRoleSchemaNode.assert(node);
52
+ return this.id === node.id;
53
+ }
54
+
55
+ static is(node: SqlSchemaIRNode): node is PostgresRoleSchemaNode {
56
+ return node.nodeKind === PostgresSchemaNodeKind.role;
57
+ }
58
+
59
+ static assert(node: SqlSchemaIRNode): asserts node is PostgresRoleSchemaNode {
60
+ if (!PostgresRoleSchemaNode.is(node)) {
61
+ throw new Error(
62
+ `Expected a PostgresRoleSchemaNode but got nodeKind=${node.nodeKind ?? 'undefined'}`,
63
+ );
64
+ }
65
+ }
66
+ }
@@ -11,25 +11,28 @@ import {
11
11
  type SqlTableIRInput,
12
12
  SqlUniqueIR,
13
13
  } from '@prisma-next/sql-schema-ir/types';
14
- import type { PostgresRlsPolicy } from './postgres-rls-policy';
14
+ import type { PostgresPolicySchemaNode } from './postgres-policy-schema-node';
15
+ import { PostgresSchemaNodeKind } from './schema-node-kinds';
15
16
 
16
- export interface PostgresTableIRInput extends SqlTableIRInput {
17
- readonly rlsPolicies?: readonly PostgresRlsPolicy[];
17
+ export interface PostgresTableSchemaNodeInput extends SqlTableIRInput {
18
+ readonly policies?: readonly PostgresPolicySchemaNode[];
18
19
  }
19
20
 
20
21
  /**
21
- * Postgres-specific table IR node. Carries all `SqlTableIR` fields plus
22
- * `rlsPolicies`, and implements `DiffableNode` so the table instance is
22
+ * Postgres-specific table schema-diff node. Carries all `SqlTableIR` fields
23
+ * plus `policies`, and implements `DiffableNode` so the table instance is
23
24
  * directly the diff-tree node — no separate wrapper needed.
24
25
  *
25
26
  * Extends `SqlSchemaIRNode` directly rather than `SqlTableIR` because
26
27
  * `SqlTableIR` calls `freezeNode` in its own constructor, which prevents
27
- * subclass field initialisation. Same pattern as `PostgresSchemaIR`.
28
+ * subclass field initialisation.
28
29
  *
29
- * `id` is the table name. `children()` returns the policies on this table.
30
- * `isEqualTo` is always true — table-level attributes are not diffed yet.
30
+ * `id` is the table name. `children()` returns the policy nodes on this table.
31
+ * `isEqualTo` is identitytwo table nodes are equal iff their ids (names)
32
+ * match. Columns are not compared here; they become child nodes later.
31
33
  */
32
- export class PostgresTableIR extends SqlSchemaIRNode implements DiffableNode {
34
+ export class PostgresTableSchemaNode extends SqlSchemaIRNode implements DiffableNode {
35
+ override readonly nodeKind = PostgresSchemaNodeKind.table;
33
36
  readonly name: string;
34
37
  readonly columns: Readonly<Record<string, SqlColumnIR>>;
35
38
  readonly foreignKeys: ReadonlyArray<SqlForeignKeyIR>;
@@ -38,9 +41,9 @@ export class PostgresTableIR extends SqlSchemaIRNode implements DiffableNode {
38
41
  declare readonly primaryKey?: PrimaryKey;
39
42
  declare readonly annotations?: SqlAnnotations;
40
43
  declare readonly checks?: ReadonlyArray<SqlCheckConstraintIR>;
41
- readonly rlsPolicies: readonly PostgresRlsPolicy[];
44
+ readonly policies: readonly PostgresPolicySchemaNode[];
42
45
 
43
- constructor(input: PostgresTableIRInput) {
46
+ constructor(input: PostgresTableSchemaNodeInput) {
44
47
  super();
45
48
  this.name = input.name;
46
49
  this.columns = Object.freeze(
@@ -74,7 +77,7 @@ export class PostgresTableIR extends SqlSchemaIRNode implements DiffableNode {
74
77
  ),
75
78
  );
76
79
  }
77
- this.rlsPolicies = Object.freeze([...(input.rlsPolicies ?? [])]);
80
+ this.policies = Object.freeze([...(input.policies ?? [])]);
78
81
  freezeNode(this);
79
82
  }
80
83
 
@@ -82,15 +85,23 @@ export class PostgresTableIR extends SqlSchemaIRNode implements DiffableNode {
82
85
  return this.name;
83
86
  }
84
87
 
85
- isEqualTo(_other: DiffableNode): boolean {
86
- return true;
88
+ isEqualTo(other: DiffableNode): boolean {
89
+ return this.id === other.id;
87
90
  }
88
91
 
89
92
  children(): readonly DiffableNode[] {
90
- return this.rlsPolicies;
93
+ return this.policies;
94
+ }
95
+
96
+ static is(node: SqlSchemaIRNode): node is PostgresTableSchemaNode {
97
+ return node.nodeKind === PostgresSchemaNodeKind.table;
91
98
  }
92
- }
93
99
 
94
- export function isPostgresTableIR(node: DiffableNode): node is PostgresTableIR {
95
- return node instanceof PostgresTableIR;
100
+ static assert(node: SqlSchemaIRNode): asserts node is PostgresTableSchemaNode {
101
+ if (!PostgresTableSchemaNode.is(node)) {
102
+ throw new Error(
103
+ `Expected a PostgresTableSchemaNode but got nodeKind=${node.nodeKind ?? 'undefined'}`,
104
+ );
105
+ }
106
+ }
96
107
  }
@@ -0,0 +1,28 @@
1
+ import type { DiffableNode } from '@prisma-next/framework-components/control';
2
+ import type { SqlSchemaIRNode } from '@prisma-next/sql-schema-ir/types';
3
+
4
+ /**
5
+ * A Postgres schema-diff-tree node: a `SqlSchemaIRNode` that also implements
6
+ * `DiffableNode` (the five `Postgres*SchemaNode` classes). `SqlSchemaIRNode`
7
+ * alone is not a `DiffableNode` — its relational subclasses (`SqlColumnIR`, …)
8
+ * carry no `id`/`isEqualTo`/`children` — so this intersection is the honest node
9
+ * type the differ produces and the planner consumes (`SchemaDiff<SqlSchemaDiffNode>`).
10
+ */
11
+ export type SqlSchemaDiffNode = SqlSchemaIRNode & DiffableNode;
12
+
13
+ /**
14
+ * The `nodeKind` discriminant for each Postgres schema-diff node. Each node
15
+ * carries a unique value; the static `is`/`assert` guards compare against these
16
+ * identifiers rather than spelling the string inline or using `instanceof`. The
17
+ * field is an enumerable own property carried on every node instance.
18
+ */
19
+ export const PostgresSchemaNodeKind = {
20
+ database: 'postgres-database',
21
+ namespace: 'postgres-namespace',
22
+ table: 'postgres-table',
23
+ policy: 'postgres-policy',
24
+ role: 'postgres-role',
25
+ } as const;
26
+
27
+ export type PostgresSchemaNodeKind =
28
+ (typeof PostgresSchemaNodeKind)[keyof typeof PostgresSchemaNodeKind];
@@ -11,7 +11,11 @@ import type { StorageColumn } from '@prisma-next/sql-contract/types';
11
11
  import { blindCast } from '@prisma-next/utils/casts';
12
12
  import { ifDefined } from '@prisma-next/utils/defined';
13
13
  import { postgresTargetDescriptorMeta } from '../core/descriptor-meta';
14
- import { contractToPostgresSchemaIR } from '../core/migrations/contract-to-postgres-schema-ir';
14
+ import { contractToPostgresDatabaseSchemaNode } from '../core/migrations/contract-to-postgres-database-schema-node';
15
+ import {
16
+ diffPostgresDatabaseSchema,
17
+ verifyPostgresDatabaseSchema,
18
+ } from '../core/migrations/diff-database-schema';
15
19
  import { createPostgresMigrationPlanner } from '../core/migrations/planner';
16
20
  import { renderDefaultLiteral } from '../core/migrations/planner-ddl-builders';
17
21
  import type { PostgresPlanTargetDetails } from '../core/migrations/planner-target-details';
@@ -19,6 +23,8 @@ import { createPostgresMigrationRunner } from '../core/migrations/runner';
19
23
  import { PostgresContractSerializer } from '../core/postgres-contract-serializer';
20
24
  import type { PostgresContract } from '../core/postgres-schema';
21
25
  import { PostgresSchemaVerifier } from '../core/postgres-schema-verifier';
26
+ import { inferPostgresPslContract } from '../core/psl-infer/infer-psl-contract';
27
+ import { PostgresDatabaseSchemaNode } from '../core/schema-ir/postgres-database-schema-node';
22
28
 
23
29
  function buildNativeTypeExpander(
24
30
  frameworkComponents?: ReadonlyArray<TargetBoundComponentDescriptor<'sql', 'postgres'>>,
@@ -52,6 +58,28 @@ const postgresTargetDescriptor: SqlControlTargetDescriptor<'postgres', PostgresP
52
58
  ...postgresTargetDescriptorMeta,
53
59
  contractSerializer: new PostgresContractSerializer(),
54
60
  schemaVerifier: new PostgresSchemaVerifier(),
61
+ inferPslContract(schema) {
62
+ PostgresDatabaseSchemaNode.assert(schema);
63
+ return inferPostgresPslContract(schema);
64
+ },
65
+ diffDatabaseSchema(input) {
66
+ return diffPostgresDatabaseSchema({
67
+ contract: input.contract,
68
+ actualSchema: input.schema,
69
+ strict: input.strict,
70
+ typeMetadataRegistry: input.typeMetadataRegistry,
71
+ frameworkComponents: input.frameworkComponents,
72
+ });
73
+ },
74
+ verifyDatabaseSchema(input) {
75
+ return verifyPostgresDatabaseSchema({
76
+ contract: input.contract,
77
+ actualSchema: input.schema,
78
+ strict: input.strict,
79
+ typeMetadataRegistry: input.typeMetadataRegistry,
80
+ frameworkComponents: input.frameworkComponents,
81
+ });
82
+ },
55
83
  migrations: {
56
84
  createPlanner(adapter: SqlControlAdapter<'postgres'>) {
57
85
  return createPostgresMigrationPlanner(adapter);
@@ -65,7 +93,7 @@ const postgresTargetDescriptor: SqlControlTargetDescriptor<'postgres', PostgresP
65
93
  PostgresContract | null,
66
94
  'the family resolver only binds this hook for a Postgres-target contract'
67
95
  >(contract);
68
- return contractToPostgresSchemaIR(postgresContract, {
96
+ return contractToPostgresDatabaseSchemaNode(postgresContract, {
69
97
  annotationNamespace: 'pg',
70
98
  ...ifDefined('expandNativeType', expander),
71
99
  renderDefault: postgresRenderDefault,
@@ -1,6 +1,3 @@
1
- export { contractToPostgresSchemaIR } from '../core/migrations/contract-to-postgres-schema-ir';
2
- export {
3
- diffPostgresSchema,
4
- filterIssuesByOwnership,
5
- } from '../core/migrations/diff-postgres-schema';
1
+ export { contractToPostgresDatabaseSchemaNode } from '../core/migrations/contract-to-postgres-database-schema-node';
2
+ export { diffPostgresDatabaseSchema } from '../core/migrations/diff-database-schema';
6
3
  export { createPostgresMigrationPlanner } from '../core/migrations/planner';
@@ -1 +1 @@
1
- export { resolveDdlSchemaForNamespaceStorage } from '../core/schema-ir/postgres-schema-ir-annotations';
1
+ export { resolveDdlSchemaForNamespaceStorage } from '../core/migrations/resolve-ddl-schema';