@prisma-next/target-postgres 0.14.0-dev.7 → 0.14.0-dev.71

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 (265) hide show
  1. package/dist/authoring-Bi9aa8zG.mjs +574 -0
  2. package/dist/authoring-Bi9aa8zG.mjs.map +1 -0
  3. package/dist/canonicalize-CAbXtVtB.d.mts +37 -0
  4. package/dist/canonicalize-CAbXtVtB.d.mts.map +1 -0
  5. package/dist/{codec-ids-CnXu9Qy3.d.mts → codec-ids-BzrFF-I4.d.mts} +3 -2
  6. package/dist/codec-ids-BzrFF-I4.d.mts.map +1 -0
  7. package/dist/{codec-ids-BvytN2P8.mjs → codec-ids-C_-Hj6bL.mjs} +3 -2
  8. package/dist/codec-ids-C_-Hj6bL.mjs.map +1 -0
  9. package/dist/codec-ids.d.mts +2 -2
  10. package/dist/codec-ids.mjs +2 -2
  11. package/dist/{codec-types-DHCkwPKE.d.mts → codec-types-BiIWziRO.d.mts} +3 -2
  12. package/dist/codec-types-BiIWziRO.d.mts.map +1 -0
  13. package/dist/codec-types.d.mts +1 -1
  14. package/dist/codecs-Cl_hQHg-.mjs +913 -0
  15. package/dist/codecs-Cl_hQHg-.mjs.map +1 -0
  16. package/dist/{codecs--0A5_4Bq.d.mts → codecs-Cm-nS4mQ.d.mts} +111 -4
  17. package/dist/codecs-Cm-nS4mQ.d.mts.map +1 -0
  18. package/dist/codecs.d.mts +2 -2
  19. package/dist/codecs.mjs +3 -757
  20. package/dist/codecs.mjs.map +1 -1
  21. package/dist/contract-free.d.mts +33 -3
  22. package/dist/contract-free.d.mts.map +1 -1
  23. package/dist/contract-free.mjs +23 -7
  24. package/dist/contract-free.mjs.map +1 -1
  25. package/dist/control-policy-CuUhjWnn.mjs +1258 -0
  26. package/dist/control-policy-CuUhjWnn.mjs.map +1 -0
  27. package/dist/control.d.mts +2 -2
  28. package/dist/control.d.mts.map +1 -1
  29. package/dist/control.mjs +866 -36
  30. package/dist/control.mjs.map +1 -1
  31. package/dist/data-transform-BOWpliq8.mjs.map +1 -1
  32. package/dist/{data-transform-DDgWdB5o.d.mts → data-transform-DPzwBMhW.d.mts} +3 -3
  33. package/dist/{data-transform-DDgWdB5o.d.mts.map → data-transform-DPzwBMhW.d.mts.map} +1 -1
  34. package/dist/data-transform.d.mts +1 -1
  35. package/dist/{ddl-QDyOSeLc.mjs → ddl-Dqgs-bDc.mjs} +93 -6
  36. package/dist/ddl-Dqgs-bDc.mjs.map +1 -0
  37. package/dist/ddl.d.mts +3 -2
  38. package/dist/ddl.mjs +2 -2
  39. package/dist/default-normalizer.d.mts +1 -1
  40. package/dist/default-normalizer.d.mts.map +1 -1
  41. package/dist/default-normalizer.mjs +247 -1
  42. package/dist/default-normalizer.mjs.map +1 -0
  43. package/dist/descriptor-meta-UugzVM-U.mjs +22 -0
  44. package/dist/descriptor-meta-UugzVM-U.mjs.map +1 -0
  45. package/dist/descriptor-meta-runtime-UwYdV8fD.mjs +12 -0
  46. package/dist/descriptor-meta-runtime-UwYdV8fD.mjs.map +1 -0
  47. package/dist/diff-database-schema-ClN3bkRS.mjs +305 -0
  48. package/dist/diff-database-schema-ClN3bkRS.mjs.map +1 -0
  49. package/dist/diff-database-schema.d.mts +46 -0
  50. package/dist/diff-database-schema.d.mts.map +1 -0
  51. package/dist/diff-database-schema.mjs +2 -0
  52. package/dist/errors-DO-w0EUS.mjs +35 -0
  53. package/dist/errors-DO-w0EUS.mjs.map +1 -0
  54. package/dist/errors.d.mts +11 -9
  55. package/dist/errors.d.mts.map +1 -1
  56. package/dist/errors.mjs +1 -1
  57. package/dist/issue-planner.d.mts +35 -11
  58. package/dist/issue-planner.d.mts.map +1 -1
  59. package/dist/issue-planner.mjs +2 -2
  60. package/dist/migration.d.mts +5 -5
  61. package/dist/migration.d.mts.map +1 -1
  62. package/dist/migration.mjs +4 -4
  63. package/dist/namespace-ids-xtp_ZY86.mjs +7 -0
  64. package/dist/namespace-ids-xtp_ZY86.mjs.map +1 -0
  65. package/dist/native-type-normalizer.mjs +37 -1
  66. package/dist/native-type-normalizer.mjs.map +1 -0
  67. package/dist/nodes-Bc2N3tMy.d.mts +170 -0
  68. package/dist/nodes-Bc2N3tMy.d.mts.map +1 -0
  69. package/dist/nodes-DJC5B8Id.mjs +192 -0
  70. package/dist/nodes-DJC5B8Id.mjs.map +1 -0
  71. package/dist/{op-factory-call-DmQEc3XV.d.mts → op-factory-call-CSIg5XDu.d.mts} +91 -15
  72. package/dist/op-factory-call-CSIg5XDu.d.mts.map +1 -0
  73. package/dist/{op-factory-call-D_p5vxwt.mjs → op-factory-call-DtVSP_7Y.mjs} +400 -32
  74. package/dist/op-factory-call-DtVSP_7Y.mjs.map +1 -0
  75. package/dist/op-factory-call.d.mts +2 -2
  76. package/dist/op-factory-call.mjs +2 -2
  77. package/dist/pack.d.mts +300 -3
  78. package/dist/pack.d.mts.map +1 -1
  79. package/dist/pack.mjs +1 -1
  80. package/dist/planner-ChVasl04.mjs +390 -0
  81. package/dist/planner-ChVasl04.mjs.map +1 -0
  82. package/dist/{planner-ddl-builders-B2wOwLqI.mjs → planner-ddl-builders-BKlmGDJl.mjs} +17 -28
  83. package/dist/planner-ddl-builders-BKlmGDJl.mjs.map +1 -0
  84. package/dist/planner-ddl-builders.d.mts +4 -6
  85. package/dist/planner-ddl-builders.d.mts.map +1 -1
  86. package/dist/planner-ddl-builders.mjs +2 -2
  87. package/dist/planner-identity-values-CJPha2Sz.mjs.map +1 -1
  88. package/dist/planner-identity-values.d.mts +8 -2
  89. package/dist/planner-identity-values.d.mts.map +1 -1
  90. package/dist/planner-identity-values.mjs +2 -2
  91. package/dist/{planner-produced-postgres-migration-QqHa2C2l.d.mts → planner-produced-postgres-migration-CXue-6M9.d.mts} +4 -4
  92. package/dist/{planner-produced-postgres-migration-QqHa2C2l.d.mts.map → planner-produced-postgres-migration-CXue-6M9.d.mts.map} +1 -1
  93. package/dist/{planner-produced-postgres-migration-Cji5vxUf.mjs → planner-produced-postgres-migration-qpU05c__.mjs} +3 -3
  94. package/dist/{planner-produced-postgres-migration-Cji5vxUf.mjs.map → planner-produced-postgres-migration-qpU05c__.mjs.map} +1 -1
  95. package/dist/planner-produced-postgres-migration.d.mts +1 -1
  96. package/dist/planner-produced-postgres-migration.mjs +1 -1
  97. package/dist/{planner-sql-checks-jqUUGyQR.mjs → planner-sql-checks-CDXn2uaS.mjs} +5 -5
  98. package/dist/{planner-sql-checks-jqUUGyQR.mjs.map → planner-sql-checks-CDXn2uaS.mjs.map} +1 -1
  99. package/dist/planner-sql-checks.d.mts +2 -2
  100. package/dist/planner-sql-checks.d.mts.map +1 -1
  101. package/dist/planner-sql-checks.mjs +1 -1
  102. package/dist/{planner-target-details-CIY6tLeo.d.mts → planner-target-details-HkP4RrRO.d.mts} +2 -2
  103. package/dist/planner-target-details-HkP4RrRO.d.mts.map +1 -0
  104. package/dist/planner-target-details.d.mts +1 -1
  105. package/dist/planner.d.mts +72 -17
  106. package/dist/planner.d.mts.map +1 -1
  107. package/dist/planner.mjs +3 -2
  108. package/dist/{postgres-contract-serializer-k3TAcPMY.mjs → postgres-contract-view-C3LPCLP_.mjs} +45 -41
  109. package/dist/postgres-contract-view-C3LPCLP_.mjs.map +1 -0
  110. package/dist/postgres-contract-view-C9j5cqP0.d.mts +34 -0
  111. package/dist/postgres-contract-view-C9j5cqP0.d.mts.map +1 -0
  112. package/dist/postgres-database-schema-node-CJv5bfbs.d.mts +262 -0
  113. package/dist/postgres-database-schema-node-CJv5bfbs.d.mts.map +1 -0
  114. package/dist/{postgres-migration-Y4YBJqkS.d.mts → postgres-migration-DkmgGNcv.d.mts} +47 -9
  115. package/dist/postgres-migration-DkmgGNcv.d.mts.map +1 -0
  116. package/dist/{postgres-migration-B5jKrXv3.mjs → postgres-migration-fndp3Mbm.mjs} +69 -45
  117. package/dist/postgres-migration-fndp3Mbm.mjs.map +1 -0
  118. package/dist/postgres-native-enum-BwVNf0Ku.mjs +45 -0
  119. package/dist/postgres-native-enum-BwVNf0Ku.mjs.map +1 -0
  120. package/dist/postgres-rls-policy--CSddvMR.d.mts +52 -0
  121. package/dist/postgres-rls-policy--CSddvMR.d.mts.map +1 -0
  122. package/dist/postgres-role-schema-node-Cm-j0ZGM.mjs +190 -0
  123. package/dist/postgres-role-schema-node-Cm-j0ZGM.mjs.map +1 -0
  124. package/dist/postgres-schema-DKq6s1P3.d.mts +269 -0
  125. package/dist/postgres-schema-DKq6s1P3.d.mts.map +1 -0
  126. package/dist/{postgres-schema-COGZ1ark.mjs → postgres-schema-DYATroNx.mjs} +200 -11
  127. package/dist/postgres-schema-DYATroNx.mjs.map +1 -0
  128. package/dist/postgres-table-schema-node-D6LvInCe.mjs +216 -0
  129. package/dist/postgres-table-schema-node-D6LvInCe.mjs.map +1 -0
  130. package/dist/render-ops.d.mts +1 -1
  131. package/dist/{render-typescript-KMgosran.mjs → render-typescript-7yqILcwr.mjs} +45 -16
  132. package/dist/render-typescript-7yqILcwr.mjs.map +1 -0
  133. package/dist/render-typescript.mjs +1 -1
  134. package/dist/resolve-ddl-schema-DpGte50u.mjs +22 -0
  135. package/dist/resolve-ddl-schema-DpGte50u.mjs.map +1 -0
  136. package/dist/rls-canonicalize.d.mts +20 -0
  137. package/dist/rls-canonicalize.d.mts.map +1 -0
  138. package/dist/rls-canonicalize.mjs +2 -0
  139. package/dist/runtime.d.mts +6 -8
  140. package/dist/runtime.d.mts.map +1 -1
  141. package/dist/runtime.mjs +3 -3
  142. package/dist/runtime.mjs.map +1 -1
  143. package/dist/schema-ir-annotations.d.mts +15 -0
  144. package/dist/schema-ir-annotations.d.mts.map +1 -0
  145. package/dist/schema-ir-annotations.mjs +2 -0
  146. package/dist/{sql-utils-DcfMz4MQ.mjs → sql-utils-SU4FDvIV.mjs} +12 -3
  147. package/dist/{sql-utils-DcfMz4MQ.mjs.map → sql-utils-SU4FDvIV.mjs.map} +1 -1
  148. package/dist/sql-utils.d.mts +8 -1
  149. package/dist/sql-utils.d.mts.map +1 -1
  150. package/dist/sql-utils.mjs +2 -2
  151. package/dist/types.d.mts +4 -140
  152. package/dist/types.mjs +5 -2
  153. package/dist/wire-name-rHHtr1h0.mjs +75 -0
  154. package/dist/wire-name-rHHtr1h0.mjs.map +1 -0
  155. package/package.json +26 -20
  156. package/src/contract-free/checks.ts +73 -0
  157. package/src/contract-free/control-bootstrap.ts +20 -3
  158. package/src/contract-free/ddl.ts +98 -1
  159. package/src/core/authoring.ts +509 -2
  160. package/src/core/codec-ids.ts +1 -0
  161. package/src/core/codec-type-map.ts +2 -0
  162. package/src/core/codecs.ts +180 -1
  163. package/src/core/ddl/nodes.ts +185 -2
  164. package/src/core/default-normalizer.ts +122 -2
  165. package/src/core/descriptor-meta.ts +10 -1
  166. package/src/core/entity-kinds.ts +41 -0
  167. package/src/core/errors.ts +14 -12
  168. package/src/core/migrations/bound-schema.ts +10 -0
  169. package/src/core/migrations/column-ddl-rendering.ts +117 -0
  170. package/src/core/migrations/contract-to-postgres-database-schema-node.ts +189 -0
  171. package/src/core/migrations/control-policy.ts +217 -65
  172. package/src/core/migrations/diff-database-schema.ts +252 -0
  173. package/src/core/migrations/issue-planner.ts +769 -656
  174. package/src/core/migrations/op-factory-call.ts +273 -21
  175. package/src/core/migrations/operations/columns.ts +24 -12
  176. package/src/core/migrations/operations/constraints.ts +1 -1
  177. package/src/core/migrations/operations/data-transform.ts +6 -0
  178. package/src/core/migrations/operations/dependencies.ts +2 -1
  179. package/src/core/migrations/operations/native-enum-types.ts +62 -0
  180. package/src/core/migrations/operations/rls.ts +187 -0
  181. package/src/core/migrations/operations/shared.ts +0 -30
  182. package/src/core/migrations/planner-ddl-builders.ts +31 -59
  183. package/src/core/migrations/planner-identity-values.ts +1 -1
  184. package/src/core/migrations/planner-recipes.ts +31 -13
  185. package/src/core/migrations/planner-sql-checks.ts +1 -1
  186. package/src/core/migrations/planner-strategies.ts +248 -216
  187. package/src/core/migrations/planner-target-details.ts +3 -1
  188. package/src/core/migrations/planner.ts +357 -67
  189. package/src/core/migrations/postgres-migration.ts +101 -86
  190. package/src/core/migrations/render-typescript.ts +42 -15
  191. package/src/core/migrations/resolve-ddl-schema.ts +26 -0
  192. package/src/core/migrations/runner.ts +11 -16
  193. package/src/core/migrations/verify-postgres-namespaces.ts +46 -31
  194. package/src/core/namespace-ids.ts +3 -0
  195. package/src/core/postgres-contract-serializer.ts +46 -71
  196. package/src/core/postgres-contract-view.ts +48 -0
  197. package/src/core/postgres-native-enum.ts +61 -0
  198. package/src/core/postgres-rls-enablement.ts +34 -0
  199. package/src/core/postgres-rls-policy.ts +64 -0
  200. package/src/core/postgres-role.ts +59 -0
  201. package/src/core/postgres-schema-verifier.ts +10 -7
  202. package/src/core/postgres-schema.ts +85 -15
  203. package/src/core/postgres-validators.ts +34 -0
  204. package/src/core/psl-infer/infer-psl-contract.ts +1273 -0
  205. package/src/core/psl-infer/postgres-default-mapping.ts +16 -0
  206. package/src/core/psl-infer/postgres-type-map.ts +128 -0
  207. package/src/core/rls/canonicalize.ts +48 -0
  208. package/src/core/rls/wire-name.ts +33 -0
  209. package/src/core/schema-ir/node-storage-coordinate.ts +26 -0
  210. package/src/core/schema-ir/postgres-database-schema-node.ts +61 -0
  211. package/src/core/schema-ir/postgres-namespace-schema-node.ts +70 -0
  212. package/src/core/schema-ir/postgres-native-enum-schema-node.ts +83 -0
  213. package/src/core/schema-ir/postgres-policy-schema-node.ts +90 -0
  214. package/src/core/schema-ir/postgres-role-schema-node.ts +65 -0
  215. package/src/core/schema-ir/postgres-table-schema-node.ts +140 -0
  216. package/src/core/schema-ir/schema-node-kinds.ts +122 -0
  217. package/src/core/sql-utils.ts +15 -2
  218. package/src/exports/codecs.ts +3 -0
  219. package/src/exports/contract-free.ts +11 -1
  220. package/src/exports/control.ts +27 -33
  221. package/src/exports/ddl.ts +8 -0
  222. package/src/exports/diff-database-schema.ts +7 -0
  223. package/src/exports/issue-planner.ts +1 -1
  224. package/src/exports/migration.ts +1 -0
  225. package/src/exports/op-factory-call.ts +3 -0
  226. package/src/exports/planner-ddl-builders.ts +0 -2
  227. package/src/exports/planner-identity-values.ts +4 -1
  228. package/src/exports/planner.ts +1 -0
  229. package/src/exports/rls-canonicalize.ts +7 -0
  230. package/src/exports/runtime.ts +1 -0
  231. package/src/exports/schema-ir-annotations.ts +1 -0
  232. package/src/exports/sql-utils.ts +1 -0
  233. package/src/exports/types.ts +37 -0
  234. package/dist/codec-ids-BvytN2P8.mjs.map +0 -1
  235. package/dist/codec-ids-CnXu9Qy3.d.mts.map +0 -1
  236. package/dist/codec-types-DHCkwPKE.d.mts.map +0 -1
  237. package/dist/codecs--0A5_4Bq.d.mts.map +0 -1
  238. package/dist/ddl-QDyOSeLc.mjs.map +0 -1
  239. package/dist/default-normalizer-DyyCHQWs.mjs +0 -131
  240. package/dist/default-normalizer-DyyCHQWs.mjs.map +0 -1
  241. package/dist/descriptor-meta-CpGygXpI.mjs +0 -140
  242. package/dist/descriptor-meta-CpGygXpI.mjs.map +0 -1
  243. package/dist/errors-CUk87ByX.mjs +0 -33
  244. package/dist/errors-CUk87ByX.mjs.map +0 -1
  245. package/dist/issue-planner-DL6g3CmE.mjs +0 -824
  246. package/dist/issue-planner-DL6g3CmE.mjs.map +0 -1
  247. package/dist/native-type-normalizer-Bc9XJzWC.mjs +0 -38
  248. package/dist/native-type-normalizer-Bc9XJzWC.mjs.map +0 -1
  249. package/dist/nodes-Bbhs2rwj.mjs +0 -76
  250. package/dist/nodes-Bbhs2rwj.mjs.map +0 -1
  251. package/dist/nodes-pLeLgdis.d.mts +0 -67
  252. package/dist/nodes-pLeLgdis.d.mts.map +0 -1
  253. package/dist/op-factory-call-D_p5vxwt.mjs.map +0 -1
  254. package/dist/op-factory-call-DmQEc3XV.d.mts.map +0 -1
  255. package/dist/planner-Bs_baQax.mjs +0 -324
  256. package/dist/planner-Bs_baQax.mjs.map +0 -1
  257. package/dist/planner-ddl-builders-B2wOwLqI.mjs.map +0 -1
  258. package/dist/planner-target-details-CIY6tLeo.d.mts.map +0 -1
  259. package/dist/postgres-contract-serializer-k3TAcPMY.mjs.map +0 -1
  260. package/dist/postgres-migration-B5jKrXv3.mjs.map +0 -1
  261. package/dist/postgres-migration-Y4YBJqkS.d.mts.map +0 -1
  262. package/dist/postgres-schema-COGZ1ark.mjs.map +0 -1
  263. package/dist/render-typescript-KMgosran.mjs.map +0 -1
  264. package/dist/types.d.mts.map +0 -1
  265. package/src/core/postgres-enum-type-schema.ts +0 -17
@@ -4,7 +4,7 @@
4
4
  * Each codec ships as three artifacts:
5
5
  *
6
6
  * 1. A `PgXCodec` class extending {@link CodecImpl} that wraps the module-level encode/decode/encodeJson/decodeJson constants exported from `codec-helpers.ts` (the single source of truth for non-trivial runtime conversions; trivial identity passthroughs are inlined). 2. A `PgXDescriptor` class extending {@link CodecDescriptorImpl} declaring the codec id, traits, target types, params schema, meta, and (where applicable)
7
- * the emit-path `renderOutputType`. 3. A per-codec column helper (`pgXColumn`) that calls `descriptor.factory(...)` directly and packages the result into a {@link ColumnSpec} via the framework {@link column} packager. The helper is tied to its descriptor with `satisfies ColumnHelperFor` (and `ColumnHelperForStrict` where the resolved codec type is well-defined).
7
+ * the emit-path `renderOutputType`. 3. A per-codec column helper (`pgXColumn`) that calls `descriptor.factory(...)` directly and packages the result into a framework `ColumnSpec` via the framework {@link column} packager. The helper is tied to its descriptor with `satisfies ColumnHelperFor` (and `ColumnHelperForStrict` where the resolved codec type is well-defined).
8
8
  *
9
9
  * After TML-2357 this is the canonical source of Postgres codec metadata and runtime behaviour — the legacy `mkCodec` / `defineCodec` carriers (and the parallel `byScalar`/`codecDescriptorDefinitions`/ `codecDescriptorList` collection exports) retired with the deletion sweep.
10
10
  *
@@ -19,11 +19,14 @@ import {
19
19
  CodecDescriptorImpl,
20
20
  CodecImpl,
21
21
  type CodecInstanceContext,
22
+ type CodecMeta,
22
23
  type ColumnHelperFor,
23
24
  type ColumnHelperForStrict,
24
25
  column,
26
+ renderTsLiteral,
25
27
  voidParamsSchema,
26
28
  } from '@prisma-next/framework-components/codec';
29
+ import { UNBOUND_NAMESPACE_ID } from '@prisma-next/framework-components/ir';
27
30
  import {
28
31
  SqlCharCodec,
29
32
  SqlFloatCodec,
@@ -59,6 +62,7 @@ import {
59
62
  PG_BOOL_CODEC_ID,
60
63
  PG_BYTEA_CODEC_ID,
61
64
  PG_CHAR_CODEC_ID,
65
+ PG_ENUM_CODEC_ID,
62
66
  PG_FLOAT_CODEC_ID,
63
67
  PG_FLOAT4_CODEC_ID,
64
68
  PG_FLOAT8_CODEC_ID,
@@ -80,6 +84,8 @@ import {
80
84
  PG_VARBIT_CODEC_ID,
81
85
  PG_VARCHAR_CODEC_ID,
82
86
  } from './codec-ids';
87
+ import { DEFAULT_NAMESPACE_ID } from './namespace-ids';
88
+ import { PostgresNativeEnum } from './postgres-native-enum';
83
89
 
84
90
  type LengthParams = { readonly length?: number };
85
91
  type PrecisionParams = { readonly precision?: number };
@@ -148,6 +154,9 @@ export class PgTextDescriptor extends CodecDescriptorImpl<void> {
148
154
  override readonly targetTypes = ['text'] as const;
149
155
  override readonly meta = PG_TEXT_META;
150
156
  override readonly paramsSchema: StandardSchemaV1<void> = voidParamsSchema;
157
+ override renderValueLiteral(value: JsonValue): string | undefined {
158
+ return renderTsLiteral(value);
159
+ }
151
160
  override factory(): (ctx: CodecInstanceContext) => PgTextCodec {
152
161
  return () => new PgTextCodec(this);
153
162
  }
@@ -161,6 +170,145 @@ export const pgTextColumn = () =>
161
170
  pgTextColumn satisfies ColumnHelperFor<PgTextDescriptor>;
162
171
  pgTextColumn satisfies ColumnHelperForStrict<PgTextDescriptor>;
163
172
 
173
+ /**
174
+ * Codec for a `pg.enum(Ref)` column bound to a native Postgres enum type.
175
+ * Text passthrough, identical to `pg/text@1` — encode/decode do not carry the
176
+ * enum's member values; membership is enforced by the native type itself, not
177
+ * by this codec. `renderValueLiteral` renders a member value as its TS
178
+ * literal, which is what drives the column's typed value-union (via
179
+ * `renderValueSetType` reading the column's `valueSet` ref) — the codec
180
+ * itself carries no params of its own; typing comes entirely from the
181
+ * column's value-set, not from `pg/enum@1`.
182
+ *
183
+ * A distinct codec id (rather than reusing `pg/text@1` on a plain text
184
+ * column) keeps native-enum columns independently identifiable — from a
185
+ * column's `codecId` alone, without also inspecting `nativeType` — which
186
+ * the managed (DDL) phase needs to target `CREATE TYPE`/`ALTER TYPE`
187
+ * operations at exactly the columns that use one.
188
+ */
189
+ export class PgEnumCodec extends CodecImpl<
190
+ typeof PG_ENUM_CODEC_ID,
191
+ readonly ['equality', 'order', 'textual'],
192
+ string,
193
+ string
194
+ > {
195
+ async encode(value: string, _ctx: CodecCallContext): Promise<string> {
196
+ return value;
197
+ }
198
+ async decode(wire: string, _ctx: CodecCallContext): Promise<string> {
199
+ return wire;
200
+ }
201
+ encodeJson(value: string): JsonValue {
202
+ return value;
203
+ }
204
+ decodeJson(json: JsonValue): string {
205
+ return blindCast<
206
+ string,
207
+ 'text codec: a native-enum member value is stored as its wire string form'
208
+ >(json);
209
+ }
210
+ }
211
+
212
+ type PgEnumParams = { readonly typeName: string };
213
+
214
+ const pgEnumParamsSchema = arktype({
215
+ typeName: 'string',
216
+ }) satisfies StandardSchemaV1<PgEnumParams>;
217
+
218
+ function isJsonObject(
219
+ value: JsonValue | undefined,
220
+ ): value is { readonly [key: string]: JsonValue } {
221
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
222
+ }
223
+
224
+ export class PgEnumDescriptor extends CodecDescriptorImpl<PgEnumParams> {
225
+ override readonly codecId = PG_ENUM_CODEC_ID;
226
+ override readonly traits = ['equality', 'order', 'textual'] as const;
227
+ override readonly targetTypes = ['text'] as const;
228
+ override readonly meta = PG_TEXT_META;
229
+ override readonly paramsSchema = pgEnumParamsSchema satisfies StandardSchemaV1<PgEnumParams>;
230
+ override renderValueLiteral(value: JsonValue): string | undefined {
231
+ return renderTsLiteral(value);
232
+ }
233
+ override metaFor(typeParams: JsonValue | undefined): CodecMeta | undefined {
234
+ if (!isJsonObject(typeParams)) return this.meta;
235
+ const typeName = typeParams['typeName'];
236
+ return typeof typeName === 'string'
237
+ ? { db: { sql: { postgres: { nativeType: typeName } } } }
238
+ : this.meta;
239
+ }
240
+ override factory(_params: PgEnumParams): (ctx: CodecInstanceContext) => PgEnumCodec {
241
+ return () => new PgEnumCodec(this);
242
+ }
243
+
244
+ /**
245
+ * Authoring-time hook a `pg.enum(<ref>)` type constructor calls once it has
246
+ * resolved its ref argument to the referenced `native_enum` entity:
247
+ * produces this codec's per-column `typeParams` and native type from the
248
+ * entity's bare type name. Schema-qualification (`auth.aal_level` for a
249
+ * named non-default schema) is not this hook's concern — the field's
250
+ * namespace isn't known at this call site for every authoring path (the TS
251
+ * builder resolves a column before it knows its model's namespace), so it is
252
+ * applied later, at contract construction, by {@link qualifyNativeType} via
253
+ * the target's `authoring.qualifyColumnType` hook. `nativeType` mirrors
254
+ * `typeParams.typeName` — the same value {@link metaFor} derives at render
255
+ * time — so the column's declared native type and the render-time cast
256
+ * agree. Returns `undefined` if `entity` is not a `PostgresNativeEnum` (a
257
+ * contributor bug, not a user-schema error — the caller decides how to
258
+ * report it).
259
+ */
260
+ columnFromEntity(
261
+ entity: object,
262
+ ): { readonly typeParams: PgEnumParams; readonly nativeType: string } | undefined {
263
+ if (!PostgresNativeEnum.is(entity)) return undefined;
264
+ return { typeParams: { typeName: entity.typeName }, nativeType: entity.typeName };
265
+ }
266
+
267
+ /**
268
+ * Schema-qualifies this native enum type's name for the namespace the
269
+ * consuming column lives in: `${namespaceId}.${typeName}` for a named
270
+ * non-default schema, bare for the target's default schema (`public`) or
271
+ * the late-bound unbound sentinel (whose schema `search_path` resolves at
272
+ * runtime). Postgres's `format_type()` reports the bare name for a
273
+ * public-schema type, so a public column's declared native type must stay
274
+ * bare to match. Owned here because the codec owns its native type.
275
+ */
276
+ qualifyNativeType(typeName: string, namespaceId: string): string {
277
+ return namespaceId === DEFAULT_NAMESPACE_ID || namespaceId === UNBOUND_NAMESPACE_ID
278
+ ? typeName
279
+ : `${namespaceId}.${typeName}`;
280
+ }
281
+ }
282
+
283
+ export const pgEnumDescriptor = new PgEnumDescriptor();
284
+
285
+ /**
286
+ * Contract-construction-time column-type qualifier the Postgres target
287
+ * contributes through `authoring.qualifyColumnType`.
288
+ * `buildSqlContractFromDefinition` calls this for every column as it is
289
+ * constructed, passing the column's bare type info and its owning
290
+ * `namespaceId`; a native-enum column (`pg/enum@1`) gets its type name
291
+ * schema-qualified for that namespace (via
292
+ * {@link PgEnumDescriptor.qualifyNativeType}), keeping `nativeType` and
293
+ * `typeParams.typeName` in sync. Every other codec passes through unchanged.
294
+ * Both the PSL `pg.enum(Ref)` path and the TS `pg.enum(handle)` path route
295
+ * through here — the dispatch keys off the codec id, not authoring surface.
296
+ */
297
+ export function postgresQualifyColumnType(
298
+ input: {
299
+ readonly codecId: string;
300
+ readonly nativeType: string;
301
+ readonly typeParams?: Record<string, unknown>;
302
+ },
303
+ namespaceId: string,
304
+ ): { readonly nativeType: string; readonly typeParams?: Record<string, unknown> } {
305
+ if (input.codecId !== PG_ENUM_CODEC_ID) return input;
306
+ const bareTypeName = input.typeParams?.['typeName'];
307
+ if (typeof bareTypeName !== 'string') return input;
308
+ const qualified = pgEnumDescriptor.qualifyNativeType(bareTypeName, namespaceId);
309
+ return { nativeType: qualified, typeParams: { ...input.typeParams, typeName: qualified } };
310
+ }
311
+
164
312
  /**
165
313
  * Postgres `text[]` codec. Encode is an identity pass-through: the pg wire
166
314
  * driver serialises a JS `string[]` to a Postgres array literal under the
@@ -228,6 +376,9 @@ export class PgInt4Descriptor extends CodecDescriptorImpl<void> {
228
376
  override readonly targetTypes = ['int4'] as const;
229
377
  override readonly meta = PG_INT4_META;
230
378
  override readonly paramsSchema: StandardSchemaV1<void> = voidParamsSchema;
379
+ override renderValueLiteral(value: JsonValue): string | undefined {
380
+ return renderTsLiteral(value);
381
+ }
231
382
  override factory(): (ctx: CodecInstanceContext) => PgInt4Codec {
232
383
  return () => new PgInt4Codec(this);
233
384
  }
@@ -267,6 +418,9 @@ export class PgInt2Descriptor extends CodecDescriptorImpl<void> {
267
418
  override readonly targetTypes = ['int2'] as const;
268
419
  override readonly meta = PG_INT2_META;
269
420
  override readonly paramsSchema: StandardSchemaV1<void> = voidParamsSchema;
421
+ override renderValueLiteral(value: JsonValue): string | undefined {
422
+ return renderTsLiteral(value);
423
+ }
270
424
  override factory(): (ctx: CodecInstanceContext) => PgInt2Codec {
271
425
  return () => new PgInt2Codec(this);
272
426
  }
@@ -306,6 +460,9 @@ export class PgInt8Descriptor extends CodecDescriptorImpl<void> {
306
460
  override readonly targetTypes = ['int8'] as const;
307
461
  override readonly meta = PG_INT8_META;
308
462
  override readonly paramsSchema: StandardSchemaV1<void> = voidParamsSchema;
463
+ override renderValueLiteral(value: JsonValue): string | undefined {
464
+ return renderTsLiteral(value);
465
+ }
309
466
  override factory(): (ctx: CodecInstanceContext) => PgInt8Codec {
310
467
  return () => new PgInt8Codec(this);
311
468
  }
@@ -345,6 +502,9 @@ export class PgFloat4Descriptor extends CodecDescriptorImpl<void> {
345
502
  override readonly targetTypes = ['float4'] as const;
346
503
  override readonly meta = PG_FLOAT4_META;
347
504
  override readonly paramsSchema: StandardSchemaV1<void> = voidParamsSchema;
505
+ override renderValueLiteral(value: JsonValue): string | undefined {
506
+ return renderTsLiteral(value);
507
+ }
348
508
  override factory(): (ctx: CodecInstanceContext) => PgFloat4Codec {
349
509
  return () => new PgFloat4Codec(this);
350
510
  }
@@ -384,6 +544,9 @@ export class PgFloat8Descriptor extends CodecDescriptorImpl<void> {
384
544
  override readonly targetTypes = ['float8'] as const;
385
545
  override readonly meta = PG_FLOAT8_META;
386
546
  override readonly paramsSchema: StandardSchemaV1<void> = voidParamsSchema;
547
+ override renderValueLiteral(value: JsonValue): string | undefined {
548
+ return renderTsLiteral(value);
549
+ }
387
550
  override factory(): (ctx: CodecInstanceContext) => PgFloat8Codec {
388
551
  return () => new PgFloat8Codec(this);
389
552
  }
@@ -423,6 +586,9 @@ export class PgBoolDescriptor extends CodecDescriptorImpl<void> {
423
586
  override readonly targetTypes = ['bool'] as const;
424
587
  override readonly meta = PG_BOOL_META;
425
588
  override readonly paramsSchema: StandardSchemaV1<void> = voidParamsSchema;
589
+ override renderValueLiteral(value: JsonValue): string | undefined {
590
+ return renderTsLiteral(value);
591
+ }
426
592
  override factory(): (ctx: CodecInstanceContext) => PgBoolCodec {
427
593
  return () => new PgBoolCodec(this);
428
594
  }
@@ -968,6 +1134,9 @@ export class PgCharDescriptor extends CodecDescriptorImpl<LengthParams> {
968
1134
  override renderOutputType(params: LengthParams): string | undefined {
969
1135
  return sqlCharDescriptor.renderOutputType(params);
970
1136
  }
1137
+ override renderValueLiteral(value: JsonValue): string | undefined {
1138
+ return renderTsLiteral(value);
1139
+ }
971
1140
  override factory(_params: LengthParams): (ctx: CodecInstanceContext) => SqlCharCodec {
972
1141
  return () => new SqlCharCodec(this);
973
1142
  }
@@ -989,6 +1158,9 @@ export class PgVarcharDescriptor extends CodecDescriptorImpl<LengthParams> {
989
1158
  override renderOutputType(params: LengthParams): string | undefined {
990
1159
  return sqlVarcharDescriptor.renderOutputType(params);
991
1160
  }
1161
+ override renderValueLiteral(value: JsonValue): string | undefined {
1162
+ return renderTsLiteral(value);
1163
+ }
992
1164
  override factory(_params: LengthParams): (ctx: CodecInstanceContext) => SqlVarcharCodec {
993
1165
  return () => new SqlVarcharCodec(this);
994
1166
  }
@@ -1012,6 +1184,9 @@ export class PgIntDescriptor extends CodecDescriptorImpl<void> {
1012
1184
  override readonly meta = PG_INT_META;
1013
1185
  override readonly traits = sqlIntDescriptor.traits;
1014
1186
  override readonly paramsSchema = sqlIntDescriptor.paramsSchema;
1187
+ override renderValueLiteral(value: JsonValue): string | undefined {
1188
+ return renderTsLiteral(value);
1189
+ }
1015
1190
  override factory(): (ctx: CodecInstanceContext) => SqlIntCodec {
1016
1191
  return () => new SqlIntCodec(this);
1017
1192
  }
@@ -1030,6 +1205,9 @@ export class PgFloatDescriptor extends CodecDescriptorImpl<void> {
1030
1205
  override readonly meta = PG_FLOAT_META;
1031
1206
  override readonly traits = sqlFloatDescriptor.traits;
1032
1207
  override readonly paramsSchema = sqlFloatDescriptor.paramsSchema;
1208
+ override renderValueLiteral(value: JsonValue): string | undefined {
1209
+ return renderTsLiteral(value);
1210
+ }
1033
1211
  override factory(): (ctx: CodecInstanceContext) => SqlFloatCodec {
1034
1212
  return () => new SqlFloatCodec(this);
1035
1213
  }
@@ -1052,6 +1230,7 @@ export const codecDescriptors: readonly AnyCodecDescriptor[] = [
1052
1230
  sqlTextDescriptor,
1053
1231
  sqlTimestampDescriptor,
1054
1232
  pgTextDescriptor,
1233
+ pgEnumDescriptor,
1055
1234
  pgCharDescriptor,
1056
1235
  pgVarcharDescriptor,
1057
1236
  pgIntDescriptor,
@@ -3,6 +3,7 @@ import {
3
3
  DdlNode,
4
4
  type DdlTableConstraint,
5
5
  } from '@prisma-next/sql-relational-core/ast';
6
+ import type { RlsPolicyOperation } from '../rls/canonicalize';
6
7
 
7
8
  // ---------------------------------------------------------------------------
8
9
  // AlterTableAction — nested polymorphic hierarchy
@@ -10,6 +11,7 @@ import {
10
11
 
11
12
  export interface AlterTableActionVisitor<R> {
12
13
  addColumn(action: AddColumnAction): R;
14
+ dropDefault(action: DropDefaultAction): R;
13
15
  }
14
16
 
15
17
  export abstract class AlterTableAction {
@@ -32,7 +34,31 @@ export class AddColumnAction extends AlterTableAction {
32
34
  }
33
35
  }
34
36
 
35
- export type AnyAlterTableAction = AddColumnAction;
37
+ export class DropDefaultAction extends AlterTableAction {
38
+ readonly kind = 'drop-default' as const;
39
+ readonly columnName: string;
40
+
41
+ constructor(columnName: string) {
42
+ super();
43
+ this.columnName = columnName;
44
+ Object.freeze(this);
45
+ }
46
+
47
+ override accept<R>(visitor: AlterTableActionVisitor<R>): R {
48
+ return visitor.dropDefault(this);
49
+ }
50
+ }
51
+
52
+ /**
53
+ * The set of ALTER TABLE subactions currently expressible as typed DDL.
54
+ *
55
+ * The remaining actions — SetDefault, SetNotNull, DropNotNull,
56
+ * AlterColumnType — are still emitted as raw SQL by `operations/columns.ts`
57
+ * and join this union as they are converted to typed DDL. Until then it is
58
+ * intentionally partial: only the ALTER subactions used by the
59
+ * already-converted ops (AddColumn, DropDefault) appear here.
60
+ */
61
+ export type AnyAlterTableAction = AddColumnAction | DropDefaultAction;
36
62
 
37
63
  // ---------------------------------------------------------------------------
38
64
  // Top-level DDL visitor
@@ -41,7 +67,13 @@ export type AnyAlterTableAction = AddColumnAction;
41
67
  export interface PostgresDdlVisitor<R> {
42
68
  createTable(node: PostgresCreateTable): R;
43
69
  createSchema(node: PostgresCreateSchema): R;
70
+ createType(node: PostgresCreateType): R;
71
+ dropType(node: PostgresDropType): R;
44
72
  alterTable(node: PostgresAlterTable): R;
73
+ createPolicy(node: PostgresCreatePolicy): R;
74
+ dropPolicy(node: PostgresDropPolicy): R;
75
+ alterPolicyRename(node: PostgresAlterPolicyRename): R;
76
+ disableRowLevelSecurity(node: PostgresDisableRowLevelSecurity): R;
45
77
  }
46
78
 
47
79
  export abstract class PostgresDdlNode extends DdlNode {
@@ -104,6 +136,46 @@ export class PostgresCreateSchema extends PostgresDdlNode {
104
136
  }
105
137
  }
106
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
+
107
179
  export class PostgresAlterTable extends PostgresDdlNode {
108
180
  readonly kind = 'alter-table' as const;
109
181
  readonly table: string;
@@ -127,4 +199,115 @@ export class PostgresAlterTable extends PostgresDdlNode {
127
199
  }
128
200
  }
129
201
 
130
- export type AnyPostgresDdlNode = PostgresCreateTable | PostgresCreateSchema | PostgresAlterTable;
202
+ export type { RlsPolicyOperation };
203
+
204
+ export class PostgresCreatePolicy extends PostgresDdlNode {
205
+ readonly kind = 'create-policy' as const;
206
+ readonly schema: string;
207
+ readonly table: string;
208
+ readonly name: string;
209
+ readonly permissive: boolean;
210
+ readonly operation: RlsPolicyOperation;
211
+ readonly roles: ReadonlyArray<string>;
212
+ readonly using: string | undefined;
213
+ readonly withCheck: string | undefined;
214
+
215
+ constructor(options: {
216
+ readonly schema: string;
217
+ readonly table: string;
218
+ readonly name: string;
219
+ readonly permissive: boolean;
220
+ readonly operation: RlsPolicyOperation;
221
+ readonly roles: readonly string[];
222
+ readonly using?: string;
223
+ readonly withCheck?: string;
224
+ }) {
225
+ super();
226
+ this.schema = options.schema;
227
+ this.table = options.table;
228
+ this.name = options.name;
229
+ this.permissive = options.permissive;
230
+ this.operation = options.operation;
231
+ this.roles = Object.freeze([...options.roles]);
232
+ this.using = options.using;
233
+ this.withCheck = options.withCheck;
234
+ this.freeze();
235
+ }
236
+
237
+ override accept<R>(visitor: PostgresDdlVisitor<R>): R {
238
+ return visitor.createPolicy(this);
239
+ }
240
+ }
241
+
242
+ export class PostgresDropPolicy extends PostgresDdlNode {
243
+ readonly kind = 'drop-policy' as const;
244
+ readonly schema: string;
245
+ readonly table: string;
246
+ readonly name: string;
247
+
248
+ constructor(options: { readonly schema: string; readonly table: string; readonly name: string }) {
249
+ super();
250
+ this.schema = options.schema;
251
+ this.table = options.table;
252
+ this.name = options.name;
253
+ this.freeze();
254
+ }
255
+
256
+ override accept<R>(visitor: PostgresDdlVisitor<R>): R {
257
+ return visitor.dropPolicy(this);
258
+ }
259
+ }
260
+
261
+ export class PostgresAlterPolicyRename extends PostgresDdlNode {
262
+ readonly kind = 'alter-policy-rename' as const;
263
+ readonly schema: string;
264
+ readonly table: string;
265
+ readonly name: string;
266
+ readonly newName: string;
267
+
268
+ constructor(options: {
269
+ readonly schema: string;
270
+ readonly table: string;
271
+ readonly name: string;
272
+ readonly newName: string;
273
+ }) {
274
+ super();
275
+ this.schema = options.schema;
276
+ this.table = options.table;
277
+ this.name = options.name;
278
+ this.newName = options.newName;
279
+ this.freeze();
280
+ }
281
+
282
+ override accept<R>(visitor: PostgresDdlVisitor<R>): R {
283
+ return visitor.alterPolicyRename(this);
284
+ }
285
+ }
286
+
287
+ export class PostgresDisableRowLevelSecurity extends PostgresDdlNode {
288
+ readonly kind = 'disable-row-level-security' as const;
289
+ readonly schema: string;
290
+ readonly table: string;
291
+
292
+ constructor(options: { readonly schema: string; readonly table: string }) {
293
+ super();
294
+ this.schema = options.schema;
295
+ this.table = options.table;
296
+ this.freeze();
297
+ }
298
+
299
+ override accept<R>(visitor: PostgresDdlVisitor<R>): R {
300
+ return visitor.disableRowLevelSecurity(this);
301
+ }
302
+ }
303
+
304
+ export type AnyPostgresDdlNode =
305
+ | PostgresCreateTable
306
+ | PostgresCreateSchema
307
+ | PostgresCreateType
308
+ | PostgresDropType
309
+ | PostgresAlterTable
310
+ | PostgresCreatePolicy
311
+ | PostgresDropPolicy
312
+ | PostgresAlterPolicyRename
313
+ | PostgresDisableRowLevelSecurity;
@@ -1,4 +1,4 @@
1
- import type { ColumnDefault } from '@prisma-next/contract/types';
1
+ import type { ColumnDefault, JsonValue } from '@prisma-next/contract/types';
2
2
 
3
3
  /**
4
4
  * Pre-compiled regex patterns for performance.
@@ -18,6 +18,13 @@ const FALSE_PATTERN = /^false$/i;
18
18
  const NUMERIC_PATTERN = /^-?\d+(\.\d+)?$/;
19
19
  const STRING_LITERAL_PATTERN = /^'((?:[^']|'')*)'(?:::(?:"[^"]+"|[\w\s]+)(?:\(\d+\))?)?$/;
20
20
 
21
+ /**
22
+ * Matches a Postgres array literal default of the form `'{...}'::elemtype[]`.
23
+ * The literal body is captured in group 1; the cast (including `[]`) is optional.
24
+ * Examples: `'{}'::text[]`, `'{1,2}'::integer[]`, `'{}'`
25
+ */
26
+ const ARRAY_LITERAL_PATTERN = /^'(\{.*\})'(?:::.+\[\])?$/;
27
+
21
28
  /**
22
29
  * Returns the canonical expression for a timestamp default function, or undefined
23
30
  * if the expression is not a recognized timestamp default.
@@ -51,6 +58,108 @@ function canonicalizeTimestampDefault(expr: string): string | undefined {
51
58
  return undefined;
52
59
  }
53
60
 
61
+ type ArrayElementToken = { readonly value: string; readonly quoted: boolean };
62
+
63
+ /**
64
+ * Splits a Postgres array literal body (without the enclosing braces) into its
65
+ * element tokens, honouring quoting. A comma only separates elements when it is
66
+ * outside double quotes; inside a quoted element a doubled quote (`""`) or a
67
+ * backslash-escaped quote (`\"`) is a literal quote, and a backslash escapes the
68
+ * next character. Returns undefined if the body is malformed (e.g. an unbalanced
69
+ * quote).
70
+ */
71
+ function splitArrayElements(inner: string): readonly ArrayElementToken[] | undefined {
72
+ const tokens: ArrayElementToken[] = [];
73
+ let current = '';
74
+ let inQuotes = false;
75
+ let quoted = false;
76
+
77
+ for (let i = 0; i < inner.length; i++) {
78
+ const char = inner[i];
79
+ if (inQuotes) {
80
+ if (char === '\\') {
81
+ const next = inner[i + 1];
82
+ if (next === undefined) return undefined;
83
+ current += next;
84
+ i++;
85
+ continue;
86
+ }
87
+ if (char === '"') {
88
+ if (inner[i + 1] === '"') {
89
+ current += '"';
90
+ i++;
91
+ continue;
92
+ }
93
+ inQuotes = false;
94
+ continue;
95
+ }
96
+ current += char;
97
+ continue;
98
+ }
99
+ if (char === '"') {
100
+ inQuotes = true;
101
+ quoted = true;
102
+ continue;
103
+ }
104
+ if (char === ',') {
105
+ tokens.push({ value: current, quoted });
106
+ current = '';
107
+ quoted = false;
108
+ continue;
109
+ }
110
+ current += char;
111
+ }
112
+
113
+ if (inQuotes) return undefined;
114
+ tokens.push({ value: current, quoted });
115
+ return tokens;
116
+ }
117
+
118
+ /**
119
+ * Parses a Postgres array literal body (`{...}`) into a JS array of primitives.
120
+ * Returns undefined if the body cannot be reliably parsed.
121
+ *
122
+ * Handles:
123
+ * - `{}` → `[]`
124
+ * - `{elem1,elem2,...}` → `[elem1, elem2, ...]` with numeric and string element coercion
125
+ * - quoted elements that contain commas, doubled/escaped quotes, and the literal
126
+ * strings `NULL`/`true`/`false` (a quoted token is always a string)
127
+ */
128
+ function parseArrayLiteralBody(body: string): readonly JsonValue[] | undefined {
129
+ const inner = body.slice(1, -1).trim();
130
+ if (inner === '') return [];
131
+ const tokens = splitArrayElements(inner);
132
+ if (tokens === undefined) return undefined;
133
+ const result: JsonValue[] = [];
134
+ for (const token of tokens) {
135
+ if (token.quoted) {
136
+ // A quoted token is always a string — `"NULL"`, `"true"`, `"1"` are the
137
+ // literal text, never the keyword/number.
138
+ result.push(token.value);
139
+ continue;
140
+ }
141
+ const el = token.value.trim();
142
+ if (el.toUpperCase() === 'NULL') {
143
+ result.push(null);
144
+ continue;
145
+ }
146
+ if (el === 'true') {
147
+ result.push(true);
148
+ continue;
149
+ }
150
+ if (el === 'false') {
151
+ result.push(false);
152
+ continue;
153
+ }
154
+ if (NUMERIC_PATTERN.test(el)) {
155
+ result.push(Number(el));
156
+ continue;
157
+ }
158
+ return undefined;
159
+ }
160
+ return result;
161
+ }
162
+
54
163
  /**
55
164
  * Parses a raw Postgres column default expression into a normalized ColumnDefault.
56
165
  * This enables semantic comparison between contract defaults and introspected schema defaults.
@@ -59,7 +168,7 @@ function canonicalizeTimestampDefault(expr: string): string | undefined {
59
168
  * keeping the introspection layer focused on faithful data capture.
60
169
  *
61
170
  * @param rawDefault - Raw default expression from information_schema.columns.column_default
62
- * @param nativeType - Native column type, used for type-aware parsing (bigint tagging, JSON detection)
171
+ * @param nativeType - Native column type, used for type-aware parsing (array, bigint, JSON)
63
172
  * @returns Normalized ColumnDefault or undefined if the expression cannot be parsed
64
173
  */
65
174
  export function parsePostgresDefault(
@@ -69,11 +178,22 @@ export function parsePostgresDefault(
69
178
  const trimmed = rawDefault.trim();
70
179
  const normalizedType = nativeType?.toLowerCase();
71
180
  const isBigInt = normalizedType === 'bigint' || normalizedType === 'int8';
181
+ const isArrayType = normalizedType?.endsWith('[]') ?? false;
72
182
 
73
183
  if (NEXTVAL_PATTERN.test(trimmed)) {
74
184
  return { kind: 'function', expression: 'autoincrement()' };
75
185
  }
76
186
 
187
+ if (isArrayType) {
188
+ const arrayMatch = trimmed.match(ARRAY_LITERAL_PATTERN);
189
+ if (arrayMatch?.[1] !== undefined) {
190
+ const parsed = parseArrayLiteralBody(arrayMatch[1]);
191
+ if (parsed !== undefined) {
192
+ return { kind: 'literal', value: parsed };
193
+ }
194
+ }
195
+ }
196
+
77
197
  const canonicalTimestamp = canonicalizeTimestampDefault(trimmed);
78
198
  if (canonicalTimestamp) {
79
199
  return { kind: 'function', expression: canonicalTimestamp };