camusdb 0.1.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/LICENSE +21 -0
- package/README.md +950 -0
- package/dist/auth/credentials.d.ts +30 -0
- package/dist/auth/credentials.d.ts.map +1 -0
- package/dist/auth/credentials.js +23 -0
- package/dist/auth/credentials.js.map +1 -0
- package/dist/auth/login-client.d.ts +37 -0
- package/dist/auth/login-client.d.ts.map +1 -0
- package/dist/auth/login-client.js +8 -0
- package/dist/auth/login-client.js.map +1 -0
- package/dist/auth/rest-login-client.d.ts +17 -0
- package/dist/auth/rest-login-client.d.ts.map +1 -0
- package/dist/auth/rest-login-client.js +71 -0
- package/dist/auth/rest-login-client.js.map +1 -0
- package/dist/auth/token-provider.d.ts +123 -0
- package/dist/auth/token-provider.d.ts.map +1 -0
- package/dist/auth/token-provider.js +209 -0
- package/dist/auth/token-provider.js.map +1 -0
- package/dist/backup.d.ts +98 -0
- package/dist/backup.d.ts.map +1 -0
- package/dist/backup.js +153 -0
- package/dist/backup.js.map +1 -0
- package/dist/cache.d.ts +90 -0
- package/dist/cache.d.ts.map +1 -0
- package/dist/cache.js +121 -0
- package/dist/cache.js.map +1 -0
- package/dist/client.d.ts +406 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +724 -0
- package/dist/client.js.map +1 -0
- package/dist/column-type.d.ts +33 -0
- package/dist/column-type.d.ts.map +1 -0
- package/dist/column-type.js +50 -0
- package/dist/column-type.js.map +1 -0
- package/dist/column-value.d.ts +77 -0
- package/dist/column-value.d.ts.map +1 -0
- package/dist/column-value.js +60 -0
- package/dist/column-value.js.map +1 -0
- package/dist/config.d.ts +181 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +315 -0
- package/dist/config.js.map +1 -0
- package/dist/connection-string.d.ts +45 -0
- package/dist/connection-string.d.ts.map +1 -0
- package/dist/connection-string.js +170 -0
- package/dist/connection-string.js.map +1 -0
- package/dist/endpoint-pool.d.ts +70 -0
- package/dist/endpoint-pool.d.ts.map +1 -0
- package/dist/endpoint-pool.js +120 -0
- package/dist/endpoint-pool.js.map +1 -0
- package/dist/error-codes.d.ts +44 -0
- package/dist/error-codes.d.ts.map +1 -0
- package/dist/error-codes.js +43 -0
- package/dist/error-codes.js.map +1 -0
- package/dist/errors.d.ts +24 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +33 -0
- package/dist/errors.js.map +1 -0
- package/dist/hlc.d.ts +19 -0
- package/dist/hlc.d.ts.map +1 -0
- package/dist/hlc.js +11 -0
- package/dist/hlc.js.map +1 -0
- package/dist/index.d.ts +55 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +51 -0
- package/dist/index.js.map +1 -0
- package/dist/json.d.ts +41 -0
- package/dist/json.d.ts.map +1 -0
- package/dist/json.js +345 -0
- package/dist/json.js.map +1 -0
- package/dist/object-id.d.ts +39 -0
- package/dist/object-id.d.ts.map +1 -0
- package/dist/object-id.js +99 -0
- package/dist/object-id.js.map +1 -0
- package/dist/options.d.ts +77 -0
- package/dist/options.d.ts.map +1 -0
- package/dist/options.js +68 -0
- package/dist/options.js.map +1 -0
- package/dist/prepared/binder.d.ts +20 -0
- package/dist/prepared/binder.d.ts.map +1 -0
- package/dist/prepared/binder.js +32 -0
- package/dist/prepared/binder.js.map +1 -0
- package/dist/prepared/policy.d.ts +81 -0
- package/dist/prepared/policy.d.ts.map +1 -0
- package/dist/prepared/policy.js +164 -0
- package/dist/prepared/policy.js.map +1 -0
- package/dist/query-stream.d.ts +55 -0
- package/dist/query-stream.d.ts.map +1 -0
- package/dist/query-stream.js +94 -0
- package/dist/query-stream.js.map +1 -0
- package/dist/result-set.d.ts +62 -0
- package/dist/result-set.d.ts.map +1 -0
- package/dist/result-set.js +100 -0
- package/dist/result-set.js.map +1 -0
- package/dist/retry.d.ts +38 -0
- package/dist/retry.d.ts.map +1 -0
- package/dist/retry.js +93 -0
- package/dist/retry.js.map +1 -0
- package/dist/routing/advice.d.ts +63 -0
- package/dist/routing/advice.d.ts.map +1 -0
- package/dist/routing/advice.js +61 -0
- package/dist/routing/advice.js.map +1 -0
- package/dist/routing/route-cache.d.ts +53 -0
- package/dist/routing/route-cache.d.ts.map +1 -0
- package/dist/routing/route-cache.js +123 -0
- package/dist/routing/route-cache.js.map +1 -0
- package/dist/routing/router.d.ts +53 -0
- package/dist/routing/router.d.ts.map +1 -0
- package/dist/routing/router.js +89 -0
- package/dist/routing/router.js.map +1 -0
- package/dist/row-source.d.ts +48 -0
- package/dist/row-source.d.ts.map +1 -0
- package/dist/row-source.js +35 -0
- package/dist/row-source.js.map +1 -0
- package/dist/runtime.d.ts +58 -0
- package/dist/runtime.d.ts.map +1 -0
- package/dist/runtime.js +137 -0
- package/dist/runtime.js.map +1 -0
- package/dist/shared-registry.d.ts +32 -0
- package/dist/shared-registry.d.ts.map +1 -0
- package/dist/shared-registry.js +48 -0
- package/dist/shared-registry.js.map +1 -0
- package/dist/sql-syntax.d.ts +44 -0
- package/dist/sql-syntax.d.ts.map +1 -0
- package/dist/sql-syntax.js +83 -0
- package/dist/sql-syntax.js.map +1 -0
- package/dist/statements.d.ts +15 -0
- package/dist/statements.d.ts.map +1 -0
- package/dist/statements.js +90 -0
- package/dist/statements.js.map +1 -0
- package/dist/transaction.d.ts +117 -0
- package/dist/transaction.d.ts.map +1 -0
- package/dist/transaction.js +204 -0
- package/dist/transaction.js.map +1 -0
- package/dist/transport/authenticating-transport.d.ts +63 -0
- package/dist/transport/authenticating-transport.d.ts.map +1 -0
- package/dist/transport/authenticating-transport.js +119 -0
- package/dist/transport/authenticating-transport.js.map +1 -0
- package/dist/transport/branch-statements.d.ts +39 -0
- package/dist/transport/branch-statements.d.ts.map +1 -0
- package/dist/transport/branch-statements.js +74 -0
- package/dist/transport/branch-statements.js.map +1 -0
- package/dist/transport/error-text.d.ts +26 -0
- package/dist/transport/error-text.d.ts.map +1 -0
- package/dist/transport/error-text.js +75 -0
- package/dist/transport/error-text.js.map +1 -0
- package/dist/transport/grpc/batcher.d.ts +127 -0
- package/dist/transport/grpc/batcher.d.ts.map +1 -0
- package/dist/transport/grpc/batcher.js +399 -0
- package/dist/transport/grpc/batcher.js.map +1 -0
- package/dist/transport/grpc/codec.d.ts +40 -0
- package/dist/transport/grpc/codec.d.ts.map +1 -0
- package/dist/transport/grpc/codec.js +231 -0
- package/dist/transport/grpc/codec.js.map +1 -0
- package/dist/transport/grpc/grpc-transport.d.ts +115 -0
- package/dist/transport/grpc/grpc-transport.d.ts.map +1 -0
- package/dist/transport/grpc/grpc-transport.js +577 -0
- package/dist/transport/grpc/grpc-transport.js.map +1 -0
- package/dist/transport/grpc/messages.d.ts +221 -0
- package/dist/transport/grpc/messages.d.ts.map +1 -0
- package/dist/transport/grpc/messages.js +56 -0
- package/dist/transport/grpc/messages.js.map +1 -0
- package/dist/transport/grpc/proto.d.ts +71 -0
- package/dist/transport/grpc/proto.d.ts.map +1 -0
- package/dist/transport/grpc/proto.js +67 -0
- package/dist/transport/grpc/proto.js.map +1 -0
- package/dist/transport/http.d.ts +62 -0
- package/dist/transport/http.d.ts.map +1 -0
- package/dist/transport/http.js +169 -0
- package/dist/transport/http.js.map +1 -0
- package/dist/transport/ndjson-row-source.d.ts +38 -0
- package/dist/transport/ndjson-row-source.d.ts.map +1 -0
- package/dist/transport/ndjson-row-source.js +197 -0
- package/dist/transport/ndjson-row-source.js.map +1 -0
- package/dist/transport/rest-decode.d.ts +33 -0
- package/dist/transport/rest-decode.d.ts.map +1 -0
- package/dist/transport/rest-decode.js +162 -0
- package/dist/transport/rest-decode.js.map +1 -0
- package/dist/transport/rest-prepared-cache.d.ts +50 -0
- package/dist/transport/rest-prepared-cache.d.ts.map +1 -0
- package/dist/transport/rest-prepared-cache.js +101 -0
- package/dist/transport/rest-prepared-cache.js.map +1 -0
- package/dist/transport/rest-transport.d.ts +88 -0
- package/dist/transport/rest-transport.d.ts.map +1 -0
- package/dist/transport/rest-transport.js +388 -0
- package/dist/transport/rest-transport.js.map +1 -0
- package/dist/transport/transport-pool.d.ts +40 -0
- package/dist/transport/transport-pool.d.ts.map +1 -0
- package/dist/transport/transport-pool.js +48 -0
- package/dist/transport/transport-pool.js.map +1 -0
- package/dist/transport/transport.d.ts +199 -0
- package/dist/transport/transport.d.ts.map +1 -0
- package/dist/transport/transport.js +11 -0
- package/dist/transport/transport.js.map +1 -0
- package/dist/values/decode.d.ts +39 -0
- package/dist/values/decode.d.ts.map +1 -0
- package/dist/values/decode.js +75 -0
- package/dist/values/decode.js.map +1 -0
- package/dist/values/encode.d.ts +45 -0
- package/dist/values/encode.d.ts.map +1 -0
- package/dist/values/encode.js +271 -0
- package/dist/values/encode.js.map +1 -0
- package/dist/values/ticks.d.ts +31 -0
- package/dist/values/ticks.d.ts.map +1 -0
- package/dist/values/ticks.js +52 -0
- package/dist/values/ticks.js.map +1 -0
- package/dist/values/typed.d.ts +67 -0
- package/dist/values/typed.d.ts.map +1 -0
- package/dist/values/typed.js +134 -0
- package/dist/values/typed.js.map +1 -0
- package/dist/values/uuid.d.ts +27 -0
- package/dist/values/uuid.d.ts.map +1 -0
- package/dist/values/uuid.js +76 -0
- package/dist/values/uuid.js.map +1 -0
- package/dist/vector.d.ts +26 -0
- package/dist/vector.d.ts.map +1 -0
- package/dist/vector.js +48 -0
- package/dist/vector.js.map +1 -0
- package/package.json +86 -0
- package/proto/camus_sql.proto +566 -0
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* This file is part of CamusDB
|
|
3
|
+
*
|
|
4
|
+
* For the full copyright and license information, please view the LICENSE.txt
|
|
5
|
+
* file that was distributed with this source code.
|
|
6
|
+
*/
|
|
7
|
+
import type { CamusTransactionOptions } from './options.js';
|
|
8
|
+
import type { ClientRuntime } from './runtime.js';
|
|
9
|
+
/**
|
|
10
|
+
* A set of reads and writes that take effect atomically, at one logical instant, across columns,
|
|
11
|
+
* rows, and tables.
|
|
12
|
+
*
|
|
13
|
+
* **Where a transaction starts.** With learned routing off, `BEGIN` is sent when the transaction is
|
|
14
|
+
* created, on the pool's next endpoint. With learned routing on, `BEGIN` is deferred: the
|
|
15
|
+
* transaction is started by its first statement, on the endpoint that statement's learned route
|
|
16
|
+
* names, so a transaction whose first statement touches a table runs where that table's leader is.
|
|
17
|
+
* Once started it is pinned to that endpoint for its whole life; a statement's own route may inform
|
|
18
|
+
* a later transaction, but never relocates this one. A caller that knows the transaction's hot
|
|
19
|
+
* statement can choose the endpoint at `BEGIN` with `options.affinity` instead.
|
|
20
|
+
*
|
|
21
|
+
* Two consequences of the deferral are visible, and only in routed mode: the server-minted identity
|
|
22
|
+
* reads as zero until the first statement, and a failure to begin surfaces from that first
|
|
23
|
+
* statement rather than from the call that created the transaction.
|
|
24
|
+
*/
|
|
25
|
+
export declare class CamusTransaction {
|
|
26
|
+
private readonly runtime;
|
|
27
|
+
private txnIdPTValue;
|
|
28
|
+
private txnIdCounterValue;
|
|
29
|
+
private endpointValue;
|
|
30
|
+
private startedValue;
|
|
31
|
+
/**
|
|
32
|
+
* The single `BEGIN` in flight or finished, for a deferred transaction. Every racing first
|
|
33
|
+
* statement awaits this same promise, and a `BEGIN` that failed stays failed: nothing was minted,
|
|
34
|
+
* the transaction is unusable, and the caller begins a new one — which is the state a failed
|
|
35
|
+
* eager `BEGIN` leaves too.
|
|
36
|
+
*/
|
|
37
|
+
private startPromise;
|
|
38
|
+
private startFailed;
|
|
39
|
+
private finalized;
|
|
40
|
+
/** The concurrency knobs this transaction was begun with, after every default was applied. */
|
|
41
|
+
readonly options: CamusTransactionOptions;
|
|
42
|
+
/**
|
|
43
|
+
* The stream slot this transaction's statements pin to, so the server sees them in one ordering
|
|
44
|
+
* chain. It is a gRPC concept; REST does not pin.
|
|
45
|
+
*/
|
|
46
|
+
streamSlot: number | undefined;
|
|
47
|
+
/** @internal Use `CamusClient.beginTransaction`. */
|
|
48
|
+
constructor(runtime: ClientRuntime, options: CamusTransactionOptions, started?: {
|
|
49
|
+
txnIdPT: bigint;
|
|
50
|
+
txnIdCounter: number;
|
|
51
|
+
endpoint: string;
|
|
52
|
+
streamSlot?: number | undefined;
|
|
53
|
+
});
|
|
54
|
+
/**
|
|
55
|
+
* True once the server minted this transaction.
|
|
56
|
+
*
|
|
57
|
+
* It is always true with learned routing off. With routing on it is false until the first
|
|
58
|
+
* statement, or until an explicit `affinity`, starts it.
|
|
59
|
+
*/
|
|
60
|
+
get isStarted(): boolean;
|
|
61
|
+
/** True once this transaction was committed or rolled back. */
|
|
62
|
+
get isFinalized(): boolean;
|
|
63
|
+
/** The physical-time half of the server-minted identity. Zero until the transaction starts. */
|
|
64
|
+
get txnIdPT(): bigint;
|
|
65
|
+
/** The counter half of the server-minted identity. Zero until the transaction starts. */
|
|
66
|
+
get txnIdCounter(): number;
|
|
67
|
+
/** The identity as one string, for logs and correlation. */
|
|
68
|
+
get transactionId(): string;
|
|
69
|
+
/** The endpoint this transaction is pinned to, or `undefined` while it has not started. */
|
|
70
|
+
get endpoint(): string | undefined;
|
|
71
|
+
/**
|
|
72
|
+
* Starts the transaction if it has not started, and reports the endpoint it is pinned to.
|
|
73
|
+
*
|
|
74
|
+
* It starts on `preferredEndpoint` when one is given — a learned route for the statement about
|
|
75
|
+
* to run — and on the pool's rotation otherwise. The first caller sends `BEGIN`; a concurrent
|
|
76
|
+
* caller awaits that same send. Once started, a later call's preferred endpoint is ignored,
|
|
77
|
+
* because the pin is final.
|
|
78
|
+
*
|
|
79
|
+
* @internal
|
|
80
|
+
*/
|
|
81
|
+
ensureStarted(preferredEndpoint: string | undefined, signal?: AbortSignal): Promise<string>;
|
|
82
|
+
private startOn;
|
|
83
|
+
/** Makes this transaction's writes durable. */
|
|
84
|
+
commit(signal?: AbortSignal): Promise<void>;
|
|
85
|
+
/** Discards this transaction's writes. */
|
|
86
|
+
rollback(signal?: AbortSignal): Promise<void>;
|
|
87
|
+
/**
|
|
88
|
+
* Issues a commit or a rollback, and resolves a `CADB0509` outcome by re-issuing the **same**
|
|
89
|
+
* finalize on the **same** handle, bounded and backing off.
|
|
90
|
+
*
|
|
91
|
+
* `CADB0509` says the outcome is not known yet. The transaction is not dead, so the operation
|
|
92
|
+
* must never be replayed from `BEGIN`: that could apply an already-durable commit a second time.
|
|
93
|
+
* Every other failure reaches the caller.
|
|
94
|
+
*
|
|
95
|
+
* A deferred transaction that ran no statement is started here first, on the pool's rotation, so
|
|
96
|
+
* the server sees the same `BEGIN` and finalize pair it always did.
|
|
97
|
+
*/
|
|
98
|
+
private finalize;
|
|
99
|
+
/**
|
|
100
|
+
* Rolls the transaction back if it was neither committed nor rolled back, so a `using` block
|
|
101
|
+
* cannot leave one open. A rollback that itself fails is swallowed: the block is unwinding, and
|
|
102
|
+
* the server ends an abandoned transaction on its own session timeout.
|
|
103
|
+
*/
|
|
104
|
+
[Symbol.asyncDispose](): Promise<void>;
|
|
105
|
+
/**
|
|
106
|
+
* The routing kind a statement of this transaction reports. Statements inside a transaction still
|
|
107
|
+
* negotiate and learn, for the benefit of the next transaction, but the route they learn never
|
|
108
|
+
* moves this one.
|
|
109
|
+
*
|
|
110
|
+
* @internal
|
|
111
|
+
*/
|
|
112
|
+
static readonly routeKind: {
|
|
113
|
+
readonly Query: 0;
|
|
114
|
+
readonly NonQuery: 1;
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
//# sourceMappingURL=transaction.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"transaction.d.ts","sourceRoot":"","sources":["../src/transaction.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,cAAc,CAAC;AAE5D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAGlD;;;;;;;;;;;;;;;GAeG;AACH,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAgB;IAExC,OAAO,CAAC,YAAY,CAAM;IAE1B,OAAO,CAAC,iBAAiB,CAAK;IAE9B,OAAO,CAAC,aAAa,CAAqB;IAE1C,OAAO,CAAC,YAAY,CAAS;IAE7B;;;;;OAKG;IACH,OAAO,CAAC,YAAY,CAA4B;IAEhD,OAAO,CAAC,WAAW,CAAS;IAE5B,OAAO,CAAC,SAAS,CAAS;IAE1B,8FAA8F;IAC9F,QAAQ,CAAC,OAAO,EAAE,uBAAuB,CAAC;IAE1C;;;OAGG;IACH,UAAU,EAAE,MAAM,GAAG,SAAS,CAAC;IAE/B,oDAAoD;gBAElD,OAAO,EAAE,aAAa,EACtB,OAAO,EAAE,uBAAuB,EAChC,OAAO,CAAC,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,YAAY,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAC;QAAC,UAAU,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;KAAE;IAcxG;;;;;OAKG;IACH,IAAI,SAAS,IAAI,OAAO,CAEvB;IAED,+DAA+D;IAC/D,IAAI,WAAW,IAAI,OAAO,CAEzB;IAED,+FAA+F;IAC/F,IAAI,OAAO,IAAI,MAAM,CAEpB;IAED,yFAAyF;IACzF,IAAI,YAAY,IAAI,MAAM,CAEzB;IAED,4DAA4D;IAC5D,IAAI,aAAa,IAAI,MAAM,CAE1B;IAED,2FAA2F;IAC3F,IAAI,QAAQ,IAAI,MAAM,GAAG,SAAS,CAEjC;IAED;;;;;;;;;OASG;IACG,aAAa,CAAC,iBAAiB,EAAE,MAAM,GAAG,SAAS,EAAE,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,MAAM,CAAC;YAUnF,OAAO;IAqBrB,+CAA+C;IAC/C,MAAM,CAAC,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC;IAI3C,0CAA0C;IAC1C,QAAQ,CAAC,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC;IAI7C;;;;;;;;;;OAUG;YACW,QAAQ;IA4CtB;;;;OAIG;IACG,CAAC,MAAM,CAAC,YAAY,CAAC,IAAI,OAAO,CAAC,IAAI,CAAC;IAU5C;;;;;;OAMG;IACH,MAAM,CAAC,QAAQ,CAAC,SAAS;;;MAAoB;CAC9C"}
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* This file is part of CamusDB
|
|
3
|
+
*
|
|
4
|
+
* For the full copyright and license information, please view the LICENSE.txt
|
|
5
|
+
* file that was distributed with this source code.
|
|
6
|
+
*/
|
|
7
|
+
import { CamusError } from './errors.js';
|
|
8
|
+
import { CamusErrorCode } from './error-codes.js';
|
|
9
|
+
import { delay } from './retry.js';
|
|
10
|
+
import { CamusRouteOpKind } from './routing/route-cache.js';
|
|
11
|
+
/**
|
|
12
|
+
* A set of reads and writes that take effect atomically, at one logical instant, across columns,
|
|
13
|
+
* rows, and tables.
|
|
14
|
+
*
|
|
15
|
+
* **Where a transaction starts.** With learned routing off, `BEGIN` is sent when the transaction is
|
|
16
|
+
* created, on the pool's next endpoint. With learned routing on, `BEGIN` is deferred: the
|
|
17
|
+
* transaction is started by its first statement, on the endpoint that statement's learned route
|
|
18
|
+
* names, so a transaction whose first statement touches a table runs where that table's leader is.
|
|
19
|
+
* Once started it is pinned to that endpoint for its whole life; a statement's own route may inform
|
|
20
|
+
* a later transaction, but never relocates this one. A caller that knows the transaction's hot
|
|
21
|
+
* statement can choose the endpoint at `BEGIN` with `options.affinity` instead.
|
|
22
|
+
*
|
|
23
|
+
* Two consequences of the deferral are visible, and only in routed mode: the server-minted identity
|
|
24
|
+
* reads as zero until the first statement, and a failure to begin surfaces from that first
|
|
25
|
+
* statement rather than from the call that created the transaction.
|
|
26
|
+
*/
|
|
27
|
+
export class CamusTransaction {
|
|
28
|
+
runtime;
|
|
29
|
+
txnIdPTValue = 0n;
|
|
30
|
+
txnIdCounterValue = 0;
|
|
31
|
+
endpointValue;
|
|
32
|
+
startedValue = false;
|
|
33
|
+
/**
|
|
34
|
+
* The single `BEGIN` in flight or finished, for a deferred transaction. Every racing first
|
|
35
|
+
* statement awaits this same promise, and a `BEGIN` that failed stays failed: nothing was minted,
|
|
36
|
+
* the transaction is unusable, and the caller begins a new one — which is the state a failed
|
|
37
|
+
* eager `BEGIN` leaves too.
|
|
38
|
+
*/
|
|
39
|
+
startPromise;
|
|
40
|
+
startFailed = false;
|
|
41
|
+
finalized = false;
|
|
42
|
+
/** The concurrency knobs this transaction was begun with, after every default was applied. */
|
|
43
|
+
options;
|
|
44
|
+
/**
|
|
45
|
+
* The stream slot this transaction's statements pin to, so the server sees them in one ordering
|
|
46
|
+
* chain. It is a gRPC concept; REST does not pin.
|
|
47
|
+
*/
|
|
48
|
+
streamSlot;
|
|
49
|
+
/** @internal Use `CamusClient.beginTransaction`. */
|
|
50
|
+
constructor(runtime, options, started) {
|
|
51
|
+
this.runtime = runtime;
|
|
52
|
+
this.options = options;
|
|
53
|
+
if (started !== undefined) {
|
|
54
|
+
this.txnIdPTValue = started.txnIdPT;
|
|
55
|
+
this.txnIdCounterValue = started.txnIdCounter;
|
|
56
|
+
this.endpointValue = started.endpoint;
|
|
57
|
+
this.streamSlot = started.streamSlot;
|
|
58
|
+
this.startedValue = true;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* True once the server minted this transaction.
|
|
63
|
+
*
|
|
64
|
+
* It is always true with learned routing off. With routing on it is false until the first
|
|
65
|
+
* statement, or until an explicit `affinity`, starts it.
|
|
66
|
+
*/
|
|
67
|
+
get isStarted() {
|
|
68
|
+
return this.startedValue;
|
|
69
|
+
}
|
|
70
|
+
/** True once this transaction was committed or rolled back. */
|
|
71
|
+
get isFinalized() {
|
|
72
|
+
return this.finalized;
|
|
73
|
+
}
|
|
74
|
+
/** The physical-time half of the server-minted identity. Zero until the transaction starts. */
|
|
75
|
+
get txnIdPT() {
|
|
76
|
+
return this.txnIdPTValue;
|
|
77
|
+
}
|
|
78
|
+
/** The counter half of the server-minted identity. Zero until the transaction starts. */
|
|
79
|
+
get txnIdCounter() {
|
|
80
|
+
return this.txnIdCounterValue;
|
|
81
|
+
}
|
|
82
|
+
/** The identity as one string, for logs and correlation. */
|
|
83
|
+
get transactionId() {
|
|
84
|
+
return `${this.txnIdPTValue.toString()}:${String(this.txnIdCounterValue)}`;
|
|
85
|
+
}
|
|
86
|
+
/** The endpoint this transaction is pinned to, or `undefined` while it has not started. */
|
|
87
|
+
get endpoint() {
|
|
88
|
+
return this.endpointValue;
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Starts the transaction if it has not started, and reports the endpoint it is pinned to.
|
|
92
|
+
*
|
|
93
|
+
* It starts on `preferredEndpoint` when one is given — a learned route for the statement about
|
|
94
|
+
* to run — and on the pool's rotation otherwise. The first caller sends `BEGIN`; a concurrent
|
|
95
|
+
* caller awaits that same send. Once started, a later call's preferred endpoint is ignored,
|
|
96
|
+
* because the pin is final.
|
|
97
|
+
*
|
|
98
|
+
* @internal
|
|
99
|
+
*/
|
|
100
|
+
async ensureStarted(preferredEndpoint, signal) {
|
|
101
|
+
if (!this.startedValue) {
|
|
102
|
+
this.startPromise ??= this.startOn(preferredEndpoint ?? this.runtime.nextEndpoint(), signal);
|
|
103
|
+
await this.startPromise;
|
|
104
|
+
}
|
|
105
|
+
return this.endpointValue;
|
|
106
|
+
}
|
|
107
|
+
async startOn(target, signal) {
|
|
108
|
+
try {
|
|
109
|
+
const result = await this.runtime.transport.startTransaction(target, this.runtime.database, this.options, this.runtime.timeoutSeconds, signal);
|
|
110
|
+
this.txnIdPTValue = result.txnIdPT;
|
|
111
|
+
this.txnIdCounterValue = result.txnIdCounter;
|
|
112
|
+
this.streamSlot = result.streamSlot;
|
|
113
|
+
this.endpointValue = target;
|
|
114
|
+
this.startedValue = true;
|
|
115
|
+
}
|
|
116
|
+
catch (error) {
|
|
117
|
+
this.startFailed = true;
|
|
118
|
+
throw error;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
/** Makes this transaction's writes durable. */
|
|
122
|
+
commit(signal) {
|
|
123
|
+
return this.finalize(true, signal);
|
|
124
|
+
}
|
|
125
|
+
/** Discards this transaction's writes. */
|
|
126
|
+
rollback(signal) {
|
|
127
|
+
return this.finalize(false, signal);
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Issues a commit or a rollback, and resolves a `CADB0509` outcome by re-issuing the **same**
|
|
131
|
+
* finalize on the **same** handle, bounded and backing off.
|
|
132
|
+
*
|
|
133
|
+
* `CADB0509` says the outcome is not known yet. The transaction is not dead, so the operation
|
|
134
|
+
* must never be replayed from `BEGIN`: that could apply an already-durable commit a second time.
|
|
135
|
+
* Every other failure reaches the caller.
|
|
136
|
+
*
|
|
137
|
+
* A deferred transaction that ran no statement is started here first, on the pool's rotation, so
|
|
138
|
+
* the server sees the same `BEGIN` and finalize pair it always did.
|
|
139
|
+
*/
|
|
140
|
+
async finalize(commit, signal) {
|
|
141
|
+
if (this.finalized) {
|
|
142
|
+
throw new CamusError(CamusErrorCode.Generic, 'This transaction was already committed or rolled back.');
|
|
143
|
+
}
|
|
144
|
+
// A deferred `BEGIN` that failed left nothing on the server. The failure already surfaced at
|
|
145
|
+
// the statement that triggered it, so a rollback — which a scope issues without looking — has
|
|
146
|
+
// nothing to undo and finishes quietly, while a commit still reports the failed start.
|
|
147
|
+
if (!commit && !this.startedValue && this.startFailed) {
|
|
148
|
+
this.finalized = true;
|
|
149
|
+
return;
|
|
150
|
+
}
|
|
151
|
+
const target = await this.ensureStarted(undefined, signal);
|
|
152
|
+
for (let attempt = 0;; attempt++) {
|
|
153
|
+
try {
|
|
154
|
+
await this.runtime.transport.finalizeTransaction(commit, target, this.runtime.database, this.txnIdPTValue, this.txnIdCounterValue, this.streamSlot, this.runtime.timeoutSeconds, signal);
|
|
155
|
+
this.finalized = true;
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
158
|
+
catch (error) {
|
|
159
|
+
if (!CamusError.is(error) ||
|
|
160
|
+
error.code !== CamusErrorCode.FinalizeUnresolved ||
|
|
161
|
+
attempt >= MAX_FINALIZE_ATTEMPTS) {
|
|
162
|
+
throw error;
|
|
163
|
+
}
|
|
164
|
+
await delay(finalizeDelayMs(attempt), signal);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
/**
|
|
169
|
+
* Rolls the transaction back if it was neither committed nor rolled back, so a `using` block
|
|
170
|
+
* cannot leave one open. A rollback that itself fails is swallowed: the block is unwinding, and
|
|
171
|
+
* the server ends an abandoned transaction on its own session timeout.
|
|
172
|
+
*/
|
|
173
|
+
async [Symbol.asyncDispose]() {
|
|
174
|
+
if (this.finalized)
|
|
175
|
+
return;
|
|
176
|
+
try {
|
|
177
|
+
await this.rollback();
|
|
178
|
+
}
|
|
179
|
+
catch {
|
|
180
|
+
// Nothing useful can be done while a scope is unwinding.
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
/**
|
|
184
|
+
* The routing kind a statement of this transaction reports. Statements inside a transaction still
|
|
185
|
+
* negotiate and learn, for the benefit of the next transaction, but the route they learn never
|
|
186
|
+
* moves this one.
|
|
187
|
+
*
|
|
188
|
+
* @internal
|
|
189
|
+
*/
|
|
190
|
+
static routeKind = CamusRouteOpKind;
|
|
191
|
+
}
|
|
192
|
+
/**
|
|
193
|
+
* How many times an unresolved commit or rollback is re-issued on the same handle before the error
|
|
194
|
+
* reaches the caller. The server's own session timeout is the ultimate backstop.
|
|
195
|
+
*/
|
|
196
|
+
const MAX_FINALIZE_ATTEMPTS = 10;
|
|
197
|
+
/**
|
|
198
|
+
* The finalize back-off: 50 ms doubling to about 3.2 seconds, which matches the server's contract
|
|
199
|
+
* for an unresolved outcome.
|
|
200
|
+
*/
|
|
201
|
+
function finalizeDelayMs(attempt) {
|
|
202
|
+
return 50 * 2 ** Math.min(attempt, 6);
|
|
203
|
+
}
|
|
204
|
+
//# sourceMappingURL=transaction.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"transaction.js","sourceRoot":"","sources":["../src/transaction.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* This file is part of CamusDB
|
|
3
|
+
*
|
|
4
|
+
* For the full copyright and license information, please view the LICENSE.txt
|
|
5
|
+
* file that was distributed with this source code.
|
|
6
|
+
*/
|
|
7
|
+
import type { CamusTokenProvider } from '../auth/token-provider.js';
|
|
8
|
+
import type { CamusProtocol } from '../config.js';
|
|
9
|
+
import type { CamusTransactionOptions } from '../options.js';
|
|
10
|
+
import type { CamusRowSource } from '../row-source.js';
|
|
11
|
+
import type { CamusBranchRow, CamusTransport, NonQueryTransportResult, PreparedStatementInfo, QueryTransportResult, StartTransactionResult, TransportInsertRequest, TransportSqlRequest } from './transport.js';
|
|
12
|
+
/**
|
|
13
|
+
* Wraps a transport with the reactive half of token management.
|
|
14
|
+
*
|
|
15
|
+
* The inner transport attaches whatever token the provider currently holds. This wrapper watches
|
|
16
|
+
* for the server rejecting it with `CADB0516`, discards it, and replays the operation once with a
|
|
17
|
+
* freshly minted one.
|
|
18
|
+
*
|
|
19
|
+
* That covers the failures a client-side expiry timer cannot see: a rotated password, a dropped
|
|
20
|
+
* user, a logout from elsewhere, a server configured with a shorter token lifetime than the driver
|
|
21
|
+
* assumes, or a token minted before a server restart. It does **not** retry `CADB0517`,
|
|
22
|
+
* insufficient privilege: authenticating again as the same user cannot grant a privilege, so a
|
|
23
|
+
* retry would only double the work before the same refusal.
|
|
24
|
+
*
|
|
25
|
+
* There is exactly one replay per call, and only when the attempt actually presented a token the
|
|
26
|
+
* provider can replace. A rejection with no token in hand came from the login itself — a wrong
|
|
27
|
+
* password — and replaying it would burn two attempts per statement against a limit of twenty per
|
|
28
|
+
* account per minute. A token supplied directly is likewise reported as it is, because there is no
|
|
29
|
+
* password to mint a replacement with.
|
|
30
|
+
*
|
|
31
|
+
* A replayed statement never reached execution, so a replay here is no less safe than the retry
|
|
32
|
+
* paths the driver already runs for a serializable conflict.
|
|
33
|
+
*/
|
|
34
|
+
export declare class AuthenticatingTransport implements CamusTransport {
|
|
35
|
+
/** The wrapped transport, for tests and for the login client the gRPC transport also is. */
|
|
36
|
+
readonly inner: CamusTransport;
|
|
37
|
+
private readonly auth;
|
|
38
|
+
constructor(inner: CamusTransport, auth: CamusTokenProvider);
|
|
39
|
+
get protocol(): CamusProtocol;
|
|
40
|
+
startTransaction(endpoint: string, database: string, options: CamusTransactionOptions, timeoutSeconds: number, signal?: AbortSignal): Promise<StartTransactionResult>;
|
|
41
|
+
finalizeTransaction(commit: boolean, endpoint: string, database: string, txnIdPT: bigint, txnIdCounter: number, streamSlot: number | undefined, timeoutSeconds: number, signal?: AbortSignal): Promise<void>;
|
|
42
|
+
executeQuery(request: TransportSqlRequest): Promise<QueryTransportResult>;
|
|
43
|
+
executeQueryStream(request: TransportSqlRequest): Promise<CamusRowSource>;
|
|
44
|
+
executeNonQuery(request: TransportSqlRequest): Promise<NonQueryTransportResult>;
|
|
45
|
+
insert(request: TransportInsertRequest): Promise<number>;
|
|
46
|
+
executeDdl(request: TransportSqlRequest): Promise<boolean>;
|
|
47
|
+
prepare(endpoint: string, database: string, sql: string, timeoutSeconds: number, signal?: AbortSignal): Promise<PreparedStatementInfo>;
|
|
48
|
+
/**
|
|
49
|
+
* Closing is best-effort and idempotent. A token the server has already rejected means the
|
|
50
|
+
* handle it named is unreachable anyway, so there is nothing a replay could still release.
|
|
51
|
+
*/
|
|
52
|
+
closePrepared(endpoint: string, database: string, sql: string, signal?: AbortSignal): Promise<void>;
|
|
53
|
+
ping(endpoint: string, timeoutSeconds: number, signal?: AbortSignal): Promise<boolean>;
|
|
54
|
+
createDatabase(endpoint: string, database: string, ifNotExists: boolean, timeoutSeconds: number, signal?: AbortSignal): Promise<void>;
|
|
55
|
+
createBranchDatabase(endpoint: string, branchName: string, sourceDatabaseName: string, ifNotExists: boolean, timeoutSeconds: number, signal?: AbortSignal): Promise<void>;
|
|
56
|
+
dropDatabase(endpoint: string, database: string, timeoutSeconds: number, signal?: AbortSignal): Promise<void>;
|
|
57
|
+
showBranches(endpoint: string, database: string, timeoutSeconds: number, signal?: AbortSignal): Promise<CamusBranchRow[]>;
|
|
58
|
+
showAncestors(endpoint: string, database: string, timeoutSeconds: number, signal?: AbortSignal): Promise<CamusBranchRow[]>;
|
|
59
|
+
close(): Promise<void>;
|
|
60
|
+
private run;
|
|
61
|
+
private isRenewable;
|
|
62
|
+
}
|
|
63
|
+
//# sourceMappingURL=authenticating-transport.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"authenticating-transport.d.ts","sourceRoot":"","sources":["../../src/transport/authenticating-transport.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* This file is part of CamusDB
|
|
3
|
+
*
|
|
4
|
+
* For the full copyright and license information, please view the LICENSE.txt
|
|
5
|
+
* file that was distributed with this source code.
|
|
6
|
+
*/
|
|
7
|
+
import { CamusError } from '../errors.js';
|
|
8
|
+
import { CamusErrorCode } from '../error-codes.js';
|
|
9
|
+
/**
|
|
10
|
+
* Wraps a transport with the reactive half of token management.
|
|
11
|
+
*
|
|
12
|
+
* The inner transport attaches whatever token the provider currently holds. This wrapper watches
|
|
13
|
+
* for the server rejecting it with `CADB0516`, discards it, and replays the operation once with a
|
|
14
|
+
* freshly minted one.
|
|
15
|
+
*
|
|
16
|
+
* That covers the failures a client-side expiry timer cannot see: a rotated password, a dropped
|
|
17
|
+
* user, a logout from elsewhere, a server configured with a shorter token lifetime than the driver
|
|
18
|
+
* assumes, or a token minted before a server restart. It does **not** retry `CADB0517`,
|
|
19
|
+
* insufficient privilege: authenticating again as the same user cannot grant a privilege, so a
|
|
20
|
+
* retry would only double the work before the same refusal.
|
|
21
|
+
*
|
|
22
|
+
* There is exactly one replay per call, and only when the attempt actually presented a token the
|
|
23
|
+
* provider can replace. A rejection with no token in hand came from the login itself — a wrong
|
|
24
|
+
* password — and replaying it would burn two attempts per statement against a limit of twenty per
|
|
25
|
+
* account per minute. A token supplied directly is likewise reported as it is, because there is no
|
|
26
|
+
* password to mint a replacement with.
|
|
27
|
+
*
|
|
28
|
+
* A replayed statement never reached execution, so a replay here is no less safe than the retry
|
|
29
|
+
* paths the driver already runs for a serializable conflict.
|
|
30
|
+
*/
|
|
31
|
+
export class AuthenticatingTransport {
|
|
32
|
+
/** The wrapped transport, for tests and for the login client the gRPC transport also is. */
|
|
33
|
+
inner;
|
|
34
|
+
auth;
|
|
35
|
+
constructor(inner, auth) {
|
|
36
|
+
this.inner = inner;
|
|
37
|
+
this.auth = auth;
|
|
38
|
+
}
|
|
39
|
+
get protocol() {
|
|
40
|
+
return this.inner.protocol;
|
|
41
|
+
}
|
|
42
|
+
startTransaction(endpoint, database, options, timeoutSeconds, signal) {
|
|
43
|
+
return this.run(() => this.inner.startTransaction(endpoint, database, options, timeoutSeconds, signal));
|
|
44
|
+
}
|
|
45
|
+
finalizeTransaction(commit, endpoint, database, txnIdPT, txnIdCounter, streamSlot, timeoutSeconds, signal) {
|
|
46
|
+
return this.run(() => this.inner.finalizeTransaction(commit, endpoint, database, txnIdPT, txnIdCounter, streamSlot, timeoutSeconds, signal));
|
|
47
|
+
}
|
|
48
|
+
executeQuery(request) {
|
|
49
|
+
return this.run(() => this.inner.executeQuery(request));
|
|
50
|
+
}
|
|
51
|
+
executeQueryStream(request) {
|
|
52
|
+
return this.run(() => this.inner.executeQueryStream(request));
|
|
53
|
+
}
|
|
54
|
+
executeNonQuery(request) {
|
|
55
|
+
return this.run(() => this.inner.executeNonQuery(request));
|
|
56
|
+
}
|
|
57
|
+
insert(request) {
|
|
58
|
+
return this.run(() => this.inner.insert(request));
|
|
59
|
+
}
|
|
60
|
+
executeDdl(request) {
|
|
61
|
+
return this.run(() => this.inner.executeDdl(request));
|
|
62
|
+
}
|
|
63
|
+
prepare(endpoint, database, sql, timeoutSeconds, signal) {
|
|
64
|
+
return this.run(() => this.inner.prepare(endpoint, database, sql, timeoutSeconds, signal));
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Closing is best-effort and idempotent. A token the server has already rejected means the
|
|
68
|
+
* handle it named is unreachable anyway, so there is nothing a replay could still release.
|
|
69
|
+
*/
|
|
70
|
+
closePrepared(endpoint, database, sql, signal) {
|
|
71
|
+
return this.inner.closePrepared(endpoint, database, sql, signal);
|
|
72
|
+
}
|
|
73
|
+
ping(endpoint, timeoutSeconds, signal) {
|
|
74
|
+
return this.run(() => this.inner.ping(endpoint, timeoutSeconds, signal));
|
|
75
|
+
}
|
|
76
|
+
createDatabase(endpoint, database, ifNotExists, timeoutSeconds, signal) {
|
|
77
|
+
return this.run(() => this.inner.createDatabase(endpoint, database, ifNotExists, timeoutSeconds, signal));
|
|
78
|
+
}
|
|
79
|
+
createBranchDatabase(endpoint, branchName, sourceDatabaseName, ifNotExists, timeoutSeconds, signal) {
|
|
80
|
+
return this.run(() => this.inner.createBranchDatabase(endpoint, branchName, sourceDatabaseName, ifNotExists, timeoutSeconds, signal));
|
|
81
|
+
}
|
|
82
|
+
dropDatabase(endpoint, database, timeoutSeconds, signal) {
|
|
83
|
+
return this.run(() => this.inner.dropDatabase(endpoint, database, timeoutSeconds, signal));
|
|
84
|
+
}
|
|
85
|
+
showBranches(endpoint, database, timeoutSeconds, signal) {
|
|
86
|
+
return this.run(() => this.inner.showBranches(endpoint, database, timeoutSeconds, signal));
|
|
87
|
+
}
|
|
88
|
+
showAncestors(endpoint, database, timeoutSeconds, signal) {
|
|
89
|
+
return this.run(() => this.inner.showAncestors(endpoint, database, timeoutSeconds, signal));
|
|
90
|
+
}
|
|
91
|
+
close() {
|
|
92
|
+
return this.inner.close();
|
|
93
|
+
}
|
|
94
|
+
async run(operation) {
|
|
95
|
+
// Read before the call, so a refresh by a concurrent caller is not thrown away: only the token
|
|
96
|
+
// this attempt actually presented is discarded.
|
|
97
|
+
let presented = this.auth.currentToken;
|
|
98
|
+
try {
|
|
99
|
+
return await operation();
|
|
100
|
+
}
|
|
101
|
+
catch (error) {
|
|
102
|
+
if (!this.isRenewable(error))
|
|
103
|
+
throw error;
|
|
104
|
+
// The cache is normally warm before the call. On a client's first statement it is not: the
|
|
105
|
+
// inner transport mints the token during the call, and the read above returned undefined.
|
|
106
|
+
// Falling back to the token the provider holds now discards the one that call actually
|
|
107
|
+
// presented, instead of invalidating nothing and replaying with the same rejected token.
|
|
108
|
+
presented ??= this.auth.currentToken;
|
|
109
|
+
if (presented === undefined)
|
|
110
|
+
throw error;
|
|
111
|
+
this.auth.invalidate(presented);
|
|
112
|
+
}
|
|
113
|
+
return operation();
|
|
114
|
+
}
|
|
115
|
+
isRenewable(error) {
|
|
116
|
+
return CamusError.is(error) && error.code === CamusErrorCode.AuthenticationFailed && this.auth.canRenew;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
//# sourceMappingURL=authenticating-transport.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"authenticating-transport.js","sourceRoot":"","sources":["../../src/transport/authenticating-transport.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* This file is part of CamusDB
|
|
3
|
+
*
|
|
4
|
+
* For the full copyright and license information, please view the LICENSE.txt
|
|
5
|
+
* file that was distributed with this source code.
|
|
6
|
+
*/
|
|
7
|
+
import type { CamusResultSet } from '../result-set.js';
|
|
8
|
+
import type { CamusBranchRow } from './transport.js';
|
|
9
|
+
/**
|
|
10
|
+
* The branch statements, composed as SQL, and the mapping of what they return.
|
|
11
|
+
*
|
|
12
|
+
* Database branching has no REST route of its own: the server implements it only as SQL, so both
|
|
13
|
+
* transports compose the statement and send it down the ordinary DDL and query routes. This module
|
|
14
|
+
* holds that composition once, so the two transports cannot spell a statement differently.
|
|
15
|
+
*
|
|
16
|
+
* A database name is a bare identifier in the grammar, so it is backtick-delimited here, and a name
|
|
17
|
+
* carrying a backtick is refused rather than doubled — the server trims the delimiters instead of
|
|
18
|
+
* decoding a doubled backtick, so doubling would neutralize nothing. These statements are among the
|
|
19
|
+
* few this driver composes as text, and a database name can come from an application's own input.
|
|
20
|
+
*/
|
|
21
|
+
/** `CREATE DATABASE <branch> BRANCH FROM <source>`. */
|
|
22
|
+
export declare function createBranchDatabaseSql(branchName: string, sourceDatabaseName: string, ifNotExists: boolean): string;
|
|
23
|
+
/** `CREATE DATABASE <name>`. */
|
|
24
|
+
export declare function createDatabaseSql(database: string, ifNotExists: boolean): string;
|
|
25
|
+
/** `DROP DATABASE <name>`. */
|
|
26
|
+
export declare function dropDatabaseSql(database: string): string;
|
|
27
|
+
/** `SHOW BRANCHES FROM <name>`. */
|
|
28
|
+
export declare function showBranchesSql(database: string): string;
|
|
29
|
+
/** `SHOW ANCESTORS FROM <name>`. */
|
|
30
|
+
export declare function showAncestorsSql(database: string): string;
|
|
31
|
+
/**
|
|
32
|
+
* Reads what a branch listing returned.
|
|
33
|
+
*
|
|
34
|
+
* `SHOW BRANCHES` emits `[database, id, depth, parent, fork_timestamp]`, and `SHOW ANCESTORS` emits
|
|
35
|
+
* the same without `parent`. The columns are read by name, so either shape reconstructs, and a
|
|
36
|
+
* server that adds a column changes nothing here.
|
|
37
|
+
*/
|
|
38
|
+
export declare function mapBranchRows(result: CamusResultSet): CamusBranchRow[];
|
|
39
|
+
//# sourceMappingURL=branch-statements.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"branch-statements.d.ts","sourceRoot":"","sources":["../../src/transport/branch-statements.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAEvD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAErD;;;;;;;;;;;GAWG;AAEH,uDAAuD;AACvD,wBAAgB,uBAAuB,CACrC,UAAU,EAAE,MAAM,EAClB,kBAAkB,EAAE,MAAM,EAC1B,WAAW,EAAE,OAAO,GACnB,MAAM,CAIR;AAED,gCAAgC;AAChC,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,EAAE,WAAW,EAAE,OAAO,GAAG,MAAM,CAIhF;AAED,8BAA8B;AAC9B,wBAAgB,eAAe,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAExD;AAED,mCAAmC;AACnC,wBAAgB,eAAe,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAExD;AAED,oCAAoC;AACpC,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAEzD;AAED;;;;;;GAMG;AACH,wBAAgB,aAAa,CAAC,MAAM,EAAE,cAAc,GAAG,cAAc,EAAE,CA2BtE"}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* This file is part of CamusDB
|
|
3
|
+
*
|
|
4
|
+
* For the full copyright and license information, please view the LICENSE.txt
|
|
5
|
+
* file that was distributed with this source code.
|
|
6
|
+
*/
|
|
7
|
+
import { delimitIdentifier } from '../sql-syntax.js';
|
|
8
|
+
/**
|
|
9
|
+
* The branch statements, composed as SQL, and the mapping of what they return.
|
|
10
|
+
*
|
|
11
|
+
* Database branching has no REST route of its own: the server implements it only as SQL, so both
|
|
12
|
+
* transports compose the statement and send it down the ordinary DDL and query routes. This module
|
|
13
|
+
* holds that composition once, so the two transports cannot spell a statement differently.
|
|
14
|
+
*
|
|
15
|
+
* A database name is a bare identifier in the grammar, so it is backtick-delimited here, and a name
|
|
16
|
+
* carrying a backtick is refused rather than doubled — the server trims the delimiters instead of
|
|
17
|
+
* decoding a doubled backtick, so doubling would neutralize nothing. These statements are among the
|
|
18
|
+
* few this driver composes as text, and a database name can come from an application's own input.
|
|
19
|
+
*/
|
|
20
|
+
/** `CREATE DATABASE <branch> BRANCH FROM <source>`. */
|
|
21
|
+
export function createBranchDatabaseSql(branchName, sourceDatabaseName, ifNotExists) {
|
|
22
|
+
const prefix = ifNotExists ? 'CREATE DATABASE IF NOT EXISTS ' : 'CREATE DATABASE ';
|
|
23
|
+
return `${prefix}${quote(branchName)} BRANCH FROM ${quote(sourceDatabaseName)}`;
|
|
24
|
+
}
|
|
25
|
+
/** `CREATE DATABASE <name>`. */
|
|
26
|
+
export function createDatabaseSql(database, ifNotExists) {
|
|
27
|
+
const prefix = ifNotExists ? 'CREATE DATABASE IF NOT EXISTS ' : 'CREATE DATABASE ';
|
|
28
|
+
return `${prefix}${quote(database)}`;
|
|
29
|
+
}
|
|
30
|
+
/** `DROP DATABASE <name>`. */
|
|
31
|
+
export function dropDatabaseSql(database) {
|
|
32
|
+
return `DROP DATABASE ${quote(database)}`;
|
|
33
|
+
}
|
|
34
|
+
/** `SHOW BRANCHES FROM <name>`. */
|
|
35
|
+
export function showBranchesSql(database) {
|
|
36
|
+
return `SHOW BRANCHES FROM ${quote(database)}`;
|
|
37
|
+
}
|
|
38
|
+
/** `SHOW ANCESTORS FROM <name>`. */
|
|
39
|
+
export function showAncestorsSql(database) {
|
|
40
|
+
return `SHOW ANCESTORS FROM ${quote(database)}`;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Reads what a branch listing returned.
|
|
44
|
+
*
|
|
45
|
+
* `SHOW BRANCHES` emits `[database, id, depth, parent, fork_timestamp]`, and `SHOW ANCESTORS` emits
|
|
46
|
+
* the same without `parent`. The columns are read by name, so either shape reconstructs, and a
|
|
47
|
+
* server that adds a column changes nothing here.
|
|
48
|
+
*/
|
|
49
|
+
export function mapBranchRows(result) {
|
|
50
|
+
const index = new Map();
|
|
51
|
+
result.columnNames.forEach((name, position) => index.set(name, position));
|
|
52
|
+
const rows = [];
|
|
53
|
+
for (let r = 0; r < result.rowCount; r++) {
|
|
54
|
+
const text = (name) => {
|
|
55
|
+
const column = index.get(name);
|
|
56
|
+
if (column === undefined)
|
|
57
|
+
return undefined;
|
|
58
|
+
return result.cell(r, column).strValue ?? undefined;
|
|
59
|
+
};
|
|
60
|
+
const depthColumn = index.get('depth');
|
|
61
|
+
rows.push({
|
|
62
|
+
database: text('database'),
|
|
63
|
+
id: text('id'),
|
|
64
|
+
depth: depthColumn === undefined ? 0 : Number(result.cell(r, depthColumn).longValue ?? 0n),
|
|
65
|
+
parent: text('parent'),
|
|
66
|
+
forkTimestamp: text('fork_timestamp'),
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
return rows;
|
|
70
|
+
}
|
|
71
|
+
function quote(name) {
|
|
72
|
+
return delimitIdentifier(name, 'name');
|
|
73
|
+
}
|
|
74
|
+
//# sourceMappingURL=branch-statements.js.map
|