@amritk/nish-aarch64-darwin 0.35.0 → 0.36.0
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.
- package/INSTALL.md +6 -1
- package/bin/nish +0 -0
- package/docs/AI.md +9 -2
- package/package.json +1 -1
- package/runtime/nish.d.ts +4612 -430
- package/runtime/vite.d.ts +30 -0
- package/runtime/vite.mjs +150 -0
- package/scripts/build.sh +108 -13
- package/std/base64.ts +34 -5
- package/std/bytes.ts +256 -40
- package/std/cli-args.ts +296 -70
- package/std/cli-flags.ts +184 -19
- package/std/cli.ts +475 -54
- package/std/collections.ts +121 -23
- package/std/compress/checksum.ts +44 -14
- package/std/compress/deflate.ts +224 -111
- package/std/compress/gzip.ts +217 -53
- package/std/compress/zlib.ts +63 -7
- package/std/crypto/aes.ts +80 -9
- package/std/crypto/base64url.ts +22 -0
- package/std/crypto/bcrypt-batch.ts +88 -21
- package/std/crypto/bcrypt.ts +60 -0
- package/std/crypto/chacha20poly1305.ts +53 -0
- package/std/crypto/ct.ts +32 -13
- package/std/crypto/ed25519.ts +47 -0
- package/std/crypto/hkdf.ts +198 -13
- package/std/crypto/hmac.ts +235 -31
- package/std/crypto/jwt.ts +192 -63
- package/std/crypto/p256.ts +159 -16
- package/std/crypto/p384.ts +28 -3
- package/std/crypto/pki-roots.ts +32 -2
- package/std/crypto/pki.ts +81 -15
- package/std/crypto/rsa.ts +33 -3
- package/std/crypto/sha1.ts +3 -0
- package/std/crypto/sha256.ts +24 -1
- package/std/crypto/sha512.ts +99 -14
- package/std/crypto/x25519.ts +152 -14
- package/std/crypto/x509-random.ts +10 -0
- package/std/crypto/x509.ts +67 -7
- package/std/json-equal.ts +19 -0
- package/std/json-format.ts +112 -13
- package/std/json-number.ts +64 -14
- package/std/json-pointer.ts +23 -1
- package/std/json-tokenizer.ts +100 -29
- package/std/json-typed.ts +713 -66
- package/std/json-value.ts +400 -124
- package/std/json.ts +138 -10
- package/std/map.ts +25 -7
- package/std/math.ts +36 -4
- package/std/net/buffer-pool.ts +42 -11
- package/std/net/dns-config.ts +109 -23
- package/std/net/dns-message.ts +62 -7
- package/std/net/dns-resolver.ts +247 -53
- package/std/net/dns.ts +203 -35
- package/std/net/fetch.ts +151 -61
- package/std/net/handler.ts +278 -80
- package/std/net/hpack.ts +262 -52
- package/std/net/http-compress.ts +125 -15
- package/std/net/http-fields.ts +85 -9
- package/std/net/http1-client-codec.ts +100 -19
- package/std/net/http1-codec.ts +235 -27
- package/std/net/http1-server.ts +343 -85
- package/std/net/http2-frame.ts +272 -35
- package/std/net/http2-tls.ts +135 -34
- package/std/net/http2.ts +247 -106
- package/std/net/http3-frame.ts +154 -17
- package/std/net/http3-server.ts +246 -96
- package/std/net/http3.ts +307 -115
- package/std/net/outbound.ts +37 -3
- package/std/net/postgres-carrier.ts +144 -8
- package/std/net/postgres.ts +723 -103
- package/std/net/prefork.ts +164 -31
- package/std/net/qpack.ts +105 -18
- package/std/net/quic-conn-ack.ts +59 -12
- package/std/net/quic-conn-cid.ts +124 -27
- package/std/net/quic-conn-params.ts +70 -10
- package/std/net/quic-datagram.ts +43 -3
- package/std/net/quic-frame.ts +286 -12
- package/std/net/quic-listener.ts +101 -32
- package/std/net/quic-packet.ts +302 -21
- package/std/net/quic-recovery.ts +228 -44
- package/std/net/quic-stream.ts +353 -91
- package/std/net/quic.ts +489 -171
- package/std/net/router.ts +87 -40
- package/std/net/tls/client.ts +166 -51
- package/std/net/tls/codec.ts +413 -56
- package/std/net/tls/record-client.ts +110 -55
- package/std/net/tls/record-server.ts +159 -59
- package/std/net/tls/record.ts +109 -12
- package/std/net/tls/schedule.ts +235 -26
- package/std/net/tls/ticket.ts +159 -40
- package/std/net/tls/verify.ts +49 -3
- package/std/net/tls-tcp.ts +250 -45
- package/std/net/tls.ts +221 -59
- package/std/net/upstream.ts +81 -45
- package/std/net/url.ts +97 -13
- package/std/net/websocket.ts +122 -12
- package/std/net/webtransport.ts +220 -89
- package/std/pair.ts +10 -2
- package/std/random.ts +34 -9
- package/std/regex.ts +352 -198
- package/std/region.ts +70 -15
- package/std/secret.ts +52 -11
- package/std/sort.ts +17 -2
- package/std/sqlite.ts +256 -35
- package/std/sync/ring.ts +205 -35
- package/std/sync/shm.ts +194 -16
- package/std/term-io.ts +137 -64
- package/std/term-pixels.ts +144 -19
- package/std/term-probe.ts +66 -25
- package/std/term-rate.ts +128 -57
- package/std/term.ts +332 -121
- package/std/testing.ts +130 -32
- package/std/text.ts +73 -7
- package/std/threads.ts +229 -41
- package/std/yaml-json.ts +94 -82
- package/std/yaml-lines.ts +47 -15
- package/std/yaml-write.ts +24 -16
- package/std/yaml.ts +764 -384
package/std/net/postgres.ts
CHANGED
|
@@ -4,12 +4,17 @@
|
|
|
4
4
|
* messages into an output buffer and reads the backend's from an input
|
|
5
5
|
* buffer, for a program that owns its socket (`nish:net`) and its loop.
|
|
6
6
|
*
|
|
7
|
+
* import { PG_FAILED, PG_NEED_MORE, PgConfig, PgConnection } from "nish/net/postgres";
|
|
8
|
+
* import { secret, wipe } from "nish:secret";
|
|
9
|
+
*
|
|
7
10
|
* const config = new PgConfig();
|
|
8
11
|
* config.user = "app";
|
|
9
12
|
* config.database = "app";
|
|
10
13
|
* const nonce = new Array<u8>(18);
|
|
11
14
|
* crypto.getRandomValues(nonce);
|
|
15
|
+
* const password = secret(passwordBytes); // a u8[] the program read from its secret store
|
|
12
16
|
* const conn = new PgConnection(config, password, nonce);
|
|
17
|
+
* wipe(password); // the connection keeps its own copy
|
|
13
18
|
* // write conn.outputBuffer()[conn.outputStart() .. conn.outputEnd()), then conn.sent(n)
|
|
14
19
|
* // read into conn.inputBuffer() at conn.inputEnd(), up to conn.inputRoom() bytes, then conn.received(n)
|
|
15
20
|
* let event: i32 = conn.next();
|
|
@@ -334,7 +339,11 @@ export class PgConfig {
|
|
|
334
339
|
|
|
335
340
|
/** A server's ErrorResponse or NoticeResponse: the fields a program acts on. */
|
|
336
341
|
export class PgError {
|
|
337
|
-
/**
|
|
342
|
+
/**
|
|
343
|
+
* ERROR, FATAL or PANIC; WARNING, NOTICE, DEBUG, INFO or LOG for a notice.
|
|
344
|
+
* Never localised, unless the server sent no `V` field (before 9.6) and the
|
|
345
|
+
* localised `S` field stands in for it.
|
|
346
|
+
*/
|
|
338
347
|
readonly severity: string
|
|
339
348
|
/** The SQLSTATE, five characters: `23505` is a unique violation. */
|
|
340
349
|
readonly code: string
|
|
@@ -349,6 +358,19 @@ export class PgError {
|
|
|
349
358
|
/** The 1-based character position in the statement's text, or "". */
|
|
350
359
|
readonly position: string
|
|
351
360
|
|
|
361
|
+
/**
|
|
362
|
+
* Builds the record from the fields of one ErrorResponse or
|
|
363
|
+
* NoticeResponse; a program reads it from `lastError` or `lastNotice`
|
|
364
|
+
* rather than building one.
|
|
365
|
+
* @param severity The `V` field, never localised; the `S` field, which may be
|
|
366
|
+
* localised, when the server sent no `V` (servers before 9.6).
|
|
367
|
+
* @param code The SQLSTATE, the `C` field.
|
|
368
|
+
* @param message The primary message, the `M` field.
|
|
369
|
+
* @param detail The `D` field, or "" when the server sent none.
|
|
370
|
+
* @param hint The `H` field, or "".
|
|
371
|
+
* @param constraint The `n` field, or "".
|
|
372
|
+
* @param position The `P` field, a 1-based character position as text, or "".
|
|
373
|
+
*/
|
|
352
374
|
constructor(
|
|
353
375
|
severity: string,
|
|
354
376
|
code: string,
|
|
@@ -390,19 +412,25 @@ const pgAllZero = (bytes: u8[]): boolean => {
|
|
|
390
412
|
* The holder outlives the wipe, so that `wiped()` reads the very bytes.
|
|
391
413
|
*/
|
|
392
414
|
class PgPassword {
|
|
415
|
+
/** @internal Used by `PgConnection.scramContinue` and `PgPool.connect` in this module. */
|
|
393
416
|
readonly bytes: Secret<u8[]>
|
|
394
|
-
/**
|
|
417
|
+
/**
|
|
418
|
+
* Whether `wipe()` has run; the bytes are no longer a password.
|
|
419
|
+
* @internal Used by `PgConnection.dispose`, `PgConnection.scramContinue`, `PgPool.connect` and `PgPool.dispose` in this module.
|
|
420
|
+
*/
|
|
395
421
|
gone: boolean = false
|
|
396
422
|
|
|
397
423
|
constructor(password: Secret<u8[]>) {
|
|
398
424
|
this.bytes = secret(expose(password, pgCopyBytes))
|
|
399
425
|
}
|
|
400
426
|
|
|
427
|
+
/** @internal Used by `PgConnection.dispose` and `PgPool.dispose` in this module. */
|
|
401
428
|
wipe(): void {
|
|
402
429
|
wipe(this.bytes)
|
|
403
430
|
this.gone = true
|
|
404
431
|
}
|
|
405
432
|
|
|
433
|
+
/** @internal Used by `PgConnection.passwordWiped` and `PgPool.passwordWiped` in this module. */
|
|
406
434
|
wiped(): boolean {
|
|
407
435
|
return expose(this.bytes, pgAllZero)
|
|
408
436
|
}
|
|
@@ -669,7 +697,15 @@ export class PgText {
|
|
|
669
697
|
this.failed = false
|
|
670
698
|
}
|
|
671
699
|
|
|
672
|
-
/**
|
|
700
|
+
/**
|
|
701
|
+
* An `int2`, `int4` or `int8` column: an optional `-` and its digits, in
|
|
702
|
+
* place. Leading zeros are allowed; a value outside `i64` is an overflow.
|
|
703
|
+
* @param b The buffer the column lies in, usually `conn.inputBuffer()`.
|
|
704
|
+
* @param start The column's first byte, as `colStart(c)` gives it.
|
|
705
|
+
* @param length The column's length in bytes; NULL's -1 is refused.
|
|
706
|
+
* @returns The value, or 0 with `failed` set for an empty or out-of-range
|
|
707
|
+
* window, a byte that is not a digit, or an overflow.
|
|
708
|
+
*/
|
|
673
709
|
int(b: u8[], start: i32, length: i32): i64 {
|
|
674
710
|
const end: i32 = start + length
|
|
675
711
|
if (length <= 0 || start < 0 || end < start || end > toI32(b.length)) {
|
|
@@ -707,7 +743,13 @@ export class PgText {
|
|
|
707
743
|
return negative ? acc : -acc
|
|
708
744
|
}
|
|
709
745
|
|
|
710
|
-
/**
|
|
746
|
+
/**
|
|
747
|
+
* A `bool` column: `t` or `f`, in place.
|
|
748
|
+
* @param b The buffer the column lies in.
|
|
749
|
+
* @param start The column's first byte.
|
|
750
|
+
* @param length The column's length in bytes; anything but 1 is refused.
|
|
751
|
+
* @returns True for `t`, false for `f`; false with `failed` set otherwise.
|
|
752
|
+
*/
|
|
711
753
|
bool(b: u8[], start: i32, length: i32): boolean {
|
|
712
754
|
if (length === 1 && start >= 0 && start < toI32(b.length)) {
|
|
713
755
|
const c: i32 = toI32(b[start])
|
|
@@ -728,7 +770,14 @@ export class PgText {
|
|
|
728
770
|
* `timestamp` column (the same without `+00`), written to `out[at, at + 24)`
|
|
729
771
|
* as `2024-01-02T03:04:05.123Z`, the fraction cut to milliseconds. Answers
|
|
730
772
|
* 24, or -1 and `failed` for anything else: a year outside 0001–9999, BC,
|
|
731
|
-
* `infinity`, an offset other than `+00`.
|
|
773
|
+
* `infinity`, an offset other than `+00`. Nothing is allocated.
|
|
774
|
+
* @param b The buffer the column lies in.
|
|
775
|
+
* @param start The column's first byte.
|
|
776
|
+
* @param length The column's length in bytes, at least 19.
|
|
777
|
+
* @param out The buffer to write the 24 ISO 8601 bytes into; the caller owns it.
|
|
778
|
+
* @param at Where in `out` the time starts; `out` needs 24 bytes from there.
|
|
779
|
+
* @returns 24, the bytes written; -1 with `failed` set, and `out` untouched,
|
|
780
|
+
* for a column it does not read or no room in `out`.
|
|
732
781
|
*/
|
|
733
782
|
timestamp(b: u8[], start: i32, length: i32, out: u8[], at: i32): i32 {
|
|
734
783
|
if (
|
|
@@ -854,6 +903,10 @@ export class PgBinary {
|
|
|
854
903
|
* bytes, big-endian two's complement); 0 and `failed` for any other length,
|
|
855
904
|
* NULL's -1 among them. A `timestamptz` or `timestamp` reads as an `int8`:
|
|
856
905
|
* microseconds since 2000-01-01 00:00:00 UTC, which `iso` writes out.
|
|
906
|
+
* @param b The buffer the column lies in, usually `conn.inputBuffer()`.
|
|
907
|
+
* @param start The column's first byte, as `colStart(c)` gives it.
|
|
908
|
+
* @param length The column's length in bytes: 2, 4 or 8.
|
|
909
|
+
* @returns The value, sign-extended to 64 bits, or 0 with `failed` set.
|
|
857
910
|
*/
|
|
858
911
|
int(b: u8[], start: i32, length: i32): i64 {
|
|
859
912
|
if ((length !== 2 && length !== 4 && length !== 8) || !this.inside(b, start, length)) {
|
|
@@ -870,7 +923,13 @@ export class PgBinary {
|
|
|
870
923
|
return toI64(v >= 32768 ? v - 65536 : v)
|
|
871
924
|
}
|
|
872
925
|
|
|
873
|
-
/**
|
|
926
|
+
/**
|
|
927
|
+
* A `bool` column: one byte, 1 or 0.
|
|
928
|
+
* @param b The buffer the column lies in.
|
|
929
|
+
* @param start The column's first byte.
|
|
930
|
+
* @param length The column's length in bytes; anything but 1 is refused.
|
|
931
|
+
* @returns True for 1, false for 0; false with `failed` set otherwise.
|
|
932
|
+
*/
|
|
874
933
|
bool(b: u8[], start: i32, length: i32): boolean {
|
|
875
934
|
if (length === 1 && this.inside(b, start, length)) {
|
|
876
935
|
const c: i32 = toI32(b[start])
|
|
@@ -885,7 +944,15 @@ export class PgBinary {
|
|
|
885
944
|
return false
|
|
886
945
|
}
|
|
887
946
|
|
|
888
|
-
/**
|
|
947
|
+
/**
|
|
948
|
+
* A `float8` column: eight bytes, IEEE 754 big-endian. A `float4` (four
|
|
949
|
+
* bytes) is refused.
|
|
950
|
+
* @param b The buffer the column lies in.
|
|
951
|
+
* @param start The column's first byte.
|
|
952
|
+
* @param length The column's length in bytes; anything but 8 is refused.
|
|
953
|
+
* @returns The value, NaN and the infinities included; 0 with `failed` set
|
|
954
|
+
* for a column that is not eight bytes inside `b`.
|
|
955
|
+
*/
|
|
889
956
|
float(b: u8[], start: i32, length: i32): f64 {
|
|
890
957
|
if (length !== 8 || !this.inside(b, start, length)) {
|
|
891
958
|
this.failed = true
|
|
@@ -898,6 +965,12 @@ export class PgBinary {
|
|
|
898
965
|
* A `timestamptz` or `timestamp` column, written to `out[at, at + 24)` as
|
|
899
966
|
* `iso` writes it. Answers 24, or -1 and `failed` for a column that is not
|
|
900
967
|
* eight bytes or a time `iso` refuses.
|
|
968
|
+
* @param b The buffer the column lies in.
|
|
969
|
+
* @param start The column's first byte.
|
|
970
|
+
* @param length The column's length in bytes; anything but 8 is refused.
|
|
971
|
+
* @param out The buffer to write the 24 ISO 8601 bytes into; the caller owns it.
|
|
972
|
+
* @param at Where in `out` the time starts; `out` needs 24 bytes from there.
|
|
973
|
+
* @returns 24, the bytes written, or -1 with `failed` set.
|
|
901
974
|
*/
|
|
902
975
|
timestamp(b: u8[], start: i32, length: i32, out: u8[], at: i32): i32 {
|
|
903
976
|
if (length !== 8 || !this.inside(b, start, length)) {
|
|
@@ -913,6 +986,11 @@ export class PgBinary {
|
|
|
913
986
|
* not rounded, to milliseconds, as `PgText.timestamp` cuts the same time.
|
|
914
987
|
* Answers 24, or -1 and `failed` with `out` untouched for a year outside
|
|
915
988
|
* 0001–9999 (`infinity` and `-infinity` among them) or no room in `out`.
|
|
989
|
+
* Nothing is allocated.
|
|
990
|
+
* @param micros Microseconds since 2000-01-01 00:00:00 UTC; negative is before 2000.
|
|
991
|
+
* @param out The buffer to write into; the caller owns it.
|
|
992
|
+
* @param at Where in `out` the time starts; `out` needs 24 bytes from there.
|
|
993
|
+
* @returns 24, the bytes written, or -1 with `failed` set.
|
|
916
994
|
*/
|
|
917
995
|
iso(micros: i64, out: u8[], at: i32): i32 {
|
|
918
996
|
if (at < 0 || at > toI32(out.length) - 24) {
|
|
@@ -980,9 +1058,9 @@ export class PgBinaryArray {
|
|
|
980
1058
|
/** The element type's OID, 20, 21 or 23, once `begin` has read a header it accepts. */
|
|
981
1059
|
elementOid: i32 = 0
|
|
982
1060
|
/** The current element's first byte in the buffer. */
|
|
983
|
-
start: i32 = 0
|
|
1061
|
+
private start: i32 = 0
|
|
984
1062
|
/** The current element's length: its type's size, or 0 for a NULL. */
|
|
985
|
-
length: i32 = 0
|
|
1063
|
+
private length: i32 = 0
|
|
986
1064
|
private binary: PgBinary
|
|
987
1065
|
private b: u8[]
|
|
988
1066
|
private pos: i32 = 0
|
|
@@ -992,12 +1070,26 @@ export class PgBinaryArray {
|
|
|
992
1070
|
private hasNull: boolean = false
|
|
993
1071
|
private bad: boolean = true
|
|
994
1072
|
|
|
1073
|
+
/**
|
|
1074
|
+
* A walker with nothing begun; `next()` answers `PG_ARRAY_BAD` until `begin`.
|
|
1075
|
+
* @param binary The decoders `int()` reads through and whose `failed` every
|
|
1076
|
+
* refusal sets; usually `conn.binary`.
|
|
1077
|
+
*/
|
|
995
1078
|
constructor(binary: PgBinary) {
|
|
996
1079
|
this.binary = binary
|
|
997
1080
|
this.b = []
|
|
998
1081
|
}
|
|
999
1082
|
|
|
1000
|
-
/**
|
|
1083
|
+
/**
|
|
1084
|
+
* Starts a walk over `b[start, start + length)`, reading its header; a
|
|
1085
|
+
* header this walker does not read is refused, which sets `binary.failed`
|
|
1086
|
+
* and makes `next()` answer `PG_ARRAY_BAD`. The walker keeps `b` and reads
|
|
1087
|
+
* it until the next `begin`, so the window must stay valid that long: for a
|
|
1088
|
+
* column, until the connection's next `next()`.
|
|
1089
|
+
* @param b The buffer the column lies in, usually `conn.inputBuffer()`.
|
|
1090
|
+
* @param start The column's first byte, as `colStart(c)` gives it.
|
|
1091
|
+
* @param length The column's length in bytes; NULL's -1 is refused.
|
|
1092
|
+
*/
|
|
1001
1093
|
begin(b: u8[], start: i32, length: i32): void {
|
|
1002
1094
|
this.b = b
|
|
1003
1095
|
this.count = 0
|
|
@@ -1050,7 +1142,13 @@ export class PgBinaryArray {
|
|
|
1050
1142
|
return PG_ARRAY_BAD
|
|
1051
1143
|
}
|
|
1052
1144
|
|
|
1053
|
-
/**
|
|
1145
|
+
/**
|
|
1146
|
+
* Steps to the next element, which `int()` then reads.
|
|
1147
|
+
* @returns `PG_ARRAY_ELEMENT`, `PG_ARRAY_NULL`, `PG_ARRAY_END` once all
|
|
1148
|
+
* `count` elements have been read and the column ends there, or
|
|
1149
|
+
* `PG_ARRAY_BAD` (with `binary.failed` set) for a layout it refuses; a
|
|
1150
|
+
* refused walk answers `PG_ARRAY_BAD` from then on.
|
|
1151
|
+
*/
|
|
1054
1152
|
next(): i32 {
|
|
1055
1153
|
if (this.bad) {
|
|
1056
1154
|
return PG_ARRAY_BAD
|
|
@@ -1084,7 +1182,13 @@ export class PgBinaryArray {
|
|
|
1084
1182
|
return PG_ARRAY_ELEMENT
|
|
1085
1183
|
}
|
|
1086
1184
|
|
|
1087
|
-
/**
|
|
1185
|
+
/**
|
|
1186
|
+
* The current element as an integer, through `PgBinary.int`; a NULL, or a
|
|
1187
|
+
* walk that was refused, sets `binary.failed`.
|
|
1188
|
+
* @returns The element, sign-extended to 64 bits, or 0 when this call is
|
|
1189
|
+
* refused, which sets `binary.failed`. The flag is sticky and shared, so
|
|
1190
|
+
* an earlier refusal leaves it set without making this call answer 0.
|
|
1191
|
+
*/
|
|
1088
1192
|
int(): i64 {
|
|
1089
1193
|
if (this.bad) {
|
|
1090
1194
|
this.binary.failed = true
|
|
@@ -1095,7 +1199,7 @@ export class PgBinaryArray {
|
|
|
1095
1199
|
}
|
|
1096
1200
|
|
|
1097
1201
|
/**
|
|
1098
|
-
* A walk over a one-dimensional `text[]` column in place: `
|
|
1202
|
+
* A walk over a one-dimensional `text[]` column in place: `begin` the
|
|
1099
1203
|
* column, then `next()` until `PG_ARRAY_END` or `PG_ARRAY_BAD`. Each element
|
|
1100
1204
|
* is the window `[start, start + length)` of the column's buffer; a quoted
|
|
1101
1205
|
* element with a backslash escape in it has `escaped` set, and `copy` writes
|
|
@@ -1115,11 +1219,20 @@ export class PgArrayWalker {
|
|
|
1115
1219
|
private bad: boolean = false
|
|
1116
1220
|
private b: u8[]
|
|
1117
1221
|
|
|
1222
|
+
/** A walker with nothing begun; `next()` answers `PG_ARRAY_END` until `begin`. */
|
|
1118
1223
|
constructor() {
|
|
1119
1224
|
this.b = []
|
|
1120
1225
|
}
|
|
1121
1226
|
|
|
1122
|
-
/**
|
|
1227
|
+
/**
|
|
1228
|
+
* Starts a walk over `b[start, start + length)`, which must be a `{...}`
|
|
1229
|
+
* literal; anything else makes `next()` answer `PG_ARRAY_BAD`. The walker
|
|
1230
|
+
* keeps `b` and reads it until the next `begin`, so for a column the walk
|
|
1231
|
+
* must end before the connection's next `next()`.
|
|
1232
|
+
* @param b The buffer the column lies in, usually `conn.inputBuffer()`.
|
|
1233
|
+
* @param start The column's first byte, as `colStart(c)` gives it.
|
|
1234
|
+
* @param length The column's length in bytes, braces included.
|
|
1235
|
+
*/
|
|
1123
1236
|
begin(b: u8[], start: i32, length: i32): void {
|
|
1124
1237
|
this.b = b
|
|
1125
1238
|
this.first = true
|
|
@@ -1136,7 +1249,12 @@ export class PgArrayWalker {
|
|
|
1136
1249
|
return PG_ARRAY_BAD
|
|
1137
1250
|
}
|
|
1138
1251
|
|
|
1139
|
-
/**
|
|
1252
|
+
/**
|
|
1253
|
+
* Steps to the next element, setting `start`, `length` and `escaped`.
|
|
1254
|
+
* @returns `PG_ARRAY_ELEMENT`, `PG_ARRAY_NULL`, `PG_ARRAY_END` or
|
|
1255
|
+
* `PG_ARRAY_BAD`; once `PG_ARRAY_BAD`, the walk answers it until the next
|
|
1256
|
+
* `begin`.
|
|
1257
|
+
*/
|
|
1140
1258
|
next(): i32 {
|
|
1141
1259
|
if (this.bad) {
|
|
1142
1260
|
return PG_ARRAY_BAD
|
|
@@ -1210,7 +1328,14 @@ export class PgArrayWalker {
|
|
|
1210
1328
|
return PG_ARRAY_ELEMENT
|
|
1211
1329
|
}
|
|
1212
1330
|
|
|
1213
|
-
/**
|
|
1331
|
+
/**
|
|
1332
|
+
* Writes the current element, unescaped (each backslash dropped and the
|
|
1333
|
+
* byte after it kept), to `out[at, ...)`. Nothing is allocated.
|
|
1334
|
+
* @param out The buffer to write into; the caller owns it.
|
|
1335
|
+
* @param at Where in `out` the element starts.
|
|
1336
|
+
* @returns The bytes written, at most `length`; -1 when it does not fit,
|
|
1337
|
+
* in which case `out` may hold part of it.
|
|
1338
|
+
*/
|
|
1214
1339
|
copy(out: u8[], at: i32): i32 {
|
|
1215
1340
|
const b: u8[] = this.b
|
|
1216
1341
|
let n: i32 = 0
|
|
@@ -1307,7 +1432,14 @@ export class PgConnection {
|
|
|
1307
1432
|
* A connection that has queued its startup message. `password` is copied,
|
|
1308
1433
|
* so the caller may wipe its own; `nonce` must be at least 18 bytes from a
|
|
1309
1434
|
* cryptographic random source (`crypto.getRandomValues`), and is fixed only
|
|
1310
|
-
* in a test.
|
|
1435
|
+
* in a test. Panics on a `PgConfig` that is a mistake (no user, a cap out
|
|
1436
|
+
* of range, a NUL in a startup parameter) or a nonce shorter than 18 bytes.
|
|
1437
|
+
* @param config The caps and startup parameters; kept, and read for the
|
|
1438
|
+
* connection's life, so do not change it afterwards.
|
|
1439
|
+
* @param password The role's password as UTF-8 bytes, without SASLprep;
|
|
1440
|
+
* copied into a holder the connection wipes once the SCRAM proof is
|
|
1441
|
+
* computed, or when it fails or is disposed. It may be empty for `trust`.
|
|
1442
|
+
* @param nonce The client nonce: its first 18 bytes are used, base64-encoded.
|
|
1311
1443
|
*/
|
|
1312
1444
|
constructor(config: PgConfig, password: Secret<u8[]>, nonce: u8[]) {
|
|
1313
1445
|
if (toI32(nonce.length) < PG_NONCE_BYTES) {
|
|
@@ -1354,22 +1486,38 @@ export class PgConnection {
|
|
|
1354
1486
|
|
|
1355
1487
|
// --- Buffers -----------------------------------------------------------------
|
|
1356
1488
|
|
|
1357
|
-
/**
|
|
1489
|
+
/**
|
|
1490
|
+
* The input buffer: read from the socket into it at `inputEnd()`. The
|
|
1491
|
+
* connection owns it, and `next()` may replace it with a larger one, so
|
|
1492
|
+
* fetch it again after every `next()`.
|
|
1493
|
+
* @returns The buffer itself, not a copy.
|
|
1494
|
+
*/
|
|
1358
1495
|
inputBuffer(): u8[] {
|
|
1359
1496
|
return this.input
|
|
1360
1497
|
}
|
|
1361
1498
|
|
|
1362
|
-
/**
|
|
1499
|
+
/**
|
|
1500
|
+
* Where the next byte read goes.
|
|
1501
|
+
* @returns An index into `inputBuffer()`.
|
|
1502
|
+
*/
|
|
1363
1503
|
inputEnd(): i32 {
|
|
1364
1504
|
return this.inEnd
|
|
1365
1505
|
}
|
|
1366
1506
|
|
|
1367
|
-
/**
|
|
1507
|
+
/**
|
|
1508
|
+
* How many bytes may be read into the input buffer now.
|
|
1509
|
+
* @returns The bytes free from `inputEnd()` to the buffer's end; 0 when it
|
|
1510
|
+
* is full, which `next()` mends by compacting or growing it.
|
|
1511
|
+
*/
|
|
1368
1512
|
inputRoom(): i32 {
|
|
1369
1513
|
return toI32(this.input.length) - this.inEnd
|
|
1370
1514
|
}
|
|
1371
1515
|
|
|
1372
|
-
/**
|
|
1516
|
+
/**
|
|
1517
|
+
* Records that `n` bytes were read into the input buffer at `inputEnd()`.
|
|
1518
|
+
* Panics on a negative `n` or one past `inputRoom()`.
|
|
1519
|
+
* @param n The bytes the read wrote, from 0 to `inputRoom()`.
|
|
1520
|
+
*/
|
|
1373
1521
|
received(n: i32): void {
|
|
1374
1522
|
if (n < 0 || n > this.inputRoom()) {
|
|
1375
1523
|
panic("nish/net/postgres: received more than inputRoom()")
|
|
@@ -1377,7 +1525,16 @@ export class PgConnection {
|
|
|
1377
1525
|
this.inEnd += n
|
|
1378
1526
|
}
|
|
1379
1527
|
|
|
1380
|
-
/**
|
|
1528
|
+
/**
|
|
1529
|
+
* Copies what fits of `src[start, start + length)` into the input buffer at
|
|
1530
|
+
* `inputEnd()`, for a program whose bytes arrive in a buffer of its own.
|
|
1531
|
+
* Panics on a window outside `src`.
|
|
1532
|
+
* @param src The bytes to take; the caller keeps them.
|
|
1533
|
+
* @param start The window's first byte in `src`.
|
|
1534
|
+
* @param length The window's length in bytes.
|
|
1535
|
+
* @returns The bytes copied, at most `inputRoom()`; the rest are the
|
|
1536
|
+
* caller's to give again after `next()`.
|
|
1537
|
+
*/
|
|
1381
1538
|
receive(src: u8[], start: i32, length: i32): i32 {
|
|
1382
1539
|
if (start < 0 || length < 0 || length > toI32(src.length) - start) {
|
|
1383
1540
|
panic("nish/net/postgres: receive() outside its source")
|
|
@@ -1389,22 +1546,39 @@ export class PgConnection {
|
|
|
1389
1546
|
return n
|
|
1390
1547
|
}
|
|
1391
1548
|
|
|
1392
|
-
/**
|
|
1549
|
+
/**
|
|
1550
|
+
* The output buffer: write `[outputStart(), outputEnd())` to the socket.
|
|
1551
|
+
* The connection owns it, and a builder call or `sent` may grow or compact
|
|
1552
|
+
* it, so fetch it again after each.
|
|
1553
|
+
* @returns The buffer itself, not a copy.
|
|
1554
|
+
*/
|
|
1393
1555
|
outputBuffer(): u8[] {
|
|
1394
1556
|
return this.out
|
|
1395
1557
|
}
|
|
1396
1558
|
|
|
1397
|
-
/**
|
|
1559
|
+
/**
|
|
1560
|
+
* The first byte not yet sent.
|
|
1561
|
+
* @returns An index into `outputBuffer()`.
|
|
1562
|
+
*/
|
|
1398
1563
|
outputStart(): i32 {
|
|
1399
1564
|
return this.outStart
|
|
1400
1565
|
}
|
|
1401
1566
|
|
|
1402
|
-
/**
|
|
1567
|
+
/**
|
|
1568
|
+
* One past the last byte that may be sent: a Bind still open is held back
|
|
1569
|
+
* until `bindEnd()` has written its lengths.
|
|
1570
|
+
* @returns An index into `outputBuffer()`; equal to `outputStart()` when
|
|
1571
|
+
* there is nothing to send.
|
|
1572
|
+
*/
|
|
1403
1573
|
outputEnd(): i32 {
|
|
1404
1574
|
return this.bindAt >= 0 ? this.bindAt : this.outEnd
|
|
1405
1575
|
}
|
|
1406
1576
|
|
|
1407
|
-
/**
|
|
1577
|
+
/**
|
|
1578
|
+
* Records that `n` bytes from `outputStart()` were written to the socket.
|
|
1579
|
+
* Panics on a negative `n` or one past `outputEnd() - outputStart()`.
|
|
1580
|
+
* @param n The bytes the write took.
|
|
1581
|
+
*/
|
|
1408
1582
|
sent(n: i32): void {
|
|
1409
1583
|
if (n < 0 || n > this.outputEnd() - this.outStart) {
|
|
1410
1584
|
panic("nish/net/postgres: sent more than was queued")
|
|
@@ -1418,7 +1592,12 @@ export class PgConnection {
|
|
|
1418
1592
|
}
|
|
1419
1593
|
}
|
|
1420
1594
|
|
|
1421
|
-
/**
|
|
1595
|
+
/**
|
|
1596
|
+
* Reports the end of the stream (a read of 0 bytes, or a socket error):
|
|
1597
|
+
* unless it already has, the connection fails with `PG_ERR_CLOSED`, which
|
|
1598
|
+
* wipes the password. Input already received and not yet read is dropped.
|
|
1599
|
+
* @returns `PG_FAILED`, as every later `next()` does.
|
|
1600
|
+
*/
|
|
1422
1601
|
closed(): i32 {
|
|
1423
1602
|
if (this.state !== PG_STATE_FAILED) {
|
|
1424
1603
|
return this.fail(PG_ERR_CLOSED)
|
|
@@ -1431,12 +1610,17 @@ export class PgConnection {
|
|
|
1431
1610
|
* once the proof is computed, the connection has failed or `dispose()` has
|
|
1432
1611
|
* run (and for an empty password). It reads the copy itself, so a test can
|
|
1433
1612
|
* see the wipe happen; a program has no use for it.
|
|
1613
|
+
* @returns True when the copy holds only zero bytes.
|
|
1434
1614
|
*/
|
|
1435
1615
|
passwordWiped(): boolean {
|
|
1436
1616
|
return this.password.wiped()
|
|
1437
1617
|
}
|
|
1438
1618
|
|
|
1439
|
-
/**
|
|
1619
|
+
/**
|
|
1620
|
+
* Wipes the password if the connection still holds it. A program that drops
|
|
1621
|
+
* a connection before it is ready calls this. It does not close anything or
|
|
1622
|
+
* queue a Terminate: that is `terminate()`, and the socket is the program's.
|
|
1623
|
+
*/
|
|
1440
1624
|
dispose(): void {
|
|
1441
1625
|
if (!this.password.gone) {
|
|
1442
1626
|
this.password.wipe()
|
|
@@ -1445,7 +1629,16 @@ export class PgConnection {
|
|
|
1445
1629
|
|
|
1446
1630
|
// --- Reading -----------------------------------------------------------------
|
|
1447
1631
|
|
|
1448
|
-
/**
|
|
1632
|
+
/**
|
|
1633
|
+
* Reads input until it has an event to answer. Each call may move the
|
|
1634
|
+
* unread input to the start of the buffer or replace the buffer, which ends
|
|
1635
|
+
* the windows the last event gave (`colStart`, the tag) and calls for a
|
|
1636
|
+
* fresh `inputBuffer()`.
|
|
1637
|
+
* @returns `PG_NEED_MORE` when it needs more input; `PG_READY`, `PG_ROW`,
|
|
1638
|
+
* `PG_COMPLETE`, `PG_ERROR`, `PG_GROUP_DONE`, `PG_NOTICE` or
|
|
1639
|
+
* `PG_DESCRIBED` (the module header says what each means); or `PG_FAILED`,
|
|
1640
|
+
* final, with `failure` saying why.
|
|
1641
|
+
*/
|
|
1449
1642
|
next(): i32 {
|
|
1450
1643
|
while (true) {
|
|
1451
1644
|
if (this.state === PG_STATE_FAILED) {
|
|
@@ -2106,7 +2299,12 @@ export class PgConnection {
|
|
|
2106
2299
|
setI32BE(this.out, at + 1, this.outEnd - at - 1)
|
|
2107
2300
|
}
|
|
2108
2301
|
|
|
2109
|
-
/**
|
|
2302
|
+
/**
|
|
2303
|
+
* Whether another group may start, or the open one take another message.
|
|
2304
|
+
* @returns True once the connection is ready (after `PG_READY`, until
|
|
2305
|
+
* `PG_FAILED`) and either a group is open or fewer than `maxInFlight`
|
|
2306
|
+
* wait for their answers. Room under `maxOutput` is not part of it.
|
|
2307
|
+
*/
|
|
2110
2308
|
canSend(): boolean {
|
|
2111
2309
|
return this.state === PG_STATE_READY && (this.groupOpen || this.inFlight < this.config.maxInFlight)
|
|
2112
2310
|
}
|
|
@@ -2128,8 +2326,16 @@ export class PgConnection {
|
|
|
2128
2326
|
* unnamed one), its parameters' types given by `paramTypes` (OIDs, 0 to let
|
|
2129
2327
|
* the server infer one, and parameters past the array are inferred too).
|
|
2130
2328
|
* The server answers `PG_DESCRIBED`. False, and nothing written, when
|
|
2131
|
-
* `canSend()` is false, a bind is open, `name` or `sql` holds a NUL,
|
|
2132
|
-
* message would pass `maxOutput`.
|
|
2329
|
+
* `canSend()` is false, a bind is open, `name` or `sql` holds a NUL, more
|
|
2330
|
+
* than 65535 types are given, or the message would pass `maxOutput`. It
|
|
2331
|
+
* opens a group when none is open; `sync()` ends it.
|
|
2332
|
+
* @param name The statement's name; a named statement lasts until
|
|
2333
|
+
* `closeStatement` or the connection's end, the unnamed one until the next
|
|
2334
|
+
* `prepare("")`.
|
|
2335
|
+
* @param sql The statement's text, `$1`, `$2`, ... for its parameters.
|
|
2336
|
+
* @param paramTypes The parameters' type OIDs (23 `int4`, 20 `int8`, 25
|
|
2337
|
+
* `text`), or 0 for one the server infers; read, not kept.
|
|
2338
|
+
* @returns True when the Parse and Describe were queued.
|
|
2133
2339
|
*/
|
|
2134
2340
|
prepare(name: string, sql: string, paramTypes: i32[]): boolean {
|
|
2135
2341
|
const nameLength: i32 = toI32(name.length)
|
|
@@ -2156,7 +2362,13 @@ export class PgConnection {
|
|
|
2156
2362
|
return true
|
|
2157
2363
|
}
|
|
2158
2364
|
|
|
2159
|
-
/**
|
|
2365
|
+
/**
|
|
2366
|
+
* Close: forgets the prepared statement `name` on the server. False, and
|
|
2367
|
+
* nothing written, as `prepare` is. It opens a group when none is open.
|
|
2368
|
+
* @param name The statement to close; closing one that does not exist is
|
|
2369
|
+
* not an error to the server.
|
|
2370
|
+
* @returns True when the Close was queued.
|
|
2371
|
+
*/
|
|
2160
2372
|
closeStatement(name: string): boolean {
|
|
2161
2373
|
if (pgHasNul(name) || toI32(name.length) > PG_MAX_CAP || !this.canOpen(7 + toI32(name.length))) {
|
|
2162
2374
|
return false
|
|
@@ -2170,7 +2382,12 @@ export class PgConnection {
|
|
|
2170
2382
|
|
|
2171
2383
|
/**
|
|
2172
2384
|
* Starts a Bind of the prepared statement `name`; its parameters follow, in
|
|
2173
|
-
* order, then `bindEnd()`. False as `prepare` is.
|
|
2385
|
+
* order, then `bindEnd()`. False, and nothing written, as `prepare` is. It
|
|
2386
|
+
* opens a group when none is open. Until `bindEnd()` or `bindCancel()`,
|
|
2387
|
+
* `outputEnd()` holds the Bind's bytes back.
|
|
2388
|
+
* @param name A statement prepared on this connection, in this group or an
|
|
2389
|
+
* earlier one.
|
|
2390
|
+
* @returns True when the Bind was opened.
|
|
2174
2391
|
*/
|
|
2175
2392
|
bindStart(name: string): boolean {
|
|
2176
2393
|
return this.openBind(name, false)
|
|
@@ -2188,6 +2405,8 @@ export class PgConnection {
|
|
|
2188
2405
|
* with this when its `binary` is set, and reads their routing ordinals with
|
|
2189
2406
|
* `binaryInt`. Its `bindEnd()` writes two bytes more, kept free under
|
|
2190
2407
|
* `maxOutput` from here on. False as `prepare` is.
|
|
2408
|
+
* @param name A statement prepared on this connection.
|
|
2409
|
+
* @returns True when the Bind was opened.
|
|
2191
2410
|
*/
|
|
2192
2411
|
bindStartBinary(name: string): boolean {
|
|
2193
2412
|
return this.openBind(name, true)
|
|
@@ -2271,7 +2490,12 @@ export class PgConnection {
|
|
|
2271
2490
|
// open, the bind has 65535 parameters already, or the parameter would pass
|
|
2272
2491
|
// `maxOutput`; a program that checked `bindStart()` meets only the last.
|
|
2273
2492
|
|
|
2274
|
-
/**
|
|
2493
|
+
/**
|
|
2494
|
+
* A text parameter: the string's bytes as they are. Panics while an array
|
|
2495
|
+
* parameter is open.
|
|
2496
|
+
* @param value The parameter's text; "" is an empty string, not NULL.
|
|
2497
|
+
* @returns True when it was written; false as every `param...` call is.
|
|
2498
|
+
*/
|
|
2275
2499
|
paramText(value: string): boolean {
|
|
2276
2500
|
const at: i32 = this.startParam(toI32(value.length))
|
|
2277
2501
|
if (at < 0) {
|
|
@@ -2282,7 +2506,16 @@ export class PgConnection {
|
|
|
2282
2506
|
return true
|
|
2283
2507
|
}
|
|
2284
2508
|
|
|
2285
|
-
/**
|
|
2509
|
+
/**
|
|
2510
|
+
* A text parameter from `bytes[start, start + length)`, UTF-8, copied into
|
|
2511
|
+
* the Bind so the caller may reuse `bytes` at once. Panics while an array
|
|
2512
|
+
* parameter is open.
|
|
2513
|
+
* @param bytes The buffer the text lies in.
|
|
2514
|
+
* @param start The text's first byte.
|
|
2515
|
+
* @param length The text's length in bytes.
|
|
2516
|
+
* @returns True when it was written; false as every `param...` call is, and
|
|
2517
|
+
* when the window is not inside `bytes`.
|
|
2518
|
+
*/
|
|
2286
2519
|
paramBytes(bytes: u8[], start: i32, length: i32): boolean {
|
|
2287
2520
|
this.noArrayOpen()
|
|
2288
2521
|
if (start < 0 || length < 0 || length > toI32(bytes.length) - start) {
|
|
@@ -2299,7 +2532,11 @@ export class PgConnection {
|
|
|
2299
2532
|
return true
|
|
2300
2533
|
}
|
|
2301
2534
|
|
|
2302
|
-
/**
|
|
2535
|
+
/**
|
|
2536
|
+
* A NULL parameter, of whatever type the statement gives it. Panics while
|
|
2537
|
+
* an array parameter is open.
|
|
2538
|
+
* @returns True when it was written; false as every `param...` call is.
|
|
2539
|
+
*/
|
|
2303
2540
|
paramNull(): boolean {
|
|
2304
2541
|
if (this.startParam(toI32(0)) < 0) {
|
|
2305
2542
|
return false
|
|
@@ -2308,7 +2545,14 @@ export class PgConnection {
|
|
|
2308
2545
|
return true
|
|
2309
2546
|
}
|
|
2310
2547
|
|
|
2311
|
-
/**
|
|
2548
|
+
/**
|
|
2549
|
+
* An integer parameter, in decimal text, which the server reads as the
|
|
2550
|
+
* statement's type (`int2`, `int4`, `int8`, `numeric`, ...). Panics while an
|
|
2551
|
+
* array parameter is open.
|
|
2552
|
+
* @param value The integer; one outside the parameter's type is the
|
|
2553
|
+
* server's error (22003) for that execution.
|
|
2554
|
+
* @returns True when it was written; false as every `param...` call is.
|
|
2555
|
+
*/
|
|
2312
2556
|
paramInt(value: i64): boolean {
|
|
2313
2557
|
const at: i32 = this.startParam(toI32(20))
|
|
2314
2558
|
if (at < 0) {
|
|
@@ -2327,8 +2571,14 @@ export class PgConnection {
|
|
|
2327
2571
|
}
|
|
2328
2572
|
|
|
2329
2573
|
/**
|
|
2330
|
-
* An `int8[]` parameter from `values[start, start + count)`, in decimal
|
|
2331
|
-
* panics when the range is not inside
|
|
2574
|
+
* An `int8[]` parameter from `values[start, start + count)`, in decimal
|
|
2575
|
+
* text (`{1,2,3}`, or `{}` for none); panics when the range is not inside
|
|
2576
|
+
* `values`, or while an array parameter is open.
|
|
2577
|
+
* @param values The integers; read now, not kept.
|
|
2578
|
+
* @param start The first element of the window.
|
|
2579
|
+
* @param count The elements to send, 0 for an empty array.
|
|
2580
|
+
* @returns True when it was written; false, and nothing written, as every
|
|
2581
|
+
* `param...` call is.
|
|
2332
2582
|
*/
|
|
2333
2583
|
paramIntArray(values: i64[], start: i32, count: i32): boolean {
|
|
2334
2584
|
if (start < 0 || count < 0 || count > toI32(values.length) - start) {
|
|
@@ -2361,7 +2611,14 @@ export class PgConnection {
|
|
|
2361
2611
|
return true
|
|
2362
2612
|
}
|
|
2363
2613
|
|
|
2364
|
-
/**
|
|
2614
|
+
/**
|
|
2615
|
+
* A `text[]` parameter: every element quoted, `"` and `\` escaped, so no
|
|
2616
|
+
* element reads as NULL (build one with `arrayNull` for that). Panics while
|
|
2617
|
+
* an array parameter is open.
|
|
2618
|
+
* @param items The elements, in order; read now, not kept. Empty sends `{}`.
|
|
2619
|
+
* @returns True when it was written; false, and nothing written, as every
|
|
2620
|
+
* `param...` call is.
|
|
2621
|
+
*/
|
|
2365
2622
|
paramTextArray(items: string[]): boolean {
|
|
2366
2623
|
// Its exact size first, so that room is asked for once and a refusal writes nothing.
|
|
2367
2624
|
let size: i64 = 2
|
|
@@ -2415,7 +2672,12 @@ export class PgConnection {
|
|
|
2415
2672
|
/**
|
|
2416
2673
|
* An `int4[]` parameter in binary from `values[start, start + count)`;
|
|
2417
2674
|
* false as the `param...` calls are. Panics, writing nothing, when the
|
|
2418
|
-
* window is not inside `values` or an element is outside `int4
|
|
2675
|
+
* window is not inside `values` or an element is outside `int4`, and while
|
|
2676
|
+
* an array parameter is open.
|
|
2677
|
+
* @param values The integers, each within -2^31..2^31-1; read now, not kept.
|
|
2678
|
+
* @param start The first element of the window.
|
|
2679
|
+
* @param count The elements to send, 0 for an empty array.
|
|
2680
|
+
* @returns True when it was written.
|
|
2419
2681
|
*/
|
|
2420
2682
|
paramInt4Array(values: i64[], start: i32, count: i32): boolean {
|
|
2421
2683
|
pgIntWindow(values, start, count)
|
|
@@ -2431,7 +2693,11 @@ export class PgConnection {
|
|
|
2431
2693
|
/**
|
|
2432
2694
|
* An `int8[]` parameter in binary from `values[start, start + count)`;
|
|
2433
2695
|
* false as the `param...` calls are. Panics when the window is not inside
|
|
2434
|
-
* `values
|
|
2696
|
+
* `values`, and while an array parameter is open.
|
|
2697
|
+
* @param values The integers; read now, not kept.
|
|
2698
|
+
* @param start The first element of the window.
|
|
2699
|
+
* @param count The elements to send, 0 for an empty array.
|
|
2700
|
+
* @returns True when it was written.
|
|
2435
2701
|
*/
|
|
2436
2702
|
paramInt8Array(values: i64[], start: i32, count: i32): boolean {
|
|
2437
2703
|
pgIntWindow(values, start, count)
|
|
@@ -2485,7 +2751,13 @@ export class PgConnection {
|
|
|
2485
2751
|
// with none open and a second `arrayStart()`; `sync()`, `prepare`,
|
|
2486
2752
|
// `closeStatement` and `bindStart` answer false, the bind being open.
|
|
2487
2753
|
|
|
2488
|
-
/**
|
|
2754
|
+
/**
|
|
2755
|
+
* Opens a text array parameter, `{}` so far, which `arrayElement`,
|
|
2756
|
+
* `arrayInt` and `arrayNull` fill and `arrayEnd()` closes. Panics while an
|
|
2757
|
+
* array is open.
|
|
2758
|
+
* @returns True when it was opened; false, and nothing written, as the
|
|
2759
|
+
* `param...` calls are.
|
|
2760
|
+
*/
|
|
2489
2761
|
arrayStart(): boolean {
|
|
2490
2762
|
if (this.arrayAt >= 0) {
|
|
2491
2763
|
panic("nish/net/postgres: arrayStart() while an array is open")
|
|
@@ -2519,7 +2791,12 @@ export class PgConnection {
|
|
|
2519
2791
|
/**
|
|
2520
2792
|
* A text element from `bytes[start, start + length)`, UTF-8, quoted with
|
|
2521
2793
|
* `"` and `\` escaped; false, and nothing written, when there is no room.
|
|
2522
|
-
* Panics when no array is open or the window is not inside `bytes`.
|
|
2794
|
+
* Panics when no text array is open or the window is not inside `bytes`.
|
|
2795
|
+
* @param bytes The buffer the element lies in; copied, so it may be reused at once.
|
|
2796
|
+
* @param start The element's first byte.
|
|
2797
|
+
* @param length The element's length in bytes; 0 is the empty string.
|
|
2798
|
+
* @returns True when it was written; false leaves the array open with the
|
|
2799
|
+
* elements it has.
|
|
2523
2800
|
*/
|
|
2524
2801
|
arrayElement(bytes: u8[], start: i32, length: i32): boolean {
|
|
2525
2802
|
this.arrayOpen()
|
|
@@ -2557,7 +2834,11 @@ export class PgConnection {
|
|
|
2557
2834
|
return true
|
|
2558
2835
|
}
|
|
2559
2836
|
|
|
2560
|
-
/**
|
|
2837
|
+
/**
|
|
2838
|
+
* A NULL element of the open text array. Panics when no text array is open.
|
|
2839
|
+
* @returns True when it was written; false, and nothing written, when there
|
|
2840
|
+
* is no room.
|
|
2841
|
+
*/
|
|
2561
2842
|
arrayNull(): boolean {
|
|
2562
2843
|
this.arrayOpen()
|
|
2563
2844
|
if (!this.elementRoom(toI64(4))) {
|
|
@@ -2575,7 +2856,13 @@ export class PgConnection {
|
|
|
2575
2856
|
this.arrayCount += 1
|
|
2576
2857
|
}
|
|
2577
2858
|
|
|
2578
|
-
/**
|
|
2859
|
+
/**
|
|
2860
|
+
* An integer element of the open text array, in decimal. Panics when no
|
|
2861
|
+
* text array is open.
|
|
2862
|
+
* @param value The element.
|
|
2863
|
+
* @returns True when it was written; false, and nothing written, when there
|
|
2864
|
+
* is no room.
|
|
2865
|
+
*/
|
|
2579
2866
|
arrayInt(value: i64): boolean {
|
|
2580
2867
|
this.arrayOpen()
|
|
2581
2868
|
if (!this.elementRoom(toI64(pgDecimalLength(value)))) {
|
|
@@ -2592,7 +2879,14 @@ export class PgConnection {
|
|
|
2592
2879
|
// The text builder's rules hold, and a text element in a binary array, or a
|
|
2593
2880
|
// binary element in a text one, panics.
|
|
2594
2881
|
|
|
2595
|
-
/**
|
|
2882
|
+
/**
|
|
2883
|
+
* Opens a binary array parameter of `elementOid`'s elements, which
|
|
2884
|
+
* `binaryArrayInt` and `binaryArrayNull` fill and `arrayEnd()` closes.
|
|
2885
|
+
* Panics on another OID, or while an array is open.
|
|
2886
|
+
* @param elementOid 23 for an `int4[]`, 20 for an `int8[]`.
|
|
2887
|
+
* @returns True when it was opened; false, and nothing written, as the
|
|
2888
|
+
* `param...` calls are.
|
|
2889
|
+
*/
|
|
2596
2890
|
binaryArrayStart(elementOid: i32): boolean {
|
|
2597
2891
|
if (this.arrayAt >= 0) {
|
|
2598
2892
|
panic("nish/net/postgres: binaryArrayStart() while an array is open")
|
|
@@ -2624,6 +2918,8 @@ export class PgConnection {
|
|
|
2624
2918
|
* An integer element of the open binary array; false, and nothing written,
|
|
2625
2919
|
* when there is no room. Panics when no binary array is open, or when it is
|
|
2626
2920
|
* an `int4[]` and `value` is outside `int4`.
|
|
2921
|
+
* @param value The element: four bytes in an `int4[]`, eight in an `int8[]`.
|
|
2922
|
+
* @returns True when it was written.
|
|
2627
2923
|
*/
|
|
2628
2924
|
binaryArrayInt(value: i64): boolean {
|
|
2629
2925
|
this.binaryArrayOpen()
|
|
@@ -2646,7 +2942,12 @@ export class PgConnection {
|
|
|
2646
2942
|
return true
|
|
2647
2943
|
}
|
|
2648
2944
|
|
|
2649
|
-
/**
|
|
2945
|
+
/**
|
|
2946
|
+
* A NULL element of the open binary array, length -1. Panics when no binary
|
|
2947
|
+
* array is open.
|
|
2948
|
+
* @returns True when it was written; false, and nothing written, when there
|
|
2949
|
+
* is no room.
|
|
2950
|
+
*/
|
|
2650
2951
|
binaryArrayNull(): boolean {
|
|
2651
2952
|
this.binaryArrayOpen()
|
|
2652
2953
|
if (!this.room(toI32(4))) {
|
|
@@ -2658,7 +2959,11 @@ export class PgConnection {
|
|
|
2658
2959
|
return true
|
|
2659
2960
|
}
|
|
2660
2961
|
|
|
2661
|
-
/**
|
|
2962
|
+
/**
|
|
2963
|
+
* Ends the open array parameter, text or binary; never refused for room,
|
|
2964
|
+
* which its elements kept. An array with no elements is sent as the empty
|
|
2965
|
+
* array. Panics when no array is open.
|
|
2966
|
+
*/
|
|
2662
2967
|
arrayEnd(): void {
|
|
2663
2968
|
if (this.arrayAt < 0) {
|
|
2664
2969
|
panic("nish/net/postgres: arrayEnd() with no arrayStart()")
|
|
@@ -2686,7 +2991,9 @@ export class PgConnection {
|
|
|
2686
2991
|
* Ends the Bind (every result column in text, or in binary after
|
|
2687
2992
|
* `bindStartBinary`) and queues its Execute, for every row. False, and
|
|
2688
2993
|
* nothing written, when no bind is open; never for room, which the calls
|
|
2689
|
-
* before it kept.
|
|
2994
|
+
* before it kept. Panics while an array parameter is open. The Bind's bytes
|
|
2995
|
+
* count toward `outputEnd()` from here on.
|
|
2996
|
+
* @returns True when the Bind and Execute were queued.
|
|
2690
2997
|
*/
|
|
2691
2998
|
bindEnd(): boolean {
|
|
2692
2999
|
if (this.bindAt < 0) {
|
|
@@ -2723,6 +3030,7 @@ export class PgConnection {
|
|
|
2723
3030
|
* Drops the open bind, and the array open in it, unsent, so that a program
|
|
2724
3031
|
* whose parameter was refused for room can send the group without it; a
|
|
2725
3032
|
* group the bind opened is closed again. False when no bind is open.
|
|
3033
|
+
* @returns True when an open bind was dropped.
|
|
2726
3034
|
*/
|
|
2727
3035
|
bindCancel(): boolean {
|
|
2728
3036
|
if (this.bindAt < 0) {
|
|
@@ -2766,7 +3074,12 @@ export class PgConnection {
|
|
|
2766
3074
|
this.paramCountAt = from + codes
|
|
2767
3075
|
}
|
|
2768
3076
|
|
|
2769
|
-
/**
|
|
3077
|
+
/**
|
|
3078
|
+
* Sync: ends the open group, which the server answers with
|
|
3079
|
+
* `PG_GROUP_DONE`; the group stays in `inFlight` until then.
|
|
3080
|
+
* @returns True when the Sync was queued; false when no group is open or a
|
|
3081
|
+
* bind is; never for room.
|
|
3082
|
+
*/
|
|
2770
3083
|
sync(): boolean {
|
|
2771
3084
|
if (!this.groupOpen || this.bindAt >= 0) {
|
|
2772
3085
|
return false
|
|
@@ -2778,7 +3091,11 @@ export class PgConnection {
|
|
|
2778
3091
|
return true
|
|
2779
3092
|
}
|
|
2780
3093
|
|
|
2781
|
-
/**
|
|
3094
|
+
/**
|
|
3095
|
+
* Terminate: tells the server the connection is ending; close the socket
|
|
3096
|
+
* once it is sent. A bind still open is dropped unsent. It is queued in any
|
|
3097
|
+
* state, and is never refused for room.
|
|
3098
|
+
*/
|
|
2782
3099
|
terminate(): void {
|
|
2783
3100
|
if (this.bindAt >= 0) {
|
|
2784
3101
|
this.dropBind()
|
|
@@ -2790,22 +3107,39 @@ export class PgConnection {
|
|
|
2790
3107
|
|
|
2791
3108
|
// --- Results -----------------------------------------------------------------
|
|
2792
3109
|
|
|
2793
|
-
/**
|
|
3110
|
+
/**
|
|
3111
|
+
* Column `c`'s first byte in `inputBuffer()`, valid until the next `next()`.
|
|
3112
|
+
* @param c The column, 0 to `columns - 1`.
|
|
3113
|
+
* @returns An index into `inputBuffer()`.
|
|
3114
|
+
*/
|
|
2794
3115
|
colStart(c: i32): i32 {
|
|
2795
3116
|
return this.colStarts[c]
|
|
2796
3117
|
}
|
|
2797
3118
|
|
|
2798
|
-
/**
|
|
3119
|
+
/**
|
|
3120
|
+
* Column `c`'s length.
|
|
3121
|
+
* @param c The column, 0 to `columns - 1`.
|
|
3122
|
+
* @returns The length in bytes, or -1 for NULL.
|
|
3123
|
+
*/
|
|
2799
3124
|
colLength(c: i32): i32 {
|
|
2800
3125
|
return this.colLengths[c]
|
|
2801
3126
|
}
|
|
2802
3127
|
|
|
2803
|
-
/**
|
|
3128
|
+
/**
|
|
3129
|
+
* Whether column `c` of the current row is NULL.
|
|
3130
|
+
* @param c The column, 0 to `columns - 1`.
|
|
3131
|
+
* @returns True when the column is NULL.
|
|
3132
|
+
*/
|
|
2804
3133
|
isNull(c: i32): boolean {
|
|
2805
3134
|
return this.colLengths[c] < 0
|
|
2806
3135
|
}
|
|
2807
3136
|
|
|
2808
|
-
/**
|
|
3137
|
+
/**
|
|
3138
|
+
* Column `c` as a string; the one result read that allocates, and the string
|
|
3139
|
+
* outlives the row.
|
|
3140
|
+
* @param c The column, 0 to `columns - 1`.
|
|
3141
|
+
* @returns The column's bytes as a string; "" for NULL and for an empty value.
|
|
3142
|
+
*/
|
|
2809
3143
|
columnText(c: i32): string {
|
|
2810
3144
|
const length: i32 = this.colLengths[c]
|
|
2811
3145
|
if (length <= 0) {
|
|
@@ -2815,37 +3149,70 @@ export class PgConnection {
|
|
|
2815
3149
|
return String.fromBytes(this.input, start, start + length)
|
|
2816
3150
|
}
|
|
2817
3151
|
|
|
2818
|
-
/**
|
|
3152
|
+
/**
|
|
3153
|
+
* Column `c` of the statement just described, after `PG_DESCRIBED`.
|
|
3154
|
+
* @param c The column, 0 to `columns - 1`.
|
|
3155
|
+
* @returns Its type's OID (23 is `int4`, 25 `text`).
|
|
3156
|
+
*/
|
|
2819
3157
|
columnType(c: i32): i32 {
|
|
2820
3158
|
return this.colTypes[c]
|
|
2821
3159
|
}
|
|
2822
3160
|
|
|
2823
|
-
/**
|
|
3161
|
+
/**
|
|
3162
|
+
* Text column `c` as an integer, through `text.int`; NULL or a bad value
|
|
3163
|
+
* sets `text.failed`.
|
|
3164
|
+
* @param c The column, 0 to `columns - 1`.
|
|
3165
|
+
* @returns The value, or 0 when it sets `text.failed`.
|
|
3166
|
+
*/
|
|
2824
3167
|
int(c: i32): i64 {
|
|
2825
3168
|
return this.text.int(this.input, this.colStarts[c], this.colLengths[c])
|
|
2826
3169
|
}
|
|
2827
3170
|
|
|
2828
|
-
/**
|
|
3171
|
+
/**
|
|
3172
|
+
* Text column `c` as a boolean, through `text.bool`; NULL or a bad value
|
|
3173
|
+
* sets `text.failed`.
|
|
3174
|
+
* @param c The column, 0 to `columns - 1`.
|
|
3175
|
+
* @returns True for `t`; false for `f`, and when it sets `text.failed`.
|
|
3176
|
+
*/
|
|
2829
3177
|
bool(c: i32): boolean {
|
|
2830
3178
|
return this.text.bool(this.input, this.colStarts[c], this.colLengths[c])
|
|
2831
3179
|
}
|
|
2832
3180
|
|
|
2833
|
-
/**
|
|
3181
|
+
/**
|
|
3182
|
+
* Binary column `c`, an `int2`, `int4`, `int8` or a time's microseconds
|
|
3183
|
+
* since 2000, as an integer; NULL or another length sets `binary.failed`.
|
|
3184
|
+
* @param c The column, 0 to `columns - 1`.
|
|
3185
|
+
* @returns The value, or 0 when it sets `binary.failed`.
|
|
3186
|
+
*/
|
|
2834
3187
|
binaryInt(c: i32): i64 {
|
|
2835
3188
|
return this.binary.int(this.input, this.colStarts[c], this.colLengths[c])
|
|
2836
3189
|
}
|
|
2837
3190
|
|
|
2838
|
-
/**
|
|
3191
|
+
/**
|
|
3192
|
+
* Binary column `c` as a boolean; NULL or a bad value sets `binary.failed`.
|
|
3193
|
+
* @param c The column, 0 to `columns - 1`.
|
|
3194
|
+
* @returns True for 1; false for 0, and when it sets `binary.failed`.
|
|
3195
|
+
*/
|
|
2839
3196
|
binaryBool(c: i32): boolean {
|
|
2840
3197
|
return this.binary.bool(this.input, this.colStarts[c], this.colLengths[c])
|
|
2841
3198
|
}
|
|
2842
3199
|
|
|
2843
|
-
/**
|
|
3200
|
+
/**
|
|
3201
|
+
* Binary column `c`, a `float8`; NULL or another length sets `binary.failed`.
|
|
3202
|
+
* @param c The column, 0 to `columns - 1`.
|
|
3203
|
+
* @returns The value, or 0 when it sets `binary.failed`.
|
|
3204
|
+
*/
|
|
2844
3205
|
binaryFloat(c: i32): f64 {
|
|
2845
3206
|
return this.binary.float(this.input, this.colStarts[c], this.colLengths[c])
|
|
2846
3207
|
}
|
|
2847
3208
|
|
|
2848
|
-
/**
|
|
3209
|
+
/**
|
|
3210
|
+
* Whether the tag of the execution just completed starts with the word
|
|
3211
|
+
* `word`, read after `PG_COMPLETE` and before the next `next()`.
|
|
3212
|
+
* @param word The command word, as the server spells it (`INSERT`,
|
|
3213
|
+
* `SELECT`, `UPDATE`, ...); case matters, and `INS` does not match `INSERT`.
|
|
3214
|
+
* @returns True when the tag is `word` or begins with `word` and a space.
|
|
3215
|
+
*/
|
|
2849
3216
|
tagIs(word: string): boolean {
|
|
2850
3217
|
const n: i32 = toI32(word.length)
|
|
2851
3218
|
if (n > this.tagLength || !pgBytesAre(this.input, this.tagStart, n, word)) {
|
|
@@ -2854,7 +3221,13 @@ export class PgConnection {
|
|
|
2854
3221
|
return n === this.tagLength || toI32(this.input[this.tagStart + n]) === 32
|
|
2855
3222
|
}
|
|
2856
3223
|
|
|
2857
|
-
/**
|
|
3224
|
+
/**
|
|
3225
|
+
* The row count the tag of the execution just completed ends with, read
|
|
3226
|
+
* after `PG_COMPLETE` and before the next `next()`; `text.failed` is left
|
|
3227
|
+
* as it was.
|
|
3228
|
+
* @returns The count (`INSERT 0 3` is 3, `SELECT 5` is 5), or -1 when the
|
|
3229
|
+
* tag has none (`BEGIN`) or it does not fit an i64.
|
|
3230
|
+
*/
|
|
2858
3231
|
tagRows(): i64 {
|
|
2859
3232
|
const end: i32 = this.tagStart + this.tagLength
|
|
2860
3233
|
let p: i32 = end
|
|
@@ -2904,15 +3277,27 @@ const PG_SLOT_DRAINING: i32 = 4
|
|
|
2904
3277
|
|
|
2905
3278
|
/** One of a pool's connections, and the groups it has in flight. */
|
|
2906
3279
|
class PgPoolSlot {
|
|
3280
|
+
/** @internal Used by `PgPool.dispatch`, `PgPool.wanted`, `PgPool.connect`, `PgPool.next` and `PgPool.anyReady` in this module. */
|
|
2907
3281
|
state: i32 = 0
|
|
3282
|
+
/** @internal Used by `PgPool.dispatch`, `PgPool.connect`, `PgPool.connection` and `PgPool.dispose` in this module. */
|
|
2908
3283
|
conn: PgConnection | null = null
|
|
2909
|
-
/**
|
|
3284
|
+
/**
|
|
3285
|
+
* How many registered statements this connection has queued, and how many the server has described.
|
|
3286
|
+
* @internal Used by `PgPool.dispatch`, `PgPool.connect` and `PgPool.next` in this module.
|
|
3287
|
+
*/
|
|
2910
3288
|
queued: i32 = 0
|
|
3289
|
+
/** @internal Used by `PgPool.connect` and `PgPool.next` in this module. */
|
|
2911
3290
|
described: i32 = 0
|
|
2912
|
-
/**
|
|
3291
|
+
/**
|
|
3292
|
+
* The groups in flight, oldest first, in a ring: each one's ticket, and how many of the pool's own prepares lead it.
|
|
3293
|
+
* @internal Used by `PgPool.next` in this module.
|
|
3294
|
+
*/
|
|
2913
3295
|
readonly tickets: i32[]
|
|
3296
|
+
/** @internal Used by `PgPool.next` in this module. */
|
|
2914
3297
|
readonly leads: i32[]
|
|
3298
|
+
/** @internal Used by `PgPool.connect` and `PgPool.next` in this module. */
|
|
2915
3299
|
head: i32 = 0
|
|
3300
|
+
/** @internal Used by `PgPool.inFlight`, `PgPool.send`, `PgPool.connect` and `PgPool.next` in this module. */
|
|
2916
3301
|
count: i32 = 0
|
|
2917
3302
|
|
|
2918
3303
|
constructor(ring: i32) {
|
|
@@ -2920,6 +3305,7 @@ class PgPoolSlot {
|
|
|
2920
3305
|
this.leads = new Array<i32>(ring)
|
|
2921
3306
|
}
|
|
2922
3307
|
|
|
3308
|
+
/** @internal Used by `PgPool.dispatch` and `PgPool.next` in this module. */
|
|
2923
3309
|
push(ticket: i32, leads: i32): void {
|
|
2924
3310
|
const at: i32 = (this.head + this.count) % toI32(this.tickets.length)
|
|
2925
3311
|
this.tickets[at] = ticket
|
|
@@ -2927,6 +3313,7 @@ class PgPoolSlot {
|
|
|
2927
3313
|
this.count += 1
|
|
2928
3314
|
}
|
|
2929
3315
|
|
|
3316
|
+
/** @internal Used by `PgPool.next` in this module. */
|
|
2930
3317
|
pop(): void {
|
|
2931
3318
|
this.head = (this.head + 1) % toI32(this.tickets.length)
|
|
2932
3319
|
this.count -= 1
|
|
@@ -2975,6 +3362,12 @@ export class PgPool {
|
|
|
2975
3362
|
* waiting requests (1 to 2^20). `password` is copied and kept until
|
|
2976
3363
|
* `dispose()`, since a connection is made again after a failure. Panics as
|
|
2977
3364
|
* `PgConnection` does on `config`, and on a `maxInFlight` over 1024.
|
|
3365
|
+
*
|
|
3366
|
+
* @param config The settings every connection of the pool is made with; kept, not copied.
|
|
3367
|
+
* @param password The password every connection authenticates with. The pool keeps its
|
|
3368
|
+
* own copy, so the caller may wipe this one as soon as the constructor returns.
|
|
3369
|
+
* @param size The most connections the pool makes, from 1 to 1024.
|
|
3370
|
+
* @param queueSize The most requests that may wait for a connection, from 1 to 2^20.
|
|
2978
3371
|
*/
|
|
2979
3372
|
constructor(config: PgConfig, password: Secret<u8[]>, size: i32, queueSize: i32) {
|
|
2980
3373
|
pgCheckConfig(config)
|
|
@@ -3004,6 +3397,11 @@ export class PgPool {
|
|
|
3004
3397
|
* Registers a statement for every connection to prepare before its first
|
|
3005
3398
|
* request. False when a connection has been made already, `name` or `sql`
|
|
3006
3399
|
* holds a NUL, or 256 are registered.
|
|
3400
|
+
*
|
|
3401
|
+
* @param name The statement's name, which the program's binds use; no NUL.
|
|
3402
|
+
* @param sql The statement's text, with `$1`, `$2`... for its parameters; no NUL.
|
|
3403
|
+
* @param paramTypes The type OID of each parameter, 0 to let the server infer it; copied.
|
|
3404
|
+
* @returns True when the statement was registered.
|
|
3007
3405
|
*/
|
|
3008
3406
|
register(name: string, sql: string, paramTypes: i32[]): boolean {
|
|
3009
3407
|
if (
|
|
@@ -3024,7 +3422,12 @@ export class PgPool {
|
|
|
3024
3422
|
return true
|
|
3025
3423
|
}
|
|
3026
3424
|
|
|
3027
|
-
/**
|
|
3425
|
+
/**
|
|
3426
|
+
* Queues a request and answers its ticket, or `PG_POOL_FULL` when `queueSize` are waiting.
|
|
3427
|
+
*
|
|
3428
|
+
* @returns The request's ticket, from 0 to 2^31-1 and counting up from 0, wrapping to 0
|
|
3429
|
+
* after 2^31-1; or `PG_POOL_FULL`.
|
|
3430
|
+
*/
|
|
3028
3431
|
request(): i32 {
|
|
3029
3432
|
const room: i32 = toI32(this.queue.length)
|
|
3030
3433
|
if (this.queueCount === room) {
|
|
@@ -3037,12 +3440,21 @@ export class PgPool {
|
|
|
3037
3440
|
return ticket
|
|
3038
3441
|
}
|
|
3039
3442
|
|
|
3040
|
-
/**
|
|
3443
|
+
/**
|
|
3444
|
+
* How many requests wait for a connection.
|
|
3445
|
+
*
|
|
3446
|
+
* @returns The requests queued and not yet given a connection.
|
|
3447
|
+
*/
|
|
3041
3448
|
waiting(): i32 {
|
|
3042
3449
|
return this.queueCount
|
|
3043
3450
|
}
|
|
3044
3451
|
|
|
3045
|
-
/**
|
|
3452
|
+
/**
|
|
3453
|
+
* How many groups are in flight across every connection: the `inFlight` a
|
|
3454
|
+
* `PgBatchClock` reads.
|
|
3455
|
+
*
|
|
3456
|
+
* @returns The groups sent and not yet done, summed over every connection.
|
|
3457
|
+
*/
|
|
3046
3458
|
inFlight(): i32 {
|
|
3047
3459
|
let n: i32 = 0
|
|
3048
3460
|
for (const slot of this.slots) {
|
|
@@ -3061,6 +3473,8 @@ export class PgPool {
|
|
|
3061
3473
|
* waiting requests that the connections already starting cannot take. A
|
|
3062
3474
|
* request handed out is the program's to end with `send()`: until then no
|
|
3063
3475
|
* other is handed out.
|
|
3476
|
+
*
|
|
3477
|
+
* @returns The slot the request was handed to, from 0 to `size - 1`, or `PG_POOL_NONE`.
|
|
3064
3478
|
*/
|
|
3065
3479
|
dispatch(): i32 {
|
|
3066
3480
|
if (this.building >= 0 || this.queueCount === 0) {
|
|
@@ -3117,6 +3531,8 @@ export class PgPool {
|
|
|
3117
3531
|
* is none; when a bind is still open, so finish it and call again; and
|
|
3118
3532
|
* when no message of the group was written, so the request is dropped and
|
|
3119
3533
|
* gets no events (`PG_LOST` if its connection has failed).
|
|
3534
|
+
*
|
|
3535
|
+
* @returns True when the group's Sync was written and the request is in flight.
|
|
3120
3536
|
*/
|
|
3121
3537
|
send(): boolean {
|
|
3122
3538
|
const s: i32 = this.building
|
|
@@ -3140,7 +3556,11 @@ export class PgPool {
|
|
|
3140
3556
|
return false
|
|
3141
3557
|
}
|
|
3142
3558
|
|
|
3143
|
-
/**
|
|
3559
|
+
/**
|
|
3560
|
+
* A slot that needs a socket, or `PG_POOL_NONE`: connect it, then call `connect(slot, nonce)`.
|
|
3561
|
+
*
|
|
3562
|
+
* @returns The lowest wanted slot, or `PG_POOL_NONE` when no slot is wanted.
|
|
3563
|
+
*/
|
|
3144
3564
|
wanted(): i32 {
|
|
3145
3565
|
for (let s: i32 = 0; s < this.size; s++) {
|
|
3146
3566
|
if (this.slots[s].state === PG_SLOT_WANTED) {
|
|
@@ -3155,6 +3575,10 @@ export class PgPool {
|
|
|
3155
3575
|
* send its output once the socket is connected, and if the socket fails,
|
|
3156
3576
|
* call `connection(slot).closed()`. Panics when `slot` is not wanted or the
|
|
3157
3577
|
* pool is disposed.
|
|
3578
|
+
*
|
|
3579
|
+
* @param slot The slot `wanted()` answered.
|
|
3580
|
+
* @param nonce At least 18 fresh random bytes for the SCRAM client nonce, as the
|
|
3581
|
+
* `PgConnection` constructor takes them; a new one for every connection.
|
|
3158
3582
|
*/
|
|
3159
3583
|
connect(slot: i32, nonce: u8[]): void {
|
|
3160
3584
|
if (slot < 0 || slot >= this.size || this.slots[slot].state !== PG_SLOT_WANTED) {
|
|
@@ -3173,7 +3597,13 @@ export class PgPool {
|
|
|
3173
3597
|
this.started = true
|
|
3174
3598
|
}
|
|
3175
3599
|
|
|
3176
|
-
/**
|
|
3600
|
+
/**
|
|
3601
|
+
* The connection in `slot`, its buffers the carrier's to fill and drain; the last one made
|
|
3602
|
+
* after it fails. Panics when none was made.
|
|
3603
|
+
*
|
|
3604
|
+
* @param slot The slot, from 0 to `size - 1`.
|
|
3605
|
+
* @returns The slot's connection, owned by the pool: a later `connect` on the slot replaces it.
|
|
3606
|
+
*/
|
|
3177
3607
|
connection(slot: i32): PgConnection {
|
|
3178
3608
|
const conn: PgConnection | null = slot < 0 || slot >= this.size ? null : this.slots[slot].conn
|
|
3179
3609
|
if (conn === null) {
|
|
@@ -3193,6 +3623,9 @@ export class PgPool {
|
|
|
3193
3623
|
* server that cannot be reached empties the queue rather than holds it.
|
|
3194
3624
|
* `PG_NEED_MORE` once `PG_FAILED` was answered, until `connect` builds the
|
|
3195
3625
|
* slot again. Panics as `connection(slot)` does.
|
|
3626
|
+
*
|
|
3627
|
+
* @param slot The slot whose input the carrier has just filled, from 0 to `size - 1`.
|
|
3628
|
+
* @returns A `PG_` event; `ticket` names the request it is about.
|
|
3196
3629
|
*/
|
|
3197
3630
|
next(slot: i32): i32 {
|
|
3198
3631
|
const conn: PgConnection = this.connection(slot)
|
|
@@ -3249,7 +3682,13 @@ export class PgPool {
|
|
|
3249
3682
|
return false
|
|
3250
3683
|
}
|
|
3251
3684
|
|
|
3252
|
-
/**
|
|
3685
|
+
/**
|
|
3686
|
+
* Whether every byte of the pool's copy of the password is zero, as
|
|
3687
|
+
* `PgConnection.passwordWiped()` reads its own: true once it is wiped, and
|
|
3688
|
+
* for an empty password before any wipe.
|
|
3689
|
+
*
|
|
3690
|
+
* @returns True once the pool's copy is wiped; the connections' copies are not read.
|
|
3691
|
+
*/
|
|
3253
3692
|
passwordWiped(): boolean {
|
|
3254
3693
|
return this.password.wiped()
|
|
3255
3694
|
}
|
|
@@ -3365,23 +3804,46 @@ export class PgBatchConfig {
|
|
|
3365
3804
|
|
|
3366
3805
|
/** One execution's requests, grouped by key, and how far its rows have been routed. */
|
|
3367
3806
|
class PgBatchRun {
|
|
3368
|
-
/**
|
|
3807
|
+
/**
|
|
3808
|
+
* Key `k`'s requests' tags are `tags[first[k], first[k + 1])`, in the order they were added; grown by `take` to the batch.
|
|
3809
|
+
* @internal Used by `PgBatcher.take`, `PgBatcher.emit`, `PgBatcher.next` and `PgBatcher.nextUnsorted` in this module.
|
|
3810
|
+
*/
|
|
3369
3811
|
tags: i32[]
|
|
3812
|
+
/** @internal Used by `PgBatcher.take`, `PgBatcher.emit` and `PgBatcher.nextUnsorted` in this module. */
|
|
3370
3813
|
first: i32[]
|
|
3371
|
-
/**
|
|
3814
|
+
/**
|
|
3815
|
+
* Under `sorted` false, whether key `k` has had a row; grown by `take` with `tags`, and empty when sorted.
|
|
3816
|
+
* @internal Used by `PgBatcher.take`, `PgBatcher.row` and `PgBatcher.nextUnsorted` in this module.
|
|
3817
|
+
*/
|
|
3372
3818
|
had: boolean[]
|
|
3819
|
+
/** @internal Used by `PgBatcher.row`, `PgBatcher.next` and `PgBatcher.nextUnsorted` in this module. */
|
|
3373
3820
|
keys: i32 = 0
|
|
3374
|
-
/**
|
|
3821
|
+
/**
|
|
3822
|
+
* The key rows are being read for, whether it has had one, and the next of its requests to answer.
|
|
3823
|
+
* @internal Used by `PgBatcher.row`, `PgBatcher.emit`, `PgBatcher.next` and `PgBatcher.nextUnsorted` in this module.
|
|
3824
|
+
*/
|
|
3375
3825
|
cur: i32 = 0
|
|
3826
|
+
/** @internal Used by `PgBatcher.next` in this module. */
|
|
3376
3827
|
curRows: boolean = false
|
|
3828
|
+
/** @internal Used by `PgBatcher.emit`, `PgBatcher.next` and `PgBatcher.nextUnsorted` in this module. */
|
|
3377
3829
|
at: i32 = 0
|
|
3378
|
-
/**
|
|
3830
|
+
/**
|
|
3831
|
+
* The key whose `PG_BATCH_ROW` events are owed for the connection's current row; -1 for none.
|
|
3832
|
+
* @internal Used by `PgBatcher.quietRun`, `PgBatcher.row`, `PgBatcher.next` and `PgBatcher.nextUnsorted` in this module.
|
|
3833
|
+
*/
|
|
3379
3834
|
target: i32 = -1
|
|
3380
|
-
/**
|
|
3835
|
+
/**
|
|
3836
|
+
* The handle `take` gave for the run's execution, and how many takes it has had, mod `PG_BATCH_USES`.
|
|
3837
|
+
* @internal Used by `PgBatcher.take` and `PgBatcher.run` in this module.
|
|
3838
|
+
*/
|
|
3381
3839
|
handle: i32 = -1
|
|
3840
|
+
/** @internal Used by `PgBatcher.take` in this module. */
|
|
3382
3841
|
uses: i32 = 0
|
|
3842
|
+
/** @internal Used by `PgBatcher.take`, `PgBatcher.run`, `PgBatcher.next` and `PgBatcher.nextUnsorted` in this module. */
|
|
3383
3843
|
open: boolean = false
|
|
3844
|
+
/** @internal Used by `PgBatcher.quietRun`, `PgBatcher.end`, `PgBatcher.fail`, `PgBatcher.next` and `PgBatcher.nextUnsorted` in this module. */
|
|
3384
3845
|
ended: boolean = false
|
|
3846
|
+
/** @internal Used by `PgBatcher.row`, `PgBatcher.fail`, `PgBatcher.next` and `PgBatcher.nextUnsorted` in this module. */
|
|
3385
3847
|
failed: boolean = false
|
|
3386
3848
|
|
|
3387
3849
|
constructor() {
|
|
@@ -3390,7 +3852,10 @@ class PgBatchRun {
|
|
|
3390
3852
|
this.had = []
|
|
3391
3853
|
}
|
|
3392
3854
|
|
|
3393
|
-
/**
|
|
3855
|
+
/**
|
|
3856
|
+
* Opens the run for `keys` keys, none of whose rows have been read.
|
|
3857
|
+
* @internal Used by `PgBatcher.take` in this module.
|
|
3858
|
+
*/
|
|
3394
3859
|
reset(keys: i32): void {
|
|
3395
3860
|
this.open = true
|
|
3396
3861
|
this.keys = keys
|
|
@@ -3471,7 +3936,10 @@ export class PgBatcher {
|
|
|
3471
3936
|
refused: i32 = 0
|
|
3472
3937
|
/** The number of `row`, `end` and `fail` calls on a spent handle, each ignored, stopping at 2^31 - 1. */
|
|
3473
3938
|
stray: i32 = 0
|
|
3474
|
-
/**
|
|
3939
|
+
/**
|
|
3940
|
+
* The most requests one execution takes, from `PgBatchConfig.maxRequests`.
|
|
3941
|
+
* @internal Used by `PgCarrier.routeBatch` in std/net/postgres-carrier.ts.
|
|
3942
|
+
*/
|
|
3475
3943
|
readonly maxRequests: i32
|
|
3476
3944
|
private readonly keyType: i32
|
|
3477
3945
|
private readonly maxBytes: i32
|
|
@@ -3481,7 +3949,14 @@ export class PgBatcher {
|
|
|
3481
3949
|
private head: i32 = 0
|
|
3482
3950
|
private count: i32 = 0
|
|
3483
3951
|
|
|
3484
|
-
/**
|
|
3952
|
+
/**
|
|
3953
|
+
* A batcher for `config`; panics when a name or query is empty or holds a NUL, or a cap is out
|
|
3954
|
+
* of range.
|
|
3955
|
+
*
|
|
3956
|
+
* @param config The statement shape and caps, read once here: later changes to it do nothing.
|
|
3957
|
+
* `maxRequests` is 1 to 65536, `maxBytes` at least 22, `queueSize` 1 to 2^20, `runs` 1 to
|
|
3958
|
+
* 1024, and `queueBytes` at least 0 (a text batcher with 0 can queue only empty keys).
|
|
3959
|
+
*/
|
|
3485
3960
|
constructor(config: PgBatchConfig) {
|
|
3486
3961
|
const keyType: i32 = config.keyType
|
|
3487
3962
|
if (keyType !== PG_BATCH_INT && keyType !== PG_BATCH_TEXT) {
|
|
@@ -3547,15 +4022,21 @@ export class PgBatcher {
|
|
|
3547
4022
|
}
|
|
3548
4023
|
}
|
|
3549
4024
|
|
|
3550
|
-
/**
|
|
4025
|
+
/**
|
|
4026
|
+
* How many requests wait for `take`.
|
|
4027
|
+
*
|
|
4028
|
+
* @returns The requests queued and not yet taken into a batch.
|
|
4029
|
+
*/
|
|
3551
4030
|
waiting(): i32 {
|
|
3552
4031
|
return this.count
|
|
3553
4032
|
}
|
|
3554
4033
|
|
|
3555
4034
|
/**
|
|
3556
|
-
* Drops the oldest waiting request
|
|
3557
|
-
*
|
|
3558
|
-
*
|
|
4035
|
+
* Drops the oldest waiting request, so that a carrier that cannot reach the
|
|
4036
|
+
* server fails its requests rather than holds them.
|
|
4037
|
+
*
|
|
4038
|
+
* @returns The dropped request's tag, or -1 when none waits.
|
|
4039
|
+
* @internal Used by `PgCarrier.next` in std/net/postgres-carrier.ts.
|
|
3559
4040
|
*/
|
|
3560
4041
|
shed(): i32 {
|
|
3561
4042
|
if (this.count === 0) {
|
|
@@ -3570,7 +4051,15 @@ export class PgBatcher {
|
|
|
3570
4051
|
return tag
|
|
3571
4052
|
}
|
|
3572
4053
|
|
|
3573
|
-
/**
|
|
4054
|
+
/**
|
|
4055
|
+
* Queues a request for the int8 `key`; false when `queueSize` are waiting. Panics on a text
|
|
4056
|
+
* batcher.
|
|
4057
|
+
*
|
|
4058
|
+
* @param key The key the statement's `q.key` is bound to.
|
|
4059
|
+
* @param tag The caller's own number for the request, which `tag` holds when `next(run)`
|
|
4060
|
+
* answers an event about it; best not -1, which `tag` holds for none.
|
|
4061
|
+
* @returns True when the request was queued.
|
|
4062
|
+
*/
|
|
3574
4063
|
addInt(key: i64, tag: i32): boolean {
|
|
3575
4064
|
if (this.keyType !== PG_BATCH_INT) {
|
|
3576
4065
|
panic("nish/net/postgres: PgBatcher.addInt on a text batcher")
|
|
@@ -3591,6 +4080,14 @@ export class PgBatcher {
|
|
|
3591
4080
|
* the key would not fit in `queueBytes` beside those waiting, or it is too
|
|
3592
4081
|
* long for one execution's `maxBytes`. Panics on an int8 batcher, or when
|
|
3593
4082
|
* the window is not inside `bytes`.
|
|
4083
|
+
*
|
|
4084
|
+
* @param bytes The buffer holding the key; only the window is read, and the caller may reuse
|
|
4085
|
+
* the buffer as soon as the call returns.
|
|
4086
|
+
* @param start The index of the key's first byte in `bytes`.
|
|
4087
|
+
* @param length The key's length in bytes, 0 for the empty key.
|
|
4088
|
+
* @param tag The caller's own number for the request, which `tag` holds when `next(run)`
|
|
4089
|
+
* answers an event about it; best not -1, which `tag` holds for none.
|
|
4090
|
+
* @returns True when the request was queued.
|
|
3594
4091
|
*/
|
|
3595
4092
|
addText(bytes: u8[], start: i32, length: i32, tag: i32): boolean {
|
|
3596
4093
|
if (this.keyType !== PG_BATCH_TEXT) {
|
|
@@ -3684,6 +4181,12 @@ export class PgBatcher {
|
|
|
3684
4181
|
* answers -1. The program calls `clock.arrived(now)` for each request it
|
|
3685
4182
|
* adds. Panics when the clock's waiting count is not this batcher's, or its
|
|
3686
4183
|
* `maxCount` is below `maxRequests`.
|
|
4184
|
+
*
|
|
4185
|
+
* @param conn The free connection to bind the batch on, as `take` takes it.
|
|
4186
|
+
* @param clock The clock that mirrors this batcher's queue, one `arrived` per request added.
|
|
4187
|
+
* @param nowUs The time now, in the clock's microseconds.
|
|
4188
|
+
* @param inFlight The program's executions or groups still running, for the idle bypass.
|
|
4189
|
+
* @returns The run handle `take` answered, or -1 when no batch was taken.
|
|
3687
4190
|
*/
|
|
3688
4191
|
takeDue(conn: PgConnection, clock: PgBatchClock, nowUs: i64, inFlight: i32): i32 {
|
|
3689
4192
|
if (clock.waiting() !== this.count || clock.maxCount < this.maxRequests) {
|
|
@@ -3709,6 +4212,10 @@ export class PgBatcher {
|
|
|
3709
4212
|
* the connection's `maxOutput`; those not taken stay queued. -1, and
|
|
3710
4213
|
* nothing written, when none waits, every run is in flight, or `conn`
|
|
3711
4214
|
* refused the bind or the first key.
|
|
4215
|
+
*
|
|
4216
|
+
* @param conn A connection with no bind open, on which `sql` has been prepared under `name`.
|
|
4217
|
+
* @returns The run handle, from 0 to `runs` * 2^20 - 1, to pass to `row`, `end`, `fail` and
|
|
4218
|
+
* `next` for this execution's events; or -1.
|
|
3712
4219
|
*/
|
|
3713
4220
|
take(conn: PgConnection): i32 {
|
|
3714
4221
|
if (this.count === 0) {
|
|
@@ -3892,6 +4399,10 @@ export class PgBatcher {
|
|
|
3892
4399
|
* one before the key the last row was for): that counts in `refused` and
|
|
3893
4400
|
* fails the run; and on a spent handle, which counts in `stray`. Panics on a handle
|
|
3894
4401
|
* `take` cannot give, or when the run's events are not all answered.
|
|
4402
|
+
*
|
|
4403
|
+
* @param r The run handle `take` answered for the execution the row belongs to.
|
|
4404
|
+
* @param conn The connection whose `next()` has just answered `PG_ROW` for that execution.
|
|
4405
|
+
* @returns True when the row was routed, and `next(r)` answers its `PG_BATCH_ROW` events.
|
|
3895
4406
|
*/
|
|
3896
4407
|
row(r: i32, conn: PgConnection): boolean {
|
|
3897
4408
|
const run: PgBatchRun | null = this.quietRun(r, "row")
|
|
@@ -3935,7 +4446,12 @@ export class PgBatcher {
|
|
|
3935
4446
|
return true
|
|
3936
4447
|
}
|
|
3937
4448
|
|
|
3938
|
-
/**
|
|
4449
|
+
/**
|
|
4450
|
+
* Ends run `r`'s execution, on its `PG_COMPLETE`: `next(r)` then closes every request left.
|
|
4451
|
+
* Ignores a spent handle, and panics, as `row` does.
|
|
4452
|
+
*
|
|
4453
|
+
* @param r The run handle `take` answered for the execution.
|
|
4454
|
+
*/
|
|
3939
4455
|
end(r: i32): void {
|
|
3940
4456
|
const run: PgBatchRun | null = this.quietRun(r, "end")
|
|
3941
4457
|
if (run !== null) {
|
|
@@ -3943,7 +4459,12 @@ export class PgBatcher {
|
|
|
3943
4459
|
}
|
|
3944
4460
|
}
|
|
3945
4461
|
|
|
3946
|
-
/**
|
|
4462
|
+
/**
|
|
4463
|
+
* Fails run `r`'s execution, on its `PG_ERROR` or `PG_LOST`: `next(r)` then answers
|
|
4464
|
+
* `PG_BATCH_FAILED` for every request left. Ignores a spent handle, and panics, as `row` does.
|
|
4465
|
+
*
|
|
4466
|
+
* @param r The run handle `take` answered for the execution.
|
|
4467
|
+
*/
|
|
3947
4468
|
fail(r: i32): void {
|
|
3948
4469
|
const run: PgBatchRun | null = this.quietRun(r, "fail")
|
|
3949
4470
|
if (run !== null) {
|
|
@@ -3969,6 +4490,9 @@ export class PgBatcher {
|
|
|
3969
4490
|
* Once `end` or `fail` has had all its events the run is over, free for
|
|
3970
4491
|
* `take` again, and its handle spent: `PG_BATCH_NONE` ever after. Panics
|
|
3971
4492
|
* on a handle `take` cannot give.
|
|
4493
|
+
*
|
|
4494
|
+
* @param r The run handle `take` answered for the execution.
|
|
4495
|
+
* @returns A `PG_BATCH_*` event: call again until it is `PG_BATCH_NONE`.
|
|
3972
4496
|
*/
|
|
3973
4497
|
next(r: i32): i32 {
|
|
3974
4498
|
const found: PgBatchRun | null = this.run(r)
|
|
@@ -4120,6 +4644,7 @@ export class PgBatchClock {
|
|
|
4120
4644
|
lastReason: i32 = 0
|
|
4121
4645
|
/** How long the oldest item of the last batch waited, in microseconds. */
|
|
4122
4646
|
lastWaitUs: i64 = 0
|
|
4647
|
+
/** @internal Used by `PgBatcher.takeDue` in this module. */
|
|
4123
4648
|
readonly maxCount: i32
|
|
4124
4649
|
private readonly minCount: i32
|
|
4125
4650
|
private readonly factor: f64
|
|
@@ -4138,7 +4663,12 @@ export class PgBatchClock {
|
|
|
4138
4663
|
private last: i64 = 0
|
|
4139
4664
|
private due: i32 = 0
|
|
4140
4665
|
|
|
4141
|
-
/**
|
|
4666
|
+
/**
|
|
4667
|
+
* A clock for `config`; panics when a setting is out of the range its field names, or
|
|
4668
|
+
* `minCount` is above `maxCount`.
|
|
4669
|
+
*
|
|
4670
|
+
* @param config The settings, read once here: later changes to it do nothing.
|
|
4671
|
+
*/
|
|
4142
4672
|
constructor(config: PgBatchClockConfig) {
|
|
4143
4673
|
const factor: f64 = config.factor
|
|
4144
4674
|
const weight: f64 = config.weight
|
|
@@ -4171,7 +4701,11 @@ export class PgBatchClock {
|
|
|
4171
4701
|
this.starts = new Array<i64>(5)
|
|
4172
4702
|
}
|
|
4173
4703
|
|
|
4174
|
-
/**
|
|
4704
|
+
/**
|
|
4705
|
+
* How many items wait for a batch.
|
|
4706
|
+
*
|
|
4707
|
+
* @returns The items queued and not yet started in a batch.
|
|
4708
|
+
*/
|
|
4175
4709
|
waiting(): i32 {
|
|
4176
4710
|
return this.count
|
|
4177
4711
|
}
|
|
@@ -4180,6 +4714,9 @@ export class PgBatchClock {
|
|
|
4180
4714
|
* Records an item queued at `nowUs`; false when `queueSize` are waiting. A
|
|
4181
4715
|
* time before the last one given counts as the last one, so a clock that
|
|
4182
4716
|
* steps back cannot make an item older than its neighbours.
|
|
4717
|
+
*
|
|
4718
|
+
* @param nowUs The time the item was queued, in microseconds from the program's fixed point.
|
|
4719
|
+
* @returns True when the item was recorded.
|
|
4183
4720
|
*/
|
|
4184
4721
|
arrived(nowUs: i64): boolean {
|
|
4185
4722
|
const ring: i32 = toI32(this.times.length)
|
|
@@ -4201,7 +4738,11 @@ export class PgBatchClock {
|
|
|
4201
4738
|
return true
|
|
4202
4739
|
}
|
|
4203
4740
|
|
|
4204
|
-
/**
|
|
4741
|
+
/**
|
|
4742
|
+
* The count that starts a batch now.
|
|
4743
|
+
*
|
|
4744
|
+
* @returns The larger of `minCount` and the average times `factor`, at most `maxCount`.
|
|
4745
|
+
*/
|
|
4205
4746
|
target(): i32 {
|
|
4206
4747
|
const scaled: f64 = Math.ceil(this.average * this.factor)
|
|
4207
4748
|
let target: i32 = this.minCount
|
|
@@ -4215,6 +4756,12 @@ export class PgBatchClock {
|
|
|
4215
4756
|
* The reason a batch should start at `nowUs`, with `inFlight` batches (or
|
|
4216
4757
|
* groups) still running, or `PG_CLOCK_WAIT`. It only says; the program
|
|
4217
4758
|
* starts the batch when a connection is free, then calls `started`.
|
|
4759
|
+
*
|
|
4760
|
+
* @param nowUs The time now, in the clock's microseconds.
|
|
4761
|
+
* @param inFlight The batches or groups still running; 0 or less lets the idle bypass fire.
|
|
4762
|
+
* @returns `PG_CLOCK_COUNT`, `PG_CLOCK_AVERAGE`, `PG_CLOCK_TIMER`, `PG_CLOCK_IDLE`, or
|
|
4763
|
+
* `PG_CLOCK_WAIT`, always `PG_CLOCK_WAIT` when nothing waits. The next `started` records it
|
|
4764
|
+
* as `lastReason`.
|
|
4218
4765
|
*/
|
|
4219
4766
|
check(nowUs: i64, inFlight: i32): i32 {
|
|
4220
4767
|
let reason: i32 = PG_CLOCK_WAIT
|
|
@@ -4232,7 +4779,11 @@ export class PgBatchClock {
|
|
|
4232
4779
|
return reason
|
|
4233
4780
|
}
|
|
4234
4781
|
|
|
4235
|
-
/**
|
|
4782
|
+
/**
|
|
4783
|
+
* When the oldest waiting item will have waited `maxWaitUs`.
|
|
4784
|
+
*
|
|
4785
|
+
* @returns That time in the caller's microseconds, or -1 when none waits.
|
|
4786
|
+
*/
|
|
4236
4787
|
deadline(): i64 {
|
|
4237
4788
|
if (this.count === 0) {
|
|
4238
4789
|
return -1
|
|
@@ -4244,6 +4795,10 @@ export class PgBatchClock {
|
|
|
4244
4795
|
* Records a batch of the `size` oldest items started at `nowUs`, and moves
|
|
4245
4796
|
* the average toward it; the first batch sets it. Panics when `size` is
|
|
4246
4797
|
* below 1 or more than are waiting or than `maxCount`.
|
|
4798
|
+
*
|
|
4799
|
+
* @param size The number of items the batch took, oldest first.
|
|
4800
|
+
* @param nowUs The time the batch started, in the clock's microseconds; `lastWaitUs` is
|
|
4801
|
+
* measured to it, and is 0 when it is before the oldest item's arrival.
|
|
4247
4802
|
*/
|
|
4248
4803
|
started(size: i32, nowUs: i64): void {
|
|
4249
4804
|
if (size < 1 || size > this.count || size > this.maxCount) {
|
|
@@ -4263,7 +4818,13 @@ export class PgBatchClock {
|
|
|
4263
4818
|
this.due = PG_CLOCK_WAIT
|
|
4264
4819
|
}
|
|
4265
4820
|
|
|
4266
|
-
/**
|
|
4821
|
+
/**
|
|
4822
|
+
* How many batches have started for `reason`, one of the `PG_CLOCK_*` (`PG_CLOCK_WAIT` counts
|
|
4823
|
+
* those started without a due `check`). Panics on any other `reason`.
|
|
4824
|
+
*
|
|
4825
|
+
* @param reason `PG_CLOCK_WAIT` to `PG_CLOCK_IDLE`.
|
|
4826
|
+
* @returns The count since the clock was made.
|
|
4827
|
+
*/
|
|
4267
4828
|
startsFor(reason: i32): i64 {
|
|
4268
4829
|
if (reason < 0 || reason > PG_CLOCK_IDLE) {
|
|
4269
4830
|
panic("nish/net/postgres: PgBatchClock.startsFor with a reason out of range")
|
|
@@ -4312,12 +4873,18 @@ export class PgBatchTurns {
|
|
|
4312
4873
|
/** The clock the turns read and record into. */
|
|
4313
4874
|
readonly clock: PgBatchClock
|
|
4314
4875
|
/** The number of slots. */
|
|
4315
|
-
readonly size: i32
|
|
4876
|
+
private readonly size: i32
|
|
4316
4877
|
private readonly busy: boolean[]
|
|
4317
4878
|
private running: i32 = 0
|
|
4318
4879
|
private woken: i32 = -1
|
|
4319
4880
|
|
|
4320
|
-
/**
|
|
4881
|
+
/**
|
|
4882
|
+
* Turns over `size` slots (1 to 1024), all free; panics on a size out of range.
|
|
4883
|
+
*
|
|
4884
|
+
* @param clock The clock the program's batches are formed by; kept. `push` and `started`
|
|
4885
|
+
* record into it, so the program does not call its `arrived` or `started` itself.
|
|
4886
|
+
* @param size The number of connections, numbered 0 to `size - 1`.
|
|
4887
|
+
*/
|
|
4321
4888
|
constructor(clock: PgBatchClock, size: i32) {
|
|
4322
4889
|
if (size < 1 || size > PG_POOL_MAX_SLOTS) {
|
|
4323
4890
|
panic("nish/net/postgres: a PgBatchTurns size out of range")
|
|
@@ -4327,7 +4894,11 @@ export class PgBatchTurns {
|
|
|
4327
4894
|
this.busy = new Array<boolean>(size)
|
|
4328
4895
|
}
|
|
4329
4896
|
|
|
4330
|
-
/**
|
|
4897
|
+
/**
|
|
4898
|
+
* The slot that collects the next batch.
|
|
4899
|
+
*
|
|
4900
|
+
* @returns The lowest-numbered free slot, or `PG_TURNS_NONE` when every slot holds a batch.
|
|
4901
|
+
*/
|
|
4331
4902
|
collector(): i32 {
|
|
4332
4903
|
for (let i: i32 = 0; i < this.size; i++) {
|
|
4333
4904
|
if (!this.busy[i]) {
|
|
@@ -4337,17 +4908,31 @@ export class PgBatchTurns {
|
|
|
4337
4908
|
return PG_TURNS_NONE
|
|
4338
4909
|
}
|
|
4339
4910
|
|
|
4340
|
-
/**
|
|
4911
|
+
/**
|
|
4912
|
+
* How many slots hold a batch.
|
|
4913
|
+
*
|
|
4914
|
+
* @returns The slots whose batch has `started` and not yet `finished`.
|
|
4915
|
+
*/
|
|
4341
4916
|
inFlight(): i32 {
|
|
4342
4917
|
return this.running
|
|
4343
4918
|
}
|
|
4344
4919
|
|
|
4345
|
-
/**
|
|
4920
|
+
/**
|
|
4921
|
+
* When the collector's timer fires, as `PgBatchClock.deadline()` gives it.
|
|
4922
|
+
*
|
|
4923
|
+
* @returns That time in the caller's microseconds, or -1 when nothing waits.
|
|
4924
|
+
*/
|
|
4346
4925
|
deadline(): i64 {
|
|
4347
4926
|
return this.clock.deadline()
|
|
4348
4927
|
}
|
|
4349
4928
|
|
|
4350
|
-
/**
|
|
4929
|
+
/**
|
|
4930
|
+
* Records an item queued at `nowUs` and answers the slot to wake, or `PG_TURNS_FULL` when the
|
|
4931
|
+
* clock is full.
|
|
4932
|
+
*
|
|
4933
|
+
* @param nowUs The time the item was queued, in the clock's microseconds.
|
|
4934
|
+
* @returns The slot to wake, `PG_TURNS_NONE` when none is to be woken, or `PG_TURNS_FULL`.
|
|
4935
|
+
*/
|
|
4351
4936
|
push(nowUs: i64): i32 {
|
|
4352
4937
|
if (!this.clock.arrived(nowUs)) {
|
|
4353
4938
|
return PG_TURNS_FULL
|
|
@@ -4355,7 +4940,12 @@ export class PgBatchTurns {
|
|
|
4355
4940
|
return this.poll(nowUs)
|
|
4356
4941
|
}
|
|
4357
4942
|
|
|
4358
|
-
/**
|
|
4943
|
+
/**
|
|
4944
|
+
* The slot to wake at `nowUs`, when a batch is due and no slot is named already.
|
|
4945
|
+
*
|
|
4946
|
+
* @param nowUs The time now, in the clock's microseconds; call at `deadline()`.
|
|
4947
|
+
* @returns The collector's slot, or `PG_TURNS_NONE`.
|
|
4948
|
+
*/
|
|
4359
4949
|
poll(nowUs: i64): i32 {
|
|
4360
4950
|
if (this.woken >= 0) {
|
|
4361
4951
|
return PG_TURNS_NONE
|
|
@@ -4368,7 +4958,17 @@ export class PgBatchTurns {
|
|
|
4368
4958
|
return slot
|
|
4369
4959
|
}
|
|
4370
4960
|
|
|
4371
|
-
/**
|
|
4961
|
+
/**
|
|
4962
|
+
* Records `slot` starting a batch of `size` items at `nowUs`, and answers the next slot to
|
|
4963
|
+
* wake. Panics on a slot out of range or holding a batch already, or as
|
|
4964
|
+
* `PgBatchClock.started`.
|
|
4965
|
+
*
|
|
4966
|
+
* @param slot The slot that took the batch, from 0 to `size - 1`; it holds the batch until
|
|
4967
|
+
* `finished`.
|
|
4968
|
+
* @param size The number of items the batch took.
|
|
4969
|
+
* @param nowUs The time the batch started, in the clock's microseconds.
|
|
4970
|
+
* @returns The slot to wake, or `PG_TURNS_NONE`.
|
|
4971
|
+
*/
|
|
4372
4972
|
started(slot: i32, size: i32, nowUs: i64): i32 {
|
|
4373
4973
|
this.free(slot)
|
|
4374
4974
|
this.clock.started(size, nowUs)
|
|
@@ -4380,7 +4980,14 @@ export class PgBatchTurns {
|
|
|
4380
4980
|
return this.poll(nowUs)
|
|
4381
4981
|
}
|
|
4382
4982
|
|
|
4383
|
-
/**
|
|
4983
|
+
/**
|
|
4984
|
+
* Records `slot`'s batch done at `nowUs`, and answers the slot to wake. Panics on a slot out
|
|
4985
|
+
* of range or holding no batch.
|
|
4986
|
+
*
|
|
4987
|
+
* @param slot The slot whose batch is done, from 0 to `size - 1`.
|
|
4988
|
+
* @param nowUs The time now, in the clock's microseconds.
|
|
4989
|
+
* @returns The slot to wake, or `PG_TURNS_NONE`.
|
|
4990
|
+
*/
|
|
4384
4991
|
finished(slot: i32, nowUs: i64): i32 {
|
|
4385
4992
|
if (slot < 0 || slot >= this.size || !this.busy[slot]) {
|
|
4386
4993
|
panic("nish/net/postgres: PgBatchTurns.finished on a slot holding no batch")
|
|
@@ -4390,7 +4997,12 @@ export class PgBatchTurns {
|
|
|
4390
4997
|
return this.poll(nowUs)
|
|
4391
4998
|
}
|
|
4392
4999
|
|
|
4393
|
-
/**
|
|
5000
|
+
/**
|
|
5001
|
+
* Gives back a wake that started nothing, so the next push or poll can wake a slot again.
|
|
5002
|
+
* Panics on a slot out of range or holding a batch.
|
|
5003
|
+
*
|
|
5004
|
+
* @param slot The slot that was woken and took no batch.
|
|
5005
|
+
*/
|
|
4394
5006
|
release(slot: i32): void {
|
|
4395
5007
|
this.free(slot)
|
|
4396
5008
|
if (this.woken === slot) {
|
|
@@ -4462,6 +5074,11 @@ const pgTargetFailed = (failure: i32): PgTarget => {
|
|
|
4462
5074
|
* empty host, a `,` (more than one host) or a NUL byte is refused, and so is
|
|
4463
5075
|
* a socket path over 107 bytes (an `@` name over 107 after its `@`), never
|
|
4464
5076
|
* cut short.
|
|
5077
|
+
*
|
|
5078
|
+
* @param host The host, a socket directory starting with `/`, or an abstract name starting
|
|
5079
|
+
* with `@`; taken as it is, not percent-decoded.
|
|
5080
|
+
* @param port The TCP port, or the number in the socket's name, from 1 to 65535.
|
|
5081
|
+
* @returns The target; read `failure` before anything else.
|
|
4465
5082
|
*/
|
|
4466
5083
|
export const pgTarget = (host: string, port: i32): PgTarget => {
|
|
4467
5084
|
if (port < 1 || port > 65535) {
|
|
@@ -4533,6 +5150,9 @@ const pgPortValue = (text: string): i32 => {
|
|
|
4533
5150
|
* would use its default socket directory. An empty port or query value is
|
|
4534
5151
|
* refused, where libpq would take it as unset.
|
|
4535
5152
|
* The decoded host is then resolved as `pgTarget` resolves one.
|
|
5153
|
+
*
|
|
5154
|
+
* @param url The URL; any user or password it holds is not kept in the target.
|
|
5155
|
+
* @returns The target; read `failure` before anything else.
|
|
4536
5156
|
*/
|
|
4537
5157
|
export const pgTargetUrl = (url: string): PgTarget => {
|
|
4538
5158
|
let at: i32 = 0
|