@prisma-next/adapter-postgres 0.5.0-dev.9 → 0.5.1

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 (48) hide show
  1. package/README.md +14 -16
  2. package/dist/adapter-CBaRvhQF.mjs +52 -0
  3. package/dist/adapter-CBaRvhQF.mjs.map +1 -0
  4. package/dist/adapter.d.mts +3 -4
  5. package/dist/adapter.d.mts.map +1 -1
  6. package/dist/adapter.mjs +2 -3
  7. package/dist/column-types.d.mts +19 -24
  8. package/dist/column-types.d.mts.map +1 -1
  9. package/dist/column-types.mjs +20 -60
  10. package/dist/column-types.mjs.map +1 -1
  11. package/dist/control.d.mts +83 -3
  12. package/dist/control.d.mts.map +1 -1
  13. package/dist/control.mjs +117 -29
  14. package/dist/control.mjs.map +1 -1
  15. package/dist/{descriptor-meta-RTDzyrae.mjs → descriptor-meta-CyjnYUWG.mjs} +35 -27
  16. package/dist/descriptor-meta-CyjnYUWG.mjs.map +1 -0
  17. package/dist/operation-types.d.mts +11 -10
  18. package/dist/operation-types.d.mts.map +1 -1
  19. package/dist/operation-types.mjs +1 -1
  20. package/dist/runtime.d.mts +3 -11
  21. package/dist/runtime.d.mts.map +1 -1
  22. package/dist/runtime.mjs +19 -81
  23. package/dist/runtime.mjs.map +1 -1
  24. package/dist/{sql-renderer-pEaSP82_.mjs → sql-renderer-wTVSEy5H.mjs} +109 -48
  25. package/dist/sql-renderer-wTVSEy5H.mjs.map +1 -0
  26. package/dist/{types-CfRPdAk8.d.mts → types-B1eiuBHQ.d.mts} +12 -1
  27. package/dist/types-B1eiuBHQ.d.mts.map +1 -0
  28. package/dist/types.d.mts +1 -1
  29. package/dist/types.mjs +1 -1
  30. package/package.json +23 -23
  31. package/src/core/adapter.ts +36 -43
  32. package/src/core/codec-lookup.ts +19 -0
  33. package/src/core/control-adapter.ts +252 -98
  34. package/src/core/control-mutation-defaults.ts +24 -18
  35. package/src/core/descriptor-meta.ts +27 -20
  36. package/src/core/sql-renderer.ts +129 -66
  37. package/src/core/types.ts +11 -0
  38. package/src/exports/column-types.ts +21 -61
  39. package/src/exports/control.ts +3 -2
  40. package/src/exports/runtime.ts +27 -66
  41. package/src/types/operation-types.ts +19 -9
  42. package/dist/adapter-hNElNHo4.mjs +0 -60
  43. package/dist/adapter-hNElNHo4.mjs.map +0 -1
  44. package/dist/descriptor-meta-RTDzyrae.mjs.map +0 -1
  45. package/dist/sql-renderer-pEaSP82_.mjs.map +0 -1
  46. package/dist/types-CfRPdAk8.d.mts.map +0 -1
  47. package/src/core/json-schema-validator.ts +0 -54
  48. package/src/core/standard-schema.ts +0 -71
@@ -1,4 +1,7 @@
1
+ import type { ContractMarkerRecord } from '@prisma-next/contract/types';
1
2
  import type { SqlControlAdapter } from '@prisma-next/family-sql/control-adapter';
3
+ import { parseContractMarkerRow } from '@prisma-next/family-sql/verify';
4
+ import type { CodecLookup } from '@prisma-next/framework-components/codec';
2
5
  import type { ControlDriverInstance } from '@prisma-next/framework-components/control';
3
6
  import type {
4
7
  AnyQueryAst,
@@ -6,7 +9,6 @@ import type {
6
9
  LowererContext,
7
10
  } from '@prisma-next/sql-relational-core/ast';
8
11
  import type {
9
- DependencyIR,
10
12
  PrimaryKey,
11
13
  SqlColumnIR,
12
14
  SqlForeignKeyIR,
@@ -19,6 +21,7 @@ import type {
19
21
  import { parsePostgresDefault } from '@prisma-next/target-postgres/default-normalizer';
20
22
  import { normalizeSchemaNativeType } from '@prisma-next/target-postgres/native-type-normalizer';
21
23
  import { ifDefined } from '@prisma-next/utils/defined';
24
+ import { createPostgresBuiltinCodecLookup } from './codec-lookup';
22
25
  import { pgEnumControlHooks } from './enum-control-hooks';
23
26
  import { renderLoweredSql } from './sql-renderer';
24
27
  import type { PostgresContract } from './types';
@@ -31,6 +34,19 @@ export class PostgresControlAdapter implements SqlControlAdapter<'postgres'> {
31
34
  readonly familyId = 'sql' as const;
32
35
  readonly targetId = 'postgres' as const;
33
36
 
37
+ private readonly codecLookup: CodecLookup;
38
+
39
+ /**
40
+ * @param codecLookup - Codec lookup used by the SQL renderer to resolve
41
+ * per-codec metadata at lower-time. Defaults to a Postgres-builtins-only
42
+ * lookup when omitted. Stack-aware callers
43
+ * (`SqlControlAdapterDescriptor.create(stack)`) supply
44
+ * `stack.codecLookup` so extension codecs are visible to the renderer.
45
+ */
46
+ constructor(codecLookup?: CodecLookup) {
47
+ this.codecLookup = codecLookup ?? createPostgresBuiltinCodecLookup();
48
+ }
49
+
34
50
  /**
35
51
  * Target-specific normalizer for raw Postgres default expressions.
36
52
  * Used by schema verification to normalize raw defaults before comparison.
@@ -53,7 +69,108 @@ export class PostgresControlAdapter implements SqlControlAdapter<'postgres'> {
53
69
  * without instantiating the runtime adapter.
54
70
  */
55
71
  lower(ast: AnyQueryAst, context: LowererContext<unknown>): LoweredStatement {
56
- return renderLoweredSql(ast, context.contract as PostgresContract);
72
+ return renderLoweredSql(ast, context.contract as PostgresContract, this.codecLookup);
73
+ }
74
+
75
+ /**
76
+ * Reads the contract marker from `prisma_contract.marker`. Probes
77
+ * `information_schema.tables` first so a fresh database (where the
78
+ * `prisma_contract` schema doesn't yet exist) returns `null` instead of a
79
+ * "relation does not exist" error — some Postgres wire-protocol clients
80
+ * (e.g. PGlite's TCP proxy) don't fully recover from extended-protocol
81
+ * parse errors, so we probe before reading.
82
+ */
83
+ async readMarker(
84
+ driver: ControlDriverInstance<'sql', 'postgres'>,
85
+ space: string,
86
+ ): Promise<ContractMarkerRecord | null> {
87
+ const exists = await driver.query(
88
+ `select 1
89
+ from information_schema.tables
90
+ where table_schema = $1 and table_name = $2`,
91
+ ['prisma_contract', 'marker'],
92
+ );
93
+ if (exists.rows.length === 0) {
94
+ return null;
95
+ }
96
+
97
+ const result = await driver.query<{
98
+ core_hash: string;
99
+ profile_hash: string;
100
+ contract_json: unknown | null;
101
+ canonical_version: number | null;
102
+ updated_at: Date | string;
103
+ app_tag: string | null;
104
+ meta: unknown | null;
105
+ invariants: readonly string[];
106
+ }>(
107
+ `select
108
+ core_hash,
109
+ profile_hash,
110
+ contract_json,
111
+ canonical_version,
112
+ updated_at,
113
+ app_tag,
114
+ meta,
115
+ invariants
116
+ from prisma_contract.marker
117
+ where space = $1`,
118
+ [space],
119
+ );
120
+
121
+ const row = result.rows[0];
122
+ if (!row) return null;
123
+ return parseContractMarkerRow(row);
124
+ }
125
+
126
+ /**
127
+ * Reads every row from `prisma_contract.marker` and returns them keyed
128
+ * by `space`. Mirrors the existence probe in {@link readMarker}: a
129
+ * fresh database without the `prisma_contract` schema returns an empty
130
+ * map rather than raising "relation does not exist".
131
+ */
132
+ async readAllMarkers(
133
+ driver: ControlDriverInstance<'sql', 'postgres'>,
134
+ ): Promise<ReadonlyMap<string, ContractMarkerRecord>> {
135
+ const exists = await driver.query(
136
+ `select 1
137
+ from information_schema.tables
138
+ where table_schema = $1 and table_name = $2`,
139
+ ['prisma_contract', 'marker'],
140
+ );
141
+ if (exists.rows.length === 0) {
142
+ return new Map();
143
+ }
144
+
145
+ const result = await driver.query<{
146
+ space: string;
147
+ core_hash: string;
148
+ profile_hash: string;
149
+ contract_json: unknown | null;
150
+ canonical_version: number | null;
151
+ updated_at: Date | string;
152
+ app_tag: string | null;
153
+ meta: unknown | null;
154
+ invariants: readonly string[];
155
+ }>(
156
+ `select
157
+ space,
158
+ core_hash,
159
+ profile_hash,
160
+ contract_json,
161
+ canonical_version,
162
+ updated_at,
163
+ app_tag,
164
+ meta,
165
+ invariants
166
+ from prisma_contract.marker`,
167
+ );
168
+
169
+ const rows = new Map<string, ContractMarkerRecord>();
170
+ for (const row of result.rows) {
171
+ rows.set(row.space, parseContractMarkerRow(row));
172
+ }
173
+ return rows;
57
174
  }
58
175
 
59
176
  /**
@@ -63,7 +180,7 @@ export class PostgresControlAdapter implements SqlControlAdapter<'postgres'> {
63
180
  * and returns the schema structure without type mapping or contract enrichment.
64
181
  * Type mapping and enrichment are handled separately by enrichment helpers.
65
182
  *
66
- * Uses batched queries to minimize database round trips (7 queries instead of 5T+3).
183
+ * Uses batched queries to minimize database round trips (6 queries instead of 5T+1).
67
184
  *
68
185
  * @param driver - ControlDriverInstance<'sql', 'postgres'> instance for executing queries
69
186
  * @param contract - Optional contract for contract-guided introspection (filtering, optimization)
@@ -75,39 +192,32 @@ export class PostgresControlAdapter implements SqlControlAdapter<'postgres'> {
75
192
  _contract?: unknown,
76
193
  schema = 'public',
77
194
  ): Promise<SqlSchemaIR> {
78
- // Execute all queries in parallel for efficiency (7 queries instead of 5T+3)
79
- const [
80
- tablesResult,
81
- columnsResult,
82
- pkResult,
83
- fkResult,
84
- uniqueResult,
85
- indexResult,
86
- extensionsResult,
87
- ] = await Promise.all([
88
- // Query all tables
89
- driver.query<{ table_name: string }>(
90
- `SELECT table_name
195
+ // Execute all queries in parallel for efficiency (6 queries instead of 5T+1)
196
+ const [tablesResult, columnsResult, pkResult, fkResult, uniqueResult, indexResult] =
197
+ await Promise.all([
198
+ // Query all tables
199
+ driver.query<{ table_name: string }>(
200
+ `SELECT table_name
91
201
  FROM information_schema.tables
92
202
  WHERE table_schema = $1
93
203
  AND table_type = 'BASE TABLE'
94
204
  ORDER BY table_name`,
95
- [schema],
96
- ),
97
- // Query all columns for all tables in schema
98
- driver.query<{
99
- table_name: string;
100
- column_name: string;
101
- data_type: string;
102
- udt_name: string;
103
- is_nullable: string;
104
- character_maximum_length: number | null;
105
- numeric_precision: number | null;
106
- numeric_scale: number | null;
107
- column_default: string | null;
108
- formatted_type: string | null;
109
- }>(
110
- `SELECT
205
+ [schema],
206
+ ),
207
+ // Query all columns for all tables in schema
208
+ driver.query<{
209
+ table_name: string;
210
+ column_name: string;
211
+ data_type: string;
212
+ udt_name: string;
213
+ is_nullable: string;
214
+ character_maximum_length: number | null;
215
+ numeric_precision: number | null;
216
+ numeric_scale: number | null;
217
+ column_default: string | null;
218
+ formatted_type: string | null;
219
+ }>(
220
+ `SELECT
111
221
  c.table_name,
112
222
  column_name,
113
223
  data_type,
@@ -131,16 +241,16 @@ export class PostgresControlAdapter implements SqlControlAdapter<'postgres'> {
131
241
  AND NOT a.attisdropped
132
242
  WHERE c.table_schema = $1
133
243
  ORDER BY c.table_name, c.ordinal_position`,
134
- [schema],
135
- ),
136
- // Query all primary keys for all tables in schema
137
- driver.query<{
138
- table_name: string;
139
- constraint_name: string;
140
- column_name: string;
141
- ordinal_position: number;
142
- }>(
143
- `SELECT
244
+ [schema],
245
+ ),
246
+ // Query all primary keys for all tables in schema
247
+ driver.query<{
248
+ table_name: string;
249
+ constraint_name: string;
250
+ column_name: string;
251
+ ordinal_position: number;
252
+ }>(
253
+ `SELECT
144
254
  tc.table_name,
145
255
  tc.constraint_name,
146
256
  kcu.column_name,
@@ -153,24 +263,24 @@ export class PostgresControlAdapter implements SqlControlAdapter<'postgres'> {
153
263
  WHERE tc.table_schema = $1
154
264
  AND tc.constraint_type = 'PRIMARY KEY'
155
265
  ORDER BY tc.table_name, kcu.ordinal_position`,
156
- [schema],
157
- ),
158
- // Query all foreign keys for all tables in schema, including referential actions.
159
- // Uses pg_catalog for correct positional pairing of composite FK columns
160
- // (information_schema.constraint_column_usage lacks ordinal_position,
161
- // which causes Cartesian products for multi-column FKs).
162
- driver.query<{
163
- table_name: string;
164
- constraint_name: string;
165
- column_name: string;
166
- ordinal_position: number;
167
- referenced_table_schema: string;
168
- referenced_table_name: string;
169
- referenced_column_name: string;
170
- delete_rule: string;
171
- update_rule: string;
172
- }>(
173
- `SELECT
266
+ [schema],
267
+ ),
268
+ // Query all foreign keys for all tables in schema, including referential actions.
269
+ // Uses pg_catalog for correct positional pairing of composite FK columns
270
+ // (information_schema.constraint_column_usage lacks ordinal_position,
271
+ // which causes Cartesian products for multi-column FKs).
272
+ driver.query<{
273
+ table_name: string;
274
+ constraint_name: string;
275
+ column_name: string;
276
+ ordinal_position: number;
277
+ referenced_table_schema: string;
278
+ referenced_table_name: string;
279
+ referenced_column_name: string;
280
+ delete_rule: string;
281
+ update_rule: string;
282
+ }>(
283
+ `SELECT
174
284
  tc.table_name,
175
285
  tc.constraint_name,
176
286
  kcu.column_name,
@@ -203,16 +313,16 @@ export class PostgresControlAdapter implements SqlControlAdapter<'postgres'> {
203
313
  WHERE tc.table_schema = $1
204
314
  AND tc.constraint_type = 'FOREIGN KEY'
205
315
  ORDER BY tc.table_name, tc.constraint_name, kcu.ordinal_position`,
206
- [schema],
207
- ),
208
- // Query all unique constraints for all tables in schema (excluding PKs)
209
- driver.query<{
210
- table_name: string;
211
- constraint_name: string;
212
- column_name: string;
213
- ordinal_position: number;
214
- }>(
215
- `SELECT
316
+ [schema],
317
+ ),
318
+ // Query all unique constraints for all tables in schema (excluding PKs)
319
+ driver.query<{
320
+ table_name: string;
321
+ constraint_name: string;
322
+ column_name: string;
323
+ ordinal_position: number;
324
+ }>(
325
+ `SELECT
216
326
  tc.table_name,
217
327
  tc.constraint_name,
218
328
  kcu.column_name,
@@ -225,26 +335,31 @@ export class PostgresControlAdapter implements SqlControlAdapter<'postgres'> {
225
335
  WHERE tc.table_schema = $1
226
336
  AND tc.constraint_type = 'UNIQUE'
227
337
  ORDER BY tc.table_name, tc.constraint_name, kcu.ordinal_position`,
228
- [schema],
229
- ),
230
- // Query all indexes for all tables in schema (excluding constraints)
231
- driver.query<{
232
- tablename: string;
233
- indexname: string;
234
- indisunique: boolean;
235
- attname: string;
236
- attnum: number;
237
- }>(
238
- `SELECT
338
+ [schema],
339
+ ),
340
+ // Query all indexes for all tables in schema (excluding constraints)
341
+ driver.query<{
342
+ tablename: string;
343
+ indexname: string;
344
+ indisunique: boolean;
345
+ attname: string;
346
+ attnum: number;
347
+ amname: string | null;
348
+ reloptions: string[] | null;
349
+ }>(
350
+ `SELECT
239
351
  i.tablename,
240
352
  i.indexname,
241
353
  ix.indisunique,
242
354
  a.attname,
243
- a.attnum
355
+ a.attnum,
356
+ am.amname,
357
+ ic.reloptions
244
358
  FROM pg_indexes i
245
359
  JOIN pg_class ic ON ic.relname = i.indexname
246
360
  JOIN pg_namespace ins ON ins.oid = ic.relnamespace AND ins.nspname = $1
247
361
  JOIN pg_index ix ON ix.indexrelid = ic.oid
362
+ JOIN pg_am am ON am.oid = ic.relam
248
363
  JOIN pg_class t ON t.oid = ix.indrelid
249
364
  JOIN pg_namespace tn ON tn.oid = t.relnamespace AND tn.nspname = $1
250
365
  LEFT JOIN pg_attribute a ON a.attrelid = t.oid AND a.attnum = ANY(ix.indkey) AND a.attnum > 0
@@ -257,16 +372,9 @@ export class PostgresControlAdapter implements SqlControlAdapter<'postgres'> {
257
372
  AND tc.constraint_name = i.indexname
258
373
  )
259
374
  ORDER BY i.tablename, i.indexname, a.attnum`,
260
- [schema],
261
- ),
262
- // Query extensions
263
- driver.query<{ extname: string }>(
264
- `SELECT extname
265
- FROM pg_extension
266
- ORDER BY extname`,
267
- [],
268
- ),
269
- ]);
375
+ [schema],
376
+ ),
377
+ ]);
270
378
 
271
379
  // Group results by table name for efficient lookup
272
380
  const columnsByTable = groupBy(columnsResult.rows, 'table_name');
@@ -402,7 +510,16 @@ export class PostgresControlAdapter implements SqlControlAdapter<'postgres'> {
402
510
  }));
403
511
 
404
512
  // Process indexes
405
- const indexesMap = new Map<string, { columns: string[]; name: string; unique: boolean }>();
513
+ const indexesMap = new Map<
514
+ string,
515
+ {
516
+ columns: string[];
517
+ name: string;
518
+ unique: boolean;
519
+ type: string | undefined;
520
+ options: Record<string, string> | undefined;
521
+ }
522
+ >();
406
523
  for (const idxRow of indexesByTable.get(tableName) ?? []) {
407
524
  if (!idxRow.attname) {
408
525
  continue;
@@ -411,10 +528,17 @@ export class PostgresControlAdapter implements SqlControlAdapter<'postgres'> {
411
528
  if (existing) {
412
529
  existing.columns.push(idxRow.attname);
413
530
  } else {
531
+ // Drop btree (the Postgres default) so a contract index without an
532
+ // explicit type matches a default-method introspected index without
533
+ // forcing DROP+CREATE on every plan.
534
+ const indexType = idxRow.amname && idxRow.amname !== 'btree' ? idxRow.amname : undefined;
535
+ const indexOptions = parsePgReloptions(idxRow.reloptions, idxRow.indexname);
414
536
  indexesMap.set(idxRow.indexname, {
415
537
  columns: [idxRow.attname],
416
538
  name: idxRow.indexname,
417
539
  unique: idxRow.indisunique,
540
+ type: indexType,
541
+ options: indexOptions,
418
542
  });
419
543
  }
420
544
  }
@@ -422,6 +546,8 @@ export class PostgresControlAdapter implements SqlControlAdapter<'postgres'> {
422
546
  columns: Object.freeze([...idx.columns]) as readonly string[],
423
547
  name: idx.name,
424
548
  unique: idx.unique,
549
+ ...(idx.type !== undefined && { type: idx.type }),
550
+ ...(idx.options !== undefined && { options: idx.options }),
425
551
  }));
426
552
 
427
553
  tables[tableName] = {
@@ -434,10 +560,6 @@ export class PostgresControlAdapter implements SqlControlAdapter<'postgres'> {
434
560
  };
435
561
  }
436
562
 
437
- const dependencies: readonly DependencyIR[] = extensionsResult.rows.map((row) => ({
438
- id: `postgres.extension.${row.extname}`,
439
- }));
440
-
441
563
  const storageTypes =
442
564
  (await pgEnumControlHooks.introspectTypes?.({ driver, schemaName: schema })) ?? {};
443
565
 
@@ -454,7 +576,6 @@ export class PostgresControlAdapter implements SqlControlAdapter<'postgres'> {
454
576
 
455
577
  return {
456
578
  tables,
457
- dependencies,
458
579
  annotations,
459
580
  };
460
581
  }
@@ -555,6 +676,39 @@ function mapReferentialAction(rule: string): SqlReferentialAction | undefined {
555
676
  * Groups an array of objects by a specified key.
556
677
  * Returns a Map for O(1) lookup by group key.
557
678
  */
679
+ /**
680
+ * Parses a `pg_class.reloptions` array into a `Record<string, string>`.
681
+ *
682
+ * Postgres returns reloptions as a `text[]` whose entries are `key=value`
683
+ * strings; the value side is always a string regardless of the underlying
684
+ * scalar type. The verifier compares contract options to introspected
685
+ * options after coercing both sides to strings, so keeping the raw text
686
+ * here is correct.
687
+ *
688
+ * Returns `undefined` when the input is null/empty (no WITH clause).
689
+ */
690
+ export function parsePgReloptions(
691
+ reloptions: readonly string[] | null,
692
+ indexName: string,
693
+ ): Record<string, string> | undefined {
694
+ if (!reloptions || reloptions.length === 0) {
695
+ return undefined;
696
+ }
697
+ const result: Record<string, string> = {};
698
+ for (const entry of reloptions) {
699
+ const eq = entry.indexOf('=');
700
+ if (eq === -1) {
701
+ throw new Error(
702
+ `Postgres introspection: malformed reloption entry "${entry}" on index "${indexName}" (expected "key=value")`,
703
+ );
704
+ }
705
+ const key = entry.slice(0, eq);
706
+ const value = entry.slice(eq + 1);
707
+ result[key] = value;
708
+ }
709
+ return Object.keys(result).length > 0 ? result : undefined;
710
+ }
711
+
558
712
  function groupBy<T, K extends keyof T>(items: readonly T[], key: K): Map<T[K], T[]> {
559
713
  const map = new Map<T[K], T[]>();
560
714
  for (const item of items) {
@@ -1,4 +1,5 @@
1
1
  import type { ExecutionMutationDefaultValue } from '@prisma-next/contract/types';
2
+ import { timestampNowControlDescriptor } from '@prisma-next/family-sql/control';
2
3
  import type {
3
4
  ControlMutationDefaultEntry,
4
5
  DefaultFunctionLoweringContext,
@@ -301,25 +302,30 @@ export function createPostgresDefaultFunctionRegistry(): ReadonlyMap<
301
302
  }
302
303
 
303
304
  export function createPostgresMutationDefaultGeneratorDescriptors(): readonly MutationDefaultGeneratorDescriptor[] {
304
- return builtinGeneratorRegistryMetadata.map(({ id, applicableCodecIds }) => ({
305
- id,
306
- applicableCodecIds,
307
- resolveGeneratedColumnDescriptor: ({ generated }) => {
308
- if (generated.kind !== 'generator' || generated.id !== id) {
309
- return undefined;
310
- }
311
- const descriptor = resolveBuiltinGeneratedColumnDescriptor({
305
+ return [
306
+ ...builtinGeneratorRegistryMetadata.map(
307
+ ({ id, applicableCodecIds }): MutationDefaultGeneratorDescriptor => ({
312
308
  id,
313
- ...(generated.params ? { params: generated.params } : {}),
314
- });
315
- return {
316
- codecId: descriptor.type.codecId,
317
- nativeType: descriptor.type.nativeType,
318
- ...(descriptor.type.typeRef ? { typeRef: descriptor.type.typeRef } : {}),
319
- ...(descriptor.typeParams ? { typeParams: descriptor.typeParams } : {}),
320
- };
321
- },
322
- }));
309
+ applicableCodecIds,
310
+ resolveGeneratedColumnDescriptor: ({ generated }) => {
311
+ if (generated.kind !== 'generator' || generated.id !== id) {
312
+ return undefined;
313
+ }
314
+ const descriptor = resolveBuiltinGeneratedColumnDescriptor({
315
+ id,
316
+ ...(generated.params ? { params: generated.params } : {}),
317
+ });
318
+ return {
319
+ codecId: descriptor.type.codecId,
320
+ nativeType: descriptor.type.nativeType,
321
+ ...(descriptor.type.typeRef ? { typeRef: descriptor.type.typeRef } : {}),
322
+ ...(descriptor.typeParams ? { typeParams: descriptor.typeParams } : {}),
323
+ };
324
+ },
325
+ }),
326
+ ),
327
+ timestampNowControlDescriptor(),
328
+ ];
323
329
  }
324
330
 
325
331
  export function createPostgresScalarTypeDescriptors(): ReadonlyMap<string, string> {
@@ -1,8 +1,9 @@
1
1
  import type { CodecControlHooks, ExpandNativeTypeInput } from '@prisma-next/family-sql/control';
2
- import type { SqlOperationDescriptor } from '@prisma-next/sql-operations';
2
+ import { buildOperation, refsOf, toExpr } from '@prisma-next/sql-relational-core/expression';
3
3
  import {
4
4
  PG_BIT_CODEC_ID,
5
5
  PG_BOOL_CODEC_ID,
6
+ PG_BYTEA_CODEC_ID,
6
7
  PG_CHAR_CODEC_ID,
7
8
  PG_ENUM_CODEC_ID,
8
9
  PG_FLOAT_CODEC_ID,
@@ -30,12 +31,11 @@ import {
30
31
  SQL_TIMESTAMP_CODEC_ID,
31
32
  SQL_VARCHAR_CODEC_ID,
32
33
  } from '@prisma-next/target-postgres/codec-ids';
33
- import { codecDefinitions } from '@prisma-next/target-postgres/codecs';
34
+ import { postgresCodecRegistry } from '@prisma-next/target-postgres/codecs';
35
+ import type { QueryOperationTypes } from '../types/operation-types';
34
36
  import { pgEnumControlHooks } from './enum-control-hooks';
35
37
 
36
- // ============================================================================
37
- // Helper functions for reducing boilerplate
38
- // ============================================================================
38
+ // ============================================================================ Helper functions for reducing boilerplate ============================================================================
39
39
 
40
40
  /** Creates a type import spec for codec types */
41
41
  const codecTypeImport = (named: string) =>
@@ -124,21 +124,26 @@ const precisionHooks: CodecControlHooks = { expandNativeType: expandPrecision };
124
124
  const numericHooks: CodecControlHooks = { expandNativeType: expandNumeric };
125
125
  const identityHooks: CodecControlHooks = { expandNativeType: ({ nativeType }) => nativeType };
126
126
 
127
- // ============================================================================
128
- // Descriptor metadata
129
- // ============================================================================
127
+ // ============================================================================ Descriptor metadata ============================================================================
130
128
 
131
- export const postgresQueryOperations: readonly SqlOperationDescriptor[] = [
132
- {
133
- method: 'ilike',
134
- args: [
135
- { traits: ['textual'], nullable: false },
136
- { codecId: PG_TEXT_CODEC_ID, nullable: false },
137
- ],
138
- returns: { codecId: PG_BOOL_CODEC_ID, nullable: false },
139
- lowering: { targetFamily: 'sql', strategy: 'infix', template: '{{self}} ILIKE {{arg0}}' },
140
- },
141
- ];
129
+ type CodecTypesBase = Record<string, { readonly input: unknown; readonly output: unknown }>;
130
+
131
+ export function postgresQueryOperations<CT extends CodecTypesBase>(): QueryOperationTypes<CT> {
132
+ return {
133
+ ilike: {
134
+ self: { traits: ['textual'] },
135
+ impl: (self, pattern) => {
136
+ const selfRefs = refsOf(self);
137
+ return buildOperation({
138
+ method: 'ilike',
139
+ args: [toExpr(self), toExpr(pattern, PG_TEXT_CODEC_ID, selfRefs)],
140
+ returns: { codecId: PG_BOOL_CODEC_ID, nullable: false },
141
+ lowering: { targetFamily: 'sql', strategy: 'infix', template: '{{self}} ILIKE {{arg0}}' },
142
+ });
143
+ },
144
+ },
145
+ };
146
+ }
142
147
 
143
148
  export const postgresAdapterDescriptorMeta = {
144
149
  kind: 'adapter',
@@ -162,7 +167,7 @@ export const postgresAdapterDescriptorMeta = {
162
167
  },
163
168
  types: {
164
169
  codecTypes: {
165
- codecInstances: Object.values(codecDefinitions).map((def) => def.codec),
170
+ codecDescriptors: Array.from(postgresCodecRegistry.values()),
166
171
  import: {
167
172
  package: '@prisma-next/target-postgres/codec-types',
168
173
  named: 'CodecTypes',
@@ -202,6 +207,7 @@ export const postgresAdapterDescriptorMeta = {
202
207
  [PG_ENUM_CODEC_ID]: pgEnumControlHooks,
203
208
  [PG_JSON_CODEC_ID]: identityHooks,
204
209
  [PG_JSONB_CODEC_ID]: identityHooks,
210
+ [PG_BYTEA_CODEC_ID]: identityHooks,
205
211
  },
206
212
  },
207
213
  storage: [
@@ -267,6 +273,7 @@ export const postgresAdapterDescriptorMeta = {
267
273
  },
268
274
  { typeId: PG_JSON_CODEC_ID, familyId: 'sql', targetId: 'postgres', nativeType: 'json' },
269
275
  { typeId: PG_JSONB_CODEC_ID, familyId: 'sql', targetId: 'postgres', nativeType: 'jsonb' },
276
+ { typeId: PG_BYTEA_CODEC_ID, familyId: 'sql', targetId: 'postgres', nativeType: 'bytea' },
270
277
  ],
271
278
  queryOperationTypes: {
272
279
  import: {