@prisma-next/adapter-postgres 0.16.0-dev.3 → 0.16.0-dev.31

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,4 +1,4 @@
1
- import { n as renderLoweredSql, r as createPostgresBuiltinCodecLookup, t as PostgresControlAdapter } from "./control-adapter-B6IM_oTR.mjs";
1
+ import { i as adapterError, n as renderLoweredSql, r as createPostgresBuiltinCodecLookup, t as PostgresControlAdapter } from "./control-adapter-DKbwTazN.mjs";
2
2
  import { APP_SPACE_ID } from "@prisma-next/framework-components/control";
3
3
  import { isDdlNode } from "@prisma-next/sql-relational-core/ast";
4
4
  //#region src/core/adapter.ts
@@ -42,7 +42,7 @@ var PostgresAdapterImpl = class {
42
42
  });
43
43
  }
44
44
  lower(ast, context) {
45
- if (isDdlNode(ast)) throw new Error("lower() does not lower DDL on the runtime adapter — DDL lowering is a control-plane concern handled by the control adapter.");
45
+ if (isDdlNode(ast)) throw adapterError("RUNTIME.DDL_UNSUPPORTED", "lower() does not lower DDL on the runtime adapter — DDL lowering is a control-plane concern handled by the control adapter.", { meta: { surface: "runtime-adapter" } });
46
46
  return renderLoweredSql(ast, context.contract, this.codecLookup);
47
47
  }
48
48
  };
@@ -55,7 +55,7 @@ const postgresRawCodecInferer = { inferCodec(value) {
55
55
  case "boolean": return "pg/bool";
56
56
  case "object": if (value instanceof Uint8Array) return "pg/bytea";
57
57
  }
58
- throw new Error("unsupported JS value type for raw-SQL interpolation: wrap this value in `param(...)` with an explicit codec");
58
+ throw adapterError("RUNTIME.RAW_SQL_UNSUPPORTED_INTERPOLATION", "unsupported JS value type for raw-SQL interpolation: wrap this value in `param(...)` with an explicit codec", { meta: { valueType: typeof value } });
59
59
  } };
60
60
  function createPostgresAdapter(options) {
61
61
  return Object.freeze(new PostgresAdapterImpl(options));
@@ -63,4 +63,4 @@ function createPostgresAdapter(options) {
63
63
  //#endregion
64
64
  export { postgresRawCodecInferer as n, createPostgresAdapter as t };
65
65
 
66
- //# sourceMappingURL=adapter-BaZsfXGA.mjs.map
66
+ //# sourceMappingURL=adapter-DlDJIHgB.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"adapter-DlDJIHgB.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"}
@@ -1 +1 @@
1
- {"version":3,"file":"adapter.d.mts","names":[],"sources":["../src/core/adapter.ts"],"mappings":";;;;;cAoCM,+BACO,QAAQ,aAAa,kBAAkB;WAGzC;WACA;WAEA,SAAS;mBACD;cAEL,UAAU;EA0BtB,MACE,KAAK,cAAc,iBACnB,SAAS,eAAe,oBACvB;;;cAWQ,yBAAyB;iBAoBtB,sBAAsB,UAAU,yBAAsB,SAAA"}
1
+ {"version":3,"file":"adapter.d.mts","names":[],"sources":["../src/core/adapter.ts"],"mappings":";;;;;cAqCM,+BACO,QAAQ,aAAa,kBAAkB;WAGzC;WACA;WAEA,SAAS;mBACD;cAEL,UAAU;EA0BtB,MACE,KAAK,cAAc,iBACnB,SAAS,eAAe,oBACvB;;;cAaQ,yBAAyB;iBAsBtB,sBAAsB,UAAU,yBAAsB,SAAA"}
package/dist/adapter.mjs CHANGED
@@ -1,2 +1,2 @@
1
- import { n as postgresRawCodecInferer, t as createPostgresAdapter } from "./adapter-BaZsfXGA.mjs";
1
+ import { n as postgresRawCodecInferer, t as createPostgresAdapter } from "./adapter-DlDJIHgB.mjs";
2
2
  export { createPostgresAdapter, postgresRawCodecInferer };
@@ -1,22 +1,30 @@
1
1
  import { APP_SPACE_ID, extractCodecLookup } from "@prisma-next/framework-components/control";
2
2
  import { LiteralExpr, RawExpr, collectOrderedParamRefs, isDdlNode } from "@prisma-next/sql-relational-core/ast";
3
- import { postgresCodecRegistry } from "@prisma-next/target-postgres/codecs";
3
+ import { structuredError } from "@prisma-next/utils/structured-error";
4
+ import { isPgEnumParams, postgresCodecRegistry } from "@prisma-next/target-postgres/codecs";
4
5
  import { parseMarkerRowSafely, rethrowMarkerReadError, withMarkerReadErrorHandling } from "@prisma-next/errors/execution";
5
6
  import { parseContractMarkerRow } from "@prisma-next/family-sql/verify";
6
7
  import { UNBOUND_NAMESPACE_ID } from "@prisma-next/framework-components/ir";
7
8
  import { REFERENTIAL_ACTION_SQL } from "@prisma-next/sql-contract/referential-action-sql";
9
+ import { parseWireName } from "@prisma-next/sql-schema-ir/naming";
8
10
  import { RelationalSchemaNodeKind } from "@prisma-next/sql-schema-ir/types";
9
11
  import { buildControlTableBootstrapQueries, buildSignMarkerBootstrapQueries, int4, int8, jsonb, pgTable, text, textArray, timestamptz } from "@prisma-next/target-postgres/contract-free";
10
12
  import { parsePostgresDefault } from "@prisma-next/target-postgres/default-normalizer";
13
+ import { postgresError } from "@prisma-next/target-postgres/errors";
11
14
  import { normalizeSchemaNativeType } from "@prisma-next/target-postgres/native-type-normalizer";
12
- import { parseRlsPolicyWireName } from "@prisma-next/target-postgres/rls-canonicalize";
13
- import { escapeLiteral, quoteIdentifier } from "@prisma-next/target-postgres/sql-utils";
15
+ import { escapeLiteral, quoteIdentifier, quoteQualifiedName } from "@prisma-next/target-postgres/sql-utils";
14
16
  import { PostgresDatabaseSchemaNode, PostgresNamespaceSchemaNode, PostgresNativeEnumSchemaNode, PostgresPolicySchemaNode, PostgresRoleSchemaNode, PostgresSchemaNodeKind, PostgresTableSchemaNode } from "@prisma-next/target-postgres/types";
15
17
  import { blindCast } from "@prisma-next/utils/casts";
16
18
  import { ifDefined } from "@prisma-next/utils/defined";
17
19
  import { createAstCodecRegistry, deriveParamMetadata, encodeParamsWithMetadata } from "@prisma-next/sql-runtime";
20
+ import { InternalError, assertNever } from "@prisma-next/utils/internal-error";
18
21
  import { PG_TIMESTAMPTZ_CODEC_ID } from "@prisma-next/target-postgres/codec-ids";
19
22
  import { runtimeError } from "@prisma-next/framework-components/runtime";
23
+ //#region src/core/adapter-errors.ts
24
+ function adapterError(code, message, options) {
25
+ return structuredError(code, message, options);
26
+ }
27
+ //#endregion
20
28
  //#region src/core/codec-lookup.ts
21
29
  /**
22
30
  * Build a {@link CodecRegistry} populated with the Postgres-builtin codec definitions only.
@@ -34,7 +42,7 @@ function createPostgresBuiltinCodecLookup() {
34
42
  //#endregion
35
43
  //#region ../../../1-framework/3-tooling/migration/dist/exports/ledger-origin.mjs
36
44
  function ledgerOriginFromStored(originCoreHash) {
37
- if (originCoreHash === null || originCoreHash === "" || originCoreHash === "sha256:empty") return null;
45
+ if (originCoreHash === null || originCoreHash === "" || originCoreHash === "empty") return null;
38
46
  return originCoreHash;
39
47
  }
40
48
  //#endregion
@@ -43,7 +51,7 @@ const CONTROL_CODECS = createAstCodecRegistry(postgresCodecRegistry);
43
51
  async function encodeControlQueryParams(lowered, ast, codecs = CONTROL_CODECS) {
44
52
  return encodeParamsWithMetadata(lowered.params.map((slot) => {
45
53
  if (slot.kind === "literal") return slot.value;
46
- throw new Error(`control query lowered to a bind slot '${slot.name}', which is unsupported`);
54
+ throw new InternalError(`control query lowered to a bind slot '${slot.name}', which is unsupported`);
47
55
  }), deriveParamMetadata(ast), {}, codecs);
48
56
  }
49
57
  //#endregion
@@ -163,13 +171,14 @@ const POSTGRES_INFERRABLE_NATIVE_TYPES = /* @__PURE__ */ new Set([
163
171
  function renderTypedParam(index, codecId, codecLookup, many, typeParams) {
164
172
  if (codecId === void 0) return `$${index}`;
165
173
  const meta = codecLookup.metaFor(codecId, typeParams);
166
- if (!(codecLookup.get(codecId) !== void 0 || meta !== void 0 || codecLookup.targetTypesFor(codecId) !== void 0)) throw new Error(`Postgres lowering: ParamRef carries codecId "${codecId}" but the assembled codec lookup has no entry for it. This usually indicates a missing extension pack in the runtime stack — register the pack that contributes this codec (e.g. \`extensionPacks: [pgvectorRuntime]\`), or use the codec directly from \`@prisma-next/target-postgres/codecs\` if it's a builtin.`);
174
+ if (!(codecLookup.get(codecId) !== void 0 || meta !== void 0 || codecLookup.targetTypesFor(codecId) !== void 0)) throw adapterError("RUNTIME.PARAM_REF_MISSING_CODEC", `Postgres lowering: ParamRef carries codecId "${codecId}" but the assembled codec lookup has no entry for it. This usually indicates a missing extension pack in the runtime stack — register the pack that contributes this codec (e.g. \`extensions: [pgvectorRuntime]\`), or use the codec directly from \`@prisma-next/target-postgres/codecs\` if it's a builtin.`, { meta: { codecId } });
167
175
  const dbRecord = meta?.db;
168
176
  const sqlBlock = isRecord(dbRecord) ? dbRecord["sql"] : void 0;
169
177
  const dialectBlock = isRecord(sqlBlock) ? sqlBlock["postgres"] : void 0;
170
178
  const nativeType = isRecord(dialectBlock) ? dialectBlock["nativeType"] : void 0;
171
179
  if (typeof nativeType === "string") {
172
180
  const arraySuffix = many ? "[]" : "";
181
+ if (isPgEnumParams(typeParams)) return `$${index}::${quoteQualifiedName(nativeType)}${arraySuffix}`;
173
182
  if (!POSTGRES_INFERRABLE_NATIVE_TYPES.has(nativeType)) return `$${index}::${nativeType}${arraySuffix}`;
174
183
  if (many) return `$${index}::${nativeType}${arraySuffix}`;
175
184
  }
@@ -178,6 +187,11 @@ function renderTypedParam(index, codecId, codecLookup, many, typeParams) {
178
187
  function isRecord(value) {
179
188
  return typeof value === "object" && value !== null;
180
189
  }
190
+ function unreachableKind(value) {
191
+ const candidate = value;
192
+ if (typeof candidate === "object" && candidate !== null && "kind" in candidate && typeof candidate.kind === "string") return candidate.kind;
193
+ return "unknown";
194
+ }
181
195
  /**
182
196
  * Render a SQL query AST to a Postgres-flavored `{ sql, params }` payload.
183
197
  *
@@ -219,7 +233,7 @@ function renderLoweredSql(ast, contract, codecLookup) {
219
233
  sql = renderRawSql(node, contract, pim);
220
234
  break;
221
235
  // v8 ignore next 4
222
- default: throw new Error(`Unsupported AST node kind: ${node.kind}`);
236
+ default: assertNever(node, `Unsupported AST node kind: ${unreachableKind(node)}`);
223
237
  }
224
238
  return Object.freeze({
225
239
  sql,
@@ -342,9 +356,17 @@ function qualifyTableFromNamespaceCoordinate(table, contract) {
342
356
  if (hasExplicitSchema(table)) return `${quoteIdentifier(table.schema)}.${quoteIdentifier(table.name)}`;
343
357
  if (table.namespaceId === void 0) return quoteIdentifier(table.name);
344
358
  const namespace = contract.storage.namespaces[table.namespaceId];
345
- if (namespace === void 0) throw new Error(`Table "${table.name}" references namespace "${table.namespaceId}" which is not present as a Postgres schema on the contract`);
359
+ if (namespace === void 0) throw adapterError("RUNTIME.NAMESPACE_UNKNOWN", `Table "${table.name}" references namespace "${table.namespaceId}" which is not present as a Postgres schema on the contract`, { meta: {
360
+ table: table.name,
361
+ namespaceId: table.namespaceId,
362
+ reason: "not-present"
363
+ } });
346
364
  const qualifyTable = namespace.qualifyTable;
347
- if (qualifyTable === void 0) throw new Error(`Table "${table.name}" references namespace "${table.namespaceId}" which is not materialised as a Postgres schema on the contract`);
365
+ if (qualifyTable === void 0) throw adapterError("RUNTIME.NAMESPACE_UNKNOWN", `Table "${table.name}" references namespace "${table.namespaceId}" which is not materialised as a Postgres schema on the contract`, { meta: {
366
+ table: table.name,
367
+ namespaceId: table.namespaceId,
368
+ reason: "not-materialised"
369
+ } });
348
370
  return qualifyTable.call(namespace, table.name);
349
371
  }
350
372
  function renderTableSource(source, contract) {
@@ -359,15 +381,14 @@ function renderSource(source, contract, pim) {
359
381
  case "derived-table-source": return `(${renderSelect(node.query, contract, pim)}) AS ${quoteIdentifier(node.alias)}`;
360
382
  case "function-source": {
361
383
  const args = node.args.map((arg) => renderExpr(arg, contract, pim)).join(", ");
362
- const call = `${node.fn}(${args})`;
363
- return node.alias !== void 0 ? `${call} AS ${quoteIdentifier(node.alias)}` : call;
384
+ return `${`${node.fn}(${args})`}${node.ordinality ? " WITH ORDINALITY" : ""}${node.alias === void 0 ? "" : ` AS ${quoteIdentifier(node.alias)}`}${node.columnAliases === void 0 ? "" : `(${node.columnAliases.map((column) => quoteIdentifier(column)).join(", ")})`}`;
364
385
  }
365
386
  // v8 ignore next 4
366
- default: throw new Error(`Unsupported source node kind: ${node.kind}`);
387
+ default: assertNever(node, `Unsupported source node kind: ${unreachableKind(node)}`);
367
388
  }
368
389
  }
369
390
  function assertScalarSubquery(query) {
370
- if (query.projection.length !== 1) throw new Error("Subquery expressions must project exactly one column");
391
+ if (query.projection.length !== 1) throw adapterError("RUNTIME.AST_INVALID", "Subquery expressions must project exactly one column", { meta: { node: "subquery-expr" } });
371
392
  }
372
393
  function renderSubqueryExpr(expr, contract, pim) {
373
394
  assertScalarSubquery(expr.query);
@@ -395,6 +416,9 @@ function isAtomicExpressionKind(kind) {
395
416
  case "literal":
396
417
  case "aggregate":
397
418
  case "window-func":
419
+ case "function-call":
420
+ case "cast":
421
+ case "case":
398
422
  case "json-object":
399
423
  case "json-array-agg":
400
424
  case "list": return true;
@@ -469,11 +493,26 @@ function renderAggregateExpr(expr, contract, pim) {
469
493
  function renderWindowFuncExpr(expr, contract, pim) {
470
494
  return `${expr.fn.toUpperCase()}(${expr.args.map((arg) => renderExpr(arg, contract, pim)).join(", ")}) OVER (${[expr.partitionBy && expr.partitionBy.length > 0 ? `PARTITION BY ${expr.partitionBy.map((e) => renderExpr(e, contract, pim)).join(", ")}` : "", expr.orderBy && expr.orderBy.length > 0 ? `ORDER BY ${renderOrderByItems(expr.orderBy, contract, pim)}` : ""].filter((part) => part.length > 0).join(" ")})`;
471
495
  }
496
+ function renderFunctionCallExpr(expr, contract, pim) {
497
+ const args = expr.args.map((arg) => renderExpr(arg, contract, pim)).join(", ");
498
+ return `${expr.fn}(${args})`;
499
+ }
500
+ function renderCastExpr(expr, contract, pim) {
501
+ return `CAST(${renderExpr(expr.expr, contract, pim)} AS ${expr.targetType})`;
502
+ }
503
+ function renderCaseExpr(expr, contract, pim) {
504
+ return `CASE ${expr.branches.map((branch) => `WHEN ${renderExpr(branch.condition, contract, pim)} THEN ${renderExpr(branch.value, contract, pim)}`).join(" ")}${expr.elseExpr === void 0 ? "" : ` ELSE ${renderExpr(expr.elseExpr, contract, pim)}`} END`;
505
+ }
506
+ function renderJsonValueProjection(projection, contract, pim) {
507
+ return projection.accept({
508
+ codec: ({ value }) => renderExpr(value, contract, pim),
509
+ native: ({ value }) => renderExpr(value, contract, pim),
510
+ document: ({ value }) => renderExpr(value, contract, pim)
511
+ });
512
+ }
472
513
  function renderJsonObjectExpr(expr, contract, pim) {
473
514
  return `json_build_object(${expr.entries.flatMap((entry) => {
474
- const key = `'${escapeLiteral(entry.key)}'`;
475
- if (entry.value.kind === "literal") return [key, renderLiteral(entry.value)];
476
- return [key, renderExpr(entry.value, contract, pim)];
515
+ return [`'${escapeLiteral(entry.key)}'`, renderJsonValueProjection(entry.value, contract, pim)];
477
516
  }).join(", ")})`;
478
517
  }
479
518
  function renderOrderByItems(items, contract, pim) {
@@ -481,7 +520,7 @@ function renderOrderByItems(items, contract, pim) {
481
520
  }
482
521
  function renderJsonArrayAggExpr(expr, contract, pim) {
483
522
  const aggregateOrderBy = expr.orderBy && expr.orderBy.length > 0 ? ` ORDER BY ${renderOrderByItems(expr.orderBy, contract, pim)}` : "";
484
- const aggregated = `json_agg(${renderExpr(expr.expr, contract, pim)}${aggregateOrderBy})`;
523
+ const aggregated = `json_agg(${renderJsonValueProjection(expr.expr, contract, pim)}${aggregateOrderBy})`;
485
524
  if (expr.onEmpty === "emptyArray") return `coalesce(${aggregated}, json_build_array())`;
486
525
  return aggregated;
487
526
  }
@@ -494,6 +533,9 @@ function renderExpr(expr, contract, pim) {
494
533
  case "subquery": return renderSubqueryExpr(node, contract, pim);
495
534
  case "aggregate": return renderAggregateExpr(node, contract, pim);
496
535
  case "window-func": return renderWindowFuncExpr(node, contract, pim);
536
+ case "function-call": return renderFunctionCallExpr(node, contract, pim);
537
+ case "cast": return renderCastExpr(node, contract, pim);
538
+ case "case": return renderCaseExpr(node, contract, pim);
497
539
  case "json-object": return renderJsonObjectExpr(node, contract, pim);
498
540
  case "json-array-agg": return renderJsonArrayAggExpr(node, contract, pim);
499
541
  case "binary": return renderBinary(node, contract, pim);
@@ -512,12 +554,12 @@ function renderExpr(expr, contract, pim) {
512
554
  case "list": return renderListLiteral(node, contract, pim);
513
555
  case "raw-expr": return renderRawExpr(node, contract, pim);
514
556
  // v8 ignore next 4
515
- default: throw new Error(`Unsupported expression node kind: ${node.kind}`);
557
+ default: assertNever(node, `Unsupported expression node kind: ${unreachableKind(node)}`);
516
558
  }
517
559
  }
518
560
  function renderParamRef(ref, pim) {
519
561
  const index = pim.indexMap.get(ref);
520
- if (index === void 0) throw new Error("ParamRef not found in index map");
562
+ if (index === void 0) throw new InternalError("ParamRef not found in index map");
521
563
  if (ref.kind === "prepared-param-ref") return renderTypedParam(index, ref.codec.codecId, pim.codecLookup, ref.codec.many, ref.codec.typeParams);
522
564
  if (ref.codec === void 0) throw runtimeError("RUNTIME.PARAM_REF_MISSING_CODEC", "Postgres renderer: ParamRef reached lowering without a bound CodecRef. Every column-bound ParamRef must carry a codec under the AST-bound codec contract. This usually indicates a builder path that constructed a ParamRef without threading the column codec.", {
523
565
  paramIndex: index,
@@ -545,7 +587,7 @@ function renderOperation(expr, contract, pim) {
545
587
  return expr.lowering.template.replace(/\{\{self\}\}|\{\{arg(\d+)\}\}/g, (token, argIndex) => {
546
588
  if (token === "{{self}}") return self;
547
589
  const arg = args[Number(argIndex)];
548
- if (arg === void 0) throw new Error(`Operation lowering template for "${expr.method}" referenced missing argument {{arg${argIndex}}}; template has ${args.length} arg(s)`);
590
+ if (arg === void 0) throw adapterError("CONTRACT.PACK_CONTRIBUTION_INVALID", `Operation lowering template for "${expr.method}" referenced missing argument {{arg${argIndex}}}; template has ${args.length} arg(s)`, { meta: { method: expr.method } });
549
591
  return arg;
550
592
  });
551
593
  }
@@ -578,7 +620,10 @@ function getInsertColumnOrder(rows, contract, tableRef) {
578
620
  break;
579
621
  }
580
622
  }
581
- if (!table) throw new Error(`INSERT target table not found in contract storage: ${tableName}`);
623
+ if (!table) throw adapterError("RUNTIME.AST_INVALID", `INSERT target table not found in contract storage: ${tableName}`, { meta: {
624
+ node: "insert",
625
+ table: tableName
626
+ } });
582
627
  return Object.keys(table.columns);
583
628
  }
584
629
  function renderInsertValue(value, contract, pim) {
@@ -589,13 +634,13 @@ function renderInsertValue(value, contract, pim) {
589
634
  case "column-ref": return renderColumn(value);
590
635
  case "raw-expr": return renderExpr(value, contract, pim);
591
636
  // v8 ignore next 4
592
- default: throw new Error(`Unsupported value node in INSERT: ${value.kind}`);
637
+ default: assertNever(value, `Unsupported value node in INSERT: ${unreachableKind(value)}`);
593
638
  }
594
639
  }
595
640
  function renderInsert(ast, contract, pim) {
596
641
  const table = qualifyTableFromNamespaceCoordinate(ast.table, contract);
597
642
  const rows = ast.rows;
598
- if (rows.length === 0) throw new Error("INSERT requires at least one row");
643
+ if (rows.length === 0) throw adapterError("RUNTIME.AST_INVALID", "INSERT requires at least one row", { meta: { node: "insert" } });
599
644
  const hasExplicitValues = rows.some((row) => Object.keys(row).length > 0);
600
645
  return `${(() => {
601
646
  if (!hasExplicitValues) {
@@ -614,27 +659,27 @@ function renderInsert(ast, contract, pim) {
614
659
  return `INSERT INTO ${table} (${columns.join(", ")}) VALUES ${values}`;
615
660
  })()}${ast.onConflict ? (() => {
616
661
  const conflictColumns = ast.onConflict.columns.map((col) => quoteIdentifier(col.column));
617
- if (conflictColumns.length === 0) throw new Error("INSERT onConflict requires at least one conflict column");
662
+ if (conflictColumns.length === 0) throw adapterError("RUNTIME.AST_INVALID", "INSERT onConflict requires at least one conflict column", { meta: { node: "insert-on-conflict" } });
618
663
  const action = ast.onConflict.action;
619
664
  switch (action.kind) {
620
665
  case "do-nothing": return ` ON CONFLICT (${conflictColumns.join(", ")}) DO NOTHING`;
621
666
  case "do-update-set": {
622
667
  const updateEntries = Object.entries(action.set);
623
- if (updateEntries.length === 0) throw new Error("INSERT onConflict do-update-set requires at least one assignment");
668
+ if (updateEntries.length === 0) throw adapterError("RUNTIME.AST_INVALID", "INSERT onConflict do-update-set requires at least one assignment", { meta: { node: "insert-on-conflict" } });
624
669
  const updates = updateEntries.map(([colName, value]) => {
625
670
  return `${quoteIdentifier(colName)} = ${renderExpr(value, contract, pim)}`;
626
671
  });
627
672
  return ` ON CONFLICT (${conflictColumns.join(", ")}) DO UPDATE SET ${updates.join(", ")}`;
628
673
  }
629
674
  // v8 ignore next 4
630
- default: throw new Error(`Unsupported onConflict action: ${action.kind}`);
675
+ default: assertNever(action, `Unsupported onConflict action: ${unreachableKind(action)}`);
631
676
  }
632
677
  })() : ""}${ast.returning?.length ? ` RETURNING ${renderReturning(ast.returning, contract, pim)}` : ""}`;
633
678
  }
634
679
  function renderUpdate(ast, contract, pim) {
635
680
  const table = qualifyTableFromNamespaceCoordinate(ast.table, contract);
636
681
  const setEntries = Object.entries(ast.set);
637
- if (setEntries.length === 0) throw new Error("UPDATE requires at least one SET assignment");
682
+ if (setEntries.length === 0) throw adapterError("RUNTIME.AST_INVALID", "UPDATE requires at least one SET assignment", { meta: { node: "update" } });
638
683
  const setClauses = setEntries.map(([col, val]) => {
639
684
  return `${quoteIdentifier(col)} = ${renderExpr(val, contract, pim)}`;
640
685
  });
@@ -700,7 +745,7 @@ var PostgresControlAdapter = class {
700
745
  * without instantiating the runtime adapter.
701
746
  */
702
747
  lower(ast, context) {
703
- if (isDdlNode(ast)) throw new Error("lower() cannot lower DDL: DDL default literals require inline codec encoding, which is async. Use lowerToExecuteRequest().");
748
+ if (isDdlNode(ast)) throw adapterError("RUNTIME.DDL_UNSUPPORTED", "lower() cannot lower DDL: DDL default literals require inline codec encoding, which is async. Use lowerToExecuteRequest().", { meta: { surface: "control-adapter" } });
704
749
  return renderLoweredSql(ast, blindCast(context.contract), this.codecRegistry);
705
750
  }
706
751
  /**
@@ -1087,7 +1132,9 @@ var PostgresControlAdapter = class {
1087
1132
  i.tablename,
1088
1133
  i.indexname,
1089
1134
  ix.indisunique,
1135
+ pg_get_expr(ix.indpred, ix.indrelid) AS where_predicate,
1090
1136
  a.attname,
1137
+ CASE WHEN 0 = ANY(ix.indkey::int[]) THEN pg_get_indexdef(ix.indexrelid, k.ord::int, true) END AS element_def,
1091
1138
  k.ord AS index_position,
1092
1139
  am.amname,
1093
1140
  ic.reloptions
@@ -1215,41 +1262,50 @@ var PostgresControlAdapter = class {
1215
1262
  dependsOn: postgresColumnDependsOn(schema, tableName, uq.columns)
1216
1263
  }));
1217
1264
  const indexesMap = /* @__PURE__ */ new Map();
1218
- const indexNamesWithExpressionKey = /* @__PURE__ */ new Set();
1219
1265
  for (const idxRow of indexesByTable.get(tableName) ?? []) {
1220
- if (!idxRow.attname) {
1221
- indexNamesWithExpressionKey.add(idxRow.indexname);
1222
- continue;
1223
- }
1224
1266
  const existing = indexesMap.get(idxRow.indexname);
1225
- if (existing) existing.columns.push(idxRow.attname);
1267
+ if (existing) existing.elements.push({
1268
+ attname: idxRow.attname,
1269
+ elementDef: idxRow.element_def
1270
+ });
1226
1271
  else {
1227
1272
  const indexType = idxRow.amname && idxRow.amname !== "btree" ? idxRow.amname : void 0;
1228
1273
  const indexOptions = parsePgReloptions(idxRow.reloptions, idxRow.indexname);
1229
1274
  indexesMap.set(idxRow.indexname, {
1230
- columns: [idxRow.attname],
1231
1275
  name: idxRow.indexname,
1276
+ elements: [{
1277
+ attname: idxRow.attname,
1278
+ elementDef: idxRow.element_def
1279
+ }],
1232
1280
  unique: idxRow.indisunique,
1281
+ where: idxRow.where_predicate,
1233
1282
  type: indexType,
1234
1283
  options: indexOptions
1235
1284
  });
1236
1285
  }
1237
1286
  }
1238
- const survivingIndexes = Array.from(indexesMap.values()).filter((idx) => !indexNamesWithExpressionKey.has(idx.name));
1239
- const bestByColumnTuple = /* @__PURE__ */ new Map();
1240
- for (const idx of survivingIndexes) {
1241
- const tupleKey = idx.columns.join(",");
1242
- const existing = bestByColumnTuple.get(tupleKey);
1243
- if (!existing || idx.unique && !existing.unique || idx.unique === existing.unique && idx.name < existing.name) bestByColumnTuple.set(tupleKey, idx);
1244
- }
1245
- const indexes = Array.from(bestByColumnTuple.values()).map((idx) => ({
1246
- columns: Object.freeze([...idx.columns]),
1247
- name: idx.name,
1248
- unique: idx.unique,
1249
- ...idx.type !== void 0 && { type: idx.type },
1250
- ...idx.options !== void 0 && { options: idx.options },
1251
- dependsOn: postgresColumnDependsOn(schema, tableName, idx.columns)
1252
- }));
1287
+ const indexes = Array.from(indexesMap.values()).map((idx) => {
1288
+ const isExpression = idx.elements.some((el) => el.attname === null);
1289
+ const columnNames = idx.elements.flatMap((el) => el.attname !== null ? [el.attname] : []);
1290
+ const base = {
1291
+ name: idx.name,
1292
+ prefix: parseWireName(idx.name)?.prefix,
1293
+ where: idx.where ?? void 0,
1294
+ unique: idx.unique,
1295
+ partial: idx.where !== null,
1296
+ type: idx.type,
1297
+ options: idx.options,
1298
+ annotations: void 0,
1299
+ dependsOn: postgresColumnDependsOn(schema, tableName, isExpression ? Object.keys(columns) : columnNames)
1300
+ };
1301
+ return isExpression ? {
1302
+ ...base,
1303
+ expression: idx.elements.map((el) => el.elementDef ?? "").join(", ")
1304
+ } : {
1305
+ ...base,
1306
+ columns: Object.freeze([...columnNames])
1307
+ };
1308
+ });
1253
1309
  const checksForTable = [];
1254
1310
  for (const checkRow of checksByTable.get(tableName) ?? []) {
1255
1311
  const parsed = parseCheckConstraintDef(checkRow.constraintdef);
@@ -1290,7 +1346,7 @@ var PostgresControlAdapter = class {
1290
1346
  const operation = mapPgCmd(row.cmd);
1291
1347
  const policyRoles = [...new Set(parsePgNameArray(row.roles).map((r) => r.toLowerCase()))].sort();
1292
1348
  const permissive = row.permissive.toUpperCase() === "PERMISSIVE";
1293
- const prefix = parseRlsPolicyWireName(row.policyname)?.prefix ?? row.policyname;
1349
+ const prefix = parseWireName(row.policyname)?.prefix ?? row.policyname;
1294
1350
  const policy = new PostgresPolicySchemaNode({
1295
1351
  name: row.policyname,
1296
1352
  prefix,
@@ -1462,7 +1518,7 @@ const PG_REFERENTIAL_ACTION_MAP = {
1462
1518
  */
1463
1519
  function mapReferentialAction(rule) {
1464
1520
  const mapped = PG_REFERENTIAL_ACTION_MAP[rule];
1465
- if (mapped === void 0) throw new Error(`Unknown PostgreSQL referential action rule: "${rule}". Expected one of: NO ACTION, RESTRICT, CASCADE, SET NULL, SET DEFAULT.`);
1521
+ if (mapped === void 0) throw adapterError("CONTRACT.INTROSPECTION_UNSUPPORTED", `Unknown PostgreSQL referential action rule: "${rule}". Expected one of: NO ACTION, RESTRICT, CASCADE, SET NULL, SET DEFAULT.`, { meta: { rule } });
1466
1522
  if (mapped === "noAction") return void 0;
1467
1523
  return mapped;
1468
1524
  }
@@ -1529,7 +1585,10 @@ function parsePgReloptions(reloptions, indexName) {
1529
1585
  const result = {};
1530
1586
  for (const entry of reloptions) {
1531
1587
  const eq = entry.indexOf("=");
1532
- if (eq === -1) throw new Error(`Postgres introspection: malformed reloption entry "${entry}" on index "${indexName}" (expected "key=value")`);
1588
+ if (eq === -1) throw adapterError("CONTRACT.INTROSPECTION_UNSUPPORTED", `Postgres introspection: malformed reloption entry "${entry}" on index "${indexName}" (expected "key=value")`, { meta: {
1589
+ entry,
1590
+ indexName
1591
+ } });
1533
1592
  const key = entry.slice(0, eq);
1534
1593
  result[key] = entry.slice(eq + 1);
1535
1594
  }
@@ -1634,12 +1693,12 @@ function pgInlineLiteral(wire, nativeType) {
1634
1693
  if (wire === null) return "NULL";
1635
1694
  if (typeof wire === "boolean") return wire ? "true" : "false";
1636
1695
  if (typeof wire === "number") {
1637
- if (!Number.isFinite(wire)) throw new Error(`pgRenderDdlExecuteRequest: non-finite number wire value ${String(wire)} cannot be emitted as a DEFAULT literal for native type "${nativeType}"`);
1696
+ if (!Number.isFinite(wire)) throw adapterError("CONTRACT.DEFAULT_INVALID", `pgRenderDdlExecuteRequest: non-finite number wire value ${String(wire)} cannot be emitted as a DEFAULT literal for native type "${nativeType}"`, { meta: { nativeType } });
1638
1697
  return String(wire);
1639
1698
  }
1640
1699
  if (typeof wire === "bigint") return String(wire);
1641
1700
  if (wire instanceof Date) {
1642
- if (Number.isNaN(wire.getTime())) throw new Error(`pgRenderDdlExecuteRequest: invalid Date value cannot be emitted as a DEFAULT literal for native type "${nativeType}"`);
1701
+ if (Number.isNaN(wire.getTime())) throw adapterError("CONTRACT.DEFAULT_INVALID", `pgRenderDdlExecuteRequest: invalid Date value cannot be emitted as a DEFAULT literal for native type "${nativeType}"`, { meta: { nativeType } });
1643
1702
  const quoted = `'${escapeLiteral(wire.toISOString())}'`;
1644
1703
  return pgIsTextLikeNativeType(nativeType) ? quoted : `${quoted}::${nativeType}`;
1645
1704
  }
@@ -1650,7 +1709,10 @@ function pgInlineLiteral(wire, nativeType) {
1650
1709
  if (wire instanceof Uint8Array) return `'\\x${Array.from(wire).map((b) => b.toString(16).padStart(2, "0")).join("")}'::${nativeType}`;
1651
1710
  if (Array.isArray(wire) && nativeType.endsWith("[]")) return pgRenderArrayLiteral(wire);
1652
1711
  if (typeof wire === "object") return `${`'${escapeLiteral(JSON.stringify(wire))}'`}::${nativeType}`;
1653
- throw new Error(`pgRenderDdlExecuteRequest: unexpected wire type "${typeof wire}" for native type "${nativeType}"`);
1712
+ throw adapterError("CONTRACT.PACK_CONTRIBUTION_INVALID", `pgRenderDdlExecuteRequest: unexpected wire type "${typeof wire}" for native type "${nativeType}"`, { meta: {
1713
+ wireType: typeof wire,
1714
+ nativeType
1715
+ } });
1654
1716
  }
1655
1717
  async function pgRenderDdlColumnDefault(def, nativeType, codecLookup, codecRef) {
1656
1718
  if (def.kind === "function") {
@@ -1768,6 +1830,47 @@ function pgRenderAlterPolicyRename(node) {
1768
1830
  params: []
1769
1831
  };
1770
1832
  }
1833
+ /**
1834
+ * Renders one index reloption value: strings single-quote-escaped, finite
1835
+ * numbers verbatim, booleans in the `on`/`off` catalog spelling (the
1836
+ * canonical form the wire hash and the option equality commit to).
1837
+ */
1838
+ function pgRenderIndexOptionValue(key, value) {
1839
+ if (typeof value === "string") return `'${escapeLiteral(value)}'`;
1840
+ if (typeof value === "number" && Number.isFinite(value)) return String(value);
1841
+ if (typeof value === "boolean") return value ? "on" : "off";
1842
+ throw postgresError("CONTRACT.INDEX_INVALID", `Index option "${key}" must be a string, finite number, or boolean; got ${typeof value}`, { meta: {
1843
+ key,
1844
+ valueType: typeof value
1845
+ } });
1846
+ }
1847
+ /** Qualifies an object name; an absent schema renders it unqualified (unbound namespace). */
1848
+ function pgQualify(schema, name) {
1849
+ return schema === void 0 ? quoteIdentifier(name) : `${quoteIdentifier(schema)}.${quoteIdentifier(name)}`;
1850
+ }
1851
+ function pgRenderCreateIndex(node) {
1852
+ const elementList = "columns" in node.elements ? node.elements.columns.map(quoteIdentifier).join(", ") : node.elements.expression;
1853
+ const unique = node.unique ? "UNIQUE " : "";
1854
+ const using = node.type !== void 0 ? ` USING ${quoteIdentifier(node.type)}` : "";
1855
+ const withClause = node.options !== void 0 && Object.keys(node.options).length > 0 ? ` WITH (${Object.entries(node.options).map(([key, value]) => `${quoteIdentifier(key)} = ${pgRenderIndexOptionValue(key, value)}`).join(", ")})` : "";
1856
+ const whereClause = node.where !== void 0 ? ` WHERE (${node.where})` : "";
1857
+ return {
1858
+ sql: `CREATE ${unique}INDEX ${quoteIdentifier(node.name)} ON ${pgQualify(node.schema, node.table)}${using} (${elementList})${withClause}${whereClause}`,
1859
+ params: []
1860
+ };
1861
+ }
1862
+ function pgRenderDropIndex(node) {
1863
+ return {
1864
+ sql: `DROP INDEX ${pgQualify(node.schema, node.name)}`,
1865
+ params: []
1866
+ };
1867
+ }
1868
+ function pgRenderAlterIndexRename(node) {
1869
+ return {
1870
+ sql: `ALTER INDEX ${pgQualify(node.schema, node.from)} RENAME TO ${quoteIdentifier(node.to)}`,
1871
+ params: []
1872
+ };
1873
+ }
1771
1874
  function pgRenderDisableRowLevelSecurity(node) {
1772
1875
  return {
1773
1876
  sql: `ALTER TABLE ${`${quoteIdentifier(node.schema)}.${quoteIdentifier(node.table)}`} DISABLE ROW LEVEL SECURITY`,
@@ -1784,10 +1887,13 @@ async function pgRenderDdlExecuteRequest(ast, codecLookup) {
1784
1887
  createPolicy: (node) => Promise.resolve(pgRenderCreatePolicy(node)),
1785
1888
  dropPolicy: (node) => Promise.resolve(pgRenderDropPolicy(node)),
1786
1889
  alterPolicyRename: (node) => Promise.resolve(pgRenderAlterPolicyRename(node)),
1890
+ createIndex: (node) => Promise.resolve(pgRenderCreateIndex(node)),
1891
+ dropIndex: (node) => Promise.resolve(pgRenderDropIndex(node)),
1892
+ alterIndexRename: (node) => Promise.resolve(pgRenderAlterIndexRename(node)),
1787
1893
  disableRowLevelSecurity: (node) => Promise.resolve(pgRenderDisableRowLevelSecurity(node))
1788
1894
  });
1789
1895
  }
1790
1896
  //#endregion
1791
- export { renderLoweredSql as n, createPostgresBuiltinCodecLookup as r, PostgresControlAdapter as t };
1897
+ export { adapterError as i, renderLoweredSql as n, createPostgresBuiltinCodecLookup as r, PostgresControlAdapter as t };
1792
1898
 
1793
- //# sourceMappingURL=control-adapter-B6IM_oTR.mjs.map
1899
+ //# sourceMappingURL=control-adapter-DKbwTazN.mjs.map