@bjornpagen/bumbledb 1.0.1 → 1.2.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/COOKBOOK.md +176 -36
- package/README.md +70 -26
- package/dist/capacity.d.ts +15 -55
- package/dist/capacity.d.ts.map +1 -1
- package/dist/capacity.js +63 -12
- package/dist/capacity.js.map +1 -1
- package/dist/changes.d.ts +28 -6
- package/dist/changes.d.ts.map +1 -1
- package/dist/changes.js +119 -71
- package/dist/changes.js.map +1 -1
- package/dist/closed.d.ts +23 -51
- package/dist/closed.d.ts.map +1 -1
- package/dist/closed.js +110 -168
- package/dist/closed.js.map +1 -1
- package/dist/codec.d.ts +8 -14
- package/dist/codec.d.ts.map +1 -1
- package/dist/codec.js +113 -188
- package/dist/codec.js.map +1 -1
- package/dist/compile.d.ts +17 -18
- package/dist/compile.d.ts.map +1 -1
- package/dist/compile.js +41 -20
- package/dist/compile.js.map +1 -1
- package/dist/db-native.d.ts +76 -41
- package/dist/db-native.d.ts.map +1 -1
- package/dist/db-native.js +8 -0
- package/dist/db-native.js.map +1 -1
- package/dist/db.d.ts +61 -26
- package/dist/db.d.ts.map +1 -1
- package/dist/db.js +152 -85
- package/dist/db.js.map +1 -1
- package/dist/errors.d.ts +11 -3
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +13 -1
- package/dist/errors.js.map +1 -1
- package/dist/face.d.ts +19 -25
- package/dist/face.d.ts.map +1 -1
- package/dist/face.js +30 -24
- package/dist/face.js.map +1 -1
- package/dist/fields.d.ts +13 -26
- package/dist/fields.d.ts.map +1 -1
- package/dist/fields.js +89 -116
- package/dist/fields.js.map +1 -1
- package/dist/immutable.d.ts +10 -0
- package/dist/immutable.d.ts.map +1 -0
- package/dist/immutable.js +75 -0
- package/dist/immutable.js.map +1 -0
- package/dist/index.d.ts +16 -12
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -4
- package/dist/index.js.map +1 -1
- package/dist/internal/log.d.ts +6 -4
- package/dist/internal/log.d.ts.map +1 -1
- package/dist/internal/log.js +3 -1
- package/dist/internal/log.js.map +1 -1
- package/dist/law.d.ts +11 -11
- package/dist/law.d.ts.map +1 -1
- package/dist/law.js +1 -1
- package/dist/law.js.map +1 -1
- package/dist/lower.d.ts.map +1 -1
- package/dist/lower.js +11 -6
- package/dist/lower.js.map +1 -1
- package/dist/migration.d.ts +2 -3
- package/dist/migration.d.ts.map +1 -1
- package/dist/migration.js +5 -5
- package/dist/migration.js.map +1 -1
- package/dist/native.d.ts +2 -2
- package/dist/native.d.ts.map +1 -1
- package/dist/native.js.map +1 -1
- package/dist/query/atom.d.ts +1 -1
- package/dist/query/atom.d.ts.map +1 -1
- package/dist/query/atom.js.map +1 -1
- package/dist/query/description.d.ts +33 -0
- package/dist/query/description.d.ts.map +1 -0
- package/dist/query/description.js +302 -0
- package/dist/query/description.js.map +1 -0
- package/dist/query/lower.d.ts +60 -4
- package/dist/query/lower.d.ts.map +1 -1
- package/dist/query/lower.js +126 -161
- package/dist/query/lower.js.map +1 -1
- package/dist/query/parse-ir.d.ts +7 -2
- package/dist/query/parse-ir.d.ts.map +1 -1
- package/dist/query/parse-ir.js +240 -53
- package/dist/query/parse-ir.js.map +1 -1
- package/dist/query/run.d.ts.map +1 -1
- package/dist/query/run.js +8 -10
- package/dist/query/run.js.map +1 -1
- package/dist/query/scope.d.ts +3 -4
- package/dist/query/scope.d.ts.map +1 -1
- package/dist/query/scope.js +5 -10
- package/dist/query/scope.js.map +1 -1
- package/dist/relation.d.ts +22 -48
- package/dist/relation.d.ts.map +1 -1
- package/dist/relation.js +30 -82
- package/dist/relation.js.map +1 -1
- package/dist/result.d.ts +10 -25
- package/dist/result.d.ts.map +1 -1
- package/dist/result.js +7 -12
- package/dist/result.js.map +1 -1
- package/dist/rows.d.ts +11 -14
- package/dist/rows.d.ts.map +1 -1
- package/dist/rows.js +49 -237
- package/dist/rows.js.map +1 -1
- package/dist/runtime-codes.d.ts +1 -1
- package/dist/runtime-codes.d.ts.map +1 -1
- package/dist/runtime-codes.js +1 -2
- package/dist/runtime-codes.js.map +1 -1
- package/dist/runtime-errors.d.ts +27 -15
- package/dist/runtime-errors.d.ts.map +1 -1
- package/dist/runtime-errors.js +44 -8
- package/dist/runtime-errors.js.map +1 -1
- package/dist/runtime-native.d.ts +16 -34
- package/dist/runtime-native.d.ts.map +1 -1
- package/dist/runtime-native.js.map +1 -1
- package/dist/runtime.d.ts +11 -28
- package/dist/runtime.d.ts.map +1 -1
- package/dist/runtime.js +14 -54
- package/dist/runtime.js.map +1 -1
- package/dist/schema.d.ts +9 -1
- package/dist/schema.d.ts.map +1 -1
- package/dist/schema.js +85 -24
- package/dist/schema.js.map +1 -1
- package/dist/selection.d.ts +27 -0
- package/dist/selection.d.ts.map +1 -0
- package/dist/selection.js +104 -0
- package/dist/selection.js.map +1 -0
- package/dist/shape.d.ts +6 -9
- package/dist/shape.d.ts.map +1 -1
- package/dist/statements.d.ts +17 -52
- package/dist/statements.d.ts.map +1 -1
- package/dist/statements.js +92 -111
- package/dist/statements.js.map +1 -1
- package/dist/values.d.ts +25 -0
- package/dist/values.d.ts.map +1 -0
- package/dist/values.js +203 -0
- package/dist/values.js.map +1 -0
- package/pack-provenance.json +3 -3
- package/package.json +4 -4
- package/src/capacity.ts +86 -76
- package/src/changes.ts +225 -94
- package/src/closed.ts +161 -314
- package/src/codec.ts +123 -213
- package/src/compile.ts +61 -38
- package/src/db-native.ts +73 -68
- package/src/db.ts +268 -165
- package/src/errors.ts +16 -1
- package/src/face.ts +44 -74
- package/src/fields.ts +100 -146
- package/src/immutable.ts +76 -0
- package/src/index.ts +38 -31
- package/src/internal/log.ts +4 -5
- package/src/law.ts +14 -14
- package/src/lower.ts +13 -10
- package/src/migration.ts +5 -12
- package/src/native.ts +2 -1
- package/src/query/atom.ts +1 -0
- package/src/query/description.ts +394 -0
- package/src/query/lower.ts +139 -173
- package/src/query/parse-ir.ts +247 -54
- package/src/query/run.ts +15 -9
- package/src/query/scope.ts +10 -18
- package/src/relation.ts +58 -142
- package/src/result.ts +16 -45
- package/src/rows.ts +57 -250
- package/src/runtime-codes.ts +1 -2
- package/src/runtime-errors.ts +48 -8
- package/src/runtime-native.ts +13 -46
- package/src/runtime.ts +51 -119
- package/src/schema.ts +99 -29
- package/src/selection.ts +124 -0
- package/src/shape.ts +9 -9
- package/src/statements.ts +138 -189
- package/src/values.ts +223 -0
package/src/db.ts
CHANGED
|
@@ -1,15 +1,17 @@
|
|
|
1
1
|
import type { Scope } from "effect"
|
|
2
2
|
import { Effect, Option } from "effect"
|
|
3
|
-
import type { ChangeSet } from "#changes.ts"
|
|
3
|
+
import type { ChangeCounts, ChangeSet } from "#changes.ts"
|
|
4
4
|
import { internalChanges } from "#changes.ts"
|
|
5
5
|
import { drainClose, releaseOwner } from "#close.ts"
|
|
6
|
+
import { membersAgree } from "#closed.ts"
|
|
6
7
|
import type { CompiledSchema, SchemaId } from "#compile.ts"
|
|
7
|
-
import { Schema as CoreSchema, schemaTables } from "#compile.ts"
|
|
8
|
+
import { Schema as CoreSchema, declaredKey, schemaTables } from "#compile.ts"
|
|
8
9
|
import type {
|
|
9
10
|
ApplyOutcomeWire,
|
|
10
11
|
DbInspectionWire,
|
|
11
12
|
ExpectedWire,
|
|
12
|
-
|
|
13
|
+
JudgeOutcomeWire,
|
|
14
|
+
PreparedHandle,
|
|
13
15
|
SnapshotHandle,
|
|
14
16
|
WitnessWire
|
|
15
17
|
} from "#db-native.ts"
|
|
@@ -25,22 +27,25 @@ import type { CompleteResult } from "#result.ts"
|
|
|
25
27
|
import { internalResult, makeCompleteResult } from "#result.ts"
|
|
26
28
|
import type { CellValue } from "#rows.ts"
|
|
27
29
|
import { factOfCells, keyCellsOf } from "#rows.ts"
|
|
28
|
-
import type {
|
|
29
|
-
import { nativeOperationWith,
|
|
30
|
+
import type { NativeRuntime } from "#runtime.ts"
|
|
31
|
+
import { nativeOperationWith, runtimeHandle } from "#runtime.ts"
|
|
30
32
|
import type { CloseReport } from "#runtime-errors.ts"
|
|
31
|
-
import { DbError } from "#runtime-errors.ts"
|
|
33
|
+
import { argumentError, DbError } from "#runtime-errors.ts"
|
|
32
34
|
import type { DirectoryHandle } from "#runtime-native.ts"
|
|
33
35
|
import { runtimeNative } from "#runtime-native.ts"
|
|
34
36
|
import type { AnySchema } from "#schema.ts"
|
|
37
|
+
import { schemaDescriptor, schemasAgree } from "#schema.ts"
|
|
35
38
|
import type { Key, QueryTemplate, Rel } from "#shape.ts"
|
|
39
|
+
import type { KeyStatement } from "#statements.ts"
|
|
40
|
+
import { integerValue, recordValue } from "#values.ts"
|
|
36
41
|
|
|
37
42
|
/**
|
|
38
43
|
* The core surface: `Db.create`/`Db.open`, scoped coherent
|
|
39
|
-
* `Snapshot`s, reusable `
|
|
44
|
+
* `Snapshot`s, reusable `PreparedQuery`s, the shared `QueryReader`
|
|
40
45
|
* capability, one immutable final-state `apply`, bounded `inspect` and
|
|
41
46
|
* honest `close`. Effect-only: every method constructs a lazy effect; all
|
|
42
|
-
* native work runs on the
|
|
43
|
-
*
|
|
47
|
+
* native work runs on the shared executor with cooperative cancellation.
|
|
48
|
+
* Resources are scoped with `CloseFailure`-defect
|
|
44
49
|
* finalizers. There is no Promise/sync/disposal twin, no transaction
|
|
45
50
|
* callback, no per-row fiber and no Proxy row anywhere.
|
|
46
51
|
*
|
|
@@ -57,10 +62,10 @@ interface CoreWitness {
|
|
|
57
62
|
readonly generation: bigint
|
|
58
63
|
}
|
|
59
64
|
|
|
60
|
-
type
|
|
65
|
+
type WriteExpected = { readonly kind: "any" } | { readonly kind: "exact"; readonly at: CoreWitness }
|
|
61
66
|
|
|
62
|
-
/**
|
|
63
|
-
type
|
|
67
|
+
/** Expected-state intent shared by judgment and application. */
|
|
68
|
+
type WriteOptions = { readonly expected: WriteExpected }
|
|
64
69
|
|
|
65
70
|
type ApplyOutcome =
|
|
66
71
|
| { readonly kind: "accepted"; readonly witness: CoreWitness }
|
|
@@ -68,14 +73,36 @@ type ApplyOutcome =
|
|
|
68
73
|
| { readonly kind: "invariant-rejected"; readonly violations: readonly Violation[] }
|
|
69
74
|
| { readonly kind: "moved"; readonly witnessed: CoreWitness; readonly current: CoreWitness }
|
|
70
75
|
|
|
71
|
-
/**
|
|
76
|
+
/** A judgment against the actual current base, not a promise about a later apply.
|
|
77
|
+
* A moved witness is unjudged and therefore carries no made-up change counts.
|
|
78
|
+
*/
|
|
79
|
+
type JudgeOutcome =
|
|
80
|
+
| { readonly kind: "admitted"; readonly base: CoreWitness; readonly changes: ChangeCounts }
|
|
81
|
+
| {
|
|
82
|
+
readonly kind: "invariant-rejected"
|
|
83
|
+
readonly base: CoreWitness
|
|
84
|
+
readonly changes: ChangeCounts
|
|
85
|
+
readonly violations: readonly Violation[]
|
|
86
|
+
}
|
|
87
|
+
| { readonly kind: "moved"; readonly witnessed: CoreWitness; readonly current: CoreWitness }
|
|
88
|
+
|
|
89
|
+
/** Storage measurements, not heap usage or mapped-page residency. */
|
|
90
|
+
interface StorageInspection {
|
|
91
|
+
/** Reserved virtual address range for the LMDB mapping. */
|
|
92
|
+
readonly virtualMapBytes: bigint
|
|
93
|
+
/** File length; may include sparse regions and free pages. */
|
|
94
|
+
readonly populatedFileBytes: bigint
|
|
95
|
+
/** LMDB's non-free branch, leaf and overflow pages. Not resident RAM. */
|
|
96
|
+
readonly nonFreePageBytes: bigint
|
|
97
|
+
/** Allocated filesystem blocks, or null when the OS cannot report them. */
|
|
98
|
+
readonly allocatedDiskBytes: bigint | null
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/** Database diagnostics: measurements, never retained rows. */
|
|
72
102
|
interface DbInspection {
|
|
73
103
|
readonly schemaId: SchemaId
|
|
74
104
|
readonly generation: bigint
|
|
75
|
-
readonly
|
|
76
|
-
readonly populatedBytes: bigint
|
|
77
|
-
readonly diskBytes: bigint
|
|
78
|
-
readonly residentEstimateBytes: bigint
|
|
105
|
+
readonly storage: StorageInspection
|
|
79
106
|
readonly retainedOperations: bigint
|
|
80
107
|
}
|
|
81
108
|
|
|
@@ -87,34 +114,43 @@ interface DbInspection {
|
|
|
87
114
|
* no writable authority — never a `Db`, an `apply`, or a raw transaction.
|
|
88
115
|
*/
|
|
89
116
|
interface QueryReader<S extends AnySchema> {
|
|
90
|
-
get<
|
|
117
|
+
get<K extends KeyStatement<Rel<S>, readonly string[]>>(
|
|
118
|
+
key: K,
|
|
119
|
+
value: NoInfer<Key<K>>
|
|
120
|
+
): Effect.Effect<Option.Option<Fact<K["owner"]>>, DbError>
|
|
91
121
|
execute<P extends ParamsRecord, A>(
|
|
92
122
|
query: QueryTemplate<S, P, A>,
|
|
93
|
-
params: P
|
|
94
|
-
work: ExecutionPolicy
|
|
123
|
+
params: P
|
|
95
124
|
): Effect.Effect<CompleteResult<A>, DbError, Scope.Scope>
|
|
125
|
+
prepare<P extends ParamsRecord, A>(
|
|
126
|
+
query: QueryTemplate<S, P, A>
|
|
127
|
+
): Effect.Effect<PreparedQuery<P, A>, DbError, Scope.Scope>
|
|
96
128
|
}
|
|
97
129
|
|
|
98
130
|
interface Snapshot<S extends AnySchema> extends QueryReader<S> {
|
|
99
131
|
readonly witness: CoreWitness
|
|
100
|
-
session(work: ExecutionPolicy): Effect.Effect<ExecutionSession<S>, DbError, Scope.Scope>
|
|
101
132
|
close(): Effect.Effect<CloseReport>
|
|
102
133
|
}
|
|
103
134
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
135
|
+
/** One compiled plan and its reusable buffers, pinned to the preparing snapshot.
|
|
136
|
+
* Closing it releases its own state, not the snapshot or completed results.
|
|
137
|
+
*/
|
|
138
|
+
interface PreparedQuery<P extends ParamsRecord, A> {
|
|
139
|
+
execute(params: P): Effect.Effect<CompleteResult<A>, DbError, Scope.Scope>
|
|
140
|
+
/** Drop reusable execution buffers, keeping the compiled query and snapshot. */
|
|
141
|
+
releaseMemory(): Effect.Effect<void, DbError>
|
|
110
142
|
close(): Effect.Effect<CloseReport>
|
|
111
143
|
}
|
|
112
144
|
|
|
113
145
|
interface Db<S extends AnySchema> {
|
|
114
146
|
readonly schemaId: SchemaId
|
|
115
|
-
snapshot(
|
|
116
|
-
apply(changes: ChangeSet<S>, options:
|
|
117
|
-
|
|
147
|
+
snapshot(): Effect.Effect<Snapshot<S>, DbError, Scope.Scope>
|
|
148
|
+
apply(changes: ChangeSet<S>, options: WriteOptions): Effect.Effect<ApplyOutcome, DbError>
|
|
149
|
+
/** Briefly acquires the writer, judges and aborts. Stores no facts or generation. */
|
|
150
|
+
judge(changes: ChangeSet<S>, options: WriteOptions): Effect.Effect<JudgeOutcome, DbError>
|
|
151
|
+
inspect(): Effect.Effect<DbInspection, DbError>
|
|
152
|
+
/** Clear shared query caches without invalidating live snapshots or results. */
|
|
153
|
+
clearCache(): Effect.Effect<void, DbError>
|
|
118
154
|
close(): Effect.Effect<CloseReport>
|
|
119
155
|
}
|
|
120
156
|
|
|
@@ -139,10 +175,61 @@ function outcomeOf(wire: ApplyOutcomeWire): ApplyOutcome {
|
|
|
139
175
|
}
|
|
140
176
|
}
|
|
141
177
|
|
|
178
|
+
function judgmentOf(wire: JudgeOutcomeWire): JudgeOutcome {
|
|
179
|
+
switch (wire.tag) {
|
|
180
|
+
case "admitted":
|
|
181
|
+
return Object.freeze({ kind: "admitted", base: witnessOf(wire.base), changes: Object.freeze(wire.changes) })
|
|
182
|
+
case "invariant-rejected":
|
|
183
|
+
return Object.freeze({
|
|
184
|
+
kind: "invariant-rejected",
|
|
185
|
+
base: witnessOf(wire.base),
|
|
186
|
+
changes: Object.freeze(wire.changes),
|
|
187
|
+
violations: wire.violations
|
|
188
|
+
})
|
|
189
|
+
case "moved":
|
|
190
|
+
return Object.freeze({ kind: "moved", witnessed: witnessOf(wire.witnessed), current: witnessOf(wire.current) })
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
function writeInputs(state: DbState, changes: object, options: WriteOptions, operation: string) {
|
|
195
|
+
return Effect.try({
|
|
196
|
+
try: () => {
|
|
197
|
+
const internal = internalChanges(changes)
|
|
198
|
+
if (internal === undefined || internal.schemaId !== state.schemaId) {
|
|
199
|
+
throw refusal(operation, "InvalidArgument")
|
|
200
|
+
}
|
|
201
|
+
const record = recordValue("write options", options, ["expected"])
|
|
202
|
+
const intent = recordValue(
|
|
203
|
+
"expected state",
|
|
204
|
+
record.expected,
|
|
205
|
+
options.expected.kind === "any" ? ["kind"] : ["kind", "at"]
|
|
206
|
+
)
|
|
207
|
+
let expected: ExpectedWire
|
|
208
|
+
if (intent.kind === "any") {
|
|
209
|
+
expected = { kind: "any" }
|
|
210
|
+
} else if (intent.kind === "exact") {
|
|
211
|
+
const at = recordValue("expected witness", intent.at, ["store", "generation"])
|
|
212
|
+
if (typeof at.store !== "string" || !/^[0-9a-f]{32}$/.test(at.store)) {
|
|
213
|
+
throw refusal(operation, "InvalidArgument")
|
|
214
|
+
}
|
|
215
|
+
expected = {
|
|
216
|
+
kind: "exact",
|
|
217
|
+
store: at.store,
|
|
218
|
+
generation: integerValue("expected generation", "u64", at.generation)
|
|
219
|
+
}
|
|
220
|
+
} else {
|
|
221
|
+
throw refusal(operation, "InvalidArgument")
|
|
222
|
+
}
|
|
223
|
+
return { handle: internal.handle, expected }
|
|
224
|
+
},
|
|
225
|
+
catch: (cause) => argumentError(operation, cause)
|
|
226
|
+
})
|
|
227
|
+
}
|
|
228
|
+
|
|
142
229
|
/**
|
|
143
|
-
* Validates the query template's schema
|
|
144
|
-
*
|
|
145
|
-
* engine's IR validation
|
|
230
|
+
* Validates the query template's ordered logical schema and lowers it to
|
|
231
|
+
* IR plus wire params. Pure host preparation; the
|
|
232
|
+
* engine's IR validation remains the authority.
|
|
146
233
|
*/
|
|
147
234
|
function preparedOf<S extends AnySchema>(
|
|
148
235
|
theory: S,
|
|
@@ -153,7 +240,7 @@ function preparedOf<S extends AnySchema>(
|
|
|
153
240
|
readonly wire: ReturnType<typeof wireParams>
|
|
154
241
|
readonly finds: AnyQuery["data"]["finds"]
|
|
155
242
|
} {
|
|
156
|
-
if (query.schema
|
|
243
|
+
if (!schemasAgree(query.schema, theory)) {
|
|
157
244
|
throw refusal("QueryReader.execute", "InvalidArgument")
|
|
158
245
|
}
|
|
159
246
|
const ir = lowerQuery(query)
|
|
@@ -168,29 +255,29 @@ interface SnapshotState<S extends AnySchema> {
|
|
|
168
255
|
|
|
169
256
|
function executeOn<S extends AnySchema, A>(
|
|
170
257
|
state: SnapshotState<S>,
|
|
171
|
-
session: SessionHandle | undefined,
|
|
172
258
|
query: AnyQuery,
|
|
173
|
-
params: Readonly<Record<string, unknown
|
|
174
|
-
work: ExecutionPolicy
|
|
259
|
+
params: Readonly<Record<string, unknown>>
|
|
175
260
|
): Effect.Effect<CompleteResult<A>, DbError, Scope.Scope> {
|
|
176
|
-
return
|
|
261
|
+
return scopedResult(
|
|
177
262
|
Effect.gen(function* () {
|
|
178
263
|
const prepared = yield* Effect.try({
|
|
179
264
|
try: () => preparedOf(state.theory, query, params),
|
|
180
|
-
catch: (cause) => (
|
|
265
|
+
catch: (cause) => argumentError("QueryReader.execute", cause)
|
|
181
266
|
})
|
|
182
|
-
const wire = policyWire(work, "QueryReader.execute")
|
|
183
267
|
const handle = yield* nativeOperationWith(
|
|
184
268
|
"QueryReader.execute",
|
|
185
|
-
(callback) =>
|
|
186
|
-
session === undefined
|
|
187
|
-
? dbNative.runtimeSnapshotExecute(state.handle, wire, prepared.ir, prepared.wire, callback)
|
|
188
|
-
: dbNative.runtimeSessionExecute(session, wire, prepared.ir, prepared.wire, callback),
|
|
269
|
+
(callback) => dbNative.runtimeSnapshotExecute(state.handle, prepared.ir, prepared.wire, callback),
|
|
189
270
|
dbNative.runtimeResultTake,
|
|
190
271
|
(value) => value
|
|
191
272
|
)
|
|
192
273
|
return makeCompleteResult<A>(handle, prepared.finds)
|
|
193
|
-
})
|
|
274
|
+
})
|
|
275
|
+
)
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
function scopedResult<A>(acquire: Effect.Effect<CompleteResult<A>, DbError>) {
|
|
279
|
+
return Effect.acquireRelease(
|
|
280
|
+
acquire,
|
|
194
281
|
(result) =>
|
|
195
282
|
Effect.suspend(() => {
|
|
196
283
|
const internal = internalResultHandle(result)
|
|
@@ -207,87 +294,123 @@ function internalResultHandle(result: object) {
|
|
|
207
294
|
return internalResult(result)?.handle
|
|
208
295
|
}
|
|
209
296
|
|
|
210
|
-
function getOn<S extends AnySchema,
|
|
297
|
+
function getOn<S extends AnySchema, K extends KeyStatement<Rel<S>, readonly string[]>>(
|
|
211
298
|
state: SnapshotState<S>,
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
): Effect.Effect<Option.Option<Fact<R>>, DbError> {
|
|
299
|
+
key: K,
|
|
300
|
+
value: Key<K>
|
|
301
|
+
): Effect.Effect<Option.Option<Fact<K["owner"]>>, DbError> {
|
|
216
302
|
return Effect.gen(function* () {
|
|
217
|
-
|
|
303
|
+
const tables = schemaTables(state.theory)
|
|
304
|
+
const resolved = yield* Effect.try({
|
|
305
|
+
try: () => declaredKey(state.theory, key),
|
|
306
|
+
catch: (cause) => argumentError("QueryReader.get", cause)
|
|
307
|
+
})
|
|
308
|
+
if (resolved === undefined || key.kind !== "key") {
|
|
309
|
+
return yield* Effect.fail(refusal("QueryReader.get", "InvalidArgument"))
|
|
310
|
+
}
|
|
311
|
+
const { statementId } = resolved
|
|
312
|
+
const relation = resolved.key.owner
|
|
313
|
+
if (!membersAgree(state.theory.relations[relation.name], relation)) {
|
|
218
314
|
return yield* Effect.fail(refusal("QueryReader.get", "InvalidArgument"))
|
|
219
315
|
}
|
|
220
|
-
const tables = schemaTables(state.theory)
|
|
221
316
|
const relationId = tables.relationIds.get(relation.name)
|
|
222
|
-
|
|
223
|
-
if (relationId === undefined || primary === undefined) {
|
|
317
|
+
if (relationId === undefined) {
|
|
224
318
|
return yield* Effect.fail(refusal("QueryReader.get", "InvalidArgument"))
|
|
225
319
|
}
|
|
226
320
|
const cells = yield* Effect.try({
|
|
227
|
-
try: () => keyCellsOf(relation
|
|
228
|
-
catch: (cause) => (
|
|
321
|
+
try: () => keyCellsOf(relation, resolved.key.projection, value),
|
|
322
|
+
catch: (cause) => argumentError("QueryReader.get", cause)
|
|
229
323
|
})
|
|
230
324
|
const row = yield* nativeOperationWith(
|
|
231
325
|
"QueryReader.get",
|
|
232
|
-
(callback) =>
|
|
233
|
-
dbNative.runtimeSnapshotGet(
|
|
234
|
-
state.handle,
|
|
235
|
-
policyWire(work, "QueryReader.get"),
|
|
236
|
-
relationId,
|
|
237
|
-
primary.statementId,
|
|
238
|
-
cells,
|
|
239
|
-
callback
|
|
240
|
-
),
|
|
326
|
+
(callback) => dbNative.runtimeSnapshotGet(state.handle, relationId, statementId, cells, callback),
|
|
241
327
|
dbNative.runtimeRowTake,
|
|
242
328
|
(value) => value
|
|
243
329
|
)
|
|
244
330
|
if (row === null) {
|
|
245
|
-
return Option.none<Fact<
|
|
331
|
+
return Option.none<Fact<K["owner"]>>()
|
|
246
332
|
}
|
|
247
333
|
return Option.some(factOfCells(relation, row as readonly CellValue[]))
|
|
248
334
|
})
|
|
249
335
|
}
|
|
250
336
|
|
|
251
|
-
function
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
337
|
+
function makePrepared<P extends ParamsRecord, A>(
|
|
338
|
+
handle: PreparedHandle,
|
|
339
|
+
definitions: AnyQuery["data"]["params"],
|
|
340
|
+
finds: AnyQuery["data"]["finds"]
|
|
341
|
+
): PreparedQuery<P, A> {
|
|
342
|
+
return Object.freeze({
|
|
343
|
+
releaseMemory() {
|
|
344
|
+
return nativeOperationWith(
|
|
345
|
+
"PreparedQuery.releaseMemory",
|
|
346
|
+
(callback) => dbNative.runtimePreparedReleaseMemory(handle, callback),
|
|
347
|
+
runtimeNative.runtimeTake,
|
|
348
|
+
() => undefined
|
|
349
|
+
)
|
|
350
|
+
},
|
|
351
|
+
execute(params: P) {
|
|
352
|
+
return scopedResult(
|
|
353
|
+
Effect.gen(function* () {
|
|
354
|
+
const args = yield* Effect.try({
|
|
355
|
+
try: () => wireParams(definitions, params),
|
|
356
|
+
catch: (cause) => argumentError("PreparedQuery.execute", cause)
|
|
357
|
+
})
|
|
358
|
+
const result = yield* nativeOperationWith(
|
|
359
|
+
"PreparedQuery.execute",
|
|
360
|
+
(callback) => dbNative.runtimePreparedExecute(handle, args, callback),
|
|
361
|
+
dbNative.runtimeResultTake,
|
|
362
|
+
(value) => makeCompleteResult<A>(value, finds)
|
|
363
|
+
)
|
|
364
|
+
return result
|
|
365
|
+
})
|
|
366
|
+
)
|
|
255
367
|
},
|
|
256
368
|
close() {
|
|
257
|
-
return drainClose("
|
|
369
|
+
return drainClose("PreparedQuery.close", (callback) => dbNative.runtimePreparedClose(handle, callback))
|
|
258
370
|
}
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
371
|
+
})
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
function prepareOn<S extends AnySchema, P extends ParamsRecord, A>(
|
|
375
|
+
state: SnapshotState<S>,
|
|
376
|
+
query: QueryTemplate<S, P, A>
|
|
377
|
+
): Effect.Effect<PreparedQuery<P, A>, DbError, Scope.Scope> {
|
|
378
|
+
return Effect.gen(function* () {
|
|
379
|
+
const ir = yield* Effect.try({
|
|
380
|
+
try: () => {
|
|
381
|
+
if (!schemasAgree(query.schema, state.theory)) {
|
|
382
|
+
throw refusal("QueryReader.prepare", "InvalidArgument")
|
|
383
|
+
}
|
|
384
|
+
return lowerQuery(query)
|
|
385
|
+
},
|
|
386
|
+
catch: (cause) => argumentError("QueryReader.prepare", cause)
|
|
387
|
+
})
|
|
388
|
+
const handle = yield* Effect.acquireRelease(
|
|
389
|
+
nativeOperationWith(
|
|
390
|
+
"QueryReader.prepare",
|
|
391
|
+
(callback) => dbNative.runtimeSnapshotPrepare(state.handle, ir, callback),
|
|
392
|
+
dbNative.runtimePreparedTake,
|
|
393
|
+
(value) => value
|
|
394
|
+
),
|
|
395
|
+
(value) => releaseOwner("PreparedQuery.close", (callback) => dbNative.runtimePreparedClose(value, callback)),
|
|
396
|
+
{ interruptible: true }
|
|
397
|
+
)
|
|
398
|
+
return makePrepared<P, A>(handle, query.data.params, query.data.finds)
|
|
399
|
+
})
|
|
263
400
|
}
|
|
264
401
|
|
|
265
402
|
function makeSnapshot<S extends AnySchema>(theory: S, handle: SnapshotHandle, witness: CoreWitness): Snapshot<S> {
|
|
266
403
|
const state: SnapshotState<S> = { theory, handle }
|
|
267
404
|
const snapshot: Snapshot<S> = {
|
|
268
405
|
witness,
|
|
269
|
-
get(
|
|
270
|
-
return getOn(state,
|
|
406
|
+
get(key, value) {
|
|
407
|
+
return getOn(state, key, value)
|
|
271
408
|
},
|
|
272
|
-
execute(query, params
|
|
273
|
-
return executeOn(state,
|
|
409
|
+
execute(query, params) {
|
|
410
|
+
return executeOn(state, query, params)
|
|
274
411
|
},
|
|
275
|
-
|
|
276
|
-
return
|
|
277
|
-
nativeOperationWith(
|
|
278
|
-
"Snapshot.session",
|
|
279
|
-
(callback) => dbNative.runtimeSnapshotSession(handle, policyWire(work, "Snapshot.session"), callback),
|
|
280
|
-
dbNative.runtimeSessionTake,
|
|
281
|
-
(value) => makeSession(state, value)
|
|
282
|
-
),
|
|
283
|
-
(session) =>
|
|
284
|
-
Effect.suspend(() =>
|
|
285
|
-
releaseOwner("ExecutionSession.close", (callback) =>
|
|
286
|
-
dbNative.runtimeSessionClose(sessionHandles.get(session) ?? missingSession(), callback)
|
|
287
|
-
)
|
|
288
|
-
),
|
|
289
|
-
{ interruptible: true }
|
|
290
|
-
)
|
|
412
|
+
prepare(query) {
|
|
413
|
+
return prepareOn(state, query)
|
|
291
414
|
},
|
|
292
415
|
close() {
|
|
293
416
|
return drainClose("Snapshot.close", (callback) => dbNative.runtimeSnapshotClose(handle, callback))
|
|
@@ -298,12 +421,6 @@ function makeSnapshot<S extends AnySchema>(theory: S, handle: SnapshotHandle, wi
|
|
|
298
421
|
return snapshot
|
|
299
422
|
}
|
|
300
423
|
|
|
301
|
-
const sessionHandles = new WeakMap<object, SessionHandle>()
|
|
302
|
-
|
|
303
|
-
function missingSession(): never {
|
|
304
|
-
throw new DbError({ operation: "ExecutionSession.close", reason: { _tag: "Internal" } })
|
|
305
|
-
}
|
|
306
|
-
|
|
307
424
|
interface DbState {
|
|
308
425
|
readonly theory: AnySchema
|
|
309
426
|
readonly db: DbHandle
|
|
@@ -314,11 +431,19 @@ interface DbState {
|
|
|
314
431
|
function makeDb<S extends AnySchema>(theory: S, state: DbState): Db<S> {
|
|
315
432
|
const value: Db<S> = {
|
|
316
433
|
schemaId: state.schemaId,
|
|
317
|
-
|
|
434
|
+
clearCache() {
|
|
435
|
+
return nativeOperationWith(
|
|
436
|
+
"Db.clearCache",
|
|
437
|
+
(callback) => dbNative.runtimeDbClearCache(state.db, callback),
|
|
438
|
+
runtimeNative.runtimeTake,
|
|
439
|
+
() => undefined
|
|
440
|
+
)
|
|
441
|
+
},
|
|
442
|
+
snapshot() {
|
|
318
443
|
return Effect.acquireRelease(
|
|
319
444
|
nativeOperationWith(
|
|
320
445
|
"Db.snapshot",
|
|
321
|
-
(callback) => dbNative.runtimeDbSnapshot(state.db,
|
|
446
|
+
(callback) => dbNative.runtimeDbSnapshot(state.db, callback),
|
|
322
447
|
dbNative.runtimeSnapshotTake,
|
|
323
448
|
(wire) => makeSnapshot(theory, wire.snapshot, witnessOf(wire.witness))
|
|
324
449
|
),
|
|
@@ -333,41 +458,36 @@ function makeDb<S extends AnySchema>(theory: S, state: DbState): Db<S> {
|
|
|
333
458
|
},
|
|
334
459
|
apply(changes, options) {
|
|
335
460
|
return Effect.gen(function* () {
|
|
336
|
-
const
|
|
337
|
-
if (internal === undefined) {
|
|
338
|
-
// A foreign object supplied dynamically refuses before
|
|
339
|
-
// any native dispatch.
|
|
340
|
-
return yield* Effect.fail(refusal("Db.apply", "InvalidArgument"))
|
|
341
|
-
}
|
|
342
|
-
if (internal.schemaId !== state.schemaId) {
|
|
343
|
-
return yield* Effect.fail(refusal("Db.apply", "InvalidArgument"))
|
|
344
|
-
}
|
|
345
|
-
const expected: ExpectedWire =
|
|
346
|
-
options.expected.kind === "any"
|
|
347
|
-
? { kind: "any" }
|
|
348
|
-
: { kind: "exact", store: options.expected.at.store, generation: options.expected.at.generation }
|
|
461
|
+
const input = yield* writeInputs(state, changes, options, "Db.apply")
|
|
349
462
|
return yield* nativeOperationWith(
|
|
350
463
|
"Db.apply",
|
|
351
|
-
(callback) =>
|
|
352
|
-
dbNative.runtimeDbApply(state.db, policyWire(options, "Db.apply"), internal.handle, expected, callback),
|
|
464
|
+
(callback) => dbNative.runtimeDbApply(state.db, input.handle, input.expected, callback),
|
|
353
465
|
dbNative.runtimeApplyTake,
|
|
354
466
|
outcomeOf
|
|
355
467
|
)
|
|
356
468
|
})
|
|
357
469
|
},
|
|
358
|
-
|
|
470
|
+
judge(changes, options) {
|
|
471
|
+
return Effect.gen(function* () {
|
|
472
|
+
const input = yield* writeInputs(state, changes, options, "Db.judge")
|
|
473
|
+
return yield* nativeOperationWith(
|
|
474
|
+
"Db.judge",
|
|
475
|
+
(callback) => dbNative.runtimeDbJudge(state.db, input.handle, input.expected, callback),
|
|
476
|
+
dbNative.runtimeJudgeTake,
|
|
477
|
+
judgmentOf
|
|
478
|
+
)
|
|
479
|
+
})
|
|
480
|
+
},
|
|
481
|
+
inspect() {
|
|
359
482
|
return nativeOperationWith(
|
|
360
483
|
"Db.inspect",
|
|
361
|
-
(callback) => dbNative.runtimeDbInspect(state.db,
|
|
484
|
+
(callback) => dbNative.runtimeDbInspect(state.db, callback),
|
|
362
485
|
dbNative.runtimeDbInspectTake,
|
|
363
486
|
(wire: DbInspectionWire): DbInspection =>
|
|
364
487
|
Object.freeze({
|
|
365
488
|
schemaId: state.schemaId,
|
|
366
489
|
generation: wire.generation,
|
|
367
|
-
|
|
368
|
-
populatedBytes: wire.populatedBytes,
|
|
369
|
-
diskBytes: wire.diskBytes,
|
|
370
|
-
residentEstimateBytes: wire.residentEstimateBytes,
|
|
490
|
+
storage: Object.freeze(wire.storage),
|
|
371
491
|
retainedOperations: wire.retainedOperations
|
|
372
492
|
})
|
|
373
493
|
)
|
|
@@ -402,23 +522,18 @@ function openDatabase<S extends AnySchema>(
|
|
|
402
522
|
operation: "Db.create" | "Db.open",
|
|
403
523
|
path: string,
|
|
404
524
|
schema: S,
|
|
405
|
-
work: ExecutionPolicy,
|
|
406
525
|
create: boolean
|
|
407
526
|
): Effect.Effect<Db<S>, DbError, NativeRuntime | Scope.Scope> {
|
|
408
527
|
return Effect.gen(function* () {
|
|
409
528
|
const runtime = yield* runtimeHandle()
|
|
410
|
-
const compiled: CompiledSchema<S> = yield* CoreSchema.compile(schema
|
|
411
|
-
const spec = lower(schema)
|
|
412
|
-
const wire = yield* Effect.try({
|
|
413
|
-
try: () => policyWire(work, operation),
|
|
414
|
-
catch: () => refusal(operation, "InvalidArgument")
|
|
415
|
-
})
|
|
529
|
+
const compiled: CompiledSchema<S> = yield* CoreSchema.compile(schema)
|
|
530
|
+
const spec = lower(compiled.schema)
|
|
416
531
|
// Compound acquisition: register the directory owner and
|
|
417
532
|
// its finalizer BEFORE any interruptible child-open step.
|
|
418
533
|
const directory = yield* Effect.acquireRelease(
|
|
419
534
|
nativeOperationWith(
|
|
420
535
|
operation,
|
|
421
|
-
(callback) => runtimeNative.runtimeDirectoryAcquire(runtime,
|
|
536
|
+
(callback) => runtimeNative.runtimeDirectoryAcquire(runtime, path, callback),
|
|
422
537
|
runtimeNative.runtimeDirectoryTake,
|
|
423
538
|
(value) => value
|
|
424
539
|
),
|
|
@@ -432,7 +547,7 @@ function openDatabase<S extends AnySchema>(
|
|
|
432
547
|
Effect.gen(function* () {
|
|
433
548
|
const outcome = yield* nativeOperationWith(
|
|
434
549
|
operation,
|
|
435
|
-
(callback) => runtimeNative.runtimeDirectoryDbOpen(directory,
|
|
550
|
+
(callback) => runtimeNative.runtimeDirectoryDbOpen(directory, CHILD, spec, create, callback),
|
|
436
551
|
runtimeNative.runtimeDbTake,
|
|
437
552
|
(value) => value
|
|
438
553
|
).pipe(
|
|
@@ -448,8 +563,8 @@ function openDatabase<S extends AnySchema>(
|
|
|
448
563
|
)
|
|
449
564
|
return yield* Effect.fail(refusal(operation, "InvalidArgument"))
|
|
450
565
|
}
|
|
451
|
-
const state: DbState = { theory: schema, db: outcome.db, directory, schemaId: compiled.schemaId }
|
|
452
|
-
const db = makeDb(schema, state)
|
|
566
|
+
const state: DbState = { theory: compiled.schema, db: outcome.db, directory, schemaId: compiled.schemaId }
|
|
567
|
+
const db = makeDb(compiled.schema, state)
|
|
453
568
|
dbStates.set(db, state)
|
|
454
569
|
return db
|
|
455
570
|
}),
|
|
@@ -481,11 +596,11 @@ const dbStates = new WeakMap<object, DbState>()
|
|
|
481
596
|
* implementation as `Schema.compile` — prior compilation is optional.
|
|
482
597
|
*/
|
|
483
598
|
const Db = Object.freeze({
|
|
484
|
-
create<S extends AnySchema>(path: string, schema: S
|
|
485
|
-
return openDatabase("Db.create", path, schema,
|
|
599
|
+
create<S extends AnySchema>(path: string, schema: S) {
|
|
600
|
+
return openDatabase("Db.create", path, schema, true)
|
|
486
601
|
},
|
|
487
|
-
open<S extends AnySchema>(path: string, schema: S
|
|
488
|
-
return openDatabase("Db.open", path, schema,
|
|
602
|
+
open<S extends AnySchema>(path: string, schema: S) {
|
|
603
|
+
return openDatabase("Db.open", path, schema, false)
|
|
489
604
|
}
|
|
490
605
|
})
|
|
491
606
|
|
|
@@ -506,44 +621,32 @@ function internalPublishedReader<S extends AnySchema>(
|
|
|
506
621
|
): {
|
|
507
622
|
readonly get: QueryReader<S>["get"]
|
|
508
623
|
readonly execute: QueryReader<S>["execute"]
|
|
509
|
-
readonly
|
|
624
|
+
readonly prepare: QueryReader<S>["prepare"]
|
|
510
625
|
} {
|
|
511
|
-
const state: SnapshotState<S> = { theory, handle: core as SnapshotHandle }
|
|
626
|
+
const state: SnapshotState<S> = { theory: schemaDescriptor(theory), handle: core as SnapshotHandle }
|
|
512
627
|
return Object.freeze({
|
|
513
|
-
get<
|
|
514
|
-
return getOn(state,
|
|
628
|
+
get<K extends KeyStatement<Rel<S>, readonly string[]>>(key: K, value: NoInfer<Key<K>>) {
|
|
629
|
+
return getOn(state, key, value)
|
|
515
630
|
},
|
|
516
|
-
execute<P extends ParamsRecord, A>(queryValue: QueryTemplate<S, P, A>, params: P
|
|
517
|
-
return executeOn<S, A>(state,
|
|
631
|
+
execute<P extends ParamsRecord, A>(queryValue: QueryTemplate<S, P, A>, params: P) {
|
|
632
|
+
return executeOn<S, A>(state, queryValue as AnyQuery, params)
|
|
518
633
|
},
|
|
519
|
-
|
|
520
|
-
return
|
|
521
|
-
nativeOperationWith(
|
|
522
|
-
"Snapshot.session",
|
|
523
|
-
(callback) => dbNative.runtimeSnapshotSession(state.handle, policyWire(work, "Snapshot.session"), callback),
|
|
524
|
-
dbNative.runtimeSessionTake,
|
|
525
|
-
(value) => makeSession(state, value)
|
|
526
|
-
),
|
|
527
|
-
(session) =>
|
|
528
|
-
Effect.suspend(() =>
|
|
529
|
-
releaseOwner("ExecutionSession.close", (callback) =>
|
|
530
|
-
dbNative.runtimeSessionClose(sessionHandles.get(session) ?? missingSession(), callback)
|
|
531
|
-
)
|
|
532
|
-
),
|
|
533
|
-
{ interruptible: true }
|
|
534
|
-
)
|
|
634
|
+
prepare<P extends ParamsRecord, A>(queryValue: QueryTemplate<S, P, A>) {
|
|
635
|
+
return prepareOn(state, queryValue)
|
|
535
636
|
}
|
|
536
637
|
})
|
|
537
638
|
}
|
|
538
639
|
|
|
539
640
|
export type {
|
|
540
|
-
ApplyExpected,
|
|
541
|
-
ApplyOptions,
|
|
542
641
|
ApplyOutcome,
|
|
543
642
|
CoreWitness,
|
|
544
643
|
DbInspection,
|
|
545
|
-
|
|
644
|
+
JudgeOutcome,
|
|
645
|
+
PreparedQuery,
|
|
546
646
|
QueryReader,
|
|
547
|
-
Snapshot
|
|
647
|
+
Snapshot,
|
|
648
|
+
StorageInspection,
|
|
649
|
+
WriteExpected,
|
|
650
|
+
WriteOptions
|
|
548
651
|
}
|
|
549
652
|
export { Db, internalPublishedReader }
|