@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.
Files changed (127) hide show
  1. package/dist/{authoring-WkqG_MkU.mjs → authoring-CdIZ0yvD.mjs} +147 -38
  2. package/dist/authoring-CdIZ0yvD.mjs.map +1 -0
  3. package/dist/contract-free.d.mts +1 -1
  4. package/dist/contract-free.d.mts.map +1 -1
  5. package/dist/contract-free.mjs +2 -2
  6. package/dist/{control-policy-Co_13Hxa.mjs → control-policy-DKp0HKpY.mjs} +203 -45
  7. package/dist/control-policy-DKp0HKpY.mjs.map +1 -0
  8. package/dist/control.mjs +22 -25
  9. package/dist/control.mjs.map +1 -1
  10. package/dist/{ddl-B-krEXY1.mjs → ddl-gmxHf68z.mjs} +20 -4
  11. package/dist/{ddl-B-krEXY1.mjs.map → ddl-gmxHf68z.mjs.map} +1 -1
  12. package/dist/ddl.d.mts +2 -2
  13. package/dist/ddl.mjs +2 -2
  14. package/dist/{descriptor-meta-BOcKPaLE.mjs → descriptor-meta-DEuXJfpR.mjs} +3 -3
  15. package/dist/{descriptor-meta-BOcKPaLE.mjs.map → descriptor-meta-DEuXJfpR.mjs.map} +1 -1
  16. package/dist/{diff-database-schema-BOsDnH64.mjs → diff-database-schema-D2sjvMw9.mjs} +55 -19
  17. package/dist/diff-database-schema-D2sjvMw9.mjs.map +1 -0
  18. package/dist/diff-database-schema.d.mts +4 -4
  19. package/dist/diff-database-schema.d.mts.map +1 -1
  20. package/dist/diff-database-schema.mjs +1 -1
  21. package/dist/issue-planner.d.mts +1 -1
  22. package/dist/issue-planner.d.mts.map +1 -1
  23. package/dist/issue-planner.mjs +1 -1
  24. package/dist/migration.d.mts +2 -2
  25. package/dist/migration.mjs +2 -2
  26. package/dist/{nodes-BtQyZha6.d.mts → nodes-Bc2N3tMy.d.mts} +27 -3
  27. package/dist/nodes-Bc2N3tMy.d.mts.map +1 -0
  28. package/dist/{nodes-B815pj_X.mjs → nodes-DJC5B8Id.mjs} +32 -2
  29. package/dist/nodes-DJC5B8Id.mjs.map +1 -0
  30. package/dist/{op-factory-call-DxNNKUeZ.mjs → op-factory-call-C4flTtTG.mjs} +105 -6
  31. package/dist/op-factory-call-C4flTtTG.mjs.map +1 -0
  32. package/dist/{op-factory-call-D6_7-7Vc.d.mts → op-factory-call-CSIg5XDu.d.mts} +26 -3
  33. package/dist/{op-factory-call-D6_7-7Vc.d.mts.map → op-factory-call-CSIg5XDu.d.mts.map} +1 -1
  34. package/dist/op-factory-call.d.mts +2 -2
  35. package/dist/op-factory-call.mjs +2 -2
  36. package/dist/pack.d.mts +146 -3
  37. package/dist/pack.d.mts.map +1 -1
  38. package/dist/pack.mjs +1 -1
  39. package/dist/{planner-CNrc1Odu.mjs → planner-BtEd5LMr.mjs} +32 -64
  40. package/dist/planner-BtEd5LMr.mjs.map +1 -0
  41. package/dist/{planner-ddl-builders-B8Nn6nHN.mjs → planner-ddl-builders-BKlmGDJl.mjs} +7 -3
  42. package/dist/planner-ddl-builders-BKlmGDJl.mjs.map +1 -0
  43. package/dist/planner-ddl-builders.d.mts.map +1 -1
  44. package/dist/planner-ddl-builders.mjs +1 -1
  45. package/dist/{planner-produced-postgres-migration-a-tjfKsA.d.mts → planner-produced-postgres-migration-CXue-6M9.d.mts} +2 -2
  46. package/dist/{planner-produced-postgres-migration-a-tjfKsA.d.mts.map → planner-produced-postgres-migration-CXue-6M9.d.mts.map} +1 -1
  47. package/dist/{planner-produced-postgres-migration-NI-oGKcw.mjs → planner-produced-postgres-migration-Jat3hg4u.mjs} +2 -2
  48. package/dist/{planner-produced-postgres-migration-NI-oGKcw.mjs.map → planner-produced-postgres-migration-Jat3hg4u.mjs.map} +1 -1
  49. package/dist/planner-produced-postgres-migration.d.mts +1 -1
  50. package/dist/planner-produced-postgres-migration.mjs +1 -1
  51. package/dist/{planner-sql-checks-AXoxB1Jl.mjs → planner-sql-checks-Br1c_QsH.mjs} +3 -3
  52. package/dist/{planner-sql-checks-AXoxB1Jl.mjs.map → planner-sql-checks-Br1c_QsH.mjs.map} +1 -1
  53. package/dist/planner-sql-checks.mjs +1 -1
  54. package/dist/planner.d.mts +3 -3
  55. package/dist/planner.d.mts.map +1 -1
  56. package/dist/planner.mjs +2 -2
  57. package/dist/{postgres-contract-view-Dg-N7KA7.mjs → postgres-contract-view-BAPW0ec1.mjs} +3 -3
  58. package/dist/{postgres-contract-view-Dg-N7KA7.mjs.map → postgres-contract-view-BAPW0ec1.mjs.map} +1 -1
  59. package/dist/{postgres-database-schema-node-CfdTY-Qs.d.mts → postgres-database-schema-node-CJv5bfbs.d.mts} +70 -26
  60. package/dist/postgres-database-schema-node-CJv5bfbs.d.mts.map +1 -0
  61. package/dist/{postgres-migration-ChyRsWvE.mjs → postgres-migration-ChLMQRd-.mjs} +19 -3
  62. package/dist/postgres-migration-ChLMQRd-.mjs.map +1 -0
  63. package/dist/{postgres-migration-Bzzi0Asr.d.mts → postgres-migration-DkmgGNcv.d.mts} +21 -2
  64. package/dist/postgres-migration-DkmgGNcv.d.mts.map +1 -0
  65. package/dist/{postgres-role-schema-node-BiSTCTNg.mjs → postgres-role-schema-node-Cm-j0ZGM.mjs} +20 -25
  66. package/dist/postgres-role-schema-node-Cm-j0ZGM.mjs.map +1 -0
  67. package/dist/{postgres-schema-DTLA_aRi.mjs → postgres-schema-DWQ0-CPb.mjs} +32 -4
  68. package/dist/postgres-schema-DWQ0-CPb.mjs.map +1 -0
  69. package/dist/{postgres-schema-CU4gF6QZ.d.mts → postgres-schema-DsrtYp2v.d.mts} +13 -1
  70. package/dist/postgres-schema-DsrtYp2v.d.mts.map +1 -0
  71. package/dist/{postgres-table-schema-node-9FCtCUz2.mjs → postgres-table-schema-node-D6LvInCe.mjs} +71 -8
  72. package/dist/postgres-table-schema-node-D6LvInCe.mjs.map +1 -0
  73. package/dist/{resolve-ddl-schema-Ca88Tg6W.mjs → resolve-ddl-schema-CAza65Iy.mjs} +2 -2
  74. package/dist/{resolve-ddl-schema-Ca88Tg6W.mjs.map → resolve-ddl-schema-CAza65Iy.mjs.map} +1 -1
  75. package/dist/runtime.mjs +1 -1
  76. package/dist/schema-ir-annotations.mjs +1 -1
  77. package/dist/{sql-utils-DcfMz4MQ.mjs → sql-utils-SU4FDvIV.mjs} +12 -3
  78. package/dist/{sql-utils-DcfMz4MQ.mjs.map → sql-utils-SU4FDvIV.mjs.map} +1 -1
  79. package/dist/sql-utils.d.mts +8 -1
  80. package/dist/sql-utils.d.mts.map +1 -1
  81. package/dist/sql-utils.mjs +2 -2
  82. package/dist/types.d.mts +3 -3
  83. package/dist/types.mjs +4 -4
  84. package/package.json +20 -20
  85. package/src/contract-free/ddl.ts +27 -0
  86. package/src/core/authoring.ts +136 -25
  87. package/src/core/ddl/nodes.ts +44 -0
  88. package/src/core/migrations/contract-to-postgres-database-schema-node.ts +12 -1
  89. package/src/core/migrations/control-policy.ts +172 -45
  90. package/src/core/migrations/diff-database-schema.ts +48 -13
  91. package/src/core/migrations/issue-planner.ts +77 -4
  92. package/src/core/migrations/op-factory-call.ts +84 -0
  93. package/src/core/migrations/operations/native-enum-types.ts +62 -0
  94. package/src/core/migrations/planner-ddl-builders.ts +12 -2
  95. package/src/core/migrations/planner.ts +30 -62
  96. package/src/core/migrations/postgres-migration.ts +31 -0
  97. package/src/core/migrations/verify-postgres-namespaces.ts +0 -1
  98. package/src/core/postgres-role.ts +22 -0
  99. package/src/core/postgres-schema.ts +22 -1
  100. package/src/core/postgres-validators.ts +1 -0
  101. package/src/core/psl-infer/infer-psl-contract.ts +7 -16
  102. package/src/core/schema-ir/node-storage-coordinate.ts +26 -0
  103. package/src/core/schema-ir/postgres-database-schema-node.ts +5 -4
  104. package/src/core/schema-ir/postgres-namespace-schema-node.ts +18 -27
  105. package/src/core/schema-ir/postgres-native-enum-schema-node.ts +83 -0
  106. package/src/core/schema-ir/postgres-role-schema-node.ts +4 -2
  107. package/src/core/schema-ir/schema-node-kinds.ts +19 -3
  108. package/src/core/sql-utils.ts +15 -2
  109. package/src/exports/ddl.ts +2 -0
  110. package/src/exports/op-factory-call.ts +2 -0
  111. package/src/exports/sql-utils.ts +1 -0
  112. package/src/exports/types.ts +4 -1
  113. package/dist/authoring-WkqG_MkU.mjs.map +0 -1
  114. package/dist/control-policy-Co_13Hxa.mjs.map +0 -1
  115. package/dist/diff-database-schema-BOsDnH64.mjs.map +0 -1
  116. package/dist/nodes-B815pj_X.mjs.map +0 -1
  117. package/dist/nodes-BtQyZha6.d.mts.map +0 -1
  118. package/dist/op-factory-call-DxNNKUeZ.mjs.map +0 -1
  119. package/dist/planner-CNrc1Odu.mjs.map +0 -1
  120. package/dist/planner-ddl-builders-B8Nn6nHN.mjs.map +0 -1
  121. package/dist/postgres-database-schema-node-CfdTY-Qs.d.mts.map +0 -1
  122. package/dist/postgres-migration-Bzzi0Asr.d.mts.map +0 -1
  123. package/dist/postgres-migration-ChyRsWvE.mjs.map +0 -1
  124. package/dist/postgres-role-schema-node-BiSTCTNg.mjs.map +0 -1
  125. package/dist/postgres-schema-CU4gF6QZ.d.mts.map +0 -1
  126. package/dist/postgres-schema-DTLA_aRi.mjs.map +0 -1
  127. package/dist/postgres-table-schema-node-9FCtCUz2.mjs.map +0 -1
package/package.json CHANGED
@@ -1,35 +1,35 @@
1
1
  {
2
2
  "name": "@prisma-next/target-postgres",
3
- "version": "0.14.0-dev.60",
3
+ "version": "0.14.0-dev.62",
4
4
  "license": "Apache-2.0",
5
5
  "type": "module",
6
6
  "sideEffects": false,
7
7
  "description": "Postgres target pack for Prisma Next",
8
8
  "dependencies": {
9
- "@prisma-next/cli": "0.14.0-dev.60",
10
- "@prisma-next/contract": "0.14.0-dev.60",
11
- "@prisma-next/errors": "0.14.0-dev.60",
12
- "@prisma-next/family-sql": "0.14.0-dev.60",
13
- "@prisma-next/framework-components": "0.14.0-dev.60",
14
- "@prisma-next/migration-tools": "0.14.0-dev.60",
15
- "@prisma-next/psl-parser": "0.14.0-dev.60",
16
- "@prisma-next/sql-contract": "0.14.0-dev.60",
17
- "@prisma-next/sql-errors": "0.14.0-dev.60",
18
- "@prisma-next/sql-operations": "0.14.0-dev.60",
19
- "@prisma-next/sql-relational-core": "0.14.0-dev.60",
20
- "@prisma-next/sql-schema-ir": "0.14.0-dev.60",
21
- "@prisma-next/ts-render": "0.14.0-dev.60",
22
- "@prisma-next/utils": "0.14.0-dev.60",
9
+ "@prisma-next/cli": "0.14.0-dev.62",
10
+ "@prisma-next/contract": "0.14.0-dev.62",
11
+ "@prisma-next/errors": "0.14.0-dev.62",
12
+ "@prisma-next/family-sql": "0.14.0-dev.62",
13
+ "@prisma-next/framework-components": "0.14.0-dev.62",
14
+ "@prisma-next/migration-tools": "0.14.0-dev.62",
15
+ "@prisma-next/psl-parser": "0.14.0-dev.62",
16
+ "@prisma-next/sql-contract": "0.14.0-dev.62",
17
+ "@prisma-next/sql-errors": "0.14.0-dev.62",
18
+ "@prisma-next/sql-operations": "0.14.0-dev.62",
19
+ "@prisma-next/sql-relational-core": "0.14.0-dev.62",
20
+ "@prisma-next/sql-schema-ir": "0.14.0-dev.62",
21
+ "@prisma-next/ts-render": "0.14.0-dev.62",
22
+ "@prisma-next/utils": "0.14.0-dev.62",
23
23
  "@standard-schema/spec": "^1.1.0",
24
24
  "arktype": "^2.2.0",
25
25
  "pathe": "^2.0.3"
26
26
  },
27
27
  "devDependencies": {
28
- "@prisma-next/psl-printer": "0.14.0-dev.60",
29
- "@prisma-next/sql-contract-psl": "0.14.0-dev.60",
30
- "@prisma-next/test-utils": "0.14.0-dev.60",
31
- "@prisma-next/tsconfig": "0.14.0-dev.60",
32
- "@prisma-next/tsdown": "0.14.0-dev.60",
28
+ "@prisma-next/psl-printer": "0.14.0-dev.62",
29
+ "@prisma-next/sql-contract-psl": "0.14.0-dev.62",
30
+ "@prisma-next/test-utils": "0.14.0-dev.62",
31
+ "@prisma-next/tsconfig": "0.14.0-dev.62",
32
+ "@prisma-next/tsdown": "0.14.0-dev.62",
33
33
  "tsdown": "0.22.1",
34
34
  "typescript": "5.9.3",
35
35
  "vitest": "4.1.8"
@@ -8,8 +8,10 @@ import {
8
8
  PostgresCreatePolicy,
9
9
  PostgresCreateSchema,
10
10
  PostgresCreateTable,
11
+ PostgresCreateType,
11
12
  PostgresDisableRowLevelSecurity,
12
13
  PostgresDropPolicy,
14
+ PostgresDropType,
13
15
  type RlsPolicyOperation,
14
16
  } from '../core/ddl/nodes';
15
17
 
@@ -48,6 +50,31 @@ export function createSchema(options: {
48
50
  return new PostgresCreateSchema(options);
49
51
  }
50
52
 
53
+ /**
54
+ * Build a Postgres `CREATE TYPE … AS ENUM (…)` DDL node. The type name (and
55
+ * optional schema) are quoted by the renderer; the enum member `values` are
56
+ * emitted as single-quote-escaped string literals. An absent `schema` renders
57
+ * the type name unqualified so the connection's `search_path` resolves it.
58
+ */
59
+ export function createType(options: {
60
+ readonly schema?: string;
61
+ readonly name: string;
62
+ readonly values: readonly string[];
63
+ }): PostgresCreateType {
64
+ return new PostgresCreateType(options);
65
+ }
66
+
67
+ /**
68
+ * Build a Postgres `DROP TYPE` DDL node. See {@link createType} for the
69
+ * identifier-quoting and unqualified-when-schemaless behaviour.
70
+ */
71
+ export function dropType(options: {
72
+ readonly schema?: string;
73
+ readonly name: string;
74
+ }): PostgresDropType {
75
+ return new PostgresDropType(options);
76
+ }
77
+
51
78
  /**
52
79
  * Build an `ADD COLUMN` action for use inside {@link alterTable}.
53
80
  * The column is a structured `DdlColumn` so codec-encoded defaults flow
@@ -12,10 +12,11 @@ import type {
12
12
  } from '@prisma-next/framework-components/authoring';
13
13
  import { modelAttribute } from '@prisma-next/psl-parser';
14
14
  import type { SqlValueSetDerivingEntityTypeOutput } from '@prisma-next/sql-contract/value-set-derivation-hook';
15
+ import { ifDefined } from '@prisma-next/utils/defined';
15
16
  import { PG_ENUM_CODEC_ID } from './codec-ids';
16
17
  import { PostgresNativeEnum } from './postgres-native-enum';
17
18
  import { PostgresRlsEnablement, type PostgresRlsEnablementInput } from './postgres-rls-enablement';
18
- import { PostgresRlsPolicy } from './postgres-rls-policy';
19
+ import { PostgresRlsPolicy, type RlsPolicyOperation } from './postgres-rls-policy';
19
20
  import { PostgresRole, type PostgresRoleInput } from './postgres-role';
20
21
  import {
21
22
  PostgresNativeEnumSchema,
@@ -50,6 +51,35 @@ export interface RlsPolicyExtensionBlock extends PslExtensionBlock {
50
51
  readonly namespaceId: string;
51
52
  }
52
53
 
54
+ /**
55
+ * Maps a `policy_<op>` keyword to the RLS operation it authors. The keyword
56
+ * IS the operation (per the project's rejection of a `policy { operation = … }`
57
+ * conditional block).
58
+ */
59
+ const POLICY_KEYWORD_OPERATION: Readonly<Record<string, RlsPolicyOperation>> = {
60
+ policy_select: 'select',
61
+ policy_insert: 'insert',
62
+ policy_update: 'update',
63
+ policy_delete: 'delete',
64
+ policy_all: 'all',
65
+ };
66
+
67
+ /**
68
+ * Which predicate each RLS operation admits, mirroring Postgres: SELECT and
69
+ * DELETE decide row visibility/eligibility with `USING` only; INSERT validates
70
+ * the new row with `WITH CHECK` only; UPDATE and ALL take both. A predicate
71
+ * outside this set is a load-time error (see {@link lowerRlsPolicyFromBlock}).
72
+ */
73
+ const POLICY_OPERATION_PREDICATES: Readonly<
74
+ Record<RlsPolicyOperation, { readonly using: boolean; readonly withCheck: boolean }>
75
+ > = {
76
+ select: { using: true, withCheck: false },
77
+ insert: { using: false, withCheck: true },
78
+ update: { using: true, withCheck: true },
79
+ delete: { using: true, withCheck: false },
80
+ all: { using: true, withCheck: true },
81
+ };
82
+
53
83
  function readRefParam(block: PslExtensionBlock, key: string): string | undefined {
54
84
  const param = block.parameters[key];
55
85
  return param?.kind === 'ref' ? param.identifier : undefined;
@@ -101,18 +131,43 @@ function unwrapQuotedString(raw: string): string {
101
131
 
102
132
  function lowerRlsPolicyFromBlock(
103
133
  block: RlsPolicyExtensionBlock,
104
- _ctx: AuthoringEntityContext,
105
- ): PostgresRlsPolicy {
134
+ ctx: AuthoringEntityContext,
135
+ ): PostgresRlsPolicy | undefined {
106
136
  const prefix = block.name;
137
+ const operation = POLICY_KEYWORD_OPERATION[block.keyword] ?? 'select';
107
138
  const targetModelName = readRefParam(block, 'target') ?? '';
108
139
  const tableName = targetModelName.charAt(0).toLowerCase() + targetModelName.slice(1);
109
140
  const roles = [...readListRefParams(block, 'roles')].sort();
110
- const using = unwrapQuotedString(readValueParam(block, 'using') ?? '');
141
+
142
+ const usingRaw = readValueParam(block, 'using');
143
+ const withCheckRaw = readValueParam(block, 'withCheck');
144
+
145
+ // Reject a predicate the operation does not take (e.g. `using` on INSERT, or
146
+ // `withCheck` on SELECT/DELETE). The descriptor's param set already omits it,
147
+ // but the generic descriptor validator is not wired into the SQL-family
148
+ // interpreter, so the lowering enforces the per-operation predicate matrix
149
+ // directly — a wrong predicate is a load-time diagnostic, not a silent drop.
150
+ const support = POLICY_OPERATION_PREDICATES[operation];
151
+ const rejectPredicate = (predicate: 'using' | 'withCheck'): undefined => {
152
+ ctx.diagnostics?.push({
153
+ code: 'PSL_RLS_PREDICATE_NOT_FOR_OPERATION',
154
+ message: `\`${block.keyword}\` policy "${block.name}" does not take a \`${predicate}\` predicate; the ${operation.toUpperCase()} operation uses ${support.using ? '`using`' : '`withCheck`'}${support.using && support.withCheck ? ' and `withCheck`' : ' only'}.`,
155
+ sourceId: ctx.sourceId ?? 'unknown',
156
+ span: block.parameters[predicate]?.span ?? block.span,
157
+ });
158
+ return undefined;
159
+ };
160
+ if (usingRaw !== undefined && !support.using) return rejectPredicate('using');
161
+ if (withCheckRaw !== undefined && !support.withCheck) return rejectPredicate('withCheck');
162
+
163
+ const using = usingRaw !== undefined ? unwrapQuotedString(usingRaw) : undefined;
164
+ const withCheck = withCheckRaw !== undefined ? unwrapQuotedString(withCheckRaw) : undefined;
111
165
 
112
166
  const wireHash = computeContentHash({
113
- using: normalizePredicate(using),
167
+ ...ifDefined('using', using !== undefined ? normalizePredicate(using) : undefined),
168
+ ...ifDefined('withCheck', withCheck !== undefined ? normalizePredicate(withCheck) : undefined),
114
169
  roles,
115
- operation: 'select',
170
+ operation,
116
171
  permissive: true,
117
172
  });
118
173
  const wireName = formatRlsPolicyWireName(prefix, wireHash);
@@ -122,9 +177,10 @@ function lowerRlsPolicyFromBlock(
122
177
  prefix,
123
178
  tableName,
124
179
  namespaceId: block.namespaceId,
125
- operation: 'select',
180
+ operation,
126
181
  roles,
127
- using,
182
+ ...ifDefined('using', using),
183
+ ...ifDefined('withCheck', withCheck),
128
184
  permissive: true,
129
185
  });
130
186
  }
@@ -298,13 +354,10 @@ export const postgresAuthoringEntityTypes = {
298
354
  * the family pack instead.
299
355
  */
300
356
  /**
301
- * PSL block descriptor for `policy_select`.
302
- *
303
- * The parser learns the block shape from this descriptor; lowering from
304
- * `PslExtensionBlock` to `PostgresRlsPolicy` is wired in the PSL
305
- * interpreter (a later dispatch). The `discriminator` matches
306
- * `PostgresRlsPolicy.kind` so the parsed block node carries the same
307
- * discriminant as the IR class it will lower to.
357
+ * Shared parameter descriptors for the five `policy_<op>` PSL block
358
+ * keywords. All five share the `policy` discriminator — the parser
359
+ * dispatches by keyword (see the framework's PSL-block SPI), and every
360
+ * keyword's factory lowers to `PostgresRlsPolicy` via `lowerRlsPolicyFromBlock`.
308
361
  *
309
362
  * The `roles` list uses `scope:'cross-space'` because same-namespace
310
363
  * role ref resolution requires PSL namespace entries keyed by `refKind`
@@ -313,24 +366,82 @@ export const postgresAuthoringEntityTypes = {
313
366
  * slice 4 (cross-space roles). Until then cross-space passes validation
314
367
  * unconditionally and the authored role names flow through unchanged.
315
368
  */
369
+ const policyTargetParam = {
370
+ kind: 'ref',
371
+ refKind: 'model',
372
+ scope: 'same-namespace',
373
+ required: true,
374
+ } as const;
375
+ const policyRolesParam = {
376
+ kind: 'list',
377
+ of: { kind: 'ref', refKind: 'role', scope: 'cross-space' },
378
+ } as const;
379
+ const policyPredicateParam = { kind: 'value', codecId: 'pg/text@1', required: true } as const;
380
+ // A policy may only target an RLS-controlled model: the model named by
381
+ // `target` must declare `@@rls`, or the load fails with a diagnostic naming
382
+ // the model and the policy prefix.
383
+ const policyRequiresRls = { parameter: 'target', attribute: 'rls' } as const;
384
+
316
385
  export const postgresAuthoringPslBlockDescriptors = {
386
+ // The predicate param set per keyword mirrors Postgres: SELECT/DELETE take
387
+ // USING only; INSERT takes WITH CHECK only; UPDATE/ALL take both. The
388
+ // per-operation predicate matrix is enforced in `lowerRlsPolicyFromBlock`
389
+ // (a wrong predicate for the operation is a load-time diagnostic there),
390
+ // since the generic descriptor validator is not wired into the SQL-family
391
+ // interpreter.
317
392
  policy_select: {
318
393
  kind: 'pslBlock',
319
394
  keyword: 'policy_select',
320
395
  discriminator: 'policy',
321
396
  name: { required: true },
397
+ parameters: { target: policyTargetParam, roles: policyRolesParam, using: policyPredicateParam },
398
+ requiresModelAttribute: policyRequiresRls,
399
+ },
400
+ policy_delete: {
401
+ kind: 'pslBlock',
402
+ keyword: 'policy_delete',
403
+ discriminator: 'policy',
404
+ name: { required: true },
405
+ parameters: { target: policyTargetParam, roles: policyRolesParam, using: policyPredicateParam },
406
+ requiresModelAttribute: policyRequiresRls,
407
+ },
408
+ policy_insert: {
409
+ kind: 'pslBlock',
410
+ keyword: 'policy_insert',
411
+ discriminator: 'policy',
412
+ name: { required: true },
322
413
  parameters: {
323
- target: { kind: 'ref', refKind: 'model', scope: 'same-namespace', required: true },
324
- roles: {
325
- kind: 'list',
326
- of: { kind: 'ref', refKind: 'role', scope: 'cross-space' },
327
- },
328
- using: { kind: 'value', codecId: 'pg/text@1', required: true },
414
+ target: policyTargetParam,
415
+ roles: policyRolesParam,
416
+ withCheck: policyPredicateParam,
417
+ },
418
+ requiresModelAttribute: policyRequiresRls,
419
+ },
420
+ policy_update: {
421
+ kind: 'pslBlock',
422
+ keyword: 'policy_update',
423
+ discriminator: 'policy',
424
+ name: { required: true },
425
+ parameters: {
426
+ target: policyTargetParam,
427
+ roles: policyRolesParam,
428
+ using: policyPredicateParam,
429
+ withCheck: policyPredicateParam,
430
+ },
431
+ requiresModelAttribute: policyRequiresRls,
432
+ },
433
+ policy_all: {
434
+ kind: 'pslBlock',
435
+ keyword: 'policy_all',
436
+ discriminator: 'policy',
437
+ name: { required: true },
438
+ parameters: {
439
+ target: policyTargetParam,
440
+ roles: policyRolesParam,
441
+ using: policyPredicateParam,
442
+ withCheck: policyPredicateParam,
329
443
  },
330
- // A policy may only target an RLS-controlled model: the model named by
331
- // `target` must declare `@@rls`, or the load fails with a diagnostic
332
- // naming the model and the policy prefix.
333
- requiresModelAttribute: { parameter: 'target', attribute: 'rls' },
444
+ requiresModelAttribute: policyRequiresRls,
334
445
  },
335
446
  /**
336
447
  * PSL block descriptor for `native_enum`.
@@ -67,6 +67,8 @@ export type AnyAlterTableAction = AddColumnAction | DropDefaultAction;
67
67
  export interface PostgresDdlVisitor<R> {
68
68
  createTable(node: PostgresCreateTable): R;
69
69
  createSchema(node: PostgresCreateSchema): R;
70
+ createType(node: PostgresCreateType): R;
71
+ dropType(node: PostgresDropType): R;
70
72
  alterTable(node: PostgresAlterTable): R;
71
73
  createPolicy(node: PostgresCreatePolicy): R;
72
74
  dropPolicy(node: PostgresDropPolicy): R;
@@ -134,6 +136,46 @@ export class PostgresCreateSchema extends PostgresDdlNode {
134
136
  }
135
137
  }
136
138
 
139
+ export class PostgresCreateType extends PostgresDdlNode {
140
+ readonly kind = 'create-type' as const;
141
+ readonly schema: string | undefined;
142
+ readonly name: string;
143
+ readonly values: ReadonlyArray<string>;
144
+
145
+ constructor(options: {
146
+ readonly schema?: string;
147
+ readonly name: string;
148
+ readonly values: readonly string[];
149
+ }) {
150
+ super();
151
+ this.schema = options.schema;
152
+ this.name = options.name;
153
+ this.values = Object.freeze([...options.values]);
154
+ this.freeze();
155
+ }
156
+
157
+ override accept<R>(visitor: PostgresDdlVisitor<R>): R {
158
+ return visitor.createType(this);
159
+ }
160
+ }
161
+
162
+ export class PostgresDropType extends PostgresDdlNode {
163
+ readonly kind = 'drop-type' as const;
164
+ readonly schema: string | undefined;
165
+ readonly name: string;
166
+
167
+ constructor(options: { readonly schema?: string; readonly name: string }) {
168
+ super();
169
+ this.schema = options.schema;
170
+ this.name = options.name;
171
+ this.freeze();
172
+ }
173
+
174
+ override accept<R>(visitor: PostgresDdlVisitor<R>): R {
175
+ return visitor.dropType(this);
176
+ }
177
+ }
178
+
137
179
  export class PostgresAlterTable extends PostgresDdlNode {
138
180
  readonly kind = 'alter-table' as const;
139
181
  readonly table: string;
@@ -262,6 +304,8 @@ export class PostgresDisableRowLevelSecurity extends PostgresDdlNode {
262
304
  export type AnyPostgresDdlNode =
263
305
  | PostgresCreateTable
264
306
  | PostgresCreateSchema
307
+ | PostgresCreateType
308
+ | PostgresDropType
265
309
  | PostgresAlterTable
266
310
  | PostgresCreatePolicy
267
311
  | PostgresDropPolicy
@@ -8,6 +8,7 @@ import type { PostgresContract } from '../postgres-schema';
8
8
  import { isPostgresSchema } from '../postgres-schema';
9
9
  import { PostgresDatabaseSchemaNode } from '../schema-ir/postgres-database-schema-node';
10
10
  import { PostgresNamespaceSchemaNode } from '../schema-ir/postgres-namespace-schema-node';
11
+ import { PostgresNativeEnumSchemaNode } from '../schema-ir/postgres-native-enum-schema-node';
11
12
  import { PostgresPolicySchemaNode } from '../schema-ir/postgres-policy-schema-node';
12
13
  import { PostgresRoleSchemaNode } from '../schema-ir/postgres-role-schema-node';
13
14
  import { PostgresTableSchemaNode } from '../schema-ir/postgres-table-schema-node';
@@ -140,10 +141,20 @@ export function contractToPostgresDatabaseSchemaNode(
140
141
  }
141
142
  }
142
143
 
144
+ const nativeEnums = Object.values(ns.entries.native_enum ?? {}).map(
145
+ (entity) =>
146
+ new PostgresNativeEnumSchemaNode({
147
+ typeName: entity.typeName,
148
+ namespaceId: ddlSchema,
149
+ members: entity.members,
150
+ ...ifDefined('control', entity.control),
151
+ }),
152
+ );
153
+
143
154
  namespaces[ddlSchema] = new PostgresNamespaceSchemaNode({
144
155
  schemaName: ddlSchema,
145
156
  tables,
146
- nativeEnumTypeNames: [],
157
+ nativeEnums,
147
158
  });
148
159
 
149
160
  for (const role of Object.values(ns.role)) {