@prisma-next/adapter-postgres 0.16.0-dev.32 → 0.16.0-dev.34

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.
@@ -1,5 +1,5 @@
1
1
  import type { JsonValue } from '@prisma-next/contract/types';
2
- import type { CodecLookup } from '@prisma-next/framework-components/codec';
2
+ import type { CodecRef } from '@prisma-next/framework-components/codec';
3
3
  import { runtimeError } from '@prisma-next/framework-components/runtime';
4
4
  import {
5
5
  type AggregateExpr,
@@ -37,6 +37,7 @@ import {
37
37
  type UpdateAst,
38
38
  type WindowFuncExpr,
39
39
  } from '@prisma-next/sql-relational-core/ast';
40
+ import type { PostgresCodecDescriptorRegistry } from '@prisma-next/target-postgres/codec-descriptor';
40
41
  import { isPgEnumParams } from '@prisma-next/target-postgres/codecs';
41
42
  import {
42
43
  escapeLiteral,
@@ -49,11 +50,11 @@ import { adapterError } from './adapter-errors';
49
50
  import type { PostgresContract } from './types';
50
51
 
51
52
  /**
52
- * Postgres native types whose unknown-OID parameter inference is reliable in arbitrary expression positions. Parameters bound to a codec whose `meta.db.sql.postgres.nativeType` falls in this set are emitted as plain `$N`; everything else (including `json`, `jsonb`, extension types like `vector`, and unknown user types) is emitted as `$N::<nativeType>` so the planner picks an unambiguous overload.
53
+ * Postgres native types whose unknown-OID parameter inference is reliable in arbitrary expression positions. Parameters bound to a descriptor whose `nativeTypeFor` result falls in this set are emitted as plain `$N`; everything else (including `json`, `jsonb`, extension types like `vector`, and unknown user types) is emitted as `$N::<nativeType>` so the planner picks an unambiguous overload.
53
54
  *
54
55
  * `json` / `jsonb` are intentionally excluded despite being Postgres builtins: their operator overloads make context inference unreliable in expression positions (e.g. `$1 -> 'key'` is ambiguous between the two).
55
56
  *
56
- * Spellings match the on-disk `meta.db.sql.postgres.nativeType` values in `@prisma-next/target-postgres`'s codec definitions, not the `udt_name` abbreviations that ADR 205 used as illustrative shorthand. The lookup-based cast policy compares against these strings directly.
57
+ * Spellings match the target descriptors' `nativeTypeFor` results, not the `udt_name` abbreviations that ADR 205 used as illustrative shorthand. The registry-based cast policy compares against these strings directly.
57
58
  */
58
59
  const POSTGRES_INFERRABLE_NATIVE_TYPES: ReadonlySet<string> = new Set([
59
60
  // Numeric
@@ -84,23 +85,19 @@ const POSTGRES_INFERRABLE_NATIVE_TYPES: ReadonlySet<string> = new Set([
84
85
  function renderTypedParam(
85
86
  index: number,
86
87
  codecId: string | undefined,
87
- codecLookup: CodecLookup,
88
+ codecDescriptorRegistry: PostgresCodecDescriptorRegistry,
88
89
  many?: boolean,
89
90
  typeParams?: JsonValue,
90
91
  ): string {
91
92
  if (codecId === undefined) {
92
93
  return `$${index}`;
93
94
  }
94
- const meta = codecLookup.metaFor(codecId, typeParams);
95
- const isRegistered =
96
- codecLookup.get(codecId) !== undefined ||
97
- meta !== undefined ||
98
- codecLookup.targetTypesFor(codecId) !== undefined;
99
- if (!isRegistered) {
95
+ const descriptor = codecDescriptorRegistry.descriptorFor(codecId);
96
+ if (descriptor === undefined) {
100
97
  throw adapterError(
101
98
  'RUNTIME.PARAM_REF_MISSING_CODEC',
102
99
  `Postgres lowering: ParamRef carries codecId "${codecId}" but the ` +
103
- 'assembled codec lookup has no entry for it. This usually indicates ' +
100
+ 'validated PostgreSQL codec registry has no entry for it. This usually indicates ' +
104
101
  'a missing extension pack in the runtime stack — register the pack ' +
105
102
  'that contributes this codec (e.g. `extensions: [pgvectorRuntime]`), ' +
106
103
  'or use the codec directly from `@prisma-next/target-postgres/codecs` ' +
@@ -108,42 +105,22 @@ function renderTypedParam(
108
105
  { meta: { codecId } },
109
106
  );
110
107
  }
111
- // `typeParams` above already resolved a parameterized codec's per-instance
112
- // meta (e.g. a native enum's type name) ahead of its static fallback.
113
- //
114
- // The framework `CodecLookup.metaFor` returns the family-agnostic
115
- // `CodecMeta`, whose `db` is `Record<string, unknown>`. The SQL family
116
- // populates a narrower shape with `db.sql.<dialect>.nativeType: string`, so
117
- // navigate that path defensively and string-check the leaf.
118
- const dbRecord = meta?.db;
119
- const sqlBlock = isRecord(dbRecord) ? dbRecord['sql'] : undefined;
120
- const dialectBlock = isRecord(sqlBlock) ? sqlBlock['postgres'] : undefined;
121
- const nativeType = isRecord(dialectBlock) ? dialectBlock['nativeType'] : undefined;
122
- if (typeof nativeType === 'string') {
123
- const arraySuffix = many ? '[]' : '';
124
- // A `typeParams.typeName` marks a named database type (e.g. a native
125
- // enum): cast to its quoted, schema-qualified identifier so Postgres does
126
- // not case-fold it (`$1::HoldType` would resolve as `holdtype`). Mirrors
127
- // the DDL-side policy in `buildColumnTypeSql`. Builtin spellings
128
- // (`double precision`, `jsonb`, …) stay verbatim — quoting them would
129
- // turn them into (nonexistent) user-type lookups.
130
- if (isPgEnumParams(typeParams)) {
131
- return `$${index}::${quoteQualifiedName(nativeType)}${arraySuffix}`;
132
- }
133
- if (!POSTGRES_INFERRABLE_NATIVE_TYPES.has(nativeType)) {
134
- return `$${index}::${nativeType}${arraySuffix}`;
135
- }
136
- if (many) {
137
- return `$${index}::${nativeType}${arraySuffix}`;
138
- }
108
+ const ref: CodecRef = {
109
+ codecId,
110
+ ...ifDefined('many', many),
111
+ ...ifDefined('typeParams', typeParams),
112
+ };
113
+ const nativeType = descriptor.nativeTypeFor(ref);
114
+ const arraySuffix = many ? '[]' : '';
115
+ if (isPgEnumParams(typeParams)) {
116
+ return `$${index}::${quoteQualifiedName(nativeType)}${arraySuffix}`;
117
+ }
118
+ if (!POSTGRES_INFERRABLE_NATIVE_TYPES.has(nativeType) || many) {
119
+ return `$${index}::${nativeType}${arraySuffix}`;
139
120
  }
140
121
  return `$${index}`;
141
122
  }
142
123
 
143
- function isRecord(value: unknown): value is Record<string, unknown> {
144
- return typeof value === 'object' && value !== null;
145
- }
146
-
147
124
  function unreachableKind(value: never): string {
148
125
  const candidate: unknown = value;
149
126
  if (
@@ -158,11 +135,11 @@ function unreachableKind(value: never): string {
158
135
  }
159
136
 
160
137
  /**
161
- * Per-render carrier threaded through every helper. Bundles the param-index map (for `$N` numbering) and the assembled-stack `codecLookup` (for cast policy at the `renderTypedParam` chokepoint). Carrying both on a single value keeps helper signatures stable.
138
+ * Per-render carrier threaded through every helper. Bundles the param-index map (for `$N` numbering) and the assembled-stack target descriptor registry (for cast policy at the `renderTypedParam` chokepoint). Carrying both on a single value keeps helper signatures stable.
162
139
  */
163
140
  interface ParamIndexMap {
164
141
  readonly indexMap: Map<AnyParamRef, number>;
165
- readonly codecLookup: CodecLookup;
142
+ readonly codecDescriptorRegistry: PostgresCodecDescriptorRegistry;
166
143
  }
167
144
 
168
145
  /**
@@ -173,7 +150,7 @@ interface ParamIndexMap {
173
150
  export function renderLoweredSql(
174
151
  ast: AnyQueryAst,
175
152
  contract: PostgresContract,
176
- codecLookup: CodecLookup,
153
+ codecDescriptorRegistry: PostgresCodecDescriptorRegistry,
177
154
  ): { readonly sql: string; readonly params: readonly LoweredParam[] } {
178
155
  const orderedRefs = collectOrderedParamRefs(ast);
179
156
  const indexMap = new Map<AnyParamRef, number>();
@@ -183,7 +160,7 @@ export function renderLoweredSql(
183
160
  ? { kind: 'bind', name: ref.name }
184
161
  : { kind: 'literal', value: ref.value };
185
162
  });
186
- const pim: ParamIndexMap = { indexMap, codecLookup };
163
+ const pim: ParamIndexMap = { indexMap, codecDescriptorRegistry };
187
164
 
188
165
  const node = ast;
189
166
  let sql: string;
@@ -841,7 +818,7 @@ function renderParamRef(ref: AnyParamRef, pim: ParamIndexMap): string {
841
818
  return renderTypedParam(
842
819
  index,
843
820
  ref.codec.codecId,
844
- pim.codecLookup,
821
+ pim.codecDescriptorRegistry,
845
822
  ref.codec.many,
846
823
  ref.codec.typeParams,
847
824
  );
@@ -858,7 +835,7 @@ function renderParamRef(ref: AnyParamRef, pim: ParamIndexMap): string {
858
835
  return renderTypedParam(
859
836
  index,
860
837
  ref.codec.codecId,
861
- pim.codecLookup,
838
+ pim.codecDescriptorRegistry,
862
839
  ref.codec.many,
863
840
  ref.codec.typeParams,
864
841
  );
package/src/core/types.ts CHANGED
@@ -16,19 +16,21 @@ import type {
16
16
  SelectAst,
17
17
  UpdateAst,
18
18
  } from '@prisma-next/sql-relational-core/ast';
19
+ import type {
20
+ AnyPostgresCodecDescriptor,
21
+ PostgresCodecDescriptorRegistry,
22
+ } from '@prisma-next/target-postgres/codec-descriptor';
23
+
24
+ export type PostgresCodecRegistry = CodecRegistry & PostgresCodecDescriptorRegistry;
19
25
 
20
26
  export interface PostgresAdapterOptions {
21
27
  readonly profileId?: string;
22
28
  /**
23
- * Codec lookup used by the SQL renderer to resolve per-codec metadata at
24
- * lower-time. Defaults to a Postgres-builtins-only lookup when omitted
25
- * see {@link createPostgresBuiltinCodecLookup} in `./codec-lookup`.
26
- *
27
- * Stack-aware callers (`SqlRuntimeAdapterDescriptor.create(stack)` /
28
- * `SqlControlAdapterDescriptor.create(stack)`) supply the assembled stack
29
- * lookup so extension codecs are visible to the renderer.
29
+ * Custom PostgreSQL codec descriptors contributed alongside the built-ins.
30
+ * The complete descriptor set is validated at construction and becomes the
31
+ * single source for both codec materialization and target-specific lowering.
30
32
  */
31
- readonly codecLookup?: CodecRegistry;
33
+ readonly codecDescriptors?: readonly AnyPostgresCodecDescriptor[];
32
34
  }
33
35
 
34
36
  export type { PostgresContract } from '@prisma-next/target-postgres/types';
@@ -5,6 +5,7 @@ import {
5
5
  qualifyName,
6
6
  quoteIdentifier,
7
7
  } from '@prisma-next/target-postgres/sql-utils';
8
+ import { assemblePostgresCodecRegistry } from '../core/codec-lookup';
8
9
  import { PostgresControlAdapter } from '../core/control-adapter';
9
10
  import {
10
11
  createPostgresDefaultFunctionRegistry,
@@ -21,7 +22,13 @@ const postgresAdapterDescriptor: SqlControlAdapterDescriptor<'postgres'> = {
21
22
  generatorDescriptors: createPostgresMutationDefaultGeneratorDescriptors(),
22
23
  },
23
24
  create(stack): SqlControlAdapter<'postgres'> {
24
- return new PostgresControlAdapter(stack.codecLookup);
25
+ const components = [
26
+ stack.target,
27
+ ...(stack.adapter === undefined ? [] : [stack.adapter]),
28
+ ...stack.extensions,
29
+ ];
30
+ const codecRegistry = assemblePostgresCodecRegistry(components);
31
+ return new PostgresControlAdapter(codecRegistry);
25
32
  },
26
33
  };
27
34
 
@@ -29,6 +36,9 @@ export default postgresAdapterDescriptor;
29
36
 
30
37
  export { parsePostgresDefault } from '@prisma-next/target-postgres/default-normalizer';
31
38
  export { normalizeSchemaNativeType } from '@prisma-next/target-postgres/native-type-normalizer';
32
- export { createPostgresBuiltinCodecLookup } from '../core/codec-lookup';
39
+ export {
40
+ createPostgresBuiltinCodecLookup,
41
+ createPostgresCodecRegistryWithBuiltins,
42
+ } from '../core/codec-lookup';
33
43
  export { PostgresControlAdapter } from '../core/control-adapter';
34
44
  export { escapeLiteral, qualifyName, quoteIdentifier };
@@ -1,13 +1,13 @@
1
1
  import type { GeneratedValueSpec } from '@prisma-next/contract/types';
2
2
  import { timestampNowRuntimeGenerator } from '@prisma-next/family-sql/runtime';
3
- import { extractCodecLookup } from '@prisma-next/framework-components/control';
4
3
  import type { RuntimeAdapterInstance } from '@prisma-next/framework-components/execution';
5
4
  import { builtinGeneratorIds } from '@prisma-next/ids';
6
5
  import { generateId } from '@prisma-next/ids/runtime';
7
6
  import type { Adapter, AnyQueryAst } from '@prisma-next/sql-relational-core/ast';
8
7
  import type { SqlRuntimeAdapterDescriptor } from '@prisma-next/sql-runtime';
9
8
  import { postgresCodecRegistry } from '@prisma-next/target-postgres/codecs';
10
- import { createPostgresAdapter, postgresRawCodecInferer } from '../core/adapter';
9
+ import { createPostgresAdapterWithCodecRegistry, postgresRawCodecInferer } from '../core/adapter';
10
+ import { assemblePostgresCodecRegistry } from '../core/codec-lookup';
11
11
  import { postgresAdapterDescriptorMeta, postgresQueryOperations } from '../core/descriptor-meta';
12
12
  import type { PostgresContract, PostgresLoweredStatement } from '../core/types';
13
13
 
@@ -37,11 +37,9 @@ const postgresRuntimeAdapterDescriptor: SqlRuntimeAdapterDescriptor<'postgres',
37
37
  mutationDefaultGenerators: createPostgresMutationDefaultGenerators,
38
38
  rawCodecInferer: postgresRawCodecInferer,
39
39
  create(stack): SqlRuntimeAdapter {
40
- // The runtime `ExecutionStack` does not (yet) carry a pre-assembled `codecLookup` field the way the control `ControlStack` does, so we derive an equivalent lookup here from the stack's component metadata (target + adapter + extension packs) using the same assembly helper that `createControlStack` uses. This keeps the renderer fed with the same codec set on both planes — including extension-contributed codecs like
41
- // `pg/vector@1` from `@prisma-next/extension-pgvector`.
42
40
  const components = [stack.target, stack.adapter, ...stack.extensions];
43
- const codecLookup = extractCodecLookup(components);
44
- return createPostgresAdapter({ codecLookup });
41
+ const codecRegistry = assemblePostgresCodecRegistry(components);
42
+ return createPostgresAdapterWithCodecRegistry(codecRegistry);
45
43
  },
46
44
  };
47
45
 
@@ -6,6 +6,7 @@ export type {
6
6
  OrderClause,
7
7
  ParamRef,
8
8
  PostgresAdapterOptions,
9
+ PostgresCodecRegistry,
9
10
  PostgresContract,
10
11
  PostgresLoweredStatement,
11
12
  SelectAst,
@@ -1 +0,0 @@
1
- {"version":3,"file":"adapter-CZfCp5TH.mjs","names":[],"sources":["../src/core/adapter.ts"],"sourcesContent":["import type { CodecRegistry } from '@prisma-next/framework-components/codec';\nimport { APP_SPACE_ID } from '@prisma-next/framework-components/control';\nimport type {\n Adapter,\n AdapterProfile,\n AnyQueryAst,\n LowererContext,\n RawSqlLiteral,\n SqlQueryable,\n} from '@prisma-next/sql-relational-core/ast';\nimport { isDdlNode } from '@prisma-next/sql-relational-core/ast';\nimport type { RawCodecInferer } from '@prisma-next/sql-relational-core/expression';\nimport type { PostgresDdlNode } from '@prisma-next/target-postgres/ddl';\nimport { adapterError } from './adapter-errors';\nimport { createPostgresBuiltinCodecLookup } from './codec-lookup';\nimport { PostgresControlAdapter } from './control-adapter';\nimport { renderLoweredSql } from './sql-renderer';\nimport type { PostgresAdapterOptions, PostgresContract, PostgresLoweredStatement } from './types';\n\nconst defaultCapabilities = Object.freeze({\n postgres: {\n orderBy: true,\n limit: true,\n lateral: true,\n jsonAgg: true,\n returning: true,\n distinctOn: true,\n },\n sql: {\n enums: true,\n returning: true,\n defaultInInsert: true,\n lateral: true,\n scalarList: true,\n },\n});\n\nclass PostgresAdapterImpl\n implements Adapter<AnyQueryAst, PostgresContract, PostgresLoweredStatement>\n{\n // These fields make the adapter instance structurally compatible with RuntimeAdapterInstance<'sql', 'postgres'> without introducing a runtime-plane dependency.\n readonly familyId = 'sql' as const;\n readonly targetId = 'postgres' as const;\n\n readonly profile: AdapterProfile<'postgres'>;\n private readonly codecLookup: CodecRegistry;\n\n constructor(options?: PostgresAdapterOptions) {\n this.codecLookup = options?.codecLookup ?? createPostgresBuiltinCodecLookup();\n const controlAdapter = new PostgresControlAdapter(this.codecLookup);\n this.profile = Object.freeze({\n id: options?.profileId ?? 'postgres/default@1',\n target: 'postgres',\n capabilities: defaultCapabilities,\n readMarker: (queryable: SqlQueryable) =>\n controlAdapter.readMarkerDiscriminated(\n {\n familyId: 'sql',\n targetId: 'postgres',\n query: async <Row = Record<string, unknown>>(\n sql: string,\n params?: readonly unknown[],\n ) => {\n const result = await queryable.query<Row>(sql, params);\n return { rows: [...result.rows] };\n },\n close: async () => {},\n },\n APP_SPACE_ID,\n ),\n });\n }\n\n lower(\n ast: AnyQueryAst | PostgresDdlNode,\n context: LowererContext<PostgresContract>,\n ): PostgresLoweredStatement {\n if (isDdlNode(ast)) {\n throw adapterError(\n 'RUNTIME.DDL_UNSUPPORTED',\n 'lower() does not lower DDL on the runtime adapter — DDL lowering is a control-plane concern handled by the control adapter.',\n { meta: { surface: 'runtime-adapter' } },\n );\n }\n return renderLoweredSql(ast, context.contract, this.codecLookup);\n }\n}\n\n/** Codec-id lookup for bare-literal interpolations used by `fns.raw` on a postgres client. Contributed as the descriptor's static `rawCodecInferer` slot. */\nexport const postgresRawCodecInferer: RawCodecInferer = {\n inferCodec(value: RawSqlLiteral): string {\n switch (typeof value) {\n case 'number':\n return Number.isSafeInteger(value) && value % 1 === 0 ? 'pg/int4' : 'pg/float8';\n case 'bigint':\n return 'pg/int8';\n case 'string':\n return 'pg/text';\n case 'boolean':\n return 'pg/bool';\n case 'object':\n if (value instanceof Uint8Array) return 'pg/bytea';\n }\n throw adapterError(\n 'RUNTIME.RAW_SQL_UNSUPPORTED_INTERPOLATION',\n 'unsupported JS value type for raw-SQL interpolation: wrap this value in `param(...)` with an explicit codec',\n { meta: { valueType: typeof value } },\n );\n },\n};\n\nexport function createPostgresAdapter(options?: PostgresAdapterOptions) {\n return Object.freeze(new PostgresAdapterImpl(options));\n}\n"],"mappings":";;;;AAmBA,MAAM,sBAAsB,OAAO,OAAO;CACxC,UAAU;EACR,SAAS;EACT,OAAO;EACP,SAAS;EACT,SAAS;EACT,WAAW;EACX,YAAY;CACd;CACA,KAAK;EACH,OAAO;EACP,WAAW;EACX,iBAAiB;EACjB,SAAS;EACT,YAAY;CACd;AACF,CAAC;AAED,IAAM,sBAAN,MAEA;CAEE,WAAoB;CACpB,WAAoB;CAEpB;CACA;CAEA,YAAY,SAAkC;EAC5C,KAAK,cAAc,SAAS,eAAe,iCAAiC;EAC5E,MAAM,iBAAiB,IAAI,uBAAuB,KAAK,WAAW;EAClE,KAAK,UAAU,OAAO,OAAO;GAC3B,IAAI,SAAS,aAAa;GAC1B,QAAQ;GACR,cAAc;GACd,aAAa,cACX,eAAe,wBACb;IACE,UAAU;IACV,UAAU;IACV,OAAO,OACL,KACA,WACG;KAEH,OAAO,EAAE,MAAM,CAAC,IAAG,MADE,UAAU,MAAW,KAAK,MAAM,EAAA,CAC3B,IAAI,EAAE;IAClC;IACA,OAAO,YAAY,CAAC;GACtB,GACA,YACF;EACJ,CAAC;CACH;CAEA,MACE,KACA,SAC0B;EAC1B,IAAI,UAAU,GAAG,GACf,MAAM,aACJ,2BACA,+HACA,EAAE,MAAM,EAAE,SAAS,kBAAkB,EAAE,CACzC;EAEF,OAAO,iBAAiB,KAAK,QAAQ,UAAU,KAAK,WAAW;CACjE;AACF;;AAGA,MAAa,0BAA2C,EACtD,WAAW,OAA8B;CACvC,QAAQ,OAAO,OAAf;EACE,KAAK,UACH,OAAO,OAAO,cAAc,KAAK,KAAK,QAAQ,MAAM,IAAI,YAAY;EACtE,KAAK,UACH,OAAO;EACT,KAAK,UACH,OAAO;EACT,KAAK,WACH,OAAO;EACT,KAAK,UACH,IAAI,iBAAiB,YAAY,OAAO;CAC5C;CACA,MAAM,aACJ,6CACA,+GACA,EAAE,MAAM,EAAE,WAAW,OAAO,MAAM,EAAE,CACtC;AACF,EACF;AAEA,SAAgB,sBAAsB,SAAkC;CACtE,OAAO,OAAO,OAAO,IAAI,oBAAoB,OAAO,CAAC;AACvD"}