@prisma-next/target-postgres 0.4.1 → 0.4.3

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 (179) hide show
  1. package/dist/codec-ids-CojIXVf9.mjs +29 -0
  2. package/dist/codec-ids-CojIXVf9.mjs.map +1 -0
  3. package/dist/codec-ids.d.mts +28 -0
  4. package/dist/codec-ids.d.mts.map +1 -0
  5. package/dist/codec-ids.mjs +3 -0
  6. package/dist/codec-types.d.mts +42 -0
  7. package/dist/codec-types.d.mts.map +1 -0
  8. package/dist/codec-types.mjs +3 -0
  9. package/dist/codecs-CE5EUsNM.d.mts +323 -0
  10. package/dist/codecs-CE5EUsNM.d.mts.map +1 -0
  11. package/dist/codecs-dzZ_dMpK.mjs +290 -0
  12. package/dist/codecs-dzZ_dMpK.mjs.map +1 -0
  13. package/dist/codecs.d.mts +2 -0
  14. package/dist/codecs.mjs +3 -0
  15. package/dist/control.d.mts +1 -1
  16. package/dist/control.mjs +24 -1989
  17. package/dist/control.mjs.map +1 -1
  18. package/dist/data-transform-C83dy0vk.mjs +41 -0
  19. package/dist/data-transform-C83dy0vk.mjs.map +1 -0
  20. package/dist/data-transform-D8x5m1YV.d.mts +38 -0
  21. package/dist/data-transform-D8x5m1YV.d.mts.map +1 -0
  22. package/dist/data-transform.d.mts +2 -0
  23. package/dist/data-transform.mjs +3 -0
  24. package/dist/default-normalizer-DNOpRoOF.mjs +131 -0
  25. package/dist/default-normalizer-DNOpRoOF.mjs.map +1 -0
  26. package/dist/default-normalizer.d.mts +19 -0
  27. package/dist/default-normalizer.d.mts.map +1 -0
  28. package/dist/default-normalizer.mjs +3 -0
  29. package/dist/{descriptor-meta-DkvCmY98.mjs → descriptor-meta-BVoVtyp-.mjs} +1 -1
  30. package/dist/{descriptor-meta-DkvCmY98.mjs.map → descriptor-meta-BVoVtyp-.mjs.map} +1 -1
  31. package/dist/errors-AFvEPZ1R.mjs +34 -0
  32. package/dist/errors-AFvEPZ1R.mjs.map +1 -0
  33. package/dist/errors.d.mts +27 -0
  34. package/dist/errors.d.mts.map +1 -0
  35. package/dist/errors.mjs +3 -0
  36. package/dist/issue-planner-CFjB0_oO.mjs +879 -0
  37. package/dist/issue-planner-CFjB0_oO.mjs.map +1 -0
  38. package/dist/issue-planner.d.mts +85 -0
  39. package/dist/issue-planner.d.mts.map +1 -0
  40. package/dist/issue-planner.mjs +3 -0
  41. package/dist/migration.d.mts +5 -79
  42. package/dist/migration.d.mts.map +1 -1
  43. package/dist/migration.mjs +6 -428
  44. package/dist/migration.mjs.map +1 -1
  45. package/dist/native-type-normalizer-CInai_oY.mjs +38 -0
  46. package/dist/native-type-normalizer-CInai_oY.mjs.map +1 -0
  47. package/dist/native-type-normalizer.d.mts +18 -0
  48. package/dist/native-type-normalizer.d.mts.map +1 -0
  49. package/dist/native-type-normalizer.mjs +3 -0
  50. package/dist/op-factory-call-BKlruaiC.mjs +605 -0
  51. package/dist/op-factory-call-BKlruaiC.mjs.map +1 -0
  52. package/dist/op-factory-call-C3bWXKSP.d.mts +304 -0
  53. package/dist/op-factory-call-C3bWXKSP.d.mts.map +1 -0
  54. package/dist/op-factory-call.d.mts +3 -0
  55. package/dist/op-factory-call.mjs +3 -0
  56. package/dist/pack.d.mts +1 -1
  57. package/dist/pack.mjs +1 -1
  58. package/dist/planner-B4ZSLHRI.mjs +98 -0
  59. package/dist/planner-B4ZSLHRI.mjs.map +1 -0
  60. package/dist/planner-ddl-builders-Dxvw1LHw.mjs +132 -0
  61. package/dist/planner-ddl-builders-Dxvw1LHw.mjs.map +1 -0
  62. package/dist/planner-ddl-builders.d.mts +22 -0
  63. package/dist/planner-ddl-builders.d.mts.map +1 -0
  64. package/dist/planner-ddl-builders.mjs +3 -0
  65. package/dist/planner-identity-values-Dju-o5GF.mjs +91 -0
  66. package/dist/planner-identity-values-Dju-o5GF.mjs.map +1 -0
  67. package/dist/planner-identity-values.d.mts +20 -0
  68. package/dist/planner-identity-values.d.mts.map +1 -0
  69. package/dist/planner-identity-values.mjs +3 -0
  70. package/dist/planner-produced-postgres-migration-C0GNhHGw.mjs +32 -0
  71. package/dist/planner-produced-postgres-migration-C0GNhHGw.mjs.map +1 -0
  72. package/dist/planner-produced-postgres-migration-Dw_mPMKt.d.mts +20 -0
  73. package/dist/planner-produced-postgres-migration-Dw_mPMKt.d.mts.map +1 -0
  74. package/dist/planner-produced-postgres-migration.d.mts +5 -0
  75. package/dist/planner-produced-postgres-migration.mjs +3 -0
  76. package/dist/planner-schema-lookup-B7lkypwn.mjs +29 -0
  77. package/dist/planner-schema-lookup-B7lkypwn.mjs.map +1 -0
  78. package/dist/planner-schema-lookup.d.mts +22 -0
  79. package/dist/planner-schema-lookup.d.mts.map +1 -0
  80. package/dist/planner-schema-lookup.mjs +3 -0
  81. package/dist/planner-sql-checks-7jkgm9TX.mjs +241 -0
  82. package/dist/planner-sql-checks-7jkgm9TX.mjs.map +1 -0
  83. package/dist/planner-sql-checks.d.mts +55 -0
  84. package/dist/planner-sql-checks.d.mts.map +1 -0
  85. package/dist/planner-sql-checks.mjs +3 -0
  86. package/dist/{planner-target-details-MXb3oeul.d.mts → planner-target-details-DH-azLu-.d.mts} +1 -1
  87. package/dist/{planner-target-details-MXb3oeul.d.mts.map → planner-target-details-DH-azLu-.d.mts.map} +1 -1
  88. package/dist/planner-target-details.d.mts +2 -0
  89. package/dist/planner-target-details.mjs +1 -0
  90. package/dist/planner.d.mts +74 -0
  91. package/dist/planner.d.mts.map +1 -0
  92. package/dist/planner.mjs +4 -0
  93. package/dist/postgres-migration-DcfWGqhe.d.mts +50 -0
  94. package/dist/postgres-migration-DcfWGqhe.d.mts.map +1 -0
  95. package/dist/postgres-migration-EGSlO4jO.mjs +52 -0
  96. package/dist/postgres-migration-EGSlO4jO.mjs.map +1 -0
  97. package/dist/render-ops-D6_DHdOK.mjs +8 -0
  98. package/dist/render-ops-D6_DHdOK.mjs.map +1 -0
  99. package/dist/render-ops.d.mts +11 -0
  100. package/dist/render-ops.d.mts.map +1 -0
  101. package/dist/render-ops.mjs +3 -0
  102. package/dist/render-typescript-Co3Emwgz.mjs +84 -0
  103. package/dist/render-typescript-Co3Emwgz.mjs.map +1 -0
  104. package/dist/render-typescript.d.mts +14 -0
  105. package/dist/render-typescript.d.mts.map +1 -0
  106. package/dist/render-typescript.mjs +3 -0
  107. package/dist/runtime.d.mts +15 -3
  108. package/dist/runtime.d.mts.map +1 -1
  109. package/dist/runtime.mjs +10 -1
  110. package/dist/runtime.mjs.map +1 -1
  111. package/dist/shared-Bxkt8pNO.d.mts +41 -0
  112. package/dist/shared-Bxkt8pNO.d.mts.map +1 -0
  113. package/dist/sql-utils-r-Lw535w.mjs +76 -0
  114. package/dist/sql-utils-r-Lw535w.mjs.map +1 -0
  115. package/dist/sql-utils.d.mts +59 -0
  116. package/dist/sql-utils.d.mts.map +1 -0
  117. package/dist/sql-utils.mjs +3 -0
  118. package/dist/statement-builders-CHqCtSfe.mjs +121 -0
  119. package/dist/statement-builders-CHqCtSfe.mjs.map +1 -0
  120. package/dist/statement-builders.d.mts +30 -0
  121. package/dist/statement-builders.d.mts.map +1 -0
  122. package/dist/statement-builders.mjs +3 -0
  123. package/dist/tables-BmdW_FWO.mjs +477 -0
  124. package/dist/tables-BmdW_FWO.mjs.map +1 -0
  125. package/dist/types-ClK03Ojd.d.mts +10 -0
  126. package/dist/types-ClK03Ojd.d.mts.map +1 -0
  127. package/dist/types.d.mts +2 -0
  128. package/dist/types.mjs +1 -0
  129. package/package.json +40 -20
  130. package/src/core/codec-ids.ts +30 -0
  131. package/src/core/codecs.ts +622 -0
  132. package/src/core/default-normalizer.ts +131 -0
  133. package/src/core/descriptor-meta.ts +1 -1
  134. package/src/core/errors.ts +33 -0
  135. package/src/core/migrations/op-factory-call.ts +1 -5
  136. package/src/core/migrations/operations/columns.ts +1 -1
  137. package/src/core/migrations/operations/constraints.ts +1 -1
  138. package/src/core/migrations/operations/data-transform.ts +35 -21
  139. package/src/core/migrations/operations/dependencies.ts +1 -1
  140. package/src/core/migrations/operations/enums.ts +1 -1
  141. package/src/core/migrations/operations/indexes.ts +1 -1
  142. package/src/core/migrations/operations/shared.ts +1 -1
  143. package/src/core/migrations/operations/tables.ts +1 -1
  144. package/src/core/migrations/planner-ddl-builders.ts +1 -1
  145. package/src/core/migrations/planner-produced-postgres-migration.ts +0 -1
  146. package/src/core/migrations/planner-recipes.ts +1 -1
  147. package/src/core/migrations/planner-sql-checks.ts +1 -1
  148. package/src/core/migrations/planner.ts +19 -15
  149. package/src/core/migrations/postgres-migration.ts +54 -1
  150. package/src/core/migrations/render-typescript.ts +23 -17
  151. package/src/core/migrations/runner.ts +47 -13
  152. package/src/core/migrations/statement-builders.ts +22 -6
  153. package/src/core/native-type-normalizer.ts +49 -0
  154. package/src/core/sql-utils.ts +104 -0
  155. package/src/exports/codec-ids.ts +1 -0
  156. package/src/exports/codec-types.ts +51 -0
  157. package/src/exports/codecs.ts +2 -0
  158. package/src/exports/data-transform.ts +1 -0
  159. package/src/exports/default-normalizer.ts +1 -0
  160. package/src/exports/errors.ts +1 -0
  161. package/src/exports/issue-planner.ts +1 -0
  162. package/src/exports/migration.ts +6 -0
  163. package/src/exports/native-type-normalizer.ts +1 -0
  164. package/src/exports/op-factory-call.ts +25 -0
  165. package/src/exports/planner-ddl-builders.ts +8 -0
  166. package/src/exports/planner-identity-values.ts +1 -0
  167. package/src/exports/planner-produced-postgres-migration.ts +1 -0
  168. package/src/exports/planner-schema-lookup.ts +6 -0
  169. package/src/exports/planner-sql-checks.ts +11 -0
  170. package/src/exports/planner-target-details.ts +1 -0
  171. package/src/exports/planner.ts +1 -0
  172. package/src/exports/render-ops.ts +1 -0
  173. package/src/exports/render-typescript.ts +1 -0
  174. package/src/exports/runtime.ts +19 -4
  175. package/src/exports/sql-utils.ts +7 -0
  176. package/src/exports/statement-builders.ts +7 -0
  177. package/src/exports/types.ts +1 -0
  178. package/dist/postgres-migration-BsHJHV9O.mjs +0 -2793
  179. package/dist/postgres-migration-BsHJHV9O.mjs.map +0 -1
@@ -1,7 +1,3 @@
1
- import {
2
- normalizeSchemaNativeType,
3
- parsePostgresDefault,
4
- } from '@prisma-next/adapter-postgres/control';
5
1
  import type { ContractMarkerRecord } from '@prisma-next/contract/types';
6
2
  import type {
7
3
  MigrationOperationPolicy,
@@ -16,16 +12,17 @@ import type {
16
12
  } from '@prisma-next/family-sql/control';
17
13
  import { runnerFailure, runnerSuccess } from '@prisma-next/family-sql/control';
18
14
  import { verifySqlSchema } from '@prisma-next/family-sql/schema-verify';
19
- import { readMarker } from '@prisma-next/family-sql/verify';
20
15
  import type { DataTransformOperation } from '@prisma-next/framework-components/control';
21
16
  import { SqlQueryError } from '@prisma-next/sql-errors';
22
17
  import { ifDefined } from '@prisma-next/utils/defined';
23
18
  import type { Result } from '@prisma-next/utils/result';
24
19
  import { ok, okVoid } from '@prisma-next/utils/result';
20
+ import { parsePostgresDefault } from '../default-normalizer';
21
+ import { normalizeSchemaNativeType } from '../native-type-normalizer';
25
22
  import type { PostgresPlanTargetDetails } from './planner-target-details';
26
23
  import {
27
24
  buildLedgerInsertStatement,
28
- buildWriteMarkerStatements,
25
+ buildMergeMarkerStatements,
29
26
  ensureLedgerTableStatement,
30
27
  ensureMarkerTableStatement,
31
28
  ensurePrismaContractSchemaStatement,
@@ -122,7 +119,7 @@ class PostgresMigrationRunner implements SqlMigrationRunner<PostgresPlanTargetDe
122
119
  try {
123
120
  await this.acquireLock(driver, lockKey);
124
121
  await this.ensureControlTables(driver);
125
- const existingMarker = await readMarker(driver);
122
+ const existingMarker = await this.family.readMarker({ driver });
126
123
 
127
124
  // Validate plan origin matches existing marker (needs marker from DB)
128
125
  const markerCheck = this.ensureMarkerCompatibility(existingMarker, options.plan);
@@ -130,9 +127,14 @@ class PostgresMigrationRunner implements SqlMigrationRunner<PostgresPlanTargetDe
130
127
  return markerCheck;
131
128
  }
132
129
 
133
- // db update (origin: null) always applies; migration-apply (origin set) skips if marker matches.
130
+ // db update (origin: null) always applies; migration-apply (origin set,
131
+ // origin !== destination) skips if marker already matches destination.
132
+ // Self-edges (origin === destination) intentionally bypass the skip:
133
+ // the migration is data-only, and the data transform's own check
134
+ // decides whether `run` fires.
134
135
  const markerAtDestination = this.markerMatchesDestination(existingMarker, options.plan);
135
- const skipOperations = markerAtDestination && options.plan.origin != null;
136
+ const isSelfEdge = options.plan.origin?.storageHash === options.plan.destination.storageHash;
137
+ const skipOperations = markerAtDestination && options.plan.origin != null && !isSelfEdge;
136
138
  let applyValue: ApplyPlanSuccessValue;
137
139
 
138
140
  if (skipOperations) {
@@ -172,9 +174,39 @@ class PostgresMigrationRunner implements SqlMigrationRunner<PostgresPlanTargetDe
172
174
  });
173
175
  }
174
176
 
175
- // Record marker and ledger entries
176
- await this.upsertMarker(driver, options, existingMarker);
177
- await this.recordLedgerEntry(driver, options, existingMarker, applyValue.executedOperations);
177
+ // Self-edge no-op detection: a self-edge migration with zero ops in
178
+ // the plan that brings no new invariants produced no observable
179
+ // change. Skip the marker + ledger writes so an idempotent re-apply
180
+ // of a self-edge data transform doesn't churn updatedAt or pile up
181
+ // empty ledger entries. db update no-ops still write a ledger entry
182
+ // as audit trail.
183
+ //
184
+ // TODO(invariant-routing follow-up): `executeDataTransform` always
185
+ // counts every op it visits (including self-skips via `check === true`
186
+ // or empty idempotency probe), so `operationsExecuted === 0` here
187
+ // means "the plan had zero ops" rather than "every op self-skipped".
188
+ // The CLI is unaffected today because `migration-apply.ts` marker-
189
+ // subtraction empties `effectiveRequired` first and short-circuits
190
+ // before we run; the non-CLI re-apply path needs a per-op `executed`
191
+ // flag threaded through `executeDataTransform` to recover the
192
+ // intended check. See review thread A13 / future M5 ADR draft.
193
+ const incomingInvariants = options.plan.providedInvariants;
194
+ const existingInvariants = new Set(existingMarker?.invariants ?? []);
195
+ const incomingIsSubsetOfExisting = incomingInvariants.every((id) =>
196
+ existingInvariants.has(id),
197
+ );
198
+ const isSelfEdgeNoOp =
199
+ isSelfEdge && applyValue.operationsExecuted === 0 && incomingIsSubsetOfExisting;
200
+
201
+ if (!isSelfEdgeNoOp) {
202
+ await this.upsertMarker(driver, options, existingMarker);
203
+ await this.recordLedgerEntry(
204
+ driver,
205
+ options,
206
+ existingMarker,
207
+ applyValue.executedOperations,
208
+ );
209
+ }
178
210
 
179
211
  await this.commitTransaction(driver);
180
212
  committed = true;
@@ -619,7 +651,8 @@ class PostgresMigrationRunner implements SqlMigrationRunner<PostgresPlanTargetDe
619
651
  options: SqlMigrationRunnerExecuteOptions<PostgresPlanTargetDetails>,
620
652
  existingMarker: ContractMarkerRecord | null,
621
653
  ): Promise<void> {
622
- const writeStatements = buildWriteMarkerStatements({
654
+ const incomingInvariants = options.plan.providedInvariants;
655
+ const writeStatements = buildMergeMarkerStatements({
623
656
  storageHash: options.plan.destination.storageHash,
624
657
  profileHash:
625
658
  options.plan.destination.profileHash ??
@@ -628,6 +661,7 @@ class PostgresMigrationRunner implements SqlMigrationRunner<PostgresPlanTargetDe
628
661
  contractJson: options.destinationContract,
629
662
  canonicalVersion: null,
630
663
  meta: {},
664
+ invariants: incomingInvariants,
631
665
  });
632
666
  const statement = existingMarker ? writeStatements.update : writeStatements.insert;
633
667
  await this.executeStatement(driver, statement);
@@ -17,7 +17,8 @@ export const ensureMarkerTableStatement: SqlStatement = {
17
17
  canonical_version int,
18
18
  updated_at timestamptz not null default now(),
19
19
  app_tag text,
20
- meta jsonb not null default '{}'
20
+ meta jsonb not null default '{}',
21
+ invariants text[] not null default '{}'
21
22
  )`,
22
23
  params: [],
23
24
  };
@@ -37,16 +38,23 @@ export const ensureLedgerTableStatement: SqlStatement = {
37
38
  params: [],
38
39
  };
39
40
 
40
- export interface WriteMarkerInput {
41
+ export interface MergeMarkerInput {
41
42
  readonly storageHash: string;
42
43
  readonly profileHash: string;
43
44
  readonly contractJson?: unknown;
44
45
  readonly canonicalVersion?: number | null;
45
46
  readonly appTag?: string | null;
46
47
  readonly meta?: Record<string, unknown>;
48
+ /**
49
+ * Invariants to merge into `marker.invariants`. INSERT writes them as
50
+ * the initial value (callers are expected to pass a sorted, deduped
51
+ * array). UPDATE merges them with the existing column server-side via
52
+ * a single atomic SQL expression.
53
+ */
54
+ readonly invariants: readonly string[];
47
55
  }
48
56
 
49
- export function buildWriteMarkerStatements(input: WriteMarkerInput): {
57
+ export function buildMergeMarkerStatements(input: MergeMarkerInput): {
50
58
  readonly insert: SqlStatement;
51
59
  readonly update: SqlStatement;
52
60
  } {
@@ -58,6 +66,7 @@ export function buildWriteMarkerStatements(input: WriteMarkerInput): {
58
66
  input.canonicalVersion ?? null,
59
67
  input.appTag ?? null,
60
68
  jsonParam(input.meta ?? {}),
69
+ input.invariants,
61
70
  ];
62
71
 
63
72
  return {
@@ -70,7 +79,8 @@ export function buildWriteMarkerStatements(input: WriteMarkerInput): {
70
79
  canonical_version,
71
80
  updated_at,
72
81
  app_tag,
73
- meta
82
+ meta,
83
+ invariants
74
84
  ) values (
75
85
  $1,
76
86
  $2,
@@ -79,11 +89,16 @@ export function buildWriteMarkerStatements(input: WriteMarkerInput): {
79
89
  $5,
80
90
  now(),
81
91
  $6,
82
- $7::jsonb
92
+ $7::jsonb,
93
+ $8::text[]
83
94
  )`,
84
95
  params,
85
96
  },
86
97
  update: {
98
+ // `invariants = array(select distinct unnest(invariants || $8::text[]) order by 1)`
99
+ // reads the current column value under the UPDATE's row lock, unions
100
+ // with the incoming array, dedupes, and sorts ascending — single
101
+ // statement, atomic, no read-then-write window.
87
102
  sql: `update prisma_contract.marker set
88
103
  core_hash = $2,
89
104
  profile_hash = $3,
@@ -91,7 +106,8 @@ export function buildWriteMarkerStatements(input: WriteMarkerInput): {
91
106
  canonical_version = $5,
92
107
  updated_at = now(),
93
108
  app_tag = $6,
94
- meta = $7::jsonb
109
+ meta = $7::jsonb,
110
+ invariants = array(select distinct unnest(invariants || $8::text[]) order by 1)
95
111
  where id = $1`,
96
112
  params,
97
113
  },
@@ -0,0 +1,49 @@
1
+ /**
2
+ * Postgres native-type normalization.
3
+ *
4
+ * Lives in `target-postgres` because both the migration planner/runner (control
5
+ * plane) and the introspection adapter (control plane) need to normalize raw
6
+ * native-type strings to the same canonical form for comparison.
7
+ */
8
+
9
+ /**
10
+ * Lookup map for simple prefix-based type normalization.
11
+ *
12
+ * Using a Map for O(1) lookup instead of multiple startsWith checks.
13
+ */
14
+ const TYPE_PREFIX_MAP: ReadonlyMap<string, string> = new Map([
15
+ ['varchar', 'character varying'],
16
+ ['bpchar', 'character'],
17
+ ['varbit', 'bit varying'],
18
+ ]);
19
+
20
+ /**
21
+ * Normalizes a Postgres schema native type to its canonical form for comparison.
22
+ *
23
+ * Uses a pre-computed lookup map for simple prefix replacements (O(1))
24
+ * and handles complex temporal type normalization separately.
25
+ */
26
+ export function normalizeSchemaNativeType(nativeType: string): string {
27
+ const trimmed = nativeType.trim();
28
+
29
+ for (const [prefix, replacement] of TYPE_PREFIX_MAP) {
30
+ if (trimmed.startsWith(prefix)) {
31
+ return replacement + trimmed.slice(prefix.length);
32
+ }
33
+ }
34
+
35
+ if (trimmed.includes(' with time zone')) {
36
+ if (trimmed.startsWith('timestamp')) {
37
+ return `timestamptz${trimmed.slice(9).replace(' with time zone', '')}`;
38
+ }
39
+ if (trimmed.startsWith('time')) {
40
+ return `timetz${trimmed.slice(4).replace(' with time zone', '')}`;
41
+ }
42
+ }
43
+
44
+ if (trimmed.includes(' without time zone')) {
45
+ return trimmed.replace(' without time zone', '');
46
+ }
47
+
48
+ return trimmed;
49
+ }
@@ -0,0 +1,104 @@
1
+ /**
2
+ * Shared SQL utility functions for the Postgres target.
3
+ *
4
+ * These functions handle safe SQL identifier and literal escaping
5
+ * with security validations to prevent injection and encoding issues.
6
+ *
7
+ * They live in `target-postgres` because both the control adapter (used at
8
+ * emit time) and the runtime adapter (used at execute time) need the same
9
+ * escaping/validation behavior, and the target package is the natural shared
10
+ * home that both adapters can depend on without crossing planes.
11
+ */
12
+
13
+ export class SqlEscapeError extends Error {
14
+ constructor(
15
+ message: string,
16
+ public readonly value: string,
17
+ public readonly kind: 'identifier' | 'literal',
18
+ ) {
19
+ super(message);
20
+ this.name = 'SqlEscapeError';
21
+ }
22
+ }
23
+
24
+ const MAX_IDENTIFIER_LENGTH = 63;
25
+
26
+ /**
27
+ * Validates and quotes a PostgreSQL identifier (table, column, type, schema names).
28
+ *
29
+ * Security validations:
30
+ * - Rejects null bytes which could cause truncation or unexpected behavior
31
+ * - Rejects empty identifiers
32
+ * - Warns on identifiers exceeding PostgreSQL's 63-character limit
33
+ *
34
+ * @throws {SqlEscapeError} If the identifier contains null bytes or is empty
35
+ */
36
+ export function quoteIdentifier(identifier: string): string {
37
+ if (identifier.length === 0) {
38
+ throw new SqlEscapeError('Identifier cannot be empty', identifier, 'identifier');
39
+ }
40
+ if (identifier.includes('\0')) {
41
+ throw new SqlEscapeError(
42
+ 'Identifier cannot contain null bytes',
43
+ identifier.replace(/\0/g, '\\0'),
44
+ 'identifier',
45
+ );
46
+ }
47
+ if (identifier.length > MAX_IDENTIFIER_LENGTH) {
48
+ console.warn(
49
+ `Identifier "${identifier.slice(0, 20)}..." exceeds PostgreSQL's ${MAX_IDENTIFIER_LENGTH}-character limit and will be truncated`,
50
+ );
51
+ }
52
+ return `"${identifier.replace(/"/g, '""')}"`;
53
+ }
54
+
55
+ /**
56
+ * Escapes a string literal for safe use in SQL statements.
57
+ *
58
+ * Security validations:
59
+ * - Rejects null bytes which could cause truncation or unexpected behavior
60
+ *
61
+ * Note: This assumes PostgreSQL's `standard_conforming_strings` is ON (default since PG 9.1).
62
+ * Backslashes are treated as literal characters, not escape sequences.
63
+ *
64
+ * @throws {SqlEscapeError} If the value contains null bytes
65
+ */
66
+ export function escapeLiteral(value: string): string {
67
+ if (value.includes('\0')) {
68
+ throw new SqlEscapeError(
69
+ 'Literal value cannot contain null bytes',
70
+ value.replace(/\0/g, '\\0'),
71
+ 'literal',
72
+ );
73
+ }
74
+ return value.replace(/'/g, "''");
75
+ }
76
+
77
+ /**
78
+ * Builds a qualified name (schema.object) with proper quoting.
79
+ */
80
+ export function qualifyName(schemaName: string, objectName: string): string {
81
+ return `${quoteIdentifier(schemaName)}.${quoteIdentifier(objectName)}`;
82
+ }
83
+
84
+ /**
85
+ * Validates that an enum value doesn't exceed PostgreSQL's label length limit.
86
+ *
87
+ * PostgreSQL enum labels have a maximum length of NAMEDATALEN-1 (63 bytes by default).
88
+ * Unlike identifiers, enum labels that exceed this limit cause an error rather than
89
+ * silent truncation.
90
+ *
91
+ * @param value - The enum value to validate
92
+ * @param enumTypeName - Name of the enum type (for error messages)
93
+ * @throws {SqlEscapeError} If the value exceeds the maximum length
94
+ */
95
+ export function validateEnumValueLength(value: string, enumTypeName: string): void {
96
+ if (value.length > MAX_IDENTIFIER_LENGTH) {
97
+ throw new SqlEscapeError(
98
+ `Enum value "${value.slice(0, 20)}..." for type "${enumTypeName}" exceeds PostgreSQL's ` +
99
+ `${MAX_IDENTIFIER_LENGTH}-character label limit`,
100
+ value,
101
+ 'literal',
102
+ );
103
+ }
104
+ }
@@ -0,0 +1 @@
1
+ export * from '../core/codec-ids';
@@ -0,0 +1,51 @@
1
+ /**
2
+ * Codec type definitions for the Postgres target.
3
+ *
4
+ * This file exports type-only definitions for codec input/output types.
5
+ * These types are imported by generated `contract.d.ts` files for compile-time
6
+ * type inference.
7
+ *
8
+ * Lives in `target-postgres` because codec types describe the target's value
9
+ * space - both the control adapter (introspection / schema verification) and
10
+ * the runtime adapter (encode/decode) share the same definitions, and the
11
+ * target package is the natural home that both adapters depend on.
12
+ *
13
+ * Runtime codec implementations are provided by the runtime adapter's
14
+ * codec registry, which is built from `core/codecs.ts`.
15
+ */
16
+
17
+ import type { JsonValue } from '@prisma-next/contract/types';
18
+ import type { CodecTypes as CoreCodecTypes } from '../core/codecs';
19
+
20
+ export type CodecTypes = CoreCodecTypes;
21
+
22
+ export type { JsonValue };
23
+ export { dataTypes } from '../core/codecs';
24
+
25
+ type Branded<T, Shape extends Record<string, unknown>> = T & {
26
+ readonly [K in keyof Shape]: Shape[K];
27
+ };
28
+
29
+ type BrandedString<Shape extends Record<string, unknown>> = Branded<string, Shape>;
30
+
31
+ export type Char<N extends number> = BrandedString<{ __charLength: N }>;
32
+ export type Varchar<N extends number> = BrandedString<{ __varcharLength: N }>;
33
+ export type Numeric<P extends number, S extends number | undefined = undefined> = BrandedString<{
34
+ __numericPrecision: P;
35
+ __numericScale: S;
36
+ }>;
37
+ export type Bit<N extends number> = BrandedString<{ __bitLength: N }>;
38
+ export type VarBit<N extends number> = BrandedString<{ __varbitLength: N }>;
39
+ export type Timestamp<P extends number | undefined = undefined> = BrandedString<{
40
+ __timestampPrecision: P;
41
+ }>;
42
+ export type Timestamptz<P extends number | undefined = undefined> = BrandedString<{
43
+ __timestamptzPrecision: P;
44
+ }>;
45
+ export type Time<P extends number | undefined = undefined> = BrandedString<{ __timePrecision: P }>;
46
+ export type Timetz<P extends number | undefined = undefined> = BrandedString<{
47
+ __timetzPrecision: P;
48
+ }>;
49
+ export type Interval<P extends number | undefined = undefined> = BrandedString<{
50
+ __intervalPrecision: P;
51
+ }>;
@@ -0,0 +1,2 @@
1
+ export type { CodecTypes } from '../core/codecs';
2
+ export { codecDefinitions, dataTypes } from '../core/codecs';
@@ -0,0 +1 @@
1
+ export { dataTransform } from '../core/migrations/operations/data-transform';
@@ -0,0 +1 @@
1
+ export { parsePostgresDefault } from '../core/default-normalizer';
@@ -0,0 +1 @@
1
+ export { errorPostgresMigrationStackMissing } from '../core/errors';
@@ -0,0 +1 @@
1
+ export { planIssues } from '../core/migrations/issue-planner';
@@ -1,3 +1,9 @@
1
+ // Re-exported so a Postgres `migration.ts` only needs the single
2
+ // `@prisma-next/target-postgres/migration` import for its base class
3
+ // and the CLI entrypoint, mirroring how `placeholder` is surfaced
4
+ // here. The renderer emits the entrypoint call as
5
+ // `MigrationCLI.run(import.meta.url, M)`.
6
+ export { MigrationCLI } from '@prisma-next/cli/migration-cli';
1
7
  // Re-exported so user-edited migration.ts files only need to depend on
2
8
  // `@prisma-next/target-postgres/migration` to fill in planner-emitted
3
9
  // `placeholder("…")` slots, instead of pulling in `@prisma-next/errors`
@@ -0,0 +1 @@
1
+ export { normalizeSchemaNativeType } from '../core/native-type-normalizer';
@@ -0,0 +1,25 @@
1
+ export {
2
+ AddColumnCall,
3
+ AddEnumValuesCall,
4
+ AddForeignKeyCall,
5
+ AddPrimaryKeyCall,
6
+ AddUniqueCall,
7
+ AlterColumnTypeCall,
8
+ CreateEnumTypeCall,
9
+ CreateExtensionCall,
10
+ CreateIndexCall,
11
+ CreateSchemaCall,
12
+ CreateTableCall,
13
+ DataTransformCall,
14
+ DropColumnCall,
15
+ DropConstraintCall,
16
+ DropDefaultCall,
17
+ DropEnumTypeCall,
18
+ DropIndexCall,
19
+ DropNotNullCall,
20
+ DropTableCall,
21
+ RawSqlCall,
22
+ RenameTypeCall,
23
+ SetDefaultCall,
24
+ SetNotNullCall,
25
+ } from '../core/migrations/op-factory-call';
@@ -0,0 +1,8 @@
1
+ export {
2
+ buildAddColumnSql,
3
+ buildColumnDefaultSql,
4
+ buildColumnTypeSql,
5
+ buildCreateTableSql,
6
+ buildForeignKeySql,
7
+ renderDefaultLiteral,
8
+ } from '../core/migrations/planner-ddl-builders';
@@ -0,0 +1 @@
1
+ export { buildBuiltinIdentityValue } from '../core/migrations/planner-identity-values';
@@ -0,0 +1 @@
1
+ export { TypeScriptRenderablePostgresMigration } from '../core/migrations/planner-produced-postgres-migration';
@@ -0,0 +1,6 @@
1
+ export {
2
+ buildSchemaLookupMap,
3
+ hasForeignKey,
4
+ hasIndex,
5
+ hasUniqueConstraint,
6
+ } from '../core/migrations/planner-schema-lookup';
@@ -0,0 +1,11 @@
1
+ export {
2
+ buildExpectedFormatType,
3
+ columnExistsCheck,
4
+ columnHasNoDefaultCheck,
5
+ columnNullabilityCheck,
6
+ constraintExistsCheck,
7
+ qualifyTableName,
8
+ tableHasPrimaryKeyCheck,
9
+ tableIsEmptyCheck,
10
+ toRegclassLiteral,
11
+ } from '../core/migrations/planner-sql-checks';
@@ -0,0 +1 @@
1
+ export type { PostgresPlanTargetDetails } from '../core/migrations/planner-target-details';
@@ -0,0 +1 @@
1
+ export { createPostgresMigrationPlanner } from '../core/migrations/planner';
@@ -0,0 +1 @@
1
+ export { renderOps } from '../core/migrations/render-ops';
@@ -0,0 +1 @@
1
+ export { renderCallsToTypeScript } from '../core/migrations/render-typescript';
@@ -1,14 +1,29 @@
1
- import type { RuntimeTargetInstance } from '@prisma-next/framework-components/execution';
1
+ import type {
2
+ RuntimeTargetDescriptor,
3
+ RuntimeTargetInstance,
4
+ } from '@prisma-next/framework-components/execution';
2
5
  import { createCodecRegistry } from '@prisma-next/sql-relational-core/ast';
3
- import type { SqlRuntimeTargetDescriptor } from '@prisma-next/sql-runtime';
4
6
  import { postgresTargetDescriptorMeta } from '../core/descriptor-meta';
5
7
 
6
8
  export interface PostgresRuntimeTargetInstance extends RuntimeTargetInstance<'sql', 'postgres'> {}
7
9
 
8
- const postgresRuntimeTargetDescriptor: SqlRuntimeTargetDescriptor<
10
+ /**
11
+ * Target-postgres deliberately does NOT import `SqlRuntimeTargetDescriptor`
12
+ * from `@prisma-next/sql-runtime`. The target package is a control-plane
13
+ * residence and must not pull the SQL execution-plane package into its
14
+ * dependency closure. The runtime descriptor here is shaped to satisfy the
15
+ * framework's `RuntimeTargetDescriptor` plus the structural
16
+ * `SqlStaticContributions` (`codecs`, `parameterizedCodecs`) that
17
+ * `@prisma-next/sql-runtime` consumers narrow to at composition time.
18
+ */
19
+ const postgresRuntimeTargetDescriptor: RuntimeTargetDescriptor<
20
+ 'sql',
9
21
  'postgres',
10
22
  PostgresRuntimeTargetInstance
11
- > = {
23
+ > & {
24
+ readonly codecs: () => ReturnType<typeof createCodecRegistry>;
25
+ readonly parameterizedCodecs: () => readonly never[];
26
+ } = {
12
27
  ...postgresTargetDescriptorMeta,
13
28
  codecs: () => createCodecRegistry(),
14
29
  parameterizedCodecs: () => [],
@@ -0,0 +1,7 @@
1
+ export {
2
+ escapeLiteral,
3
+ qualifyName,
4
+ quoteIdentifier,
5
+ SqlEscapeError,
6
+ validateEnumValueLength,
7
+ } from '../core/sql-utils';
@@ -0,0 +1,7 @@
1
+ export type { SqlStatement } from '../core/migrations/statement-builders';
2
+ export {
3
+ buildMergeMarkerStatements,
4
+ ensureLedgerTableStatement,
5
+ ensureMarkerTableStatement,
6
+ ensurePrismaContractSchemaStatement,
7
+ } from '../core/migrations/statement-builders';
@@ -0,0 +1 @@
1
+ export type { PostgresColumnDefault } from '../core/types';