@effect/sql-pg 4.0.0-rc.112 → 4.0.0-rc.114

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 (60) hide show
  1. package/AGENTS.md +24 -9
  2. package/CLAUDE.md +24 -9
  3. package/ai-docs/package.json +2 -2
  4. package/ai-docs/src/01_effect/01_basics/02_effect-fn.ts +18 -5
  5. package/ai-docs/src/01_effect/01_basics/index.md +5 -3
  6. package/ai-docs/src/01_effect/03_services/20_layer-composition.ts +1 -1
  7. package/ai-docs/src/01_effect/03_services/20_layer-unwrap.ts +2 -2
  8. package/ai-docs/src/01_effect/05_resources/10_acquire-release.ts +2 -2
  9. package/ai-docs/src/03_stream/30_encoding.ts +5 -7
  10. package/ai-docs/src/08_observability/10_logging.ts +1 -1
  11. package/ai-docs/src/70_cli/10_basics.ts +7 -7
  12. package/ai-docs/src/71_ai/10_language-model.ts +2 -2
  13. package/ai-docs/src/71_ai/20_tools.ts +1 -1
  14. package/ai-docs/src/71_ai/30_chat.ts +1 -1
  15. package/dist/PgAuth.d.ts +24 -0
  16. package/dist/PgAuth.d.ts.map +1 -1
  17. package/dist/PgAuth.js +29 -2
  18. package/dist/PgAuth.js.map +1 -1
  19. package/dist/PgClient.d.ts +49 -82
  20. package/dist/PgClient.d.ts.map +1 -1
  21. package/dist/PgClient.js +58 -510
  22. package/dist/PgClient.js.map +1 -1
  23. package/dist/PgConnection.d.ts +177 -0
  24. package/dist/PgConnection.d.ts.map +1 -0
  25. package/dist/PgConnection.js +1788 -0
  26. package/dist/PgConnection.js.map +1 -0
  27. package/dist/PgPool.d.ts +107 -0
  28. package/dist/PgPool.d.ts.map +1 -0
  29. package/dist/PgPool.js +124 -0
  30. package/dist/PgPool.js.map +1 -0
  31. package/dist/PgProtocol.d.ts +63 -30
  32. package/dist/PgProtocol.d.ts.map +1 -1
  33. package/dist/PgProtocol.js +64 -22
  34. package/dist/PgProtocol.js.map +1 -1
  35. package/dist/PgTypes.d.ts +85 -17
  36. package/dist/PgTypes.d.ts.map +1 -1
  37. package/dist/PgTypes.js +166 -169
  38. package/dist/PgTypes.js.map +1 -1
  39. package/dist/index.d.ts +8 -0
  40. package/dist/index.d.ts.map +1 -1
  41. package/dist/index.js +8 -0
  42. package/dist/index.js.map +1 -1
  43. package/dist/internal/connection.d.ts +2 -0
  44. package/dist/internal/connection.d.ts.map +1 -0
  45. package/dist/internal/connection.js +5 -0
  46. package/dist/internal/connection.js.map +1 -0
  47. package/dist/internal/sqlError.d.ts +10 -0
  48. package/dist/internal/sqlError.d.ts.map +1 -0
  49. package/dist/internal/sqlError.js +57 -0
  50. package/dist/internal/sqlError.js.map +1 -0
  51. package/package.json +6 -14
  52. package/src/PgAuth.ts +33 -2
  53. package/src/PgClient.ts +138 -681
  54. package/src/PgConnection.ts +2296 -0
  55. package/src/PgPool.ts +231 -0
  56. package/src/PgProtocol.ts +93 -50
  57. package/src/PgTypes.ts +254 -186
  58. package/src/index.ts +10 -0
  59. package/src/internal/connection.ts +26 -0
  60. package/src/internal/sqlError.ts +75 -0
package/dist/PgTypes.js CHANGED
@@ -18,6 +18,9 @@
18
18
  */
19
19
  import * as Data from "effect/Data";
20
20
  import * as Result from "effect/Result";
21
+ import * as IpInterface from "effect/unstable/net/IpInterface";
22
+ import * as IpNetwork from "effect/unstable/net/IpNetwork";
23
+ import * as NetAddress from "effect/unstable/net/NetAddress";
21
24
  /**
22
25
  * Failure returned when a value cannot be encoded or decoded for its OID.
23
26
  *
@@ -293,7 +296,7 @@ const elementToArray = /*#__PURE__*/new Map(/*#__PURE__*/Array.from(arrayToEleme
293
296
  * @category getters
294
297
  * @since 4.0.0
295
298
  */
296
- export const arrayOidFor = elementOid => elementToArray.get(elementOid);
299
+ export const arrayOidFor = (elementOid, registry) => registry === undefined ? elementToArray.get(elementOid) : getRegistryState(registry).elementToArray.get(elementOid);
297
300
  // -----------------------------------------------------------------------------
298
301
  // calendar helpers
299
302
  // -----------------------------------------------------------------------------
@@ -533,112 +536,26 @@ const decodeNumeric = (bytes, offset, size) => {
533
536
  // -----------------------------------------------------------------------------
534
537
  const PGSQL_AF_INET = 2;
535
538
  const PGSQL_AF_INET6 = 3;
536
- const parseIPv4 = text => {
537
- const parts = text.split(".");
538
- if (parts.length !== 4) return undefined;
539
- const bytes = new Uint8Array(4);
540
- for (let i = 0; i < 4; i++) {
541
- if (!/^\d{1,3}$/.test(parts[i])) return undefined;
542
- const value = Number(parts[i]);
543
- if (value > 255) return undefined;
544
- bytes[i] = value;
545
- }
546
- return bytes;
547
- };
548
- const parseIPv6 = text => {
549
- const halves = text.split("::");
550
- if (halves.length > 2) return undefined;
551
- const toGroups = part => part === "" ? [] : part.split(":");
552
- const head = toGroups(halves[0]);
553
- const tail = halves.length === 2 ? toGroups(halves[1]) : [];
554
- const bytes = new Uint8Array(16);
555
- const trailing = tail.length > 0 ? tail[tail.length - 1] : head.length > 0 ? head[head.length - 1] : "";
556
- const embedded = trailing.includes(".") ? parseIPv4(trailing) : undefined;
557
- if (trailing.includes(".") && embedded === undefined) return undefined;
558
- if (embedded !== undefined) {
559
- if (tail.length > 0) tail.pop();else head.pop();
560
- }
561
- const groupCount = embedded === undefined ? 8 : 6;
562
- if (head.length + tail.length > groupCount) return undefined;
563
- if (halves.length === 1 && head.length !== groupCount) return undefined;
564
- const writeGroup = (group, offset) => {
565
- if (!/^[0-9a-fA-F]{1,4}$/.test(group)) return false;
566
- const value = Number.parseInt(group, 16);
567
- bytes[offset] = value >> 8;
568
- bytes[offset + 1] = value & 0xff;
569
- return true;
570
- };
571
- for (let i = 0; i < head.length; i++) {
572
- if (!writeGroup(head[i], i * 2)) return undefined;
573
- }
574
- for (let i = 0; i < tail.length; i++) {
575
- if (!writeGroup(tail[i], (groupCount - tail.length + i) * 2)) return undefined;
576
- }
577
- if (embedded !== undefined) bytes.set(embedded, 12);
578
- return bytes;
579
- };
580
- const formatIPv4 = (bytes, offset) => `${bytes[offset]}.${bytes[offset + 1]}.${bytes[offset + 2]}.${bytes[offset + 3]}`;
581
- const formatIPv6 = (bytes, offset) => {
582
- let isV4Mapped = bytes[offset + 10] === 0xff && bytes[offset + 11] === 0xff;
583
- for (let i = 0; isV4Mapped && i < 10; i++) isV4Mapped = bytes[offset + i] === 0;
584
- if (isV4Mapped) return `::ffff:${formatIPv4(bytes, offset + 12)}`;
585
- const groups = [];
586
- for (let i = 0; i < 8; i++) groups.push(readUint16(bytes, offset + i * 2));
587
- let bestStart = -1;
588
- let bestLength = 0;
589
- let start = -1;
590
- for (let i = 0; i <= 8; i++) {
591
- if (i < 8 && groups[i] === 0) {
592
- if (start === -1) start = i;
593
- } else if (start !== -1) {
594
- if (i - start > bestLength) {
595
- bestStart = start;
596
- bestLength = i - start;
597
- }
598
- start = -1;
599
- }
600
- }
601
- if (bestLength < 2) {
602
- return groups.map(group => group.toString(16)).join(":");
603
- }
604
- const head = groups.slice(0, bestStart).map(group => group.toString(16)).join(":");
605
- const tail = groups.slice(bestStart + bestLength).map(group => group.toString(16)).join(":");
606
- return `${head}::${tail}`;
607
- };
608
- const hasHostBits = (bytes, offset, length, bits) => {
609
- const wholeBytes = Math.floor(bits / 8);
610
- const partialBits = bits % 8;
611
- if (partialBits !== 0 && (bytes[offset + wholeBytes] & (1 << 8 - partialBits) - 1) !== 0) {
612
- return true;
613
- }
614
- for (let index = wholeBytes + (partialBits === 0 ? 0 : 1); index < length; index++) {
615
- if (bytes[offset + index] !== 0) return true;
616
- }
617
- return false;
618
- };
619
539
  const encodeInet = (value, isCidr) => {
620
- const text = requireString(value, isCidr ? "cidr" : "inet");
621
- const slash = text.lastIndexOf("/");
622
- const address = slash === -1 ? text : text.slice(0, slash);
623
- const v4 = parseIPv4(address);
624
- const bytes = v4 ?? parseIPv6(address);
625
- if (bytes === undefined) {
626
- return fail(`Expected an IP address, received "${text}"`);
627
- }
628
- const fullBits = bytes.length * 8;
629
- const bits = slash === -1 ? fullBits : Number(text.slice(slash + 1));
630
- if (!Number.isInteger(bits) || bits < 0 || bits > fullBits) {
631
- return fail(`Invalid netmask length in "${text}"`);
632
- }
633
- if (isCidr && hasHostBits(bytes, 0, bytes.length, bits)) {
634
- return fail(`CIDR address has host bits set in "${text}"`);
540
+ const type = isCidr ? "cidr" : "inet";
541
+ const text = requireString(value, type);
542
+ const parsed = IpInterface.fromString(text);
543
+ if (Result.isFailure(parsed)) return fail(`Invalid ${type} value ${JSON.stringify(text)}: ${parsed.failure.message}`);
544
+ const address = parsed.success.address;
545
+ const bits = parsed.success.prefixLength;
546
+ if (isCidr) {
547
+ const network = IpNetwork.make(address, bits);
548
+ if (Result.isFailure(network)) {
549
+ return fail(`Invalid ${type} value ${JSON.stringify(text)}: ${network.failure.message}`);
550
+ }
635
551
  }
636
- const result = new Uint8Array(4 + bytes.length);
637
- result[0] = v4 === undefined ? PGSQL_AF_INET6 : PGSQL_AF_INET;
552
+ const octets = NetAddress.isIpv4Address(address) ? NetAddress.ipv4ToOctets(address) : NetAddress.ipv6ToOctets(address);
553
+ const result = new Uint8Array(4 + octets.length);
554
+ result[0] = NetAddress.isIpv4Address(address) ? PGSQL_AF_INET : PGSQL_AF_INET6;
638
555
  result[1] = bits;
639
556
  result[2] = isCidr ? 1 : 0;
640
- result[3] = bytes.length;
641
- result.set(bytes, 4);
557
+ result[3] = octets.length;
558
+ result.set(octets, 4);
642
559
  return result;
643
560
  };
644
561
  const decodeInet = (bytes, offset, size) => {
@@ -655,11 +572,14 @@ const decodeInet = (bytes, offset, size) => {
655
572
  }
656
573
  requireSize(size, 4 + addressSize, "inet");
657
574
  if (bits > addressSize * 8) return fail(`Invalid inet netmask length: ${bits}`);
658
- if (isCidr && hasHostBits(bytes, offset + 4, addressSize, bits)) {
659
- return fail("CIDR address has host bits set");
575
+ const addressBytes = bytes.slice(offset + 4, offset + 4 + addressSize);
576
+ const address = family === PGSQL_AF_INET ? NetAddress.ipv4FromBytesUnsafe(addressBytes) : NetAddress.ipv6FromBytesUnsafe(addressBytes);
577
+ if (isCidr) {
578
+ const network = IpNetwork.make(address, bits);
579
+ if (Result.isFailure(network)) return fail(network.failure.message);
580
+ return IpNetwork.format(network.success);
660
581
  }
661
- const text = family === PGSQL_AF_INET ? formatIPv4(bytes, offset + 4) : formatIPv6(bytes, offset + 4);
662
- return isCidr || bits !== addressSize * 8 ? `${text}/${bits}` : text;
582
+ return bits === addressSize * 8 ? NetAddress.formatIp(address) : IpInterface.format(IpInterface.makeUnsafe(address, bits));
663
583
  };
664
584
  // -----------------------------------------------------------------------------
665
585
  // uuid
@@ -762,6 +682,31 @@ const decodeUuid = (bytes, offset, size) => {
762
682
  const b15 = bytes[offset + 15];
763
683
  return String.fromCharCode(hexHigh[b0], hexLow[b0], hexHigh[b1], hexLow[b1], hexHigh[b2], hexLow[b2], hexHigh[b3], hexLow[b3], 45, hexHigh[b4], hexLow[b4], hexHigh[b5], hexLow[b5], 45, hexHigh[b6], hexLow[b6], hexHigh[b7], hexLow[b7], 45, hexHigh[b8], hexLow[b8], hexHigh[b9], hexLow[b9], 45, hexHigh[b10], hexLow[b10], hexHigh[b11], hexLow[b11], hexHigh[b12], hexLow[b12], hexHigh[b13], hexLow[b13], hexHigh[b14], hexLow[b14], hexHigh[b15], hexLow[b15]);
764
684
  };
685
+ const toUnsafeCodec = codec => ({
686
+ encode(value) {
687
+ const encoded = codec.encode(value);
688
+ if (Result.isFailure(encoded)) throw encoded.failure;
689
+ return encoded.success;
690
+ },
691
+ decode(bytes) {
692
+ const decoded = codec.decode(bytes);
693
+ if (Result.isFailure(decoded)) throw decoded.failure;
694
+ return decoded.success;
695
+ },
696
+ ...(codec.write === undefined ? undefined : {
697
+ write(sink, value) {
698
+ const written = codec.write(sink, value);
699
+ if (Result.isFailure(written)) throw written.failure;
700
+ }
701
+ }),
702
+ ...(codec.read === undefined ? undefined : {
703
+ read(bytes, offset, size) {
704
+ const decoded = codec.read(bytes, offset, size);
705
+ if (Result.isFailure(decoded)) throw decoded.failure;
706
+ return decoded.success;
707
+ }
708
+ })
709
+ });
765
710
  /** A codec whose `decode` is its `read` over the whole of its bytes. */
766
711
  const codecOf = (read, encode, write) => {
767
712
  const decode = bytes => read(bytes, 0, bytes.length);
@@ -954,7 +899,7 @@ const jsonbCodec = /*#__PURE__*/codecOf((bytes, offset, size) => {
954
899
  sink.uint8(1);
955
900
  sink.utf8(text);
956
901
  });
957
- const builtins = /*#__PURE__*/new Map([[OID.bool, /*#__PURE__*/codecOf((bytes, offset, size) => {
902
+ const builtinScalars = /*#__PURE__*/new Map([[OID.bool, /*#__PURE__*/codecOf((bytes, offset, size) => {
958
903
  requireSize(size, 1, "bool");
959
904
  return bytes[offset] !== 0;
960
905
  }, value => {
@@ -1025,9 +970,8 @@ const builtins = /*#__PURE__*/new Map([[OID.bool, /*#__PURE__*/codecOf((bytes, o
1025
970
  scratchView8.setBigInt64(0, timeValue(value));
1026
971
  return takeScratch8();
1027
972
  }, (sink, value) => sink.bigInt64(timeValue(value)))], [OID.numeric, /*#__PURE__*/codecOf(decodeNumeric, encodeNumeric)], [OID.text, utf8Codec], [OID.varchar, utf8Codec], [OID.bpchar, utf8Codec], [OID.name, utf8Codec], [OID.json, jsonCodec], [OID.jsonb, jsonbCodec], [OID.uuid, /*#__PURE__*/codecOf(decodeUuid, encodeUuid, writeUuid)], [OID.inet, /*#__PURE__*/codecOf(decodeInet, value => encodeInet(value, false))], [OID.cidr, /*#__PURE__*/codecOf(decodeInet, value => encodeInet(value, true))], [OID.date, dateCodec], [OID.timetz, timetzCodec], [OID.timestamp, timestampCodec], [OID.timestamptz, timestampCodec]]);
1028
- for (const [arrayOid, elementOid] of arrayToElement) {
1029
- builtins.set(arrayOid, codecOf((bytes, offset, size) => decodeArray(bytes, offset, size, elementOid), value => encodeArray(value, elementOid), (sink, value) => writeArray(sink, value, elementOid)));
1030
- }
973
+ const makeArrayCodec = (elementOid, lookup) => codecOf((bytes, offset, size) => decodeArray(bytes, offset, size, elementOid, lookup), value => encodeArray(value, elementOid, lookup), (sink, value) => writeArray(sink, value, elementOid, lookup));
974
+ const builtins = /*#__PURE__*/new Map(builtinScalars);
1031
975
  /**
1032
976
  * Built-ins with registered codecs layered over them, so `encode` and `decode`
1033
977
  * resolve an OID with a single lookup.
@@ -1040,8 +984,49 @@ const codecs = /*#__PURE__*/new Map(builtins);
1040
984
  */
1041
985
  const tableSize = 4096;
1042
986
  const table = /*#__PURE__*/new Array(tableSize);
1043
- for (const [oid, codec] of codecs) table[oid] = codec;
1044
987
  const lookup = oid => oid >= 0 && oid < tableSize ? table[oid] : codecs.get(oid);
988
+ for (const [arrayOid, elementOid] of arrayToElement) {
989
+ const codec = /*#__PURE__*/makeArrayCodec(elementOid, lookup);
990
+ builtins.set(arrayOid, codec);
991
+ codecs.set(arrayOid, codec);
992
+ }
993
+ for (const [oid, codec] of codecs) table[oid] = codec;
994
+ const registryStates = /*#__PURE__*/new WeakMap();
995
+ const getRegistryState = registry => {
996
+ const state = registryStates.get(registry);
997
+ if (state === undefined) return fail("Invalid PgTypes Registry");
998
+ return state;
999
+ };
1000
+ const registerInState = (state, oid, codec, options) => {
1001
+ state.codecs.set(oid, toUnsafeCodec(codec));
1002
+ if (options?.arrayOid !== undefined) {
1003
+ state.elementToArray.set(oid, options.arrayOid);
1004
+ state.codecs.set(options.arrayOid, makeArrayCodec(oid, state.lookup));
1005
+ }
1006
+ };
1007
+ /**
1008
+ * Creates a client-specific registry containing the built-in codecs.
1009
+ *
1010
+ * @category constructors
1011
+ * @since 4.0.0
1012
+ */
1013
+ export const makeRegistry = () => {
1014
+ const codecs = new Map(builtinScalars);
1015
+ const state = {
1016
+ codecs,
1017
+ elementToArray: new Map(elementToArray),
1018
+ lookup: oid => codecs.get(oid)
1019
+ };
1020
+ for (const [arrayOid, elementOid] of arrayToElement) {
1021
+ codecs.set(arrayOid, makeArrayCodec(elementOid, state.lookup));
1022
+ }
1023
+ const registry = {
1024
+ register: (oid, codec, options) => registerInState(state, oid, codec, options)
1025
+ };
1026
+ registryStates.set(registry, state);
1027
+ return registry;
1028
+ };
1029
+ const lookupFor = registry => registry === undefined ? lookup : getRegistryState(registry).lookup;
1045
1030
  /**
1046
1031
  * Registers a binary codec for an OID the built-in catalogue does not cover,
1047
1032
  * or overrides a built-in one. Registered codecs take precedence.
@@ -1050,31 +1035,7 @@ const lookup = oid => oid >= 0 && oid < tableSize ? table[oid] : codecs.get(oid)
1050
1035
  * @since 4.0.0
1051
1036
  */
1052
1037
  export const register = (oid, codec) => {
1053
- const unsafe = {
1054
- encode(value) {
1055
- const encoded = codec.encode(value);
1056
- if (Result.isFailure(encoded)) throw encoded.failure;
1057
- return encoded.success;
1058
- },
1059
- decode(bytes) {
1060
- const decoded = codec.decode(bytes);
1061
- if (Result.isFailure(decoded)) throw decoded.failure;
1062
- return decoded.success;
1063
- },
1064
- ...(codec.write === undefined ? undefined : {
1065
- write(sink, value) {
1066
- const written = codec.write(sink, value);
1067
- if (Result.isFailure(written)) throw written.failure;
1068
- }
1069
- }),
1070
- ...(codec.read === undefined ? undefined : {
1071
- read(bytes, offset, size) {
1072
- const decoded = codec.read(bytes, offset, size);
1073
- if (Result.isFailure(decoded)) throw decoded.failure;
1074
- return decoded.success;
1075
- }
1076
- })
1077
- };
1038
+ const unsafe = toUnsafeCodec(codec);
1078
1039
  codecs.set(oid, unsafe);
1079
1040
  if (oid >= 0 && oid < tableSize) table[oid] = unsafe;
1080
1041
  };
@@ -1092,7 +1053,7 @@ export const unregister = oid => {
1092
1053
  // -----------------------------------------------------------------------------
1093
1054
  // arrays
1094
1055
  // -----------------------------------------------------------------------------
1095
- const encodeArray = (value, elementOid) => {
1056
+ const encodeArray = (value, elementOid, lookup) => {
1096
1057
  if (!Array.isArray(value)) {
1097
1058
  return fail("Expected an array");
1098
1059
  }
@@ -1143,7 +1104,7 @@ const encodeArray = (value, elementOid) => {
1143
1104
  * and written in place, so neither the elements nor the array itself needs an
1144
1105
  * array of bytes of its own.
1145
1106
  */
1146
- const writeArray = (sink, value, elementOid) => {
1107
+ const writeArray = (sink, value, elementOid, lookup) => {
1147
1108
  if (!Array.isArray(value)) {
1148
1109
  return fail("Expected an array");
1149
1110
  }
@@ -1174,7 +1135,7 @@ const writeArray = (sink, value, elementOid) => {
1174
1135
  sink.int32(-1);
1175
1136
  } else {
1176
1137
  const token = sink.beginLength();
1177
- if (write === undefined) writeValue(sink, element, elementOid);else write(sink, element);
1138
+ if (write === undefined) writeValue(sink, element, elementOid, lookup);else write(sink, element);
1178
1139
  sink.endLength(token);
1179
1140
  }
1180
1141
  }
@@ -1184,7 +1145,7 @@ const writeArray = (sink, value, elementOid) => {
1184
1145
  * where it can be, so a thousand elements cost a thousand reads rather than a
1185
1146
  * thousand lookups and a thousand views.
1186
1147
  */
1187
- const decodeArray = (bytes, start, size, elementOid) => {
1148
+ const decodeArray = (bytes, start, size, elementOid, lookup) => {
1188
1149
  if (size < 12) return fail("Truncated array value");
1189
1150
  const dimensions = readInt32(bytes, start);
1190
1151
  const wireElementOid = readUint32(bytes, start + 8);
@@ -1225,18 +1186,15 @@ const decodeArray = (bytes, start, size, elementOid) => {
1225
1186
  return values;
1226
1187
  };
1227
1188
  /**
1228
- * Builds a field reader for `PgProtocol.makeParser`, so a result's rows decode
1229
- * as they are parsed rather than through a view per column.
1189
+ * Creates a field reader for `PgProtocol.makeParser`.
1190
+ *
1191
+ * **Details**
1230
1192
  *
1231
- * Every column is resolved once here rather than once per row, and a codec that
1232
- * can read in place does; the rest are handed a view. SQL NULL reads as `null`,
1233
- * and a column whose OID has no codec reads as a copy of its bytes.
1193
+ * Codecs are resolved once per column. SQL `NULL` becomes `null`, and columns
1194
+ * without a registered codec return a copy of their bytes. Text-format columns
1195
+ * fail with `CodecError`.
1234
1196
  *
1235
- * Only the binary format is supported, and a text column returns a
1236
- * `CodecError` failure here rather than once per row. A successful result
1237
- * contains the parser's internal throwing fast path; its failures are terminal
1238
- * for that parser. The standalone `encode` and `decode` APIs remain typed
1239
- * `Result` values.
1197
+ * **Example** (Updating the reader after `RowDescription`)
1240
1198
  *
1241
1199
  * ```ts
1242
1200
  * import { PgProtocol, PgTypes } from "@effect/sql-pg"
@@ -1250,7 +1208,8 @@ const decodeArray = (bytes, start, size, elementOid) => {
1250
1208
  * @category decoding
1251
1209
  * @since 4.0.0
1252
1210
  */
1253
- export const makeFieldReader = columns => result(() => {
1211
+ export const makeFieldReader = (columns, registry) => result(() => {
1212
+ const lookup = lookupFor(registry);
1254
1213
  const codecs = columns.map((column, index) => {
1255
1214
  if (column.format !== 1) {
1256
1215
  return fail(`Only the binary format is supported, column ${index} has format ${column.format}`);
@@ -1268,45 +1227,75 @@ export const makeFieldReader = columns => result(() => {
1268
1227
  /**
1269
1228
  * Encodes a JavaScript value as the binary representation of the given OID.
1270
1229
  *
1230
+ * **Details**
1231
+ *
1271
1232
  * Returns a `CodecError` failure when the value has the wrong JavaScript type,
1272
1233
  * or when the OID is neither built in nor registered.
1273
1234
  *
1274
1235
  * @category encoding
1275
1236
  * @since 4.0.0
1276
1237
  */
1277
- export const encode = (value, oid) => result(() => {
1278
- const codec = lookup(oid);
1238
+ export const encode = (value, oid, registry) => result(() => {
1239
+ const codec = lookupFor(registry)(oid);
1279
1240
  if (codec === undefined) return fail(`No codec registered for OID ${oid}`);
1280
1241
  return codec.encode(value);
1281
1242
  });
1282
1243
  /**
1283
1244
  * Decodes the binary representation of the given OID.
1284
1245
  *
1246
+ * **Details**
1247
+ *
1285
1248
  * `format` must be `1`; the text format is not implemented. An OID that is
1286
1249
  * neither built in nor registered decodes to the raw bytes.
1287
1250
  *
1288
1251
  * @category decoding
1289
1252
  * @since 4.0.0
1290
1253
  */
1291
- export const decode = (bytes, oid, format) => result(() => {
1254
+ export const decode = (bytes, oid, format, registry) => result(() => {
1292
1255
  if (format !== 1) {
1293
1256
  return fail(`Only the binary format is supported, received format ${format}`);
1294
1257
  }
1295
- const codec = lookup(oid);
1258
+ const codec = lookupFor(registry)(oid);
1296
1259
  return codec === undefined ? bytes : codec.decode(bytes);
1297
1260
  });
1261
+ // -----------------------------------------------------------------------------
1262
+ // parameter constructors
1263
+ // -----------------------------------------------------------------------------
1264
+ /**
1265
+ * The runtime type identifier for PostgreSQL parameters.
1266
+ *
1267
+ * @category type IDs
1268
+ * @since 4.0.0
1269
+ */
1270
+ export const ParameterTypeId = "~@effect/sql-pg/PgTypes/Parameter";
1271
+ /**
1272
+ * Returns whether a value is a parameter created by this module.
1273
+ *
1274
+ * @category guards
1275
+ * @since 4.0.0
1276
+ */
1277
+ export const isParameter = value => typeof value === "object" && value !== null && value[ParameterTypeId] === ParameterTypeId;
1298
1278
  /**
1299
1279
  * Encodes a parameter for a `Bind` message. SQL NULL stays `null`.
1300
1280
  *
1301
1281
  * @category encoding
1302
1282
  * @since 4.0.0
1303
1283
  */
1304
- export const encodeParameter = parameter => parameter.value === null ? Result.succeed(null) : encode(parameter.value, parameter.oid);
1305
- const writeValue = (sink, value, oid) => {
1284
+ export const encodeParameter = (parameter, registry) => parameter.value === null ? Result.succeed(null) : encode(parameter.value, parameter.oid, registry);
1285
+ const writeValue = (sink, value, oid, lookup) => {
1306
1286
  const codec = lookup(oid);
1307
1287
  if (codec === undefined) return fail(`No codec registered for OID ${oid}`);
1308
1288
  if (codec.write === undefined) sink.raw(codec.encode(value));else codec.write(sink, value);
1309
1289
  };
1290
+ /**
1291
+ * Returns whether a parameter uses the text format in a `Bind` message.
1292
+ * Untyped parameters (OID `0`) use text so PostgreSQL can infer their type;
1293
+ * typed parameters use the binary format.
1294
+ *
1295
+ * @category encoding
1296
+ * @since 4.0.0
1297
+ */
1298
+ export const isTextFormat = parameter => parameter.oid === 0;
1310
1299
  /**
1311
1300
  * Writes a parameter into a `Bind` frame, for `PgProtocol.makeBindEncoder`.
1312
1301
  * Codecs that can write their bytes in place do; the rest fall back to
@@ -1315,10 +1304,19 @@ const writeValue = (sink, value, oid) => {
1315
1304
  * @category encoding
1316
1305
  * @since 4.0.0
1317
1306
  */
1318
- const writeParameterUnsafe = (sink, parameter) => parameter.value === null ? sink.sqlNull() : writeValue(sink, parameter.value, parameter.oid);
1319
- export const writeParameter = (sink, parameter) => {
1307
+ const writeParameterUnsafe = (sink, parameter, lookup) => parameter.value === null ? sink.sqlNull()
1308
+ // An untyped parameter is the value's text representation; see
1309
+ // `isTextFormat`.
1310
+ : parameter.oid === 0 ? sink.utf8(String(parameter.value)) : writeValue(sink, parameter.value, parameter.oid, lookup);
1311
+ /**
1312
+ * Writes a parameter into a `Bind` frame.
1313
+ *
1314
+ * @category encoding
1315
+ * @since 4.0.0
1316
+ */
1317
+ export const writeParameter = (sink, parameter, registry) => {
1320
1318
  try {
1321
- writeParameterUnsafe(sink, parameter);
1319
+ writeParameterUnsafe(sink, parameter, lookupFor(registry));
1322
1320
  return Result.void;
1323
1321
  } catch (error) {
1324
1322
  if (error instanceof CodecError) return Result.fail(error);
@@ -1327,12 +1325,14 @@ export const writeParameter = (sink, parameter) => {
1327
1325
  };
1328
1326
  const valueWriterUnsafe = /*#__PURE__*/Symbol.for("@effect/sql-pg/PgProtocol/ValueWriter/unsafe");
1329
1327
  Object.defineProperty(writeParameter, valueWriterUnsafe, {
1330
- value: writeParameterUnsafe
1328
+ value: (sink, parameter) => writeParameterUnsafe(sink, parameter, lookup)
1331
1329
  });
1332
- const parameter = oid => value => ({
1330
+ const makeParameter = (oid, value) => ({
1331
+ [ParameterTypeId]: ParameterTypeId,
1333
1332
  oid,
1334
1333
  value
1335
1334
  });
1335
+ const parameter = oid => value => makeParameter(oid, value);
1336
1336
  /**
1337
1337
  * A `bool` parameter.
1338
1338
  *
@@ -1501,14 +1501,11 @@ export const timestamptz = /*#__PURE__*/parameter(OID.timestamptz);
1501
1501
  * @category constructors
1502
1502
  * @since 4.0.0
1503
1503
  */
1504
- export const array = (values, elementOid) => result(() => {
1505
- const arrayOid = elementToArray.get(elementOid);
1504
+ export const array = (values, elementOid, registry) => result(() => {
1505
+ const arrayOid = arrayOidFor(elementOid, registry);
1506
1506
  if (arrayOid === undefined) {
1507
1507
  return fail(`No array type known for element OID ${elementOid}`);
1508
1508
  }
1509
- return {
1510
- oid: arrayOid,
1511
- value: values
1512
- };
1509
+ return makeParameter(arrayOid, values);
1513
1510
  });
1514
1511
  //# sourceMappingURL=PgTypes.js.map