@amritk/nish-aarch64-darwin 0.26.0 → 0.27.2

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.
@@ -81,7 +81,11 @@
81
81
  * A `text` column is the same bytes in either format, so `columnText` and the
82
82
  * column windows read it unchanged. The program knows which of its binds
83
83
  * asked for which, and reads each execution's rows with the matching
84
- * decoders.
84
+ * decoders. A `PgBatcher` with `binary` set opens each batch this way and
85
+ * reads its routing ordinal with `binaryInt`, so its rows are binary too.
86
+ * `PgBinaryArray` walks a one-dimensional `int2[]`, `int4[]` or `int8[]`
87
+ * column in its binary layout, and refuses any other array (a `text[]` or a
88
+ * multi-dimensional one among them).
85
89
  *
86
90
  * **Authentication.** SCRAM-SHA-256 (RFC 5802, RFC 7677) or none (`trust`).
87
91
  * The password arrives as a `Secret<u8[]>`; the connection keeps a copy in a
@@ -237,6 +241,15 @@ const PG_TAIL: i32 = 17
237
241
  const PG_BINARY_TAIL: i32 = 2
238
242
  // The result format code for binary.
239
243
  const PG_FORMAT_BINARY: i32 = 1
244
+ // The element types `PgBinaryArray` reads, by OID: int8, int2 and int4.
245
+ const PG_OID_INT8: i32 = 20
246
+ const PG_OID_INT2: i32 = 21
247
+ const PG_OID_INT4: i32 = 23
248
+ // The bytes a binary array's header takes before its dimensions: ndim,
249
+ // hasnull and the element OID, one i32 each; one dimension adds its length
250
+ // and lower bound, another two i32s.
251
+ const PG_ARRAY_HEADER: i32 = 12
252
+ const PG_ARRAY_DIM: i32 = 8
240
253
  // The largest `maxMessage` and `maxOutput`. The input buffer doubles up to one
241
254
  // past them; the output buffer doubles only while twice its size fits in an i32.
242
255
  const PG_MAX_CAP: i32 = 1073741823
@@ -858,6 +871,139 @@ export class PgBinary {
858
871
  }
859
872
  }
860
873
 
874
+ /**
875
+ * A walk over a one-dimensional `int2[]`, `int4[]` or `int8[]` column in
876
+ * PostgreSQL's binary array layout, in place: `begin` the column, then
877
+ * `next()` until `PG_ARRAY_END` or `PG_ARRAY_BAD`, and `int()` decodes the
878
+ * current element through `binary`. The header is `ndim`, `hasnull` and the
879
+ * element OID, then for `ndim` 1 the dimension's length and lower bound (the
880
+ * bound is read and not used); each element is a length word and its bytes,
881
+ * and a NULL is a length of -1 with no bytes. `ndim` 0 is the empty array.
882
+ * Every refusal sets `binary.failed`: an `ndim` above 1, another element
883
+ * type, an element whose length is not its type's size, a NULL when
884
+ * `hasnull` is 0, a column cut short, or bytes after the last element. The
885
+ * walk allocates nothing, and it reads nothing outside the column.
886
+ */
887
+ export class PgBinaryArray {
888
+ /** The number of elements the header gives: 0 for an empty array. */
889
+ count: i32 = 0
890
+ /** The element type's OID, 20, 21 or 23, once `begin` has read a header it accepts. */
891
+ elementOid: i32 = 0
892
+ /** The current element's first byte in the buffer. */
893
+ start: i32 = 0
894
+ /** The current element's length: its type's size, or 0 for a NULL. */
895
+ length: i32 = 0
896
+ private binary: PgBinary
897
+ private b: u8[]
898
+ private pos: i32 = 0
899
+ private end: i32 = 0
900
+ private size: i32 = 0
901
+ private seen: i32 = 0
902
+ private hasNull: boolean = false
903
+ private bad: boolean = true
904
+
905
+ constructor(binary: PgBinary) {
906
+ this.binary = binary
907
+ this.b = []
908
+ }
909
+
910
+ /** Starts a walk over `b[start, start + length)`; a header this walker does not read is refused. */
911
+ begin(b: u8[], start: i32, length: i32): void {
912
+ this.b = b
913
+ this.count = 0
914
+ this.elementOid = 0
915
+ this.seen = 0
916
+ this.start = 0
917
+ this.length = 0
918
+ this.bad = !(start >= 0 && length >= PG_ARRAY_HEADER && start <= toI32(b.length) - length)
919
+ if (this.bad) {
920
+ this.refuse()
921
+ return
922
+ }
923
+ const ndim: i32 = getI32BE(b, start)
924
+ const hasNull: i32 = getI32BE(b, start + 4)
925
+ const oid: i32 = getI32BE(b, start + 8)
926
+ this.size = 0
927
+ if (oid === PG_OID_INT8) {
928
+ this.size = 8
929
+ } else if (oid === PG_OID_INT2) {
930
+ this.size = 2
931
+ } else if (oid === PG_OID_INT4) {
932
+ this.size = 4
933
+ }
934
+ this.bad = (ndim !== 0 && ndim !== 1) || (hasNull !== 0 && hasNull !== 1) || this.size === 0
935
+ if (ndim === 1 && !this.bad) {
936
+ this.bad = length < PG_ARRAY_HEADER + PG_ARRAY_DIM
937
+ }
938
+ if (this.bad) {
939
+ this.refuse()
940
+ return
941
+ }
942
+ this.elementOid = oid
943
+ this.hasNull = hasNull === 1
944
+ this.pos = start + PG_ARRAY_HEADER
945
+ this.end = start + length
946
+ if (ndim === 1) {
947
+ const dim: i32 = getI32BE(b, start + PG_ARRAY_HEADER)
948
+ if (dim < 0) {
949
+ this.refuse()
950
+ return
951
+ }
952
+ this.count = dim
953
+ this.pos += PG_ARRAY_DIM
954
+ }
955
+ }
956
+
957
+ private refuse(): i32 {
958
+ this.bad = true
959
+ this.binary.failed = true
960
+ return PG_ARRAY_BAD
961
+ }
962
+
963
+ /** The next element: `PG_ARRAY_ELEMENT`, `PG_ARRAY_NULL`, `PG_ARRAY_END` or `PG_ARRAY_BAD`. */
964
+ next(): i32 {
965
+ if (this.bad) {
966
+ return PG_ARRAY_BAD
967
+ }
968
+ if (this.seen === this.count) {
969
+ if (this.pos === this.end) {
970
+ return PG_ARRAY_END
971
+ }
972
+ return this.refuse()
973
+ }
974
+ if (this.end - this.pos < 4) {
975
+ return this.refuse()
976
+ }
977
+ const n: i32 = getI32BE(this.b, this.pos)
978
+ this.pos += 4
979
+ this.seen += 1
980
+ if (n === -1) {
981
+ if (!this.hasNull) {
982
+ return this.refuse()
983
+ }
984
+ this.start = this.pos
985
+ this.length = 0
986
+ return PG_ARRAY_NULL
987
+ }
988
+ if (n !== this.size || this.end - this.pos < n) {
989
+ return this.refuse()
990
+ }
991
+ this.start = this.pos
992
+ this.length = n
993
+ this.pos += n
994
+ return PG_ARRAY_ELEMENT
995
+ }
996
+
997
+ /** The current element as an integer, through `PgBinary.int`; a NULL, or a walk that was refused, sets `binary.failed`. */
998
+ int(): i64 {
999
+ if (this.bad) {
1000
+ this.binary.failed = true
1001
+ return 0
1002
+ }
1003
+ return this.binary.int(this.b, this.start, this.length)
1004
+ }
1005
+ }
1006
+
861
1007
  /**
862
1008
  * A walk over a one-dimensional `text[]` column in place: `start` the
863
1009
  * column, then `next()` until `PG_ARRAY_END` or `PG_ARRAY_BAD`. Each element
@@ -1935,10 +2081,13 @@ export class PgConnection {
1935
2081
  * format rather than text: fixed-size integers, booleans, floats and
1936
2082
  * times, read in place by `binaryInt`, `binaryBool`, `binaryFloat` and
1937
2083
  * `binary.timestamp`. A `text` column's bytes are the same in either
1938
- * format, so `columnText` and the column windows read it as before; a
1939
- * `text[]` or `numeric` column comes in PostgreSQL's binary layout, which
1940
- * no decoder here reads. Its `bindEnd()` writes two bytes more, kept free
1941
- * under `maxOutput` from here on. False as `prepare` is.
2084
+ * format, so `columnText` and the column windows read it as before. An
2085
+ * `int2[]`, `int4[]` or `int8[]` column comes in PostgreSQL's binary layout,
2086
+ * which `PgBinaryArray` reads; a `text[]` or `numeric` column comes in that
2087
+ * layout too, and no decoder here reads it. `PgBatcher` opens its batches
2088
+ * with this when its `binary` is set, and reads their routing ordinals with
2089
+ * `binaryInt`. Its `bindEnd()` writes two bytes more, kept free under
2090
+ * `maxOutput` from here on. False as `prepare` is.
1942
2091
  */
1943
2092
  bindStartBinary(name: string): boolean {
1944
2093
  return this.openBind(name, true)
@@ -2863,6 +3012,8 @@ export class PgBatchConfig {
2863
3012
  queueBytes: i32 = 65536
2864
3013
  /** The most executions in flight at once. */
2865
3014
  runs: i32 = 16
3015
+ /** Whether every row of a batch comes in binary format, read with the binary decoders and its ordinal by `binaryInt`; false for text. */
3016
+ binary: boolean = false
2866
3017
  }
2867
3018
 
2868
3019
  /** One execution's requests, grouped by key, and how far its rows have been routed. */
@@ -2913,7 +3064,10 @@ class PgBatchRun {
2913
3064
  * connection to `fail(run)`; after each, `next(run)` answers events until
2914
3065
  * `PG_BATCH_NONE`, `tag` naming the request each is about. Every row goes to
2915
3066
  * every request for its key, and every request gets one last event:
2916
- * `PG_BATCH_DONE`, `PG_BATCH_MISSING` or `PG_BATCH_FAILED`.
3067
+ * `PG_BATCH_DONE`, `PG_BATCH_MISSING` or `PG_BATCH_FAILED`. A batch with
3068
+ * `binary` set comes in binary format: its rows are read with the binary
3069
+ * decoders, `binaryInt` for each ordinal, and the caller's `binary.failed`
3070
+ * is left as it was.
2917
3071
  *
2918
3072
  * Nothing outlives its execution: a run is free again once the events
2919
3073
  * `end` or `fail` owe are answered, and its handle is then spent. What the
@@ -2960,6 +3114,7 @@ export class PgBatcher {
2960
3114
  private readonly keyType: i32
2961
3115
  private readonly maxRequests: i32
2962
3116
  private readonly maxBytes: i32
3117
+ private readonly binary: boolean
2963
3118
  private storeEnd: i32 = 0
2964
3119
  private head: i32 = 0
2965
3120
  private count: i32 = 0
@@ -2997,6 +3152,7 @@ export class PgBatcher {
2997
3152
  this.keyType = keyType
2998
3153
  this.maxRequests = maxRequests
2999
3154
  this.maxBytes = config.maxBytes
3155
+ this.binary = config.binary
3000
3156
  this.name = config.name
3001
3157
  const type: string = keyType === PG_BATCH_INT ? "int8" : "text"
3002
3158
  const order: string = config.orderBy.length > 0 ? `, ${config.orderBy}` : ""
@@ -3164,7 +3320,7 @@ export class PgBatcher {
3164
3320
  break
3165
3321
  }
3166
3322
  }
3167
- if (r < 0 || !conn.bindStart(this.name)) {
3323
+ if (r < 0 || !(this.binary ? conn.bindStartBinary(this.name) : conn.bindStart(this.name))) {
3168
3324
  return -1
3169
3325
  }
3170
3326
  if (!conn.arrayStart()) {
@@ -3296,7 +3452,14 @@ export class PgBatcher {
3296
3452
  const last: i32 = conn.columns - 1
3297
3453
  let ordinal: i64 = 0
3298
3454
  let bad: boolean = last < 0 || conn.isNull(last)
3299
- if (!bad) {
3455
+ if (!bad && this.binary) {
3456
+ // Read without touching `binary.failed`, which belongs to the program's own decoding.
3457
+ const failed: boolean = conn.binary.failed
3458
+ conn.binary.failed = false
3459
+ ordinal = conn.binaryInt(last)
3460
+ bad = conn.binary.failed
3461
+ conn.binary.failed = failed
3462
+ } else if (!bad) {
3300
3463
  // Read without touching `text.failed`, which belongs to the program's own decoding.
3301
3464
  const failed: boolean = conn.text.failed
3302
3465
  conn.text.failed = false