@prisma-next/target-postgres 0.15.0 → 0.16.0-dev.10

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 (194) hide show
  1. package/dist/{authoring-BtsOWgI3.mjs → authoring-34L18kA1.mjs} +18 -8
  2. package/dist/authoring-34L18kA1.mjs.map +1 -0
  3. package/dist/canonicalize-D1F4vUnP.d.mts.map +1 -1
  4. package/dist/{codec-ids-CI144z7g.d.mts → codec-ids-BswpvvND.d.mts} +3 -3
  5. package/dist/codec-ids-BswpvvND.d.mts.map +1 -0
  6. package/dist/{codec-ids-B10ZZHzw.mjs → codec-ids-D3G8K0YM.mjs} +3 -2
  7. package/dist/codec-ids-D3G8K0YM.mjs.map +1 -0
  8. package/dist/codec-ids.d.mts +2 -2
  9. package/dist/codec-ids.mjs +2 -2
  10. package/dist/{codec-types-CTfT4U4O.d.mts → codec-types-CQ07nOz-.d.mts} +4 -5
  11. package/dist/codec-types-CQ07nOz-.d.mts.map +1 -0
  12. package/dist/codec-types.d.mts +1 -1
  13. package/dist/{codecs-D7W9O0Xx.d.mts → codecs-G4DTY0F_.d.mts} +35 -7
  14. package/dist/codecs-G4DTY0F_.d.mts.map +1 -0
  15. package/dist/{codecs-DRbb26Or.mjs → codecs-TCsGgW-Y.mjs} +77 -5
  16. package/dist/codecs-TCsGgW-Y.mjs.map +1 -0
  17. package/dist/codecs.d.mts +2 -3
  18. package/dist/codecs.d.mts.map +1 -1
  19. package/dist/codecs.mjs +2 -2
  20. package/dist/contract-free.d.mts +2 -3
  21. package/dist/contract-free.d.mts.map +1 -1
  22. package/dist/contract-free.mjs +3 -3
  23. package/dist/{control-policy-DtbkLLGv.mjs → control-policy-S7XBe07W.mjs} +44 -75
  24. package/dist/control-policy-S7XBe07W.mjs.map +1 -0
  25. package/dist/control.d.mts +0 -1
  26. package/dist/control.d.mts.map +1 -1
  27. package/dist/control.mjs +75 -25
  28. package/dist/control.mjs.map +1 -1
  29. package/dist/data-transform-BOWpliq8.mjs.map +1 -1
  30. package/dist/{data-transform-DPzwBMhW.d.mts → data-transform-CGvE8yGx.d.mts} +1 -2
  31. package/dist/data-transform-CGvE8yGx.d.mts.map +1 -0
  32. package/dist/data-transform.d.mts +1 -1
  33. package/dist/{ddl-GTTOvjx_.mjs → ddl-DHXKzQ99.mjs} +3 -3
  34. package/dist/{ddl-GTTOvjx_.mjs.map → ddl-DHXKzQ99.mjs.map} +1 -1
  35. package/dist/ddl.d.mts +1 -1
  36. package/dist/default-normalizer-B9ZUiyUE.mjs +264 -0
  37. package/dist/default-normalizer-B9ZUiyUE.mjs.map +1 -0
  38. package/dist/default-normalizer.d.mts +14 -2
  39. package/dist/default-normalizer.d.mts.map +1 -1
  40. package/dist/default-normalizer.mjs +2 -248
  41. package/dist/{descriptor-meta-CFQKLXl_.mjs → descriptor-meta-qubYaxQA.mjs} +9 -4
  42. package/dist/descriptor-meta-qubYaxQA.mjs.map +1 -0
  43. package/dist/{diff-database-schema-GncqMuSI.mjs → diff-database-schema-BRxq7l98.mjs} +92 -26
  44. package/dist/diff-database-schema-BRxq7l98.mjs.map +1 -0
  45. package/dist/diff-database-schema.d.mts +4 -13
  46. package/dist/diff-database-schema.d.mts.map +1 -1
  47. package/dist/diff-database-schema.mjs +1 -1
  48. package/dist/{errors-DO-w0EUS.mjs → errors-ByDS0xJL.mjs} +6 -6
  49. package/dist/errors-ByDS0xJL.mjs.map +1 -0
  50. package/dist/errors.d.mts +4 -4
  51. package/dist/errors.d.mts.map +1 -1
  52. package/dist/errors.mjs +1 -1
  53. package/dist/issue-planner.d.mts +1 -2
  54. package/dist/issue-planner.d.mts.map +1 -1
  55. package/dist/issue-planner.mjs +1 -1
  56. package/dist/migration.d.mts +3 -4
  57. package/dist/migration.d.mts.map +1 -1
  58. package/dist/migration.mjs +2 -2
  59. package/dist/native-type-normalizer.d.mts.map +1 -1
  60. package/dist/{nodes-Dv5eCSq9.d.mts → nodes-CQzM2hR0.d.mts} +1 -2
  61. package/dist/nodes-CQzM2hR0.d.mts.map +1 -0
  62. package/dist/{op-factory-call-C38NzvQD.mjs → op-factory-call-Ci3H6InB.mjs} +37 -21
  63. package/dist/op-factory-call-Ci3H6InB.mjs.map +1 -0
  64. package/dist/{op-factory-call-Bn9zDx4_.d.mts → op-factory-call-DT7kdjVP.d.mts} +10 -7
  65. package/dist/op-factory-call-DT7kdjVP.d.mts.map +1 -0
  66. package/dist/op-factory-call.d.mts +2 -2
  67. package/dist/op-factory-call.mjs +2 -2
  68. package/dist/pack.d.mts +122 -6
  69. package/dist/pack.d.mts.map +1 -1
  70. package/dist/pack.mjs +1 -1
  71. package/dist/{planner-kql24i_F.mjs → planner-BpL7uktH.mjs} +15 -15
  72. package/dist/planner-BpL7uktH.mjs.map +1 -0
  73. package/dist/planner-ddl-builders.d.mts +1 -2
  74. package/dist/planner-ddl-builders.d.mts.map +1 -1
  75. package/dist/planner-identity-values.d.mts +0 -1
  76. package/dist/planner-identity-values.d.mts.map +1 -1
  77. package/dist/{planner-produced-postgres-migration-BEY1T0Xm.d.mts → planner-produced-postgres-migration-8QP8eMph.d.mts} +3 -4
  78. package/dist/planner-produced-postgres-migration-8QP8eMph.d.mts.map +1 -0
  79. package/dist/{planner-produced-postgres-migration-D7y-PEHK.mjs → planner-produced-postgres-migration-D8exrrxA.mjs} +8 -5
  80. package/dist/planner-produced-postgres-migration-D8exrrxA.mjs.map +1 -0
  81. package/dist/planner-produced-postgres-migration.d.mts +1 -1
  82. package/dist/planner-produced-postgres-migration.mjs +1 -1
  83. package/dist/planner-schema-lookup.d.mts +0 -1
  84. package/dist/planner-schema-lookup.d.mts.map +1 -1
  85. package/dist/{planner-sql-checks-zqNfs2XZ.mjs → planner-sql-checks-BuB877wH.mjs} +2 -2
  86. package/dist/{planner-sql-checks-zqNfs2XZ.mjs.map → planner-sql-checks-BuB877wH.mjs.map} +1 -1
  87. package/dist/planner-sql-checks.d.mts +0 -1
  88. package/dist/planner-sql-checks.d.mts.map +1 -1
  89. package/dist/planner-sql-checks.mjs +1 -1
  90. package/dist/planner-target-details-HkP4RrRO.d.mts.map +1 -1
  91. package/dist/planner.d.mts +9 -4
  92. package/dist/planner.d.mts.map +1 -1
  93. package/dist/planner.mjs +2 -2
  94. package/dist/{postgres-contract-view-C9j5cqP0.d.mts → postgres-contract-view-CeU7FJjW.d.mts} +4 -3
  95. package/dist/postgres-contract-view-CeU7FJjW.d.mts.map +1 -0
  96. package/dist/{postgres-contract-view-DM8sN1Ot.mjs → postgres-contract-view-DebHMqGR.mjs} +4 -4
  97. package/dist/{postgres-contract-view-DM8sN1Ot.mjs.map → postgres-contract-view-DebHMqGR.mjs.map} +1 -1
  98. package/dist/{postgres-database-schema-node-CJv5bfbs.d.mts → postgres-database-schema-node-DMXsIwAb.d.mts} +12 -4
  99. package/dist/postgres-database-schema-node-DMXsIwAb.d.mts.map +1 -0
  100. package/dist/{postgres-migration-arMSWU9b.mjs → postgres-migration-Bn3fj1I4.mjs} +22 -6
  101. package/dist/postgres-migration-Bn3fj1I4.mjs.map +1 -0
  102. package/dist/{postgres-migration-DbckK1su.d.mts → postgres-migration-DLIvzHjX.d.mts} +31 -7
  103. package/dist/postgres-migration-DLIvzHjX.d.mts.map +1 -0
  104. package/dist/{postgres-rls-policy--CSddvMR.d.mts → postgres-rls-policy-CYXdqG9B.d.mts} +1 -2
  105. package/dist/postgres-rls-policy-CYXdqG9B.d.mts.map +1 -0
  106. package/dist/{postgres-role-schema-node-Cm-j0ZGM.mjs → postgres-role-schema-node-bg32e7I-.mjs} +3 -2
  107. package/dist/postgres-role-schema-node-bg32e7I-.mjs.map +1 -0
  108. package/dist/{postgres-schema-Cm4DtiGA.mjs → postgres-schema-CewLYHB7.mjs} +2 -2
  109. package/dist/{postgres-schema-Cm4DtiGA.mjs.map → postgres-schema-CewLYHB7.mjs.map} +1 -1
  110. package/dist/{postgres-schema-DKq6s1P3.d.mts → postgres-schema-FdKRwe7X.d.mts} +3 -4
  111. package/dist/postgres-schema-FdKRwe7X.d.mts.map +1 -0
  112. package/dist/render-ops.d.mts +0 -1
  113. package/dist/render-ops.d.mts.map +1 -1
  114. package/dist/{render-typescript-7yqILcwr.mjs → render-typescript-Ta7_6CGf.mjs} +20 -8
  115. package/dist/render-typescript-Ta7_6CGf.mjs.map +1 -0
  116. package/dist/render-typescript.d.mts +2 -1
  117. package/dist/render-typescript.d.mts.map +1 -1
  118. package/dist/render-typescript.mjs +1 -1
  119. package/dist/{resolve-ddl-schema-XyBaaenM.mjs → resolve-ddl-schema-D0wi_P9Q.mjs} +2 -2
  120. package/dist/{resolve-ddl-schema-XyBaaenM.mjs.map → resolve-ddl-schema-D0wi_P9Q.mjs.map} +1 -1
  121. package/dist/rls-canonicalize.d.mts +0 -1
  122. package/dist/rls-canonicalize.d.mts.map +1 -1
  123. package/dist/runtime.d.mts +1 -2
  124. package/dist/runtime.d.mts.map +1 -1
  125. package/dist/runtime.mjs +1 -1
  126. package/dist/schema-ir-annotations.d.mts +0 -1
  127. package/dist/schema-ir-annotations.d.mts.map +1 -1
  128. package/dist/schema-ir-annotations.mjs +1 -1
  129. package/dist/schema-node-kinds-DxvWh2Ic.d.mts +29 -0
  130. package/dist/schema-node-kinds-DxvWh2Ic.d.mts.map +1 -0
  131. package/dist/sql-utils.d.mts.map +1 -1
  132. package/dist/{table-source-BvFo7gVs.d.mts → table-source-D5c2fxAQ.d.mts} +1 -2
  133. package/dist/table-source-D5c2fxAQ.d.mts.map +1 -0
  134. package/dist/{types-BDKkx8MA.d.mts → types-D2MLzLBD.d.mts} +1 -2
  135. package/dist/types-D2MLzLBD.d.mts.map +1 -0
  136. package/dist/types.d.mts +6 -5
  137. package/dist/types.mjs +4 -4
  138. package/package.json +21 -21
  139. package/src/core/authoring.ts +18 -5
  140. package/src/core/codec-helpers.ts +53 -1
  141. package/src/core/codec-ids.ts +1 -0
  142. package/src/core/codecs.ts +56 -3
  143. package/src/core/default-normalizer.ts +22 -0
  144. package/src/core/descriptor-meta.ts +2 -0
  145. package/src/core/errors.ts +5 -5
  146. package/src/core/index-types.ts +15 -0
  147. package/src/core/migrations/contract-to-postgres-database-schema-node.ts +99 -19
  148. package/src/core/migrations/control-policy.ts +4 -3
  149. package/src/core/migrations/diff-database-schema.ts +6 -3
  150. package/src/core/migrations/issue-planner.ts +41 -80
  151. package/src/core/migrations/op-factory-call.ts +41 -22
  152. package/src/core/migrations/operations/data-transform.ts +2 -2
  153. package/src/core/migrations/planner-produced-postgres-migration.ts +9 -2
  154. package/src/core/migrations/planner-strategies.ts +6 -6
  155. package/src/core/migrations/planner.ts +13 -4
  156. package/src/core/migrations/postgres-migration.ts +62 -2
  157. package/src/core/migrations/render-typescript.ts +20 -8
  158. package/src/core/migrations/runner.ts +12 -11
  159. package/src/core/migrations/verify-postgres-namespaces.ts +0 -1
  160. package/src/core/psl-infer/infer-psl-contract.ts +130 -12
  161. package/src/core/schema-ir/postgres-policy-schema-node.ts +12 -2
  162. package/src/exports/codecs.ts +2 -0
  163. package/src/exports/control.ts +2 -0
  164. package/src/exports/default-normalizer.ts +1 -1
  165. package/src/exports/op-factory-call.ts +5 -0
  166. package/src/exports/types.ts +1 -0
  167. package/dist/authoring-BtsOWgI3.mjs.map +0 -1
  168. package/dist/codec-ids-B10ZZHzw.mjs.map +0 -1
  169. package/dist/codec-ids-CI144z7g.d.mts.map +0 -1
  170. package/dist/codec-types-CTfT4U4O.d.mts.map +0 -1
  171. package/dist/codecs-D7W9O0Xx.d.mts.map +0 -1
  172. package/dist/codecs-DRbb26Or.mjs.map +0 -1
  173. package/dist/control-policy-DtbkLLGv.mjs.map +0 -1
  174. package/dist/data-transform-DPzwBMhW.d.mts.map +0 -1
  175. package/dist/default-normalizer.mjs.map +0 -1
  176. package/dist/descriptor-meta-CFQKLXl_.mjs.map +0 -1
  177. package/dist/diff-database-schema-GncqMuSI.mjs.map +0 -1
  178. package/dist/errors-DO-w0EUS.mjs.map +0 -1
  179. package/dist/nodes-Dv5eCSq9.d.mts.map +0 -1
  180. package/dist/op-factory-call-Bn9zDx4_.d.mts.map +0 -1
  181. package/dist/op-factory-call-C38NzvQD.mjs.map +0 -1
  182. package/dist/planner-kql24i_F.mjs.map +0 -1
  183. package/dist/planner-produced-postgres-migration-BEY1T0Xm.d.mts.map +0 -1
  184. package/dist/planner-produced-postgres-migration-D7y-PEHK.mjs.map +0 -1
  185. package/dist/postgres-contract-view-C9j5cqP0.d.mts.map +0 -1
  186. package/dist/postgres-database-schema-node-CJv5bfbs.d.mts.map +0 -1
  187. package/dist/postgres-migration-DbckK1su.d.mts.map +0 -1
  188. package/dist/postgres-migration-arMSWU9b.mjs.map +0 -1
  189. package/dist/postgres-rls-policy--CSddvMR.d.mts.map +0 -1
  190. package/dist/postgres-role-schema-node-Cm-j0ZGM.mjs.map +0 -1
  191. package/dist/postgres-schema-DKq6s1P3.d.mts.map +0 -1
  192. package/dist/render-typescript-7yqILcwr.mjs.map +0 -1
  193. package/dist/table-source-BvFo7gVs.d.mts.map +0 -1
  194. package/dist/types-BDKkx8MA.d.mts.map +0 -1
@@ -47,7 +47,7 @@ export const pgNumericDecode = (wire: string | number): string => {
47
47
  };
48
48
 
49
49
  export const pgNumericRenderOutputType = (typeParams: {
50
- readonly precision: number;
50
+ readonly precision?: number;
51
51
  readonly scale?: number;
52
52
  }): string | undefined => {
53
53
  const precision = typeParams.precision;
@@ -106,6 +106,58 @@ export const pgTimestamptzDecodeJson = (json: JsonValue): Date => {
106
106
  return date;
107
107
  };
108
108
 
109
+ const ISO_8601_DATE = /^(\d{4})-(\d{2})-(\d{2})$/;
110
+
111
+ function formatDateOnly(year: number, month: number, day: number): string {
112
+ return `${String(year).padStart(4, '0')}-${String(month).padStart(2, '0')}-${String(day).padStart(2, '0')}`;
113
+ }
114
+
115
+ /**
116
+ * A Postgres `date` has no time-of-day or timezone component, so `pg/date@1`
117
+ * canonicalizes its JS-level value as a `Date` at UTC midnight
118
+ * (`Date.UTC(y, m, d)`), independent of the process's local timezone.
119
+ *
120
+ * `pgDateEncode` reads the calendar date via UTC getters (matching that
121
+ * canonical form) and formats it as `YYYY-MM-DD` directly, bypassing the pg
122
+ * driver's own `Date` serialization (`dateToString`), which reads *local*
123
+ * getters and would shift the calendar day near midnight in negative-UTC-offset
124
+ * environments.
125
+ */
126
+ export const pgDateEncode = (value: Date): string =>
127
+ formatDateOnly(value.getUTCFullYear(), value.getUTCMonth() + 1, value.getUTCDate());
128
+
129
+ /**
130
+ * Normalizes the pg driver's already-parsed `Date` for a `date` column into
131
+ * the canonical UTC-midnight form. The driver (via `postgres-date`) builds
132
+ * that `Date` at *local* midnight from the wire text; reading it back with the
133
+ * same (local) getters recovers the exact calendar date the driver parsed,
134
+ * and reconstructing via `Date.UTC` makes the result's instant independent of
135
+ * the process's timezone.
136
+ */
137
+ export const pgDateDecode = (wire: Date): Date =>
138
+ new Date(Date.UTC(wire.getFullYear(), wire.getMonth(), wire.getDate()));
139
+
140
+ export const pgDateEncodeJson = (value: Date): JsonValue => pgDateEncode(value);
141
+
142
+ export const pgDateDecodeJson = (json: JsonValue): Date => {
143
+ if (typeof json !== 'string') {
144
+ throw new Error(`Expected date string for pg/date@1, got ${typeof json}`);
145
+ }
146
+ const match = ISO_8601_DATE.exec(json);
147
+ if (!match) {
148
+ throw new Error(`Invalid date string for pg/date@1: ${json}`);
149
+ }
150
+ const [, yearText, monthText, dayText] = match;
151
+ const year = Number(yearText);
152
+ const month = Number(monthText) - 1;
153
+ const day = Number(dayText);
154
+ const date = new Date(Date.UTC(year, month, day));
155
+ if (date.getUTCFullYear() !== year || date.getUTCMonth() !== month || date.getUTCDate() !== day) {
156
+ throw new Error(`Invalid date string for pg/date@1: ${json}`);
157
+ }
158
+ return date;
159
+ };
160
+
109
161
  export const pgIntervalDecode = (wire: string | Record<string, unknown>): string => {
110
162
  if (typeof wire === 'string') return wire;
111
163
  return JSON.stringify(wire);
@@ -22,6 +22,7 @@ export const PG_NUMERIC_CODEC_ID = 'pg/numeric@1' as const;
22
22
  export const PG_BOOL_CODEC_ID = 'pg/bool@1' as const;
23
23
  export const PG_BIT_CODEC_ID = 'pg/bit@1' as const;
24
24
  export const PG_VARBIT_CODEC_ID = 'pg/varbit@1' as const;
25
+ export const PG_DATE_CODEC_ID = 'pg/date@1' as const;
25
26
  export const PG_TIMESTAMP_CODEC_ID = 'pg/timestamp@1' as const;
26
27
  export const PG_TIMESTAMPTZ_CODEC_ID = 'pg/timestamptz@1' as const;
27
28
  export const PG_TIME_CODEC_ID = 'pg/time@1' as const;
@@ -45,6 +45,10 @@ import { type as arktype } from 'arktype';
45
45
  import {
46
46
  pgByteaDecodeJson,
47
47
  pgByteaEncodeJson,
48
+ pgDateDecode,
49
+ pgDateDecodeJson,
50
+ pgDateEncode,
51
+ pgDateEncodeJson,
48
52
  pgIntervalDecode,
49
53
  pgJsonbDecode,
50
54
  pgJsonbEncode,
@@ -64,6 +68,7 @@ import {
64
68
  PG_BOOL_CODEC_ID,
65
69
  PG_BYTEA_CODEC_ID,
66
70
  PG_CHAR_CODEC_ID,
71
+ PG_DATE_CODEC_ID,
67
72
  PG_ENUM_CODEC_ID,
68
73
  PG_FLOAT_CODEC_ID,
69
74
  PG_FLOAT4_CODEC_ID,
@@ -92,14 +97,14 @@ import { PostgresNativeEnum } from './postgres-native-enum';
92
97
 
93
98
  type LengthParams = { readonly length?: number };
94
99
  type PrecisionParams = { readonly precision?: number };
95
- type NumericParams = { readonly precision: number; readonly scale?: number };
100
+ type NumericParams = { readonly precision?: number; readonly scale?: number };
96
101
 
97
102
  const lengthParamsSchema = arktype({
98
103
  'length?': 'number.integer > 0',
99
104
  }) satisfies StandardSchemaV1<LengthParams>;
100
105
 
101
106
  const numericParamsSchema = arktype({
102
- precision: 'number.integer > 0 & number.integer <= 1000',
107
+ 'precision?': 'number.integer > 0 & number.integer <= 1000',
103
108
  'scale?': 'number.integer >= 0',
104
109
  }) satisfies StandardSchemaV1<NumericParams>;
105
110
 
@@ -115,6 +120,7 @@ const PG_INT8_META = { db: { sql: { postgres: { nativeType: 'bigint' } } } } as
115
120
  const PG_FLOAT4_META = { db: { sql: { postgres: { nativeType: 'real' } } } } as const;
116
121
  const PG_FLOAT8_META = { db: { sql: { postgres: { nativeType: 'double precision' } } } } as const;
117
122
  const PG_NUMERIC_META = { db: { sql: { postgres: { nativeType: 'numeric' } } } } as const;
123
+ const PG_DATE_META = { db: { sql: { postgres: { nativeType: 'date' } } } } as const;
118
124
  const PG_TIMESTAMP_META = {
119
125
  db: { sql: { postgres: { nativeType: 'timestamp without time zone' } } },
120
126
  } as const;
@@ -648,12 +654,57 @@ export class PgNumericDescriptor extends CodecDescriptorImpl<NumericParams> {
648
654
 
649
655
  export const pgNumericDescriptor = new PgNumericDescriptor();
650
656
 
651
- export const pgNumericColumn = (params: NumericParams) =>
657
+ export const pgNumericColumn = (params: NumericParams = {}) =>
652
658
  column(pgNumericDescriptor.factory(params), pgNumericDescriptor.codecId, params, 'numeric');
653
659
 
654
660
  pgNumericColumn satisfies ColumnHelperFor<PgNumericDescriptor>;
655
661
  pgNumericColumn satisfies ColumnHelperForStrict<PgNumericDescriptor>;
656
662
 
663
+ /**
664
+ * A Postgres `date` has no time-of-day or timezone component. This codec
665
+ * canonicalizes its JS-level value as a `Date` at UTC midnight, so its
666
+ * round-trip is independent of the process's local timezone — see
667
+ * `pgDateEncode`/`pgDateDecode` in `codec-helpers.ts`.
668
+ */
669
+ export class PgDateCodec extends CodecImpl<
670
+ typeof PG_DATE_CODEC_ID,
671
+ readonly ['equality', 'order'],
672
+ Date | string,
673
+ Date
674
+ > {
675
+ async encode(value: Date, _ctx: CodecCallContext): Promise<string> {
676
+ return pgDateEncode(value);
677
+ }
678
+ async decode(wire: Date, _ctx: CodecCallContext): Promise<Date> {
679
+ return pgDateDecode(wire);
680
+ }
681
+ encodeJson(value: Date): JsonValue {
682
+ return pgDateEncodeJson(value);
683
+ }
684
+ decodeJson(json: JsonValue): Date {
685
+ return pgDateDecodeJson(json);
686
+ }
687
+ }
688
+
689
+ export class PgDateDescriptor extends CodecDescriptorImpl<void> {
690
+ override readonly codecId = PG_DATE_CODEC_ID;
691
+ override readonly traits = ['equality', 'order'] as const;
692
+ override readonly targetTypes = ['date'] as const;
693
+ override readonly meta = PG_DATE_META;
694
+ override readonly paramsSchema: StandardSchemaV1<void> = voidParamsSchema;
695
+ override factory(): (ctx: CodecInstanceContext) => PgDateCodec {
696
+ return () => new PgDateCodec(this);
697
+ }
698
+ }
699
+
700
+ export const pgDateDescriptor = new PgDateDescriptor();
701
+
702
+ export const pgDateColumn = () =>
703
+ column(pgDateDescriptor.factory(), pgDateDescriptor.codecId, undefined, 'date');
704
+
705
+ pgDateColumn satisfies ColumnHelperFor<PgDateDescriptor>;
706
+ pgDateColumn satisfies ColumnHelperForStrict<PgDateDescriptor>;
707
+
657
708
  export class PgTimestampCodec extends CodecImpl<
658
709
  typeof PG_TIMESTAMP_CODEC_ID,
659
710
  readonly ['equality', 'order'],
@@ -1285,6 +1336,8 @@ export const codecDescriptors: readonly AnyCodecDescriptor[] = [
1285
1336
  pgFloat4Descriptor,
1286
1337
  pgFloat8Descriptor,
1287
1338
  pgNumericDescriptor,
1339
+ // PSL `@db.Date` binding is deferred to remove-db-attributes' `Date` type; pin it via codecId, not a second targetTypes activation here.
1340
+ pgDateDescriptor,
1288
1341
  pgTimestampDescriptor,
1289
1342
  pgTimestamptzDescriptor,
1290
1343
  pgTimeDescriptor,
@@ -249,3 +249,25 @@ export function parsePostgresDefault(
249
249
 
250
250
  return { kind: 'function', expression: trimmed };
251
251
  }
252
+
253
+ /**
254
+ * Normalizes a contract-declared default through {@link parsePostgresDefault}
255
+ * — the same parser introspection uses — so a function-shaped default the
256
+ * parser recognizes as a literal (e.g. `dbgenerated("'{}'::jsonb")`)
257
+ * resolves to the same `resolvedDefault` shape a live introspected column
258
+ * would produce. Compensates once, at `SchemaIR` construction of the
259
+ * expected (contract-derived) side (`contractToSchemaIR`'s `resolveDefault`
260
+ * hook), instead of at every site that later compares the two sides. A
261
+ * literal default, or a function form the parser doesn't recognize, passes
262
+ * through unchanged; `nextval(...)` normalizes to `autoincrement()` on both
263
+ * sides, matching a `serial`/identity column's introspected counterpart.
264
+ */
265
+ export function postgresResolveDefault(
266
+ def: ColumnDefault,
267
+ resolvedNativeType: string,
268
+ ): ColumnDefault {
269
+ if (def.kind !== 'function') {
270
+ return def;
271
+ }
272
+ return parsePostgresDefault(def.expression, resolvedNativeType) ?? def;
273
+ }
@@ -9,12 +9,14 @@ import {
9
9
  } from './authoring';
10
10
  import { postgresQualifyColumnType } from './codecs';
11
11
  import { postgresTargetDescriptorMetaRuntime } from './descriptor-meta-runtime';
12
+ import { postgresIndexTypes } from './index-types';
12
13
  import { DEFAULT_NAMESPACE_ID } from './namespace-ids';
13
14
  import { postgresCreateNamespace } from './postgres-schema';
14
15
 
15
16
  const postgresTargetDescriptorMetaBase = {
16
17
  ...postgresTargetDescriptorMetaRuntime,
17
18
  defaultNamespaceId: DEFAULT_NAMESPACE_ID,
19
+ indexTypes: postgresIndexTypes,
18
20
  authoring: {
19
21
  type: postgresAuthoringTypes,
20
22
  field: postgresAuthoringFieldPresets,
@@ -12,9 +12,10 @@ import { CliStructuredError } from '@prisma-next/errors/control';
12
12
  * The `operation` argument is required so every throw site names the operation
13
13
  * that actually failed; a new operation cannot inherit a misattributed message.
14
14
  *
15
- * Distinct from `PN-MIG-2001` (placeholder not filled) and `PN-MIG-2005`
16
- * (data-transform query plan against wrong contract) because the missing
17
- * input is the stack itself, not the per-operation contract.
15
+ * Distinct from `MIGRATION.UNFILLED_PLACEHOLDER` (placeholder not filled)
16
+ * and `MIGRATION.DATA_TRANSFORM_CONTRACT_MISMATCH` (data-transform query
17
+ * plan against wrong contract) because the missing input is the stack
18
+ * itself, not the per-operation contract.
18
19
  *
19
20
  * Lives in `@prisma-next/target-postgres/errors` rather than the shared
20
21
  * framework migration errors module because the failure is target-specific:
@@ -23,10 +24,9 @@ import { CliStructuredError } from '@prisma-next/errors/control';
23
24
  */
24
25
  export function errorPostgresMigrationStackMissing(operation: string): CliStructuredError {
25
26
  return new CliStructuredError(
26
- '2007',
27
+ 'MIGRATION.POSTGRES_CONTROL_STACK_MISSING',
27
28
  `PostgresMigration.${operation} requires a control adapter`,
28
29
  {
29
- domain: 'MIG',
30
30
  why: `PostgresMigration.${operation} was invoked on an instance constructed without a ControlStack, so the stored controlAdapter is undefined and the operation cannot lower its plan.`,
31
31
  fix: 'Construct the migration via the migration CLI entrypoint (which assembles a ControlStack from the loaded prisma-next.config.ts), or pass a ControlStack containing a Postgres adapter to the migration constructor in test fixtures.',
32
32
  meta: { operation },
@@ -0,0 +1,15 @@
1
+ import { defineIndexTypes } from '@prisma-next/sql-contract/index-types';
2
+ import { type } from 'arktype';
3
+
4
+ // Postgres's built-in index access methods (`CREATE INDEX ... USING <method>`).
5
+ // Per-method option validation (e.g. `gin` operator classes) is out of scope;
6
+ // every method accepts any options object until a later slice narrows it.
7
+ export const postgresIndexTypes = defineIndexTypes()
8
+ .add('btree', { options: type('object') })
9
+ .add('hash', { options: type('object') })
10
+ .add('gin', { options: type('object') })
11
+ .add('gist', { options: type('object') })
12
+ .add('spgist', { options: type('object') })
13
+ .add('brin', { options: type('object') });
14
+
15
+ export type IndexTypes = typeof postgresIndexTypes.IndexTypes;
@@ -1,7 +1,14 @@
1
1
  import type { ContractToSchemaIROptions } from '@prisma-next/family-sql/control';
2
2
  import { contractNamespaceToSchemaIR } from '@prisma-next/family-sql/control';
3
+ import type { SchemaNodeRef } from '@prisma-next/framework-components/control';
3
4
  import { UNBOUND_NAMESPACE_ID } from '@prisma-next/framework-components/ir';
4
- import { SqlForeignKeyIR } from '@prisma-next/sql-schema-ir/types';
5
+ import {
6
+ PrimaryKey,
7
+ RelationalSchemaNodeKind,
8
+ SqlForeignKeyIR,
9
+ SqlIndexIR,
10
+ SqlUniqueIR,
11
+ } from '@prisma-next/sql-schema-ir/types';
5
12
  import { ifDefined } from '@prisma-next/utils/defined';
6
13
  import type { PostgresRlsPolicy } from '../postgres-rls-policy';
7
14
  import type { PostgresContract } from '../postgres-schema';
@@ -12,8 +19,43 @@ import { PostgresNativeEnumSchemaNode } from '../schema-ir/postgres-native-enum-
12
19
  import { PostgresPolicySchemaNode } from '../schema-ir/postgres-policy-schema-node';
13
20
  import { PostgresRoleSchemaNode } from '../schema-ir/postgres-role-schema-node';
14
21
  import { PostgresTableSchemaNode } from '../schema-ir/postgres-table-schema-node';
22
+ import { PostgresSchemaNodeKind } from '../schema-ir/schema-node-kinds';
15
23
  import { resolveDdlSchemaForNamespaceStorage } from './resolve-ddl-schema';
16
24
 
25
+ /** The database root's fixed sentinel id (`PostgresDatabaseSchemaNode#id`). */
26
+ function databaseStep(): { readonly nodeKind: string; readonly id: string } {
27
+ return { nodeKind: PostgresSchemaNodeKind.database, id: 'database' };
28
+ }
29
+
30
+ function tableDependsOn(namespaceId: string, tableName: string): SchemaNodeRef {
31
+ return [
32
+ databaseStep(),
33
+ { nodeKind: PostgresSchemaNodeKind.namespace, id: namespaceId },
34
+ { nodeKind: PostgresSchemaNodeKind.table, id: tableName },
35
+ ];
36
+ }
37
+
38
+ function roleDependsOn(role: string): SchemaNodeRef {
39
+ return [databaseStep(), { nodeKind: PostgresSchemaNodeKind.role, id: role }];
40
+ }
41
+
42
+ /**
43
+ * The chains from a table-child object (foreign key, index, unique, primary
44
+ * key) to each of the own columns it is built on, in the Postgres tree.
45
+ * Dropping a covered column auto-drops the object, so the object's drop must
46
+ * precede the column's; the graph derives that direction from these edges.
47
+ */
48
+ function columnDependsOn(
49
+ namespaceId: string,
50
+ tableName: string,
51
+ columns: readonly string[],
52
+ ): readonly SchemaNodeRef[] {
53
+ return columns.map((column) => [
54
+ ...tableDependsOn(namespaceId, tableName),
55
+ { nodeKind: RelationalSchemaNodeKind.column, id: `column:${column}` },
56
+ ]);
57
+ }
58
+
17
59
  function toPolicyNode(policy: PostgresRlsPolicy, namespaceId: string): PostgresPolicySchemaNode {
18
60
  return new PostgresPolicySchemaNode({
19
61
  name: policy.name,
@@ -25,6 +67,7 @@ function toPolicyNode(policy: PostgresRlsPolicy, namespaceId: string): PostgresP
25
67
  ...ifDefined('using', policy.using),
26
68
  ...ifDefined('withCheck', policy.withCheck),
27
69
  permissive: policy.permissive,
70
+ dependsOn: [tableDependsOn(namespaceId, policy.tableName), ...policy.roles.map(roleDependsOn)],
28
71
  });
29
72
  }
30
73
 
@@ -115,30 +158,67 @@ export function contractToPostgresDatabaseSchemaNode(
115
158
  // clauses, and resolves the real live DDL schema — introspected FKs
116
159
  // already carry the live schema, so this is what lets an expected FK
117
160
  // pair (by diff-node id) with its introspected counterpart.
118
- const foreignKeys = sqlTable.foreignKeys.map(
119
- (fk) =>
120
- new SqlForeignKeyIR({
121
- columns: fk.columns,
122
- referencedTable: fk.referencedTable,
123
- referencedColumns: fk.referencedColumns,
124
- referencedSchema: fk.referencedSchema ?? UNBOUND_NAMESPACE_ID,
125
- ...ifDefined('name', fk.name),
126
- ...ifDefined('onDelete', fk.onDelete),
127
- ...ifDefined('onUpdate', fk.onUpdate),
128
- ...ifDefined('annotations', fk.annotations),
129
- resolvedReferencedNamespace: resolveDdlSchemaForNamespaceStorage(
130
- contract.storage,
131
- fk.referencedSchema ?? UNBOUND_NAMESPACE_ID,
132
- ),
161
+ const foreignKeys = sqlTable.foreignKeys.map((fk) => {
162
+ const resolvedReferencedNamespace = resolveDdlSchemaForNamespaceStorage(
163
+ contract.storage,
164
+ fk.referencedSchema ?? UNBOUND_NAMESPACE_ID,
165
+ );
166
+ return new SqlForeignKeyIR({
167
+ columns: fk.columns,
168
+ referencedTable: fk.referencedTable,
169
+ referencedColumns: fk.referencedColumns,
170
+ referencedSchema: fk.referencedSchema ?? UNBOUND_NAMESPACE_ID,
171
+ ...ifDefined('name', fk.name),
172
+ ...ifDefined('onDelete', fk.onDelete),
173
+ ...ifDefined('onUpdate', fk.onUpdate),
174
+ ...ifDefined('annotations', fk.annotations),
175
+ resolvedReferencedNamespace,
176
+ dependsOn: [
177
+ tableDependsOn(resolvedReferencedNamespace, fk.referencedTable),
178
+ ...columnDependsOn(ddlSchema, tableName, fk.columns),
179
+ ],
180
+ });
181
+ });
182
+ // The family stamped these children's own-column `dependsOn` with the
183
+ // flat (single-schema) chain; the Postgres tree nests them under a
184
+ // namespace, so re-stamp with the multi-schema chain that matches this
185
+ // tree's paths. Every other field is carried through unchanged.
186
+ const uniques = sqlTable.uniques.map(
187
+ (u) =>
188
+ new SqlUniqueIR({
189
+ columns: u.columns,
190
+ ...ifDefined('name', u.name),
191
+ ...ifDefined('annotations', u.annotations),
192
+ dependsOn: columnDependsOn(ddlSchema, tableName, u.columns),
193
+ }),
194
+ );
195
+ const indexes = sqlTable.indexes.map(
196
+ (i) =>
197
+ new SqlIndexIR({
198
+ columns: i.columns,
199
+ unique: i.unique,
200
+ ...ifDefined('name', i.name),
201
+ ...ifDefined('type', i.type),
202
+ ...ifDefined('options', i.options),
203
+ ...ifDefined('annotations', i.annotations),
204
+ dependsOn: columnDependsOn(ddlSchema, tableName, i.columns),
133
205
  }),
134
206
  );
207
+ const primaryKey =
208
+ sqlTable.primaryKey !== undefined
209
+ ? new PrimaryKey({
210
+ columns: sqlTable.primaryKey.columns,
211
+ ...ifDefined('name', sqlTable.primaryKey.name),
212
+ dependsOn: columnDependsOn(ddlSchema, tableName, sqlTable.primaryKey.columns),
213
+ })
214
+ : undefined;
135
215
  tables[tableName] = new PostgresTableSchemaNode({
136
216
  name: sqlTable.name,
137
217
  columns: sqlTable.columns,
138
218
  foreignKeys,
139
- uniques: sqlTable.uniques,
140
- indexes: sqlTable.indexes,
141
- ...ifDefined('primaryKey', sqlTable.primaryKey),
219
+ uniques,
220
+ indexes,
221
+ ...ifDefined('primaryKey', primaryKey),
142
222
  ...ifDefined('annotations', sqlTable.annotations),
143
223
  ...ifDefined('checks', sqlTable.checks),
144
224
  policies: policiesByTable.get(tableName) ?? [],
@@ -5,6 +5,7 @@ import type {
5
5
  SuppressionRecord,
6
6
  } from '@prisma-next/family-sql/control';
7
7
  import type { SchemaDiffIssue } from '@prisma-next/framework-components/control';
8
+ import { issueOutcome } from '@prisma-next/framework-components/control';
8
9
  import { entityAt, UNBOUND_NAMESPACE_ID } from '@prisma-next/framework-components/ir';
9
10
  import type { SqlStorage, StorageTable } from '@prisma-next/sql-contract/types';
10
11
  import { blindCast } from '@prisma-next/utils/casts';
@@ -252,7 +253,7 @@ const POSTGRES_NODE_CREATION_FACTORY: Readonly<Record<string, string>> = Object.
252
253
  * it is not admitted as creation-class here.
253
254
  */
254
255
  function isEnablementCreationIssue(issue: SchemaDiffIssue<SqlSchemaDiffNode>): boolean {
255
- if (issue.reason !== 'not-equal') return false;
256
+ if (issueOutcome(issue) !== 'not-equal') return false;
256
257
  const { expected, actual } = issue;
257
258
  return (
258
259
  expected !== undefined &&
@@ -270,7 +271,7 @@ export function resolvePostgresNodeIssueCreationFactoryName(
270
271
  if (isEnablementCreationIssue(issue)) {
271
272
  return 'enableRowLevelSecurity';
272
273
  }
273
- if (issue.reason !== 'not-found') return undefined;
274
+ if (issueOutcome(issue) !== 'not-found') return undefined;
274
275
  const node = issue.expected ?? issue.actual;
275
276
  if (node === undefined) return undefined;
276
277
  return POSTGRES_NODE_CREATION_FACTORY[node.nodeKind];
@@ -298,7 +299,7 @@ export function resolvePostgresNodeIssueControlPolicySubject(
298
299
  >(node);
299
300
  return {
300
301
  namespaceId: resolveNamespaceIdForDdlSchema(contract, namespaceNode.schemaName),
301
- createsNewObject: issue.reason === 'not-found',
302
+ createsNewObject: issueOutcome(issue) === 'not-found',
302
303
  };
303
304
  }
304
305
 
@@ -4,12 +4,13 @@ import { buildNativeTypeExpander } from '@prisma-next/family-sql/control';
4
4
  import { classifyDiffSubjectGranularity } from '@prisma-next/family-sql/diff';
5
5
  import type { TargetBoundComponentDescriptor } from '@prisma-next/framework-components/components';
6
6
  import type { DiffableNode, SchemaDiffIssue } from '@prisma-next/framework-components/control';
7
- import { diffSchemas } from '@prisma-next/framework-components/control';
7
+ import { diffSchemas, issueOutcome } from '@prisma-next/framework-components/control';
8
8
  import { UNBOUND_NAMESPACE_ID } from '@prisma-next/framework-components/ir';
9
9
  import type { SqlStorage } from '@prisma-next/sql-contract/types';
10
10
  import type { SqlSchemaIRNode } from '@prisma-next/sql-schema-ir/types';
11
11
  import { blindCast } from '@prisma-next/utils/casts';
12
12
  import { ifDefined } from '@prisma-next/utils/defined';
13
+ import { postgresResolveDefault } from '../default-normalizer';
13
14
  import type { PostgresContract } from '../postgres-schema';
14
15
  import { PostgresDatabaseSchemaNode } from '../schema-ir/postgres-database-schema-node';
15
16
  import { PostgresNamespaceSchemaNode } from '../schema-ir/postgres-namespace-schema-node';
@@ -135,12 +136,13 @@ export function diffPostgresSchema(input: {
135
136
  const fullExpected = contractToPostgresDatabaseSchemaNode(postgresContract, {
136
137
  annotationNamespace: 'pg',
137
138
  ...ifDefined('expandNativeType', expandNativeType),
139
+ resolveDefault: postgresResolveDefault,
138
140
  });
139
141
  const expected = pruneTableLessNamespaces(fullExpected);
140
142
  const relationalOwned = ownedSchemaNames(expected);
141
143
  const structuralOwned = ownedSchemaNames(fullExpected);
142
144
  const issues = diffSchemas(expected, actual).filter((issue) => {
143
- if (issue.reason !== 'not-expected') return true;
145
+ if (issueOutcome(issue) !== 'not-expected') return true;
144
146
  if (isClusterScopedIssue(issue)) return true;
145
147
  const granularity = classifyDiffSubjectGranularity(issue, postgresDiffSubjectGranularity);
146
148
  const namespaceSegment = issue.path[1];
@@ -230,6 +232,7 @@ export function buildPostgresPlanDiff(input: {
230
232
  const projectionOptions = {
231
233
  annotationNamespace: 'pg',
232
234
  ...ifDefined('expandNativeType', expandNativeType),
235
+ resolveDefault: postgresResolveDefault,
233
236
  };
234
237
  const fullExpected = contractToPostgresDatabaseSchemaNode(postgresContract, projectionOptions);
235
238
  const expected = pruneTableLessNamespaces(fullExpected);
@@ -240,7 +243,7 @@ export function buildPostgresPlanDiff(input: {
240
243
  readonly SchemaDiffIssue<SqlSchemaDiffNode>[],
241
244
  'both trees are PostgresDatabaseSchemaNodes, so every diff-issue node is a SqlSchemaDiffNode'
242
245
  >(diffSchemas(expected, paddedActual)).filter((issue) => {
243
- if (issue.reason !== 'not-expected') return true;
246
+ if (issueOutcome(issue) !== 'not-expected') return true;
244
247
  if (isClusterScopedIssue(issue)) return true;
245
248
  const granularity = classifyDiffSubjectGranularity(issue, postgresDiffSubjectGranularity);
246
249
  const namespaceSegment = issue.path[1];