@prisma-next/adapter-postgres 0.14.0-dev.2 → 0.14.0-dev.20

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,8 @@
1
- import type { ContractMarkerRecord, LedgerEntryRecord } from '@prisma-next/contract/types';
1
+ import type {
2
+ Contract,
3
+ ContractMarkerRecord,
4
+ LedgerEntryRecord,
5
+ } from '@prisma-next/contract/types';
2
6
  import {
3
7
  parseMarkerRowSafely,
4
8
  rethrowMarkerReadError,
@@ -7,11 +11,11 @@ import {
7
11
  import type { SqlControlAdapter } from '@prisma-next/family-sql/control-adapter';
8
12
  import { parseContractMarkerRow } from '@prisma-next/family-sql/verify';
9
13
  import type { CodecLookup, CodecRegistry } from '@prisma-next/framework-components/codec';
10
- import { APP_SPACE_ID } from '@prisma-next/framework-components/control';
14
+ import { APP_SPACE_ID, type SchemaDiffIssue } from '@prisma-next/framework-components/control';
11
15
  import { UNBOUND_NAMESPACE_ID } from '@prisma-next/framework-components/ir';
12
16
  import { ledgerOriginFromStored } from '@prisma-next/migration-tools/ledger-origin';
13
17
  import { REFERENTIAL_ACTION_SQL } from '@prisma-next/sql-contract/referential-action-sql';
14
- import type { SqlControlDriverInstance } from '@prisma-next/sql-contract/types';
18
+ import type { SqlControlDriverInstance, SqlStorage } from '@prisma-next/sql-contract/types';
15
19
  import type {
16
20
  AnyQueryAst,
17
21
  CodecRef,
@@ -45,14 +49,25 @@ import {
45
49
  import type {
46
50
  AddColumnAction,
47
51
  AlterTableActionVisitor,
52
+ DropDefaultAction,
48
53
  PostgresAlterTable,
54
+ PostgresCreatePolicy,
49
55
  PostgresCreateSchema,
50
56
  PostgresCreateTable,
51
57
  PostgresDdlNode,
58
+ PostgresDropPolicy,
59
+ RlsPolicyOperation,
52
60
  } from '@prisma-next/target-postgres/ddl';
53
61
  import { parsePostgresDefault } from '@prisma-next/target-postgres/default-normalizer';
54
62
  import { normalizeSchemaNativeType } from '@prisma-next/target-postgres/native-type-normalizer';
63
+ import { diffPostgresRlsPolicies } from '@prisma-next/target-postgres/planner';
55
64
  import { escapeLiteral, quoteIdentifier } from '@prisma-next/target-postgres/sql-utils';
65
+ import {
66
+ isPostgresSchemaIR,
67
+ PostgresRlsPolicy,
68
+ PostgresRole,
69
+ PostgresSchemaIR,
70
+ } from '@prisma-next/target-postgres/types';
56
71
  import { blindCast } from '@prisma-next/utils/casts';
57
72
  import { ifDefined } from '@prisma-next/utils/defined';
58
73
  import { encodeControlQueryParams } from './control-codecs';
@@ -108,6 +123,18 @@ export class PostgresControlAdapter implements SqlControlAdapter<'postgres'> {
108
123
  */
109
124
  readonly normalizeNativeType = normalizeSchemaNativeType;
110
125
 
126
+ collectSchemaDiffIssues(
127
+ contract: Contract<SqlStorage>,
128
+ schema: SqlSchemaIR,
129
+ ): readonly SchemaDiffIssue[] {
130
+ if (!isPostgresSchemaIR(schema)) {
131
+ throw new Error(
132
+ `RLS verification requires a PostgresSchemaIR; got ${(schema as { constructor?: { name?: string } }).constructor?.name ?? typeof schema}`,
133
+ );
134
+ }
135
+ return diffPostgresRlsPolicies({ contract, schema });
136
+ }
137
+
111
138
  bootstrapControlTableQueries(): readonly DdlNode[] {
112
139
  return buildControlTableBootstrapQueries();
113
140
  }
@@ -548,26 +575,22 @@ export class PostgresControlAdapter implements SqlControlAdapter<'postgres'> {
548
575
  driver: SqlControlDriverInstance<'postgres'>,
549
576
  contract?: unknown,
550
577
  schema = 'public',
551
- ): Promise<SqlSchemaIR> {
578
+ ): Promise<PostgresSchemaIR> {
552
579
  const declaredNamespaces = extractContractNamespaceIds(contract);
553
580
  const ir =
554
581
  declaredNamespaces.length > 0
555
582
  ? await this.introspectNamespaces(driver, declaredNamespaces)
556
583
  : await this.introspectSchema(driver, schema);
557
- // Capture the list of non-system schemas so downstream planners
558
- // (e.g. `verifyPostgresNamespacePresence`) can determine which
559
- // contract-declared namespaces need a `CREATE SCHEMA` before the
560
- // table DDL.
561
584
  const existingSchemas = await this.listExistingSchemas(driver);
562
- const annotations = ir.annotations ?? {};
563
- const pg = (annotations as { pg?: Record<string, unknown> }).pg ?? {};
564
- return {
565
- ...ir,
566
- annotations: {
567
- ...annotations,
568
- pg: { ...pg, existingSchemas },
569
- },
570
- };
585
+ return new PostgresSchemaIR({
586
+ tables: ir.tables,
587
+ pgSchemaName: ir.pgSchemaName,
588
+ pgVersion: ir.pgVersion,
589
+ rlsPolicies: ir.rlsPolicies,
590
+ roles: ir.roles,
591
+ existingSchemas,
592
+ nativeEnumTypeNames: ir.nativeEnumTypeNames,
593
+ });
571
594
  }
572
595
 
573
596
  /**
@@ -602,7 +625,7 @@ export class PostgresControlAdapter implements SqlControlAdapter<'postgres'> {
602
625
  private async introspectNamespaces(
603
626
  driver: SqlControlDriverInstance<'postgres'>,
604
627
  namespaceIds: readonly string[],
605
- ): Promise<SqlSchemaIR> {
628
+ ): Promise<PostgresSchemaIR> {
606
629
  const resolvedSchemas: string[] = [];
607
630
  for (const id of namespaceIds) {
608
631
  if (id === UNBOUND_NAMESPACE_ID) {
@@ -619,7 +642,7 @@ export class PostgresControlAdapter implements SqlControlAdapter<'postgres'> {
619
642
  // Walk schemas sequentially: every introspectSchema call shares the one
620
643
  // control connection, so a parallel walk only serialises behind the wire
621
644
  // protocol and trips pg's "already executing a query" deprecation.
622
- const perSchema: SqlSchemaIR[] = [];
645
+ const perSchema: PostgresSchemaIR[] = [];
623
646
  for (const schema of uniqueSchemas) {
624
647
  perSchema.push(await this.introspectSchema(driver, schema));
625
648
  }
@@ -631,18 +654,23 @@ export class PostgresControlAdapter implements SqlControlAdapter<'postgres'> {
631
654
  }
632
655
  }
633
656
 
634
- const firstAnnotations = perSchema[0]?.annotations;
635
- const firstPg =
636
- blindCast<Record<string, unknown> | undefined, 'pg annotation envelope index slot'>(
637
- firstAnnotations?.['pg'],
638
- ) ?? {};
639
- return {
657
+ const mergedRlsPolicies: PostgresRlsPolicy[] = [];
658
+ const mergedRoles: PostgresRole[] = [];
659
+ for (const ir of perSchema) {
660
+ mergedRlsPolicies.push(...ir.rlsPolicies);
661
+ mergedRoles.push(...ir.roles);
662
+ }
663
+
664
+ const first = perSchema[0];
665
+ return new PostgresSchemaIR({
640
666
  tables: mergedTables,
641
- ...ifDefined('annotations', {
642
- ...firstAnnotations,
643
- pg: { ...firstPg },
644
- }),
645
- };
667
+ pgSchemaName: first?.pgSchemaName ?? 'public',
668
+ pgVersion: first?.pgVersion ?? 'unknown',
669
+ rlsPolicies: mergedRlsPolicies,
670
+ roles: mergedRoles,
671
+ existingSchemas: first?.existingSchemas ?? ['public'],
672
+ nativeEnumTypeNames: first?.nativeEnumTypeNames ?? [],
673
+ });
646
674
  }
647
675
 
648
676
  /**
@@ -653,7 +681,7 @@ export class PostgresControlAdapter implements SqlControlAdapter<'postgres'> {
653
681
  private async introspectSchema(
654
682
  driver: SqlControlDriverInstance<'postgres'>,
655
683
  schema: string,
656
- ): Promise<SqlSchemaIR> {
684
+ ): Promise<PostgresSchemaIR> {
657
685
  // Issue the schema-wide queries one at a time. A single control connection
658
686
  // serialises queries anyway, so Promise.all buys no parallelism here and
659
687
  // makes pg emit a "client is already executing a query" deprecation. One
@@ -927,11 +955,22 @@ export class PostgresControlAdapter implements SqlControlAdapter<'postgres'> {
927
955
  nativeType = colRow.udt_name || colRow.data_type;
928
956
  }
929
957
 
958
+ // Postgres reports array columns as data_type='ARRAY'; the element type
959
+ // is the `nativeType` string minus the trailing `[]`. Strip the suffix,
960
+ // normalize the element type to the canonical form (e.g. `integer` →
961
+ // `int4`), and record `many: true` so introspection consumers (verifier,
962
+ // psl-contract-infer) can reconstruct the full array type as needed.
963
+ const many = nativeType.endsWith('[]') ? true : undefined;
964
+ if (many) {
965
+ nativeType = normalizeSchemaNativeType(nativeType.slice(0, -2));
966
+ }
967
+
930
968
  columns[colRow.column_name] = {
931
969
  name: colRow.column_name,
932
970
  nativeType,
933
971
  nullable: colRow.is_nullable === 'YES',
934
972
  ...ifDefined('default', colRow.column_default ?? undefined),
973
+ ...ifDefined('many', many),
935
974
  };
936
975
  }
937
976
 
@@ -1090,19 +1129,61 @@ export class PostgresControlAdapter implements SqlControlAdapter<'postgres'> {
1090
1129
  [schema],
1091
1130
  );
1092
1131
  const nativeEnumTypeNames = nativeEnumResult.rows.map((r) => r.typname);
1132
+ const policiesResult = await driver.query<{
1133
+ schemaname: string;
1134
+ tablename: string;
1135
+ policyname: string;
1136
+ cmd: string;
1137
+ roles: string[];
1138
+ qual: string | null;
1139
+ with_check: string | null;
1140
+ permissive: string;
1141
+ }>(
1142
+ `SELECT schemaname, tablename, policyname, cmd, roles, qual, with_check, permissive
1143
+ FROM pg_catalog.pg_policies
1144
+ WHERE schemaname = $1
1145
+ ORDER BY tablename, policyname`,
1146
+ [schema],
1147
+ );
1148
+ const rolesResult = await driver.query<{ rolname: string }>(
1149
+ `SELECT rolname
1150
+ FROM pg_catalog.pg_roles
1151
+ WHERE rolname NOT LIKE 'pg_%'
1152
+ AND rolname != 'postgres'
1153
+ ORDER BY rolname`,
1154
+ );
1155
+ const rlsPolicies: PostgresRlsPolicy[] = policiesResult.rows.map((row) => {
1156
+ const operation = mapPgCmd(row.cmd);
1157
+ const roles = [...new Set(parsePgNameArray(row.roles).map((r) => r.toLowerCase()))].sort();
1158
+ const permissive = row.permissive.toUpperCase() === 'PERMISSIVE';
1159
+ const hashSuffixMatch = /^(.+)_([0-9a-f]{8})$/.exec(row.policyname);
1160
+ const prefix = hashSuffixMatch?.[1] ?? row.policyname;
1161
+ return new PostgresRlsPolicy({
1162
+ name: row.policyname,
1163
+ prefix,
1164
+ tableName: row.tablename,
1165
+ namespaceId: row.schemaname,
1166
+ operation,
1167
+ roles,
1168
+ ...(row.qual !== null ? { using: row.qual } : {}),
1169
+ ...(row.with_check !== null ? { withCheck: row.with_check } : {}),
1170
+ permissive,
1171
+ });
1172
+ });
1093
1173
 
1094
- const annotations = {
1095
- pg: {
1096
- schema,
1097
- version: await this.getPostgresVersion(driver),
1098
- ...(nativeEnumTypeNames.length > 0 && { nativeEnumTypeNames }),
1099
- },
1100
- };
1174
+ const roles: PostgresRole[] = rolesResult.rows.map(
1175
+ (row) => new PostgresRole({ name: row.rolname, namespaceId: UNBOUND_NAMESPACE_ID }),
1176
+ );
1101
1177
 
1102
- return {
1178
+ return new PostgresSchemaIR({
1103
1179
  tables,
1104
- annotations,
1105
- };
1180
+ pgSchemaName: schema,
1181
+ pgVersion: await this.getPostgresVersion(driver),
1182
+ rlsPolicies,
1183
+ roles,
1184
+ existingSchemas: [],
1185
+ nativeEnumTypeNames,
1186
+ });
1106
1187
  }
1107
1188
 
1108
1189
  /**
@@ -1117,6 +1198,52 @@ export class PostgresControlAdapter implements SqlControlAdapter<'postgres'> {
1117
1198
  }
1118
1199
  }
1119
1200
 
1201
+ /**
1202
+ * Normalises a `name[]` column value from `pg_policies.roles`.
1203
+ *
1204
+ * The `pg` client's type-parser registry handles `text[]` (OID 1009) but not
1205
+ * `name[]` (OID 1003). When the parser is absent the raw Postgres text-array
1206
+ * literal (`{role1,role2}`) is returned as a string instead of a JS array.
1207
+ * This function accepts either form and returns a plain string array.
1208
+ */
1209
+ function parsePgNameArray(value: unknown): string[] {
1210
+ if (Array.isArray(value)) {
1211
+ return value as string[];
1212
+ }
1213
+ if (typeof value !== 'string') {
1214
+ return [];
1215
+ }
1216
+ const trimmed = value.trim();
1217
+ if (!trimmed.startsWith('{') || !trimmed.endsWith('}')) {
1218
+ return [];
1219
+ }
1220
+ const inner = trimmed.slice(1, -1);
1221
+ if (inner === '') {
1222
+ return [];
1223
+ }
1224
+ return inner.split(',').map((s) => s.trim());
1225
+ }
1226
+
1227
+ /**
1228
+ * Maps `pg_policies.cmd` text values to the `RlsPolicyOperation` union.
1229
+ * The `pg_policies` view renders the internal command code as an uppercase
1230
+ * English keyword; this function lowercases to match the IR type.
1231
+ */
1232
+ function mapPgCmd(cmd: string): RlsPolicyOperation {
1233
+ switch (cmd.toUpperCase()) {
1234
+ case 'SELECT':
1235
+ return 'select';
1236
+ case 'INSERT':
1237
+ return 'insert';
1238
+ case 'UPDATE':
1239
+ return 'update';
1240
+ case 'DELETE':
1241
+ return 'delete';
1242
+ default:
1243
+ return 'all';
1244
+ }
1245
+ }
1246
+
1120
1247
  /**
1121
1248
  * Extracts the namespace coordinate ids declared on a contract's storage,
1122
1249
  * or returns an empty array when no contract (or no storage / namespaces)
@@ -1133,6 +1260,9 @@ function extractContractNamespaceIds(contract: unknown): readonly string[] {
1133
1260
  }
1134
1261
 
1135
1262
  function normalizeFormattedType(formattedType: string, dataType: string, udtName: string): string {
1263
+ if (formattedType.endsWith('[]')) {
1264
+ return `${normalizeFormattedType(formattedType.slice(0, -2), dataType, udtName)}[]`;
1265
+ }
1136
1266
  if (formattedType === 'integer') {
1137
1267
  return 'int4';
1138
1268
  }
@@ -1376,6 +1506,18 @@ function pgIsTextLikeNativeType(nativeType: string): boolean {
1376
1506
  );
1377
1507
  }
1378
1508
 
1509
+ function pgRenderArrayElement(el: unknown): string {
1510
+ if (el === null) return 'NULL';
1511
+ if (typeof el === 'number' || typeof el === 'boolean') return String(el);
1512
+ if (typeof el === 'string') return `'${escapeLiteral(el)}'`;
1513
+ return `'${escapeLiteral(JSON.stringify(el))}'`;
1514
+ }
1515
+
1516
+ function pgRenderArrayLiteral(elements: unknown[]): string {
1517
+ if (elements.length === 0) return "'{}'";
1518
+ return `ARRAY[${elements.map(pgRenderArrayElement).join(', ')}]`;
1519
+ }
1520
+
1379
1521
  function pgInlineLiteral(wire: unknown, nativeType: string): string {
1380
1522
  if (wire === null) return 'NULL';
1381
1523
  if (typeof wire === 'boolean') return wire ? 'true' : 'false';
@@ -1407,6 +1549,9 @@ function pgInlineLiteral(wire: unknown, nativeType: string): string {
1407
1549
  .join('');
1408
1550
  return `'\\x${hex}'::${nativeType}`;
1409
1551
  }
1552
+ if (Array.isArray(wire) && nativeType.endsWith('[]')) {
1553
+ return pgRenderArrayLiteral(wire);
1554
+ }
1410
1555
  if (typeof wire === 'object') {
1411
1556
  const quoted = `'${escapeLiteral(JSON.stringify(wire))}'`;
1412
1557
  return `${quoted}::${nativeType}`;
@@ -1524,6 +1669,9 @@ async function pgRenderAlterTable(
1524
1669
  const colFragment = await pgRenderDdlColumn(action.column, codecLookup);
1525
1670
  return `ADD COLUMN ${colFragment}`;
1526
1671
  },
1672
+ dropDefault(action: DropDefaultAction): Promise<string> {
1673
+ return Promise.resolve(`ALTER COLUMN ${quoteIdentifier(action.columnName)} DROP DEFAULT`);
1674
+ },
1527
1675
  };
1528
1676
  const actionSqls = await Promise.all(node.actions.map((a) => a.accept(actionVisitor)));
1529
1677
  return {
@@ -1532,6 +1680,37 @@ async function pgRenderAlterTable(
1532
1680
  };
1533
1681
  }
1534
1682
 
1683
+ const POLICY_OPERATION_SQL: Record<RlsPolicyOperation, string> = {
1684
+ select: 'SELECT',
1685
+ insert: 'INSERT',
1686
+ update: 'UPDATE',
1687
+ delete: 'DELETE',
1688
+ all: 'ALL',
1689
+ };
1690
+
1691
+ function pgRenderCreatePolicy(node: PostgresCreatePolicy): SqlExecuteRequest {
1692
+ const tableRef = `${quoteIdentifier(node.schema)}.${quoteIdentifier(node.table)}`;
1693
+ const permissiveness = node.permissive ? 'PERMISSIVE' : 'RESTRICTIVE';
1694
+ const command = POLICY_OPERATION_SQL[node.operation];
1695
+ const roles = node.roles.length === 0 ? 'PUBLIC' : node.roles.join(', ');
1696
+ let sql = `CREATE POLICY ${quoteIdentifier(node.name)} ON ${tableRef} AS ${permissiveness} FOR ${command} TO ${roles}`;
1697
+ if (node.using !== undefined) {
1698
+ sql += ` USING (${node.using})`;
1699
+ }
1700
+ if (node.withCheck !== undefined) {
1701
+ sql += ` WITH CHECK (${node.withCheck})`;
1702
+ }
1703
+ return { sql, params: [] };
1704
+ }
1705
+
1706
+ function pgRenderDropPolicy(node: PostgresDropPolicy): SqlExecuteRequest {
1707
+ const tableRef = `${quoteIdentifier(node.schema)}.${quoteIdentifier(node.table)}`;
1708
+ return {
1709
+ sql: `DROP POLICY ${quoteIdentifier(node.name)} ON ${tableRef}`,
1710
+ params: [],
1711
+ };
1712
+ }
1713
+
1535
1714
  async function pgRenderDdlExecuteRequest(
1536
1715
  ast: PostgresDdlNode,
1537
1716
  codecLookup: CodecLookup,
@@ -1540,6 +1719,8 @@ async function pgRenderDdlExecuteRequest(
1540
1719
  createTable: (node: PostgresCreateTable) => pgRenderCreateTable(node, codecLookup),
1541
1720
  createSchema: (node: PostgresCreateSchema) => Promise.resolve(pgRenderCreateSchema(node)),
1542
1721
  alterTable: (node: PostgresAlterTable) => pgRenderAlterTable(node, codecLookup),
1722
+ createPolicy: (node: PostgresCreatePolicy) => Promise.resolve(pgRenderCreatePolicy(node)),
1723
+ dropPolicy: (node: PostgresDropPolicy) => Promise.resolve(pgRenderDropPolicy(node)),
1543
1724
  };
1544
1725
  return ast.accept(visitor);
1545
1726
  }
@@ -172,6 +172,7 @@ export const postgresAdapterDescriptorMeta = {
172
172
  returning: true,
173
173
  defaultInInsert: true,
174
174
  lateral: true,
175
+ scalarList: true,
175
176
  },
176
177
  },
177
178
  types: {
@@ -73,6 +73,7 @@ function renderTypedParam(
73
73
  index: number,
74
74
  codecId: string | undefined,
75
75
  codecLookup: CodecLookup,
76
+ many?: boolean,
76
77
  ): string {
77
78
  if (codecId === undefined) {
78
79
  return `$${index}`;
@@ -97,8 +98,14 @@ function renderTypedParam(
97
98
  const sqlBlock = isRecord(dbRecord) ? dbRecord['sql'] : undefined;
98
99
  const dialectBlock = isRecord(sqlBlock) ? sqlBlock['postgres'] : undefined;
99
100
  const nativeType = isRecord(dialectBlock) ? dialectBlock['nativeType'] : undefined;
100
- if (typeof nativeType === 'string' && !POSTGRES_INFERRABLE_NATIVE_TYPES.has(nativeType)) {
101
- return `$${index}::${nativeType}`;
101
+ if (typeof nativeType === 'string') {
102
+ const arraySuffix = many ? '[]' : '';
103
+ if (!POSTGRES_INFERRABLE_NATIVE_TYPES.has(nativeType)) {
104
+ return `$${index}::${nativeType}${arraySuffix}`;
105
+ }
106
+ if (many) {
107
+ return `$${index}::${nativeType}${arraySuffix}`;
108
+ }
102
109
  }
103
110
  return `$${index}`;
104
111
  }
@@ -736,7 +743,7 @@ function renderParamRef(ref: AnyParamRef, pim: ParamIndexMap): string {
736
743
  throw new Error('ParamRef not found in index map');
737
744
  }
738
745
  if (ref.kind === 'prepared-param-ref') {
739
- return renderTypedParam(index, ref.codec.codecId, pim.codecLookup);
746
+ return renderTypedParam(index, ref.codec.codecId, pim.codecLookup, ref.codec.many);
740
747
  }
741
748
  if (ref.codec === undefined) {
742
749
  throw runtimeError(
@@ -747,7 +754,7 @@ function renderParamRef(ref: AnyParamRef, pim: ParamIndexMap): string {
747
754
  { paramIndex: index, ...ifDefined('name', ref.name) },
748
755
  );
749
756
  }
750
- return renderTypedParam(index, ref.codec.codecId, pim.codecLookup);
757
+ return renderTypedParam(index, ref.codec.codecId, pim.codecLookup, ref.codec.many);
751
758
  }
752
759
 
753
760
  function renderLiteral(expr: LiteralExpr): string {
@@ -1 +0,0 @@
1
- {"version":3,"file":"adapter-CwkcdpM_.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 { 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 },\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 new Error(\n 'lower() does not lower DDL on the runtime adapter — DDL lowering is a control-plane concern handled by the control 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 new Error(\n 'unsupported JS value type for raw-SQL interpolation: wrap this value in `param(...)` with an explicit codec',\n );\n },\n};\n\nexport function createPostgresAdapter(options?: PostgresAdapterOptions) {\n return Object.freeze(new PostgresAdapterImpl(options));\n}\n"],"mappings":";;;;AAkBA,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;CACX;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,IAAI,MACR,6HACF;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,IAAI,MACR,6GACF;AACF,EACF;AAEA,SAAgB,sBAAsB,SAAkC;CACtE,OAAO,OAAO,OAAO,IAAI,oBAAoB,OAAO,CAAC;AACvD"}