@lix-js/sdk 0.6.0-preview.4 → 0.6.0-preview.5
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/README.md +1 -1
- package/SKILL.md +65 -64
- package/dist/engine-wasm/index.js +4 -4
- package/dist/engine-wasm/wasm/lix_engine.d.ts +5 -5
- package/dist/engine-wasm/wasm/lix_engine.js +130 -118
- package/dist/engine-wasm/wasm/lix_engine.wasm +0 -0
- package/dist/engine-wasm/wasm/lix_engine.wasm.d.ts +9 -8
- package/dist/generated/builtin-schemas.d.ts +69 -69
- package/dist/generated/builtin-schemas.js +94 -94
- package/dist/open-lix.d.ts +33 -26
- package/dist/open-lix.js +10 -10
- package/dist/sqlite/index.js +86 -30
- package/dist-engine-src/README.md +3 -3
- package/dist-engine-src/src/backend/capabilities.rs +67 -0
- package/dist-engine-src/src/backend/conformance/baseline.rs +1127 -0
- package/dist-engine-src/src/backend/conformance/factory.rs +93 -0
- package/dist-engine-src/src/backend/conformance/failure_tests.rs +608 -0
- package/dist-engine-src/src/backend/conformance/fixtures.rs +26 -0
- package/dist-engine-src/src/backend/conformance/mod.rs +75 -0
- package/dist-engine-src/src/backend/conformance/model.rs +28 -0
- package/dist-engine-src/src/backend/conformance/model_based.rs +257 -0
- package/dist-engine-src/src/backend/conformance/persistence.rs +204 -0
- package/dist-engine-src/src/backend/conformance/projection.rs +21 -0
- package/dist-engine-src/src/backend/conformance/pushdown.rs +24 -0
- package/dist-engine-src/src/backend/conformance/runner.rs +90 -0
- package/dist-engine-src/src/backend/conformance/scan.rs +24 -0
- package/dist-engine-src/src/backend/conformance/write.rs +16 -0
- package/dist-engine-src/src/backend/error.rs +94 -0
- package/dist-engine-src/src/backend/in_memory.rs +670 -0
- package/dist-engine-src/src/backend/mod.rs +36 -9
- package/dist-engine-src/src/backend/predicate.rs +80 -0
- package/dist-engine-src/src/backend/traits.rs +260 -0
- package/dist-engine-src/src/backend/types.rs +224 -81
- package/dist-engine-src/src/binary_cas/context.rs +8 -8
- package/dist-engine-src/src/binary_cas/kv.rs +234 -259
- package/dist-engine-src/src/{version → branch}/context.rs +12 -12
- package/dist-engine-src/src/branch/lifecycle.rs +221 -0
- package/dist-engine-src/src/branch/mod.rs +13 -0
- package/dist-engine-src/src/branch/refs.rs +321 -0
- package/dist-engine-src/src/branch/stage_rows.rs +67 -0
- package/dist-engine-src/src/branch/types.rs +21 -0
- package/dist-engine-src/src/catalog/context.rs +18 -18
- package/dist-engine-src/src/catalog/snapshot.rs +8 -8
- package/dist-engine-src/src/changelog/bench_support.rs +785 -0
- package/dist-engine-src/src/changelog/change.rs +1 -0
- package/dist-engine-src/src/changelog/codec.rs +497 -0
- package/dist-engine-src/src/changelog/commit.rs +1 -0
- package/dist-engine-src/src/changelog/context.rs +1614 -0
- package/dist-engine-src/src/changelog/mod.rs +29 -0
- package/dist-engine-src/src/changelog/store.rs +163 -0
- package/dist-engine-src/src/changelog/test_support.rs +54 -0
- package/dist-engine-src/src/changelog/types.rs +213 -0
- package/dist-engine-src/src/commit_graph/context.rs +317 -274
- package/dist-engine-src/src/commit_graph/mod.rs +2 -4
- package/dist-engine-src/src/commit_graph/types.rs +22 -42
- package/dist-engine-src/src/commit_graph/walker.rs +133 -103
- package/dist-engine-src/src/common/error.rs +52 -18
- package/dist-engine-src/src/common/identity.rs +2 -2
- package/dist-engine-src/src/common/mod.rs +1 -1
- package/dist-engine-src/src/domain.rs +42 -46
- package/dist-engine-src/src/engine.rs +74 -96
- package/dist-engine-src/src/{entity_identity.rs → entity_pk.rs} +89 -92
- package/dist-engine-src/src/functions/context.rs +56 -52
- package/dist-engine-src/src/functions/state.rs +51 -52
- package/dist-engine-src/src/init.rs +288 -154
- package/dist-engine-src/src/json_store/context.rs +15 -266
- package/dist-engine-src/src/json_store/mod.rs +26 -0
- package/dist-engine-src/src/json_store/store.rs +103 -718
- package/dist-engine-src/src/json_store/types.rs +4 -9
- package/dist-engine-src/src/lib.rs +49 -19
- package/dist-engine-src/src/live_state/context.rs +654 -790
- package/dist-engine-src/src/live_state/mod.rs +9 -3
- package/dist-engine-src/src/live_state/overlay.rs +4 -4
- package/dist-engine-src/src/live_state/types.rs +30 -21
- package/dist-engine-src/src/live_state/visibility.rs +514 -71
- package/dist-engine-src/src/plugin/install.rs +48 -48
- package/dist-engine-src/src/plugin/manifest.rs +7 -7
- package/dist-engine-src/src/plugin/materializer.rs +0 -275
- package/dist-engine-src/src/plugin/plugin_manifest.json +4 -3
- package/dist-engine-src/src/schema/builtin/lix_binary_blob_ref.json +2 -2
- package/dist-engine-src/src/schema/builtin/lix_branch_descriptor.json +34 -0
- package/dist-engine-src/src/schema/builtin/lix_branch_ref.json +48 -0
- package/dist-engine-src/src/schema/builtin/lix_change.json +3 -3
- package/dist-engine-src/src/schema/builtin/lix_commit.json +1 -1
- package/dist-engine-src/src/schema/builtin/lix_label_assignment.json +6 -6
- package/dist-engine-src/src/schema/builtin/mod.rs +18 -20
- package/dist-engine-src/src/schema/compatibility.rs +11 -11
- package/dist-engine-src/src/schema/definition.json +2 -2
- package/dist-engine-src/src/schema/definition.rs +5 -5
- package/dist-engine-src/src/schema/key.rs +3 -3
- package/dist-engine-src/src/schema/mod.rs +1 -1
- package/dist-engine-src/src/schema/tests.rs +18 -18
- package/dist-engine-src/src/session/context.rs +803 -148
- package/dist-engine-src/src/session/create_branch.rs +94 -0
- package/dist-engine-src/src/session/execute.rs +223 -83
- package/dist-engine-src/src/session/merge/analysis.rs +9 -3
- package/dist-engine-src/src/session/merge/{version.rs → branch.rs} +119 -129
- package/dist-engine-src/src/session/merge/conflicts.rs +2 -2
- package/dist-engine-src/src/session/merge/mod.rs +5 -6
- package/dist-engine-src/src/session/merge/stats.rs +7 -11
- package/dist-engine-src/src/session/mod.rs +15 -12
- package/dist-engine-src/src/session/switch_branch.rs +113 -0
- package/dist-engine-src/src/session/transaction.rs +495 -14
- package/dist-engine-src/src/sql2/{classify.rs → bind/classify.rs} +3 -75
- package/dist-engine-src/src/sql2/bind/error.rs +5 -0
- package/dist-engine-src/src/sql2/bind/expr.rs +29 -0
- package/dist-engine-src/src/sql2/bind/mod.rs +12 -0
- package/dist-engine-src/src/sql2/{udfs/public_call.rs → bind/public_udf.rs} +71 -3
- package/dist-engine-src/src/sql2/bind/read.rs +65 -0
- package/dist-engine-src/src/sql2/bind/statement.rs +2236 -0
- package/dist-engine-src/src/sql2/bind/table.rs +273 -0
- package/dist-engine-src/src/sql2/bind/write.rs +86 -0
- package/dist-engine-src/src/sql2/branch_scope.rs +436 -0
- package/dist-engine-src/src/sql2/catalog/capability.rs +20 -0
- package/dist-engine-src/src/sql2/catalog/entity_surface.rs +296 -0
- package/dist-engine-src/src/sql2/catalog/mod.rs +15 -0
- package/dist-engine-src/src/sql2/catalog/registry.rs +556 -0
- package/dist-engine-src/src/sql2/catalog/schema.rs +88 -0
- package/dist-engine-src/src/sql2/catalog/surface.rs +41 -0
- package/dist-engine-src/src/sql2/change_materialization.rs +122 -0
- package/dist-engine-src/src/sql2/context.rs +36 -30
- package/dist-engine-src/src/sql2/error.rs +1 -1
- package/dist-engine-src/src/sql2/exec/bound_public_write.rs +1593 -0
- package/dist-engine-src/src/sql2/exec/datafusion.rs +5266 -0
- package/dist-engine-src/src/sql2/exec/fast_write.rs +82 -0
- package/dist-engine-src/src/sql2/exec/mod.rs +24 -0
- package/dist-engine-src/src/sql2/exec/write.rs +661 -0
- package/dist-engine-src/src/sql2/filesystem_planner.rs +72 -77
- package/dist-engine-src/src/sql2/filesystem_visibility.rs +21 -21
- package/dist-engine-src/src/sql2/history_projection.rs +8 -8
- package/dist-engine-src/src/sql2/history_route.rs +35 -31
- package/dist-engine-src/src/sql2/mod.rs +28 -23
- package/dist-engine-src/src/sql2/optimize/datafusion.rs +1 -0
- package/dist-engine-src/src/sql2/optimize/mod.rs +2 -0
- package/dist-engine-src/src/sql2/optimize/simple_write.rs +116 -0
- package/dist-engine-src/src/sql2/parse/mod.rs +69 -0
- package/dist-engine-src/src/sql2/parse/normalize.rs +1 -0
- package/dist-engine-src/src/sql2/plan/branch_scope.rs +24 -0
- package/dist-engine-src/src/sql2/plan/mod.rs +5 -0
- package/dist-engine-src/src/sql2/plan/predicate.rs +22 -0
- package/dist-engine-src/src/sql2/plan/write.rs +147 -0
- package/dist-engine-src/src/sql2/predicate_typecheck.rs +258 -0
- package/dist-engine-src/src/sql2/{version_provider.rs → providers/branch.rs} +218 -214
- package/dist-engine-src/src/sql2/{change_provider.rs → providers/change.rs} +156 -42
- package/dist-engine-src/src/sql2/{directory_provider.rs → providers/directory.rs} +291 -322
- package/dist-engine-src/src/sql2/{directory_history_provider.rs → providers/directory_history.rs} +56 -42
- package/dist-engine-src/src/sql2/providers/entity.rs +1484 -0
- package/dist-engine-src/src/sql2/{entity_history_provider.rs → providers/entity_history.rs} +43 -31
- package/dist-engine-src/src/sql2/{file_provider.rs → providers/file.rs} +323 -316
- package/dist-engine-src/src/sql2/{file_history_provider.rs → providers/file_history.rs} +60 -46
- package/dist-engine-src/src/sql2/{history_provider.rs → providers/history.rs} +46 -32
- package/dist-engine-src/src/sql2/{lix_state_provider.rs → providers/lix_state.rs} +359 -329
- package/dist-engine-src/src/sql2/providers/mod.rs +508 -0
- package/dist-engine-src/src/sql2/read_only.rs +2 -2
- package/dist-engine-src/src/sql2/session.rs +47 -96
- package/dist-engine-src/src/sql2/storage/constraints.rs +1 -0
- package/dist-engine-src/src/sql2/storage/mod.rs +1 -0
- package/dist-engine-src/src/sql2/test_support/differential.rs +712 -0
- package/dist-engine-src/src/sql2/test_support/generators.rs +354 -0
- package/dist-engine-src/src/sql2/test_support/mod.rs +2 -0
- package/dist-engine-src/src/sql2/udfs/{lix_active_version_commit_id.rs → lix_active_branch_commit_id.rs} +7 -7
- package/dist-engine-src/src/sql2/udfs/mod.rs +3 -6
- package/dist-engine-src/src/sql2/write_normalization.rs +45 -22
- package/dist-engine-src/src/storage/conformance.rs +399 -0
- package/dist-engine-src/src/storage/context.rs +552 -288
- package/dist-engine-src/src/storage/mod.rs +48 -10
- package/dist-engine-src/src/storage/point.rs +440 -0
- package/dist-engine-src/src/storage/read_scope.rs +43 -64
- package/dist-engine-src/src/storage/reader.rs +867 -0
- package/dist-engine-src/src/storage/scan.rs +784 -0
- package/dist-engine-src/src/storage/spaces.rs +236 -0
- package/dist-engine-src/src/storage/stats.rs +80 -0
- package/dist-engine-src/src/storage/write_set.rs +962 -0
- package/dist-engine-src/src/storage_bench.rs +136 -4828
- package/dist-engine-src/src/test_support.rs +360 -138
- package/dist-engine-src/src/tracked_state/bench_support.rs +394 -0
- package/dist-engine-src/src/tracked_state/codec.rs +155 -1057
- package/dist-engine-src/src/tracked_state/commit_root_rebuild.rs +358 -0
- package/dist-engine-src/src/tracked_state/context.rs +1927 -993
- package/dist-engine-src/src/tracked_state/diff.rs +1715 -261
- package/dist-engine-src/src/tracked_state/merge.rs +74 -88
- package/dist-engine-src/src/tracked_state/mod.rs +19 -16
- package/dist-engine-src/src/tracked_state/{materialization.rs → row_materialization.rs} +50 -178
- package/dist-engine-src/src/tracked_state/storage.rs +243 -191
- package/dist-engine-src/src/tracked_state/tree.rs +247 -371
- package/dist-engine-src/src/tracked_state/types.rs +49 -42
- package/dist-engine-src/src/transaction/bench_support.rs +407 -0
- package/dist-engine-src/src/transaction/commit.rs +821 -713
- package/dist-engine-src/src/transaction/context.rs +705 -600
- package/dist-engine-src/src/transaction/mod.rs +13 -2
- package/dist-engine-src/src/transaction/normalization.rs +63 -76
- package/dist-engine-src/src/transaction/prep.rs +13 -13
- package/dist-engine-src/src/transaction/schema_resolver.rs +19 -5
- package/dist-engine-src/src/transaction/staging.rs +228 -434
- package/dist-engine-src/src/transaction/types.rs +41 -98
- package/dist-engine-src/src/transaction/validation.rs +382 -446
- package/dist-engine-src/src/untracked_state/codec.rs +337 -29
- package/dist-engine-src/src/untracked_state/context.rs +7 -7
- package/dist-engine-src/src/untracked_state/materialization.rs +2 -2
- package/dist-engine-src/src/untracked_state/mod.rs +1 -1
- package/dist-engine-src/src/untracked_state/storage.rs +659 -157
- package/dist-engine-src/src/untracked_state/types.rs +21 -21
- package/package.json +71 -68
- package/dist-engine-src/src/backend/kv.rs +0 -358
- package/dist-engine-src/src/backend/testing.rs +0 -658
- package/dist-engine-src/src/commit_store/codec.rs +0 -887
- package/dist-engine-src/src/commit_store/context.rs +0 -944
- package/dist-engine-src/src/commit_store/materialization.rs +0 -84
- package/dist-engine-src/src/commit_store/mod.rs +0 -16
- package/dist-engine-src/src/commit_store/storage.rs +0 -600
- package/dist-engine-src/src/commit_store/types.rs +0 -215
- package/dist-engine-src/src/schema/builtin/lix_version_descriptor.json +0 -34
- package/dist-engine-src/src/schema/builtin/lix_version_ref.json +0 -48
- package/dist-engine-src/src/session/create_version.rs +0 -88
- package/dist-engine-src/src/session/merge/apply.rs +0 -23
- package/dist-engine-src/src/session/optimization9_sql2_bench.rs +0 -100
- package/dist-engine-src/src/session/switch_version.rs +0 -110
- package/dist-engine-src/src/sql2/entity_provider.rs +0 -3211
- package/dist-engine-src/src/sql2/execute.rs +0 -3533
- package/dist-engine-src/src/sql2/public_bind/assignment.rs +0 -46
- package/dist-engine-src/src/sql2/public_bind/capability.rs +0 -41
- package/dist-engine-src/src/sql2/public_bind/dml.rs +0 -172
- package/dist-engine-src/src/sql2/public_bind/mod.rs +0 -26
- package/dist-engine-src/src/sql2/public_bind/table.rs +0 -168
- package/dist-engine-src/src/sql2/version_scope.rs +0 -394
- package/dist-engine-src/src/storage/types.rs +0 -501
- package/dist-engine-src/src/tracked_state/by_file_index.rs +0 -98
- package/dist-engine-src/src/tracked_state/materializer.rs +0 -488
- package/dist-engine-src/src/transaction/live_state_overlay.rs +0 -35
- package/dist-engine-src/src/version/lifecycle.rs +0 -221
- package/dist-engine-src/src/version/mod.rs +0 -13
- package/dist-engine-src/src/version/refs.rs +0 -330
- package/dist-engine-src/src/version/stage_rows.rs +0 -67
- package/dist-engine-src/src/version/types.rs +0 -21
|
@@ -1,24 +1,24 @@
|
|
|
1
1
|
use async_trait::async_trait;
|
|
2
2
|
use tokio::sync::Mutex;
|
|
3
3
|
|
|
4
|
+
use crate::branch::BRANCH_REF_SCHEMA_KEY;
|
|
4
5
|
use crate::commit_graph::CommitGraphContext;
|
|
5
|
-
use crate::
|
|
6
|
-
use crate::live_state::visibility;
|
|
6
|
+
use crate::entity_pk::EntityPk;
|
|
7
7
|
use crate::live_state::{
|
|
8
|
-
|
|
8
|
+
expanded_branch_ids, resolve_visible_rows, LiveStateReader, LiveStateRowRequest,
|
|
9
|
+
LiveStateScanRequest, MaterializedLiveStateRow, VisibilityBranchScope, VisibilityRequest,
|
|
9
10
|
};
|
|
10
|
-
use crate::storage::
|
|
11
|
+
use crate::storage::StorageRead;
|
|
11
12
|
use crate::tracked_state::{
|
|
12
|
-
MaterializedTrackedStateRow, TrackedStateContext, TrackedStateFilter,
|
|
13
|
-
|
|
13
|
+
MaterializedTrackedStateRow, TrackedStateContext, TrackedStateFilter, TrackedStateReadColumns,
|
|
14
|
+
TrackedStateScanRequest,
|
|
14
15
|
};
|
|
15
16
|
use crate::untracked_state::{
|
|
16
17
|
UntrackedStateContext, UntrackedStateRowRequest, UntrackedStateScanRequest,
|
|
17
18
|
};
|
|
18
|
-
use crate::version::VERSION_REF_SCHEMA_KEY;
|
|
19
19
|
use crate::LixError;
|
|
20
20
|
use crate::NullableKeyFilter;
|
|
21
|
-
use crate::
|
|
21
|
+
use crate::GLOBAL_BRANCH_ID;
|
|
22
22
|
|
|
23
23
|
const COMMIT_SCHEMA_KEY: &str = "lix_commit";
|
|
24
24
|
const COMMIT_EDGE_SCHEMA_KEY: &str = "lix_commit_edge";
|
|
@@ -50,7 +50,7 @@ impl LiveStateContext {
|
|
|
50
50
|
/// Creates a visible live-state reader over a caller-provided KV store.
|
|
51
51
|
pub(crate) fn reader<S>(&self, store: S) -> LiveStateStoreReader<S>
|
|
52
52
|
where
|
|
53
|
-
S:
|
|
53
|
+
S: StorageRead + Send + Sync,
|
|
54
54
|
{
|
|
55
55
|
LiveStateStoreReader {
|
|
56
56
|
store: Mutex::new(store),
|
|
@@ -71,46 +71,45 @@ pub(crate) struct LiveStateStoreReader<S> {
|
|
|
71
71
|
|
|
72
72
|
impl<S> LiveStateStoreReader<S>
|
|
73
73
|
where
|
|
74
|
-
S:
|
|
74
|
+
S: StorageRead + Send + Sync,
|
|
75
75
|
{
|
|
76
76
|
pub(crate) async fn scan_rows(
|
|
77
77
|
&self,
|
|
78
78
|
request: &LiveStateScanRequest,
|
|
79
79
|
) -> Result<Vec<MaterializedLiveStateRow>, LixError> {
|
|
80
|
-
let
|
|
81
|
-
let scope = scan_scope(
|
|
80
|
+
let store = self.store.lock().await;
|
|
81
|
+
let scope = scan_scope(&*store, &self.untracked_state, request).await?;
|
|
82
82
|
let derived_rows =
|
|
83
|
-
scan_commit_derived_rows(
|
|
83
|
+
scan_commit_derived_rows(&*store, &self.commit_graph, request, &scope).await?;
|
|
84
84
|
let mut tracked_rows = Vec::new();
|
|
85
85
|
if request.filter.untracked != Some(true) && !is_commit_derived_only_request(request) {
|
|
86
|
-
for
|
|
86
|
+
for branch_id in &scope.storage_branch_ids {
|
|
87
87
|
let Some(commit_id) =
|
|
88
|
-
|
|
89
|
-
.await?
|
|
88
|
+
load_branch_ref_commit_id(&*store, &self.untracked_state, branch_id).await?
|
|
90
89
|
else {
|
|
91
90
|
continue;
|
|
92
91
|
};
|
|
93
92
|
let tracked_request = tracked_scan_request_from_live(request);
|
|
94
|
-
let source =
|
|
95
|
-
let store
|
|
93
|
+
let source = tracked_source_from_branch_id(branch_id);
|
|
94
|
+
let store = &*store;
|
|
96
95
|
tracked_rows.extend(
|
|
97
96
|
self.tracked_state
|
|
98
97
|
.reader(store)
|
|
99
98
|
.scan_rows_at_commit(&commit_id, &tracked_request)
|
|
100
99
|
.await?
|
|
101
100
|
.into_iter()
|
|
102
|
-
.map(|row| project_tracked_row(row,
|
|
101
|
+
.map(|row| project_tracked_row(row, branch_id, source)),
|
|
103
102
|
);
|
|
104
103
|
}
|
|
105
104
|
}
|
|
106
105
|
|
|
107
106
|
let untracked_rows = if request.filter.untracked != Some(false) {
|
|
108
|
-
let store
|
|
107
|
+
let store = &*store;
|
|
109
108
|
self.untracked_state
|
|
110
109
|
.reader(store)
|
|
111
110
|
.scan_rows(&untracked_scan_request_from_live(
|
|
112
111
|
request,
|
|
113
|
-
&scope.
|
|
112
|
+
&scope.storage_branch_ids,
|
|
114
113
|
))
|
|
115
114
|
.await?
|
|
116
115
|
.into_iter()
|
|
@@ -132,14 +131,17 @@ where
|
|
|
132
131
|
.chain(derived_rows)
|
|
133
132
|
.collect()
|
|
134
133
|
};
|
|
135
|
-
rows =
|
|
134
|
+
rows = resolve_visible_rows(
|
|
136
135
|
rows,
|
|
137
|
-
|
|
138
|
-
|
|
136
|
+
Vec::new(),
|
|
137
|
+
&VisibilityRequest {
|
|
138
|
+
branch_scope: VisibilityBranchScope::BranchIds {
|
|
139
|
+
branch_ids: scope.projection_branch_ids.clone(),
|
|
140
|
+
},
|
|
141
|
+
include_tombstones: request.filter.include_tombstones,
|
|
142
|
+
limit: request.limit,
|
|
143
|
+
},
|
|
139
144
|
);
|
|
140
|
-
if let Some(limit) = request.limit {
|
|
141
|
-
rows.truncate(limit);
|
|
142
|
-
}
|
|
143
145
|
Ok(rows)
|
|
144
146
|
}
|
|
145
147
|
|
|
@@ -147,95 +149,34 @@ where
|
|
|
147
149
|
&self,
|
|
148
150
|
request: &LiveStateRowRequest,
|
|
149
151
|
) -> Result<Option<MaterializedLiveStateRow>, LixError> {
|
|
150
|
-
let mut store = self.store.lock().await;
|
|
151
|
-
if !version_ref_exists(&mut *store, &self.untracked_state, &request.version_id).await? {
|
|
152
|
-
return Ok(None);
|
|
153
|
-
}
|
|
154
|
-
if is_commit_derived_schema(&request.schema_key)
|
|
155
|
-
&& request.file_id == NullableKeyFilter::Null
|
|
156
152
|
{
|
|
157
|
-
let
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
untracked: Some(false),
|
|
171
|
-
include_tombstones: false,
|
|
172
|
-
..Default::default()
|
|
173
|
-
},
|
|
174
|
-
limit: Some(1),
|
|
153
|
+
let store = self.store.lock().await;
|
|
154
|
+
if !branch_ref_exists(&*store, &self.untracked_state, &request.branch_id).await? {
|
|
155
|
+
return Ok(None);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
let rows = self
|
|
159
|
+
.scan_rows(&LiveStateScanRequest {
|
|
160
|
+
filter: crate::live_state::LiveStateFilter {
|
|
161
|
+
schema_keys: vec![request.schema_key.clone()],
|
|
162
|
+
entity_pks: vec![request.entity_pk.clone()],
|
|
163
|
+
branch_ids: vec![request.branch_id.clone()],
|
|
164
|
+
file_ids: vec![request.file_id.clone()],
|
|
165
|
+
include_tombstones: false,
|
|
175
166
|
..Default::default()
|
|
176
167
|
},
|
|
177
|
-
|
|
178
|
-
|
|
168
|
+
limit: Some(1),
|
|
169
|
+
..Default::default()
|
|
170
|
+
})
|
|
179
171
|
.await?;
|
|
180
|
-
|
|
181
|
-
return Ok(Some(row));
|
|
182
|
-
}
|
|
183
|
-
}
|
|
184
|
-
for candidate in load_row_candidates(request) {
|
|
185
|
-
match candidate.source {
|
|
186
|
-
LiveStateLookupSource::Untracked => {
|
|
187
|
-
let store: &mut dyn StorageReader = &mut *store;
|
|
188
|
-
if let Some(row) = self
|
|
189
|
-
.untracked_state
|
|
190
|
-
.reader(store)
|
|
191
|
-
.load_row(&untracked_row_request_from_live(
|
|
192
|
-
request,
|
|
193
|
-
&candidate.version_id,
|
|
194
|
-
))
|
|
195
|
-
.await?
|
|
196
|
-
{
|
|
197
|
-
return Ok(Some(visibility::project_loaded_row(
|
|
198
|
-
MaterializedLiveStateRow::from(row),
|
|
199
|
-
&request.version_id,
|
|
200
|
-
&candidate.version_id,
|
|
201
|
-
)));
|
|
202
|
-
}
|
|
203
|
-
}
|
|
204
|
-
LiveStateLookupSource::Tracked => {
|
|
205
|
-
let Some(commit_id) = load_version_ref_commit_id(
|
|
206
|
-
&mut *store,
|
|
207
|
-
&self.untracked_state,
|
|
208
|
-
&candidate.version_id,
|
|
209
|
-
)
|
|
210
|
-
.await?
|
|
211
|
-
else {
|
|
212
|
-
continue;
|
|
213
|
-
};
|
|
214
|
-
let store: &mut dyn StorageReader = &mut *store;
|
|
215
|
-
let tracked_request = tracked_row_request_from_live(request);
|
|
216
|
-
let mut rows = self
|
|
217
|
-
.tracked_state
|
|
218
|
-
.reader(store)
|
|
219
|
-
.load_rows_at_commit(&commit_id, &[tracked_request])
|
|
220
|
-
.await?;
|
|
221
|
-
if let Some(row) = rows.pop().flatten() {
|
|
222
|
-
return Ok(Some(project_tracked_row(
|
|
223
|
-
row,
|
|
224
|
-
&request.version_id,
|
|
225
|
-
tracked_source_from_version_id(&candidate.version_id),
|
|
226
|
-
)));
|
|
227
|
-
}
|
|
228
|
-
}
|
|
229
|
-
}
|
|
230
|
-
}
|
|
231
|
-
Ok(None)
|
|
172
|
+
Ok(rows.into_iter().next())
|
|
232
173
|
}
|
|
233
174
|
}
|
|
234
175
|
|
|
235
176
|
#[async_trait]
|
|
236
177
|
impl<S> LiveStateReader for LiveStateStoreReader<S>
|
|
237
178
|
where
|
|
238
|
-
S:
|
|
179
|
+
S: StorageRead + Send + Sync,
|
|
239
180
|
{
|
|
240
181
|
async fn scan_rows(
|
|
241
182
|
&self,
|
|
@@ -253,7 +194,7 @@ where
|
|
|
253
194
|
}
|
|
254
195
|
|
|
255
196
|
async fn scan_commit_derived_rows(
|
|
256
|
-
store: &
|
|
197
|
+
store: &(impl StorageRead + Send + Sync + ?Sized),
|
|
257
198
|
commit_graph: &CommitGraphContext,
|
|
258
199
|
request: &LiveStateScanRequest,
|
|
259
200
|
scope: &LiveStateScanScope,
|
|
@@ -265,10 +206,10 @@ async fn scan_commit_derived_rows(
|
|
|
265
206
|
return Ok(Vec::new());
|
|
266
207
|
}
|
|
267
208
|
|
|
268
|
-
let
|
|
269
|
-
vec![
|
|
209
|
+
let branch_ids = if scope.projection_branch_ids.is_empty() {
|
|
210
|
+
vec![GLOBAL_BRANCH_ID.to_string()]
|
|
270
211
|
} else {
|
|
271
|
-
scope.
|
|
212
|
+
scope.projection_branch_ids.clone()
|
|
272
213
|
};
|
|
273
214
|
let mut graph = commit_graph.reader(store);
|
|
274
215
|
let commits = graph.all_commits().await?;
|
|
@@ -277,23 +218,23 @@ async fn scan_commit_derived_rows(
|
|
|
277
218
|
schema_filter_allows(&request.filter.schema_keys, COMMIT_EDGE_SCHEMA_KEY);
|
|
278
219
|
|
|
279
220
|
let mut rows = Vec::new();
|
|
280
|
-
for
|
|
221
|
+
for branch_id in &branch_ids {
|
|
281
222
|
if include_commit {
|
|
282
223
|
for commit in &commits {
|
|
283
|
-
rows.push(commit_row(commit,
|
|
224
|
+
rows.push(commit_row(commit, branch_id)?);
|
|
284
225
|
}
|
|
285
226
|
}
|
|
286
227
|
if include_commit_edge {
|
|
287
228
|
for edge in graph.commit_edges(&commits) {
|
|
288
|
-
rows.push(commit_edge_row(&edge,
|
|
229
|
+
rows.push(commit_edge_row(&edge, branch_id)?);
|
|
289
230
|
}
|
|
290
231
|
}
|
|
291
232
|
}
|
|
292
233
|
|
|
293
234
|
rows.retain(|row| {
|
|
294
|
-
(request.filter.
|
|
295
|
-
&& (request.filter.
|
|
296
|
-
|| request.filter.
|
|
235
|
+
(request.filter.entity_pks.is_empty() || request.filter.entity_pks.contains(&row.entity_pk))
|
|
236
|
+
&& (request.filter.branch_ids.is_empty()
|
|
237
|
+
|| request.filter.branch_ids.contains(&row.branch_id))
|
|
297
238
|
});
|
|
298
239
|
Ok(rows)
|
|
299
240
|
}
|
|
@@ -333,7 +274,7 @@ fn file_filter_allows_null(file_ids: &[NullableKeyFilter<String>]) -> bool {
|
|
|
333
274
|
|
|
334
275
|
fn commit_row(
|
|
335
276
|
commit: &crate::commit_graph::CommitGraphCommit,
|
|
336
|
-
|
|
277
|
+
branch_id: &str,
|
|
337
278
|
) -> Result<MaterializedLiveStateRow, LixError> {
|
|
338
279
|
let snapshot_content = serde_json::to_string(&serde_json::json!({
|
|
339
280
|
"id": commit.commit_id,
|
|
@@ -345,7 +286,7 @@ fn commit_row(
|
|
|
345
286
|
)
|
|
346
287
|
})?;
|
|
347
288
|
Ok(MaterializedLiveStateRow {
|
|
348
|
-
|
|
289
|
+
entity_pk: EntityPk::single(commit.commit_id.clone()),
|
|
349
290
|
schema_key: COMMIT_SCHEMA_KEY.to_string(),
|
|
350
291
|
file_id: None,
|
|
351
292
|
snapshot_content: Some(snapshot_content),
|
|
@@ -357,13 +298,13 @@ fn commit_row(
|
|
|
357
298
|
change_id: Some(commit.change.id.clone()),
|
|
358
299
|
commit_id: Some(commit.commit_id.clone()),
|
|
359
300
|
untracked: false,
|
|
360
|
-
|
|
301
|
+
branch_id: branch_id.to_string(),
|
|
361
302
|
})
|
|
362
303
|
}
|
|
363
304
|
|
|
364
305
|
fn commit_edge_row(
|
|
365
306
|
edge: &crate::commit_graph::CommitGraphEdge,
|
|
366
|
-
|
|
307
|
+
branch_id: &str,
|
|
367
308
|
) -> Result<MaterializedLiveStateRow, LixError> {
|
|
368
309
|
let snapshot_content = serde_json::to_string(&serde_json::json!({
|
|
369
310
|
"parent_id": edge.parent_commit_id,
|
|
@@ -377,7 +318,7 @@ fn commit_edge_row(
|
|
|
377
318
|
)
|
|
378
319
|
})?;
|
|
379
320
|
Ok(MaterializedLiveStateRow {
|
|
380
|
-
|
|
321
|
+
entity_pk: EntityPk {
|
|
381
322
|
parts: vec![edge.parent_commit_id.clone(), edge.child_commit_id.clone()],
|
|
382
323
|
},
|
|
383
324
|
schema_key: COMMIT_EDGE_SCHEMA_KEY.to_string(),
|
|
@@ -391,7 +332,7 @@ fn commit_edge_row(
|
|
|
391
332
|
change_id: None,
|
|
392
333
|
commit_id: Some(edge.child_commit_id.clone()),
|
|
393
334
|
untracked: false,
|
|
394
|
-
|
|
335
|
+
branch_id: branch_id.to_string(),
|
|
395
336
|
})
|
|
396
337
|
}
|
|
397
338
|
|
|
@@ -399,13 +340,13 @@ fn tracked_scan_request_from_live(request: &LiveStateScanRequest) -> TrackedStat
|
|
|
399
340
|
TrackedStateScanRequest {
|
|
400
341
|
filter: TrackedStateFilter {
|
|
401
342
|
schema_keys: request.filter.schema_keys.clone(),
|
|
402
|
-
|
|
343
|
+
entity_pks: request.filter.entity_pks.clone(),
|
|
403
344
|
file_ids: request.filter.file_ids.clone(),
|
|
404
|
-
// Scan tombstones internally so
|
|
345
|
+
// Scan tombstones internally so branch-local tombstones can hide
|
|
405
346
|
// global fallback rows before the serving facade filters them.
|
|
406
347
|
include_tombstones: true,
|
|
407
348
|
},
|
|
408
|
-
|
|
349
|
+
read_columns: TrackedStateReadColumns {
|
|
409
350
|
columns: request.projection.columns.clone(),
|
|
410
351
|
},
|
|
411
352
|
limit: None,
|
|
@@ -414,10 +355,10 @@ fn tracked_scan_request_from_live(request: &LiveStateScanRequest) -> TrackedStat
|
|
|
414
355
|
|
|
415
356
|
fn untracked_scan_request_from_live(
|
|
416
357
|
request: &LiveStateScanRequest,
|
|
417
|
-
|
|
358
|
+
branch_ids: &[String],
|
|
418
359
|
) -> UntrackedStateScanRequest {
|
|
419
360
|
let mut filter: crate::untracked_state::UntrackedStateFilter = request.filter.clone().into();
|
|
420
|
-
filter.
|
|
361
|
+
filter.branch_ids = branch_ids.to_vec();
|
|
421
362
|
UntrackedStateScanRequest {
|
|
422
363
|
filter,
|
|
423
364
|
projection: crate::untracked_state::UntrackedStateProjection {
|
|
@@ -429,67 +370,67 @@ fn untracked_scan_request_from_live(
|
|
|
429
370
|
|
|
430
371
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
|
431
372
|
struct LiveStateScanScope {
|
|
432
|
-
|
|
433
|
-
|
|
373
|
+
storage_branch_ids: Vec<String>,
|
|
374
|
+
projection_branch_ids: Vec<String>,
|
|
434
375
|
}
|
|
435
376
|
|
|
436
377
|
async fn scan_scope(
|
|
437
|
-
store: &
|
|
378
|
+
store: &(impl StorageRead + Send + Sync + ?Sized),
|
|
438
379
|
untracked_state: &UntrackedStateContext,
|
|
439
380
|
request: &LiveStateScanRequest,
|
|
440
381
|
) -> Result<LiveStateScanScope, LixError> {
|
|
441
|
-
if request.filter.
|
|
382
|
+
if request.filter.branch_ids.is_empty() {
|
|
442
383
|
return Ok(LiveStateScanScope {
|
|
443
|
-
|
|
444
|
-
|
|
384
|
+
storage_branch_ids: all_branch_ref_ids(store, untracked_state).await?,
|
|
385
|
+
projection_branch_ids: Vec::new(),
|
|
445
386
|
});
|
|
446
387
|
}
|
|
447
388
|
|
|
448
|
-
let mut
|
|
449
|
-
for
|
|
450
|
-
if
|
|
451
|
-
|
|
389
|
+
let mut projection_branch_ids = Vec::new();
|
|
390
|
+
for branch_id in &request.filter.branch_ids {
|
|
391
|
+
if branch_ref_exists(store, untracked_state, branch_id).await? {
|
|
392
|
+
projection_branch_ids.push(branch_id.clone());
|
|
452
393
|
}
|
|
453
394
|
}
|
|
454
395
|
|
|
455
|
-
let
|
|
396
|
+
let storage_branch_ids = expanded_branch_ids(&projection_branch_ids);
|
|
456
397
|
Ok(LiveStateScanScope {
|
|
457
|
-
|
|
458
|
-
|
|
398
|
+
storage_branch_ids,
|
|
399
|
+
projection_branch_ids,
|
|
459
400
|
})
|
|
460
401
|
}
|
|
461
402
|
|
|
462
|
-
async fn
|
|
463
|
-
store: &
|
|
403
|
+
async fn all_branch_ref_ids(
|
|
404
|
+
store: &(impl StorageRead + Send + Sync + ?Sized),
|
|
464
405
|
untracked_state: &UntrackedStateContext,
|
|
465
406
|
) -> Result<Vec<String>, LixError> {
|
|
466
407
|
let rows = untracked_state
|
|
467
408
|
.reader(store)
|
|
468
409
|
.scan_rows(&UntrackedStateScanRequest {
|
|
469
410
|
filter: crate::untracked_state::UntrackedStateFilter {
|
|
470
|
-
schema_keys: vec![
|
|
471
|
-
|
|
411
|
+
schema_keys: vec![BRANCH_REF_SCHEMA_KEY.to_string()],
|
|
412
|
+
branch_ids: vec![GLOBAL_BRANCH_ID.to_string()],
|
|
472
413
|
..Default::default()
|
|
473
414
|
},
|
|
474
415
|
..Default::default()
|
|
475
416
|
})
|
|
476
417
|
.await?;
|
|
477
418
|
rows.into_iter()
|
|
478
|
-
.map(|row| row.
|
|
419
|
+
.map(|row| row.entity_pk.as_single_string_owned())
|
|
479
420
|
.collect()
|
|
480
421
|
}
|
|
481
422
|
|
|
482
|
-
async fn
|
|
483
|
-
store: &
|
|
423
|
+
async fn load_branch_ref_commit_id(
|
|
424
|
+
store: &(impl StorageRead + Send + Sync + ?Sized),
|
|
484
425
|
untracked_state: &UntrackedStateContext,
|
|
485
|
-
|
|
426
|
+
branch_id: &str,
|
|
486
427
|
) -> Result<Option<String>, LixError> {
|
|
487
428
|
let Some(row) = untracked_state
|
|
488
429
|
.reader(store)
|
|
489
430
|
.load_row(&UntrackedStateRowRequest {
|
|
490
|
-
schema_key:
|
|
491
|
-
|
|
492
|
-
|
|
431
|
+
schema_key: BRANCH_REF_SCHEMA_KEY.to_string(),
|
|
432
|
+
branch_id: GLOBAL_BRANCH_ID.to_string(),
|
|
433
|
+
entity_pk: crate::entity_pk::EntityPk::single(branch_id),
|
|
493
434
|
file_id: crate::NullableKeyFilter::Null,
|
|
494
435
|
})
|
|
495
436
|
.await?
|
|
@@ -503,7 +444,7 @@ async fn load_version_ref_commit_id(
|
|
|
503
444
|
serde_json::from_str::<serde_json::Value>(snapshot_content).map_err(|error| {
|
|
504
445
|
LixError::new(
|
|
505
446
|
"LIX_ERROR_UNKNOWN",
|
|
506
|
-
format!("live_state
|
|
447
|
+
format!("live_state branch-ref snapshot parse failed: {error}"),
|
|
507
448
|
)
|
|
508
449
|
})?;
|
|
509
450
|
Ok(snapshot
|
|
@@ -512,39 +453,37 @@ async fn load_version_ref_commit_id(
|
|
|
512
453
|
.map(str::to_string))
|
|
513
454
|
}
|
|
514
455
|
|
|
515
|
-
async fn
|
|
516
|
-
store: &
|
|
456
|
+
async fn branch_ref_exists(
|
|
457
|
+
store: &(impl StorageRead + Send + Sync + ?Sized),
|
|
517
458
|
untracked_state: &UntrackedStateContext,
|
|
518
|
-
|
|
459
|
+
branch_id: &str,
|
|
519
460
|
) -> Result<bool, LixError> {
|
|
520
|
-
Ok(
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
.is_some(),
|
|
524
|
-
)
|
|
461
|
+
Ok(load_branch_ref_commit_id(store, untracked_state, branch_id)
|
|
462
|
+
.await?
|
|
463
|
+
.is_some())
|
|
525
464
|
}
|
|
526
465
|
|
|
527
466
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
|
528
467
|
enum TrackedRowSource {
|
|
529
468
|
Global,
|
|
530
|
-
|
|
469
|
+
Branch,
|
|
531
470
|
}
|
|
532
471
|
|
|
533
|
-
fn
|
|
534
|
-
if
|
|
472
|
+
fn tracked_source_from_branch_id(branch_id: &str) -> TrackedRowSource {
|
|
473
|
+
if branch_id == GLOBAL_BRANCH_ID {
|
|
535
474
|
TrackedRowSource::Global
|
|
536
475
|
} else {
|
|
537
|
-
TrackedRowSource::
|
|
476
|
+
TrackedRowSource::Branch
|
|
538
477
|
}
|
|
539
478
|
}
|
|
540
479
|
|
|
541
480
|
fn project_tracked_row(
|
|
542
481
|
row: MaterializedTrackedStateRow,
|
|
543
|
-
|
|
482
|
+
view_branch_id: &str,
|
|
544
483
|
source: TrackedRowSource,
|
|
545
484
|
) -> MaterializedLiveStateRow {
|
|
546
485
|
MaterializedLiveStateRow {
|
|
547
|
-
|
|
486
|
+
entity_pk: row.entity_pk,
|
|
548
487
|
schema_key: row.schema_key,
|
|
549
488
|
file_id: row.file_id,
|
|
550
489
|
snapshot_content: row.snapshot_content,
|
|
@@ -556,83 +495,18 @@ fn project_tracked_row(
|
|
|
556
495
|
change_id: Some(row.change_id),
|
|
557
496
|
commit_id: Some(row.commit_id),
|
|
558
497
|
untracked: false,
|
|
559
|
-
|
|
560
|
-
}
|
|
561
|
-
}
|
|
562
|
-
|
|
563
|
-
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
|
564
|
-
enum LiveStateLookupSource {
|
|
565
|
-
Untracked,
|
|
566
|
-
Tracked,
|
|
567
|
-
}
|
|
568
|
-
|
|
569
|
-
#[derive(Debug, Clone, PartialEq, Eq)]
|
|
570
|
-
struct LiveStateLookupCandidate {
|
|
571
|
-
source: LiveStateLookupSource,
|
|
572
|
-
version_id: String,
|
|
573
|
-
}
|
|
574
|
-
|
|
575
|
-
fn load_row_candidates(request: &LiveStateRowRequest) -> Vec<LiveStateLookupCandidate> {
|
|
576
|
-
let mut candidates = vec![
|
|
577
|
-
LiveStateLookupCandidate {
|
|
578
|
-
source: LiveStateLookupSource::Untracked,
|
|
579
|
-
version_id: request.version_id.clone(),
|
|
580
|
-
},
|
|
581
|
-
LiveStateLookupCandidate {
|
|
582
|
-
source: LiveStateLookupSource::Tracked,
|
|
583
|
-
version_id: request.version_id.clone(),
|
|
584
|
-
},
|
|
585
|
-
];
|
|
586
|
-
|
|
587
|
-
if request.version_id != GLOBAL_VERSION_ID {
|
|
588
|
-
candidates.extend([
|
|
589
|
-
LiveStateLookupCandidate {
|
|
590
|
-
source: LiveStateLookupSource::Untracked,
|
|
591
|
-
version_id: GLOBAL_VERSION_ID.to_string(),
|
|
592
|
-
},
|
|
593
|
-
LiveStateLookupCandidate {
|
|
594
|
-
source: LiveStateLookupSource::Tracked,
|
|
595
|
-
version_id: GLOBAL_VERSION_ID.to_string(),
|
|
596
|
-
},
|
|
597
|
-
]);
|
|
598
|
-
}
|
|
599
|
-
|
|
600
|
-
candidates
|
|
601
|
-
}
|
|
602
|
-
|
|
603
|
-
fn untracked_row_request_from_live(
|
|
604
|
-
request: &LiveStateRowRequest,
|
|
605
|
-
version_id: &str,
|
|
606
|
-
) -> crate::untracked_state::UntrackedStateRowRequest {
|
|
607
|
-
crate::untracked_state::UntrackedStateRowRequest {
|
|
608
|
-
schema_key: request.schema_key.clone(),
|
|
609
|
-
version_id: version_id.to_string(),
|
|
610
|
-
entity_id: request.entity_id.clone(),
|
|
611
|
-
file_id: request.file_id.clone(),
|
|
612
|
-
}
|
|
613
|
-
}
|
|
614
|
-
|
|
615
|
-
fn tracked_row_request_from_live(request: &LiveStateRowRequest) -> TrackedStateRowRequest {
|
|
616
|
-
TrackedStateRowRequest {
|
|
617
|
-
schema_key: request.schema_key.clone(),
|
|
618
|
-
entity_id: request.entity_id.clone(),
|
|
619
|
-
file_id: request.file_id.clone(),
|
|
498
|
+
branch_id: view_branch_id.to_string(),
|
|
620
499
|
}
|
|
621
500
|
}
|
|
622
501
|
|
|
623
502
|
#[cfg(test)]
|
|
624
503
|
mod tests {
|
|
625
|
-
use std::sync::Arc;
|
|
626
|
-
|
|
627
504
|
use super::*;
|
|
628
|
-
use crate::
|
|
629
|
-
use crate::
|
|
630
|
-
use crate::entity_identity::EntityIdentity;
|
|
631
|
-
use crate::json_store::{
|
|
632
|
-
JsonStoreContext, JsonWritePlacementRef, NormalizedJson, NormalizedJsonRef,
|
|
633
|
-
};
|
|
505
|
+
use crate::entity_pk::EntityPk;
|
|
506
|
+
use crate::json_store::{JsonRef, JsonStoreContext, JsonWritePlacementRef, NormalizedJsonRef};
|
|
634
507
|
use crate::live_state::LiveStateFilter;
|
|
635
|
-
use crate::storage::{
|
|
508
|
+
use crate::storage::{InMemoryStorageBackend, StorageReadOptions, StorageWriteOptions};
|
|
509
|
+
use crate::storage::{StorageContext, StorageWriteSet};
|
|
636
510
|
use crate::tracked_state::{TrackedStateDeltaRef, TrackedStateScanRequest};
|
|
637
511
|
use crate::untracked_state::{MaterializedUntrackedStateRow, UntrackedStateContext};
|
|
638
512
|
use crate::NullableKeyFilter;
|
|
@@ -649,10 +523,11 @@ mod tests {
|
|
|
649
523
|
}
|
|
650
524
|
|
|
651
525
|
async fn write_untracked_rows_to_store(
|
|
652
|
-
|
|
526
|
+
storage: &StorageContext,
|
|
527
|
+
_read: &(impl crate::storage::StorageRead + Send + Sync + ?Sized),
|
|
653
528
|
rows: &[MaterializedUntrackedStateRow],
|
|
654
529
|
) {
|
|
655
|
-
let mut writes =
|
|
530
|
+
let mut writes = storage.new_write_set();
|
|
656
531
|
let canonical_rows = rows
|
|
657
532
|
.iter()
|
|
658
533
|
.map(|row| crate::test_support::untracked_state_row_from_materialized(&mut writes, row))
|
|
@@ -662,51 +537,92 @@ mod tests {
|
|
|
662
537
|
.writer(&mut writes)
|
|
663
538
|
.stage_rows(canonical_rows.iter().map(|row| row.as_ref()))
|
|
664
539
|
.expect("untracked rows should write");
|
|
665
|
-
|
|
666
|
-
.
|
|
667
|
-
.
|
|
668
|
-
.expect("untracked rows should apply");
|
|
540
|
+
storage
|
|
541
|
+
.commit_write_set(writes, StorageWriteOptions::default())
|
|
542
|
+
.expect("untracked rows should commit");
|
|
669
543
|
}
|
|
670
544
|
|
|
671
545
|
async fn write_empty_commits_to_store(
|
|
672
|
-
|
|
546
|
+
storage: &StorageContext,
|
|
547
|
+
read: &(impl crate::storage::StorageRead + Send + Sync),
|
|
673
548
|
commit_ids: &[&str],
|
|
674
549
|
) {
|
|
675
|
-
let mut writes =
|
|
550
|
+
let mut writes = storage.new_write_set();
|
|
551
|
+
let mut json_writer = JsonStoreContext::new().writer();
|
|
552
|
+
let mut append = crate::changelog::ChangelogAppend::default();
|
|
676
553
|
for commit_id in commit_ids {
|
|
677
554
|
let commit_change_id = format!("{commit_id}:commit");
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
.
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
555
|
+
append.commits.push(crate::changelog::CommitRecord {
|
|
556
|
+
format_version: 1,
|
|
557
|
+
commit_id: (*commit_id).to_string(),
|
|
558
|
+
parent_commit_ids: Vec::new(),
|
|
559
|
+
change_id: commit_change_id.clone(),
|
|
560
|
+
author_account_ids: Vec::new(),
|
|
561
|
+
created_at: "1970-01-01T00:00:00.000Z".to_string(),
|
|
562
|
+
});
|
|
563
|
+
append
|
|
564
|
+
.commit_change_refs
|
|
565
|
+
.push(crate::changelog::CommitChangeRefSet {
|
|
566
|
+
commit_id: (*commit_id).to_string(),
|
|
567
|
+
entries: Vec::new(),
|
|
568
|
+
});
|
|
569
|
+
}
|
|
570
|
+
let mut changelog_read = read;
|
|
571
|
+
let mut writer =
|
|
572
|
+
crate::changelog::ChangelogContext::new().writer(&mut changelog_read, &mut writes);
|
|
573
|
+
crate::changelog::ChangelogWriter::stage_append(&mut writer, append)
|
|
574
|
+
.await
|
|
575
|
+
.expect("empty changelog commits should stage");
|
|
576
|
+
drop(writer);
|
|
577
|
+
for commit_id in commit_ids {
|
|
578
|
+
let snapshot_content =
|
|
579
|
+
commit_row_snapshot_content(commit_id).expect("commit snapshot should encode");
|
|
580
|
+
let snapshot_ref = JsonRef::for_content(snapshot_content.as_bytes());
|
|
581
|
+
json_writer
|
|
582
|
+
.stage_batch(
|
|
583
|
+
&mut writes,
|
|
584
|
+
JsonWritePlacementRef::OutOfBand,
|
|
585
|
+
[NormalizedJsonRef::trusted_prehashed(
|
|
586
|
+
&snapshot_content,
|
|
587
|
+
snapshot_ref.clone(),
|
|
588
|
+
)],
|
|
690
589
|
)
|
|
590
|
+
.expect("commit snapshot should stage");
|
|
591
|
+
let change_id = format!("{commit_id}:commit");
|
|
592
|
+
let entity_pk = EntityPk::single(*commit_id);
|
|
593
|
+
let deltas = [TrackedStateDeltaRef {
|
|
594
|
+
schema_key: COMMIT_SCHEMA_KEY,
|
|
595
|
+
file_id: None,
|
|
596
|
+
entity_pk: &entity_pk,
|
|
597
|
+
change_id: &change_id,
|
|
598
|
+
commit_id,
|
|
599
|
+
snapshot_ref: Some(&snapshot_ref),
|
|
600
|
+
metadata_ref: None,
|
|
601
|
+
deleted: false,
|
|
602
|
+
created_at: "1970-01-01T00:00:00.000Z",
|
|
603
|
+
updated_at: "1970-01-01T00:00:00.000Z",
|
|
604
|
+
}];
|
|
605
|
+
TrackedStateContext::new()
|
|
606
|
+
.writer(read, &mut writes)
|
|
607
|
+
.stage_commit_root(commit_id, None, deltas)
|
|
691
608
|
.await
|
|
692
|
-
.expect("empty
|
|
609
|
+
.expect("empty tracked roots should stage");
|
|
693
610
|
}
|
|
694
|
-
|
|
695
|
-
.
|
|
696
|
-
.
|
|
697
|
-
.expect("empty commits should apply");
|
|
611
|
+
storage
|
|
612
|
+
.commit_write_set(writes, StorageWriteOptions::default())
|
|
613
|
+
.expect("empty commits should commit");
|
|
698
614
|
}
|
|
699
615
|
|
|
700
616
|
async fn stage_materialized_live_rows(
|
|
701
|
-
store: &
|
|
617
|
+
store: &(impl StorageRead + Send + Sync),
|
|
702
618
|
writes: &mut StorageWriteSet,
|
|
703
|
-
|
|
619
|
+
json_writer: &mut crate::json_store::JsonStoreWriter,
|
|
704
620
|
rows: &[MaterializedLiveStateRow],
|
|
705
621
|
) -> Result<(), LixError> {
|
|
706
622
|
let mut untracked_rows = Vec::new();
|
|
707
623
|
let mut tracked_rows_by_commit = std::collections::BTreeMap::<
|
|
708
624
|
String,
|
|
709
|
-
Vec<(crate::
|
|
625
|
+
Vec<(crate::changelog::ChangeRecord, String, String)>,
|
|
710
626
|
>::new();
|
|
711
627
|
let mut parent_by_commit = std::collections::BTreeMap::<String, Option<String>>::new();
|
|
712
628
|
|
|
@@ -732,7 +648,7 @@ mod tests {
|
|
|
732
648
|
}
|
|
733
649
|
if row.schema_key != COMMIT_SCHEMA_KEY {
|
|
734
650
|
let change = crate::test_support::tracked_change_from_materialized(&materialized)?;
|
|
735
|
-
|
|
651
|
+
stage_json_payloads_from_materialized(writes, json_writer, &materialized)?;
|
|
736
652
|
tracked_rows_by_commit.entry(commit_id).or_default().push((
|
|
737
653
|
change,
|
|
738
654
|
materialized.created_at,
|
|
@@ -750,69 +666,125 @@ mod tests {
|
|
|
750
666
|
.as_ref()
|
|
751
667
|
.map(|parent| vec![parent.clone()])
|
|
752
668
|
.unwrap_or_default();
|
|
669
|
+
let commit_created_at = rows
|
|
670
|
+
.first()
|
|
671
|
+
.map(|(change, _, _)| change.created_at.as_str())
|
|
672
|
+
.unwrap_or("1970-01-01T00:00:00.000Z")
|
|
673
|
+
.to_string();
|
|
674
|
+
let change_refs = rows
|
|
675
|
+
.iter()
|
|
676
|
+
.map(|(change, _, _)| crate::changelog::CommitChangeRef {
|
|
677
|
+
schema_key: change.schema_key.clone(),
|
|
678
|
+
file_id: change.file_id.clone(),
|
|
679
|
+
entity_pk: change.entity_pk.clone(),
|
|
680
|
+
change_id: change.change_id.clone(),
|
|
681
|
+
})
|
|
682
|
+
.collect::<Vec<_>>();
|
|
753
683
|
let commit_change_id = format!("{commit_id}:commit");
|
|
754
|
-
let
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
let
|
|
684
|
+
let mut append = crate::changelog::ChangelogAppend::default();
|
|
685
|
+
append
|
|
686
|
+
.changes
|
|
687
|
+
.extend(rows.iter().map(|(change, _, _)| change.clone()));
|
|
688
|
+
append.commits.push(crate::changelog::CommitRecord {
|
|
689
|
+
format_version: 1,
|
|
690
|
+
commit_id: commit_id.clone(),
|
|
691
|
+
parent_commit_ids: parent_ids,
|
|
692
|
+
change_id: commit_change_id.clone(),
|
|
693
|
+
author_account_ids: Vec::new(),
|
|
694
|
+
created_at: commit_created_at.clone(),
|
|
695
|
+
});
|
|
696
|
+
append
|
|
697
|
+
.commit_change_refs
|
|
698
|
+
.push(crate::changelog::CommitChangeRefSet {
|
|
699
|
+
commit_id: commit_id.clone(),
|
|
700
|
+
entries: change_refs,
|
|
701
|
+
});
|
|
702
|
+
let mut changelog_read = store;
|
|
703
|
+
let mut writer =
|
|
704
|
+
crate::changelog::ChangelogContext::new().writer(&mut changelog_read, writes);
|
|
705
|
+
crate::changelog::ChangelogWriter::stage_append(&mut writer, append).await?;
|
|
706
|
+
drop(writer);
|
|
707
|
+
let snapshot_content = commit_row_snapshot_content(&commit_id)?;
|
|
708
|
+
let snapshot_ref = JsonRef::for_content(snapshot_content.as_bytes());
|
|
709
|
+
json_writer.stage_batch(
|
|
710
|
+
writes,
|
|
711
|
+
JsonWritePlacementRef::OutOfBand,
|
|
712
|
+
[NormalizedJsonRef::trusted_prehashed(
|
|
713
|
+
&snapshot_content,
|
|
714
|
+
snapshot_ref.clone(),
|
|
715
|
+
)],
|
|
716
|
+
)?;
|
|
717
|
+
let commit_entity_pk = EntityPk::single(&commit_id);
|
|
718
|
+
let mut deltas = rows
|
|
773
719
|
.iter()
|
|
774
|
-
.
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
720
|
+
.map(|(change, created_at, updated_at)| TrackedStateDeltaRef {
|
|
721
|
+
schema_key: &change.schema_key,
|
|
722
|
+
file_id: change.file_id.as_deref(),
|
|
723
|
+
entity_pk: &change.entity_pk,
|
|
724
|
+
change_id: &change.change_id,
|
|
725
|
+
commit_id: &commit_id,
|
|
726
|
+
snapshot_ref: change.snapshot_ref.as_ref(),
|
|
727
|
+
metadata_ref: change.metadata_ref.as_ref(),
|
|
728
|
+
deleted: change.snapshot_ref.is_none(),
|
|
729
|
+
created_at,
|
|
730
|
+
updated_at,
|
|
731
|
+
})
|
|
783
732
|
.collect::<Vec<_>>();
|
|
733
|
+
deltas.push(TrackedStateDeltaRef {
|
|
734
|
+
schema_key: COMMIT_SCHEMA_KEY,
|
|
735
|
+
file_id: None,
|
|
736
|
+
entity_pk: &commit_entity_pk,
|
|
737
|
+
change_id: &commit_change_id,
|
|
738
|
+
commit_id: &commit_id,
|
|
739
|
+
snapshot_ref: Some(&snapshot_ref),
|
|
740
|
+
metadata_ref: None,
|
|
741
|
+
deleted: false,
|
|
742
|
+
created_at: &commit_created_at,
|
|
743
|
+
updated_at: &commit_created_at,
|
|
744
|
+
});
|
|
784
745
|
TrackedStateContext::new()
|
|
785
|
-
.writer(
|
|
786
|
-
.
|
|
746
|
+
.writer(&*store, writes)
|
|
747
|
+
.stage_commit_root(&commit_id, parent_commit_id.as_deref(), deltas)
|
|
787
748
|
.await?;
|
|
788
749
|
}
|
|
789
750
|
Ok(())
|
|
790
751
|
}
|
|
791
752
|
|
|
792
|
-
fn
|
|
753
|
+
fn commit_row_snapshot_content(commit_id: &str) -> Result<String, LixError> {
|
|
754
|
+
serde_json::to_string(&json!({ "id": commit_id })).map_err(|error| {
|
|
755
|
+
LixError::new(
|
|
756
|
+
"LIX_ERROR_UNKNOWN",
|
|
757
|
+
format!("failed to encode test commit snapshot: {error}"),
|
|
758
|
+
)
|
|
759
|
+
})
|
|
760
|
+
}
|
|
761
|
+
|
|
762
|
+
fn stage_json_payloads_from_materialized(
|
|
793
763
|
writes: &mut StorageWriteSet,
|
|
794
|
-
|
|
764
|
+
json_writer: &mut crate::json_store::JsonStoreWriter,
|
|
795
765
|
row: &MaterializedTrackedStateRow,
|
|
796
766
|
) -> Result<(), LixError> {
|
|
797
|
-
let mut payloads = Vec::new();
|
|
798
767
|
if let Some(snapshot) = row.snapshot_content.as_deref() {
|
|
799
|
-
|
|
768
|
+
json_writer.stage_batch(
|
|
769
|
+
writes,
|
|
770
|
+
JsonWritePlacementRef::OutOfBand,
|
|
771
|
+
[NormalizedJsonRef::trusted_prehashed(
|
|
772
|
+
snapshot,
|
|
773
|
+
JsonRef::for_content(snapshot.as_bytes()),
|
|
774
|
+
)],
|
|
775
|
+
)?;
|
|
800
776
|
}
|
|
801
777
|
if let Some(metadata) = row.metadata.as_ref() {
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
778
|
+
let serialized = crate::serialize_row_metadata(metadata);
|
|
779
|
+
json_writer.stage_batch(
|
|
780
|
+
writes,
|
|
781
|
+
JsonWritePlacementRef::OutOfBand,
|
|
782
|
+
[NormalizedJsonRef::trusted_prehashed(
|
|
783
|
+
&serialized,
|
|
784
|
+
JsonRef::for_content(serialized.as_bytes()),
|
|
785
|
+
)],
|
|
786
|
+
)?;
|
|
805
787
|
}
|
|
806
|
-
JsonStoreContext::new().writer().stage_batch(
|
|
807
|
-
writes,
|
|
808
|
-
JsonWritePlacementRef::CommitPack {
|
|
809
|
-
commit_id,
|
|
810
|
-
pack_id: 0,
|
|
811
|
-
},
|
|
812
|
-
payloads
|
|
813
|
-
.iter()
|
|
814
|
-
.map(|payload| NormalizedJsonRef::from(payload)),
|
|
815
|
-
)?;
|
|
816
788
|
Ok(())
|
|
817
789
|
}
|
|
818
790
|
|
|
@@ -838,20 +810,18 @@ mod tests {
|
|
|
838
810
|
|
|
839
811
|
#[tokio::test]
|
|
840
812
|
async fn live_state_overlays_untracked_rows() {
|
|
841
|
-
let
|
|
842
|
-
let storage = StorageContext::new(Arc::clone(&backend));
|
|
813
|
+
let storage = StorageContext::new(InMemoryStorageBackend::new());
|
|
843
814
|
let live_state = live_state_context();
|
|
844
815
|
|
|
845
|
-
let
|
|
846
|
-
.
|
|
847
|
-
.
|
|
848
|
-
.expect("transaction should open");
|
|
816
|
+
let read = storage
|
|
817
|
+
.begin_read(StorageReadOptions::default())
|
|
818
|
+
.expect("read should open");
|
|
849
819
|
{
|
|
850
820
|
let mut writes = StorageWriteSet::new();
|
|
851
821
|
let mut json_writer = JsonStoreContext::new().writer();
|
|
852
822
|
{
|
|
853
823
|
stage_materialized_live_rows(
|
|
854
|
-
|
|
824
|
+
&read,
|
|
855
825
|
&mut writes,
|
|
856
826
|
&mut json_writer,
|
|
857
827
|
&[tracked_row_with_commit(
|
|
@@ -863,22 +833,21 @@ mod tests {
|
|
|
863
833
|
.await
|
|
864
834
|
.expect("tracked row should stage");
|
|
865
835
|
}
|
|
866
|
-
|
|
867
|
-
.
|
|
868
|
-
.
|
|
869
|
-
.expect("tracked row should apply");
|
|
836
|
+
storage
|
|
837
|
+
.commit_write_set(writes, StorageWriteOptions::default())
|
|
838
|
+
.expect("writes should commit");
|
|
870
839
|
}
|
|
871
840
|
write_untracked_rows_to_store(
|
|
872
|
-
|
|
841
|
+
&storage,
|
|
842
|
+
&read,
|
|
873
843
|
&[
|
|
874
|
-
|
|
844
|
+
branch_ref_row("global", "commit-tracked"),
|
|
875
845
|
untracked_row("untracked-value"),
|
|
876
846
|
],
|
|
877
847
|
)
|
|
878
848
|
.await;
|
|
879
|
-
transaction.commit().await.expect("commit should persist");
|
|
880
849
|
|
|
881
|
-
let rows = scan_selected_tab_at(&live_state, storage
|
|
850
|
+
let rows = scan_selected_tab_at(&live_state, &storage, "global", false)
|
|
882
851
|
.await
|
|
883
852
|
.expect("scan should succeed");
|
|
884
853
|
assert_eq!(rows.len(), 1);
|
|
@@ -890,11 +859,15 @@ mod tests {
|
|
|
890
859
|
assert_eq!(rows[0].change_id, None);
|
|
891
860
|
|
|
892
861
|
let loaded = live_state
|
|
893
|
-
.reader(
|
|
862
|
+
.reader(
|
|
863
|
+
storage
|
|
864
|
+
.begin_read(StorageReadOptions::default())
|
|
865
|
+
.expect("read should open"),
|
|
866
|
+
)
|
|
894
867
|
.load_row(&LiveStateRowRequest {
|
|
895
868
|
schema_key: "lix_key_value".to_string(),
|
|
896
|
-
|
|
897
|
-
|
|
869
|
+
branch_id: "global".to_string(),
|
|
870
|
+
entity_pk: crate::entity_pk::EntityPk::single("selected-tab"),
|
|
898
871
|
file_id: NullableKeyFilter::Null,
|
|
899
872
|
})
|
|
900
873
|
.await
|
|
@@ -909,20 +882,18 @@ mod tests {
|
|
|
909
882
|
|
|
910
883
|
#[tokio::test]
|
|
911
884
|
async fn tracked_row_is_visible_without_untracked_overlay() {
|
|
912
|
-
let
|
|
913
|
-
let storage = StorageContext::new(Arc::clone(&backend));
|
|
885
|
+
let storage = StorageContext::new(InMemoryStorageBackend::new());
|
|
914
886
|
let live_state = live_state_context();
|
|
915
887
|
|
|
916
|
-
let
|
|
917
|
-
.
|
|
918
|
-
.
|
|
919
|
-
.expect("transaction should open");
|
|
888
|
+
let read = storage
|
|
889
|
+
.begin_read(StorageReadOptions::default())
|
|
890
|
+
.expect("read should open");
|
|
920
891
|
{
|
|
921
892
|
let mut writes = StorageWriteSet::new();
|
|
922
893
|
let mut json_writer = JsonStoreContext::new().writer();
|
|
923
894
|
{
|
|
924
895
|
stage_materialized_live_rows(
|
|
925
|
-
|
|
896
|
+
&read,
|
|
926
897
|
&mut writes,
|
|
927
898
|
&mut json_writer,
|
|
928
899
|
&[tracked_row_with_commit(
|
|
@@ -934,19 +905,18 @@ mod tests {
|
|
|
934
905
|
.await
|
|
935
906
|
.expect("tracked row should stage");
|
|
936
907
|
}
|
|
937
|
-
|
|
938
|
-
.
|
|
939
|
-
.
|
|
940
|
-
.expect("tracked row should apply");
|
|
908
|
+
storage
|
|
909
|
+
.commit_write_set(writes, StorageWriteOptions::default())
|
|
910
|
+
.expect("writes should commit");
|
|
941
911
|
}
|
|
942
912
|
write_untracked_rows_to_store(
|
|
943
|
-
|
|
944
|
-
&
|
|
913
|
+
&storage,
|
|
914
|
+
&read,
|
|
915
|
+
&[branch_ref_row("global", "commit-tracked")],
|
|
945
916
|
)
|
|
946
917
|
.await;
|
|
947
|
-
transaction.commit().await.expect("commit should persist");
|
|
948
918
|
|
|
949
|
-
let loaded = load_selected_tab(&live_state, storage
|
|
919
|
+
let loaded = load_selected_tab(&live_state, &storage)
|
|
950
920
|
.await
|
|
951
921
|
.expect("load should succeed")
|
|
952
922
|
.expect("tracked row should be visible");
|
|
@@ -960,20 +930,18 @@ mod tests {
|
|
|
960
930
|
|
|
961
931
|
#[tokio::test]
|
|
962
932
|
async fn deleting_untracked_row_reveals_tracked_row() {
|
|
963
|
-
let
|
|
964
|
-
let storage = StorageContext::new(Arc::clone(&backend));
|
|
933
|
+
let storage = StorageContext::new(InMemoryStorageBackend::new());
|
|
965
934
|
let live_state = live_state_context();
|
|
966
935
|
|
|
967
|
-
let
|
|
968
|
-
.
|
|
969
|
-
.
|
|
970
|
-
.expect("transaction should open");
|
|
936
|
+
let read = storage
|
|
937
|
+
.begin_read(StorageReadOptions::default())
|
|
938
|
+
.expect("read should open");
|
|
971
939
|
{
|
|
972
940
|
let mut writes = StorageWriteSet::new();
|
|
973
941
|
let mut json_writer = JsonStoreContext::new().writer();
|
|
974
942
|
{
|
|
975
943
|
stage_materialized_live_rows(
|
|
976
|
-
|
|
944
|
+
&read,
|
|
977
945
|
&mut writes,
|
|
978
946
|
&mut json_writer,
|
|
979
947
|
&[tracked_row_with_commit(
|
|
@@ -985,15 +953,15 @@ mod tests {
|
|
|
985
953
|
.await
|
|
986
954
|
.expect("tracked row should stage");
|
|
987
955
|
}
|
|
988
|
-
|
|
989
|
-
.
|
|
990
|
-
.
|
|
991
|
-
.expect("tracked row should apply");
|
|
956
|
+
storage
|
|
957
|
+
.commit_write_set(writes, StorageWriteOptions::default())
|
|
958
|
+
.expect("writes should commit");
|
|
992
959
|
}
|
|
993
960
|
write_untracked_rows_to_store(
|
|
994
|
-
|
|
961
|
+
&storage,
|
|
962
|
+
&read,
|
|
995
963
|
&[
|
|
996
|
-
|
|
964
|
+
branch_ref_row("global", "commit-tracked"),
|
|
997
965
|
untracked_row("untracked-value"),
|
|
998
966
|
],
|
|
999
967
|
)
|
|
@@ -1001,22 +969,21 @@ mod tests {
|
|
|
1001
969
|
{
|
|
1002
970
|
let mut writes = StorageWriteSet::new();
|
|
1003
971
|
let identity = crate::untracked_state::UntrackedStateIdentity {
|
|
1004
|
-
|
|
972
|
+
branch_id: "global".to_string(),
|
|
1005
973
|
schema_key: "lix_key_value".to_string(),
|
|
1006
|
-
|
|
974
|
+
entity_pk: EntityPk::single("selected-tab"),
|
|
1007
975
|
file_id: None,
|
|
1008
976
|
};
|
|
1009
977
|
UntrackedStateContext::new()
|
|
1010
978
|
.writer(&mut writes)
|
|
1011
|
-
.stage_delete_rows(std::iter::once(identity.as_ref()))
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
.
|
|
1015
|
-
.expect("
|
|
979
|
+
.stage_delete_rows(std::iter::once(identity.as_ref()))
|
|
980
|
+
.expect("delete identity should stage");
|
|
981
|
+
storage
|
|
982
|
+
.commit_write_set(writes, StorageWriteOptions::default())
|
|
983
|
+
.expect("writes should commit");
|
|
1016
984
|
}
|
|
1017
|
-
transaction.commit().await.expect("commit should persist");
|
|
1018
985
|
|
|
1019
|
-
let loaded = load_selected_tab(&live_state, storage
|
|
986
|
+
let loaded = load_selected_tab(&live_state, &storage)
|
|
1020
987
|
.await
|
|
1021
988
|
.expect("load should succeed")
|
|
1022
989
|
.expect("tracked row should be visible again");
|
|
@@ -1029,15 +996,13 @@ mod tests {
|
|
|
1029
996
|
}
|
|
1030
997
|
|
|
1031
998
|
#[tokio::test]
|
|
1032
|
-
async fn
|
|
1033
|
-
let
|
|
1034
|
-
let storage = StorageContext::new(Arc::clone(&backend));
|
|
999
|
+
async fn load_row_falls_back_to_global_tracked_row_for_requested_branch() {
|
|
1000
|
+
let storage = StorageContext::new(InMemoryStorageBackend::new());
|
|
1035
1001
|
let live_state = live_state_context();
|
|
1036
1002
|
|
|
1037
|
-
let
|
|
1038
|
-
.
|
|
1039
|
-
.
|
|
1040
|
-
.expect("transaction should open");
|
|
1003
|
+
let read = storage
|
|
1004
|
+
.begin_read(StorageReadOptions::default())
|
|
1005
|
+
.expect("read should open");
|
|
1041
1006
|
{
|
|
1042
1007
|
let rows = [tracked_row_with_commit(
|
|
1043
1008
|
"global-tracked",
|
|
@@ -1047,37 +1012,31 @@ mod tests {
|
|
|
1047
1012
|
let mut writes = StorageWriteSet::new();
|
|
1048
1013
|
let mut json_writer = JsonStoreContext::new().writer();
|
|
1049
1014
|
{
|
|
1050
|
-
stage_materialized_live_rows(
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
&mut json_writer,
|
|
1054
|
-
&rows,
|
|
1055
|
-
)
|
|
1056
|
-
.await
|
|
1057
|
-
.expect("tracked row should stage");
|
|
1015
|
+
stage_materialized_live_rows(&read, &mut writes, &mut json_writer, &rows)
|
|
1016
|
+
.await
|
|
1017
|
+
.expect("tracked row should stage");
|
|
1058
1018
|
}
|
|
1059
|
-
|
|
1060
|
-
.
|
|
1061
|
-
.
|
|
1062
|
-
.expect("tracked row should apply");
|
|
1019
|
+
storage
|
|
1020
|
+
.commit_write_set(writes, StorageWriteOptions::default())
|
|
1021
|
+
.expect("writes should commit");
|
|
1063
1022
|
}
|
|
1064
1023
|
write_untracked_rows_to_store(
|
|
1065
|
-
|
|
1024
|
+
&storage,
|
|
1025
|
+
&read,
|
|
1066
1026
|
&[
|
|
1067
|
-
|
|
1068
|
-
|
|
1027
|
+
branch_ref_row("global", "commit-global"),
|
|
1028
|
+
branch_ref_row("branch-a", "commit-branch-a"),
|
|
1069
1029
|
],
|
|
1070
1030
|
)
|
|
1071
1031
|
.await;
|
|
1072
|
-
write_empty_commits_to_store(
|
|
1073
|
-
transaction.commit().await.expect("commit should persist");
|
|
1032
|
+
write_empty_commits_to_store(&storage, &read, &["commit-branch-a"]).await;
|
|
1074
1033
|
|
|
1075
|
-
let loaded = load_selected_tab_at(&live_state, storage
|
|
1034
|
+
let loaded = load_selected_tab_at(&live_state, &storage, "branch-a")
|
|
1076
1035
|
.await
|
|
1077
1036
|
.expect("load should succeed")
|
|
1078
|
-
.expect("global row should be visible for requested
|
|
1037
|
+
.expect("global row should be visible for requested branch");
|
|
1079
1038
|
|
|
1080
|
-
assert_eq!(loaded.
|
|
1039
|
+
assert_eq!(loaded.branch_id, "branch-a");
|
|
1081
1040
|
assert!(loaded.global);
|
|
1082
1041
|
assert!(!loaded.untracked);
|
|
1083
1042
|
assert_eq!(
|
|
@@ -1088,8 +1047,7 @@ mod tests {
|
|
|
1088
1047
|
|
|
1089
1048
|
#[tokio::test]
|
|
1090
1049
|
async fn main_sees_global_row_by_reading_global_root_separately() {
|
|
1091
|
-
let
|
|
1092
|
-
let storage = StorageContext::new(Arc::clone(&backend));
|
|
1050
|
+
let storage = StorageContext::new(InMemoryStorageBackend::new());
|
|
1093
1051
|
let tracked_state = TrackedStateContext::new();
|
|
1094
1052
|
let live_state = LiveStateContext::new(
|
|
1095
1053
|
tracked_state.clone(),
|
|
@@ -1097,10 +1055,9 @@ mod tests {
|
|
|
1097
1055
|
crate::commit_graph::CommitGraphContext::new(),
|
|
1098
1056
|
);
|
|
1099
1057
|
|
|
1100
|
-
let
|
|
1101
|
-
.
|
|
1102
|
-
.
|
|
1103
|
-
.expect("transaction should open");
|
|
1058
|
+
let read = storage
|
|
1059
|
+
.begin_read(StorageReadOptions::default())
|
|
1060
|
+
.expect("read should open");
|
|
1104
1061
|
{
|
|
1105
1062
|
let rows = [tracked_row_with_commit(
|
|
1106
1063
|
"global-tracked",
|
|
@@ -1110,121 +1067,105 @@ mod tests {
|
|
|
1110
1067
|
let mut writes = StorageWriteSet::new();
|
|
1111
1068
|
let mut json_writer = JsonStoreContext::new().writer();
|
|
1112
1069
|
{
|
|
1113
|
-
stage_materialized_live_rows(
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
&mut json_writer,
|
|
1117
|
-
&rows,
|
|
1118
|
-
)
|
|
1119
|
-
.await
|
|
1120
|
-
.expect("global tracked row should stage");
|
|
1070
|
+
stage_materialized_live_rows(&read, &mut writes, &mut json_writer, &rows)
|
|
1071
|
+
.await
|
|
1072
|
+
.expect("global tracked row should stage");
|
|
1121
1073
|
}
|
|
1122
|
-
|
|
1123
|
-
.
|
|
1124
|
-
.
|
|
1125
|
-
.expect("global tracked row should apply");
|
|
1074
|
+
storage
|
|
1075
|
+
.commit_write_set(writes, StorageWriteOptions::default())
|
|
1076
|
+
.expect("writes should commit");
|
|
1126
1077
|
}
|
|
1127
1078
|
write_untracked_rows_to_store(
|
|
1128
|
-
|
|
1079
|
+
&storage,
|
|
1080
|
+
&read,
|
|
1129
1081
|
&[
|
|
1130
|
-
|
|
1131
|
-
|
|
1082
|
+
branch_ref_row("global", "commit-global"),
|
|
1083
|
+
branch_ref_row("main", "commit-main"),
|
|
1132
1084
|
],
|
|
1133
1085
|
)
|
|
1134
1086
|
.await;
|
|
1135
|
-
write_empty_commits_to_store(
|
|
1136
|
-
transaction.commit().await.expect("commit should persist");
|
|
1087
|
+
write_empty_commits_to_store(&storage, &read, &["commit-main"]).await;
|
|
1137
1088
|
|
|
1138
|
-
let loaded = load_selected_tab_at(&live_state, storage
|
|
1089
|
+
let loaded = load_selected_tab_at(&live_state, &storage, "main")
|
|
1139
1090
|
.await
|
|
1140
1091
|
.expect("load should succeed")
|
|
1141
1092
|
.expect("global row should be projected into main");
|
|
1142
|
-
assert_eq!(loaded.
|
|
1093
|
+
assert_eq!(loaded.branch_id, "main");
|
|
1143
1094
|
assert!(loaded.global);
|
|
1144
1095
|
assert_eq!(
|
|
1145
1096
|
loaded.snapshot_content.as_deref(),
|
|
1146
1097
|
Some("{\"value\":\"global-tracked\"}")
|
|
1147
1098
|
);
|
|
1148
1099
|
|
|
1149
|
-
let main_root_rows =
|
|
1150
|
-
scan_tracked_root(&tracked_state, storage.clone(), "commit-main").await;
|
|
1100
|
+
let main_root_rows = scan_tracked_root(&tracked_state, &storage, "commit-main").await;
|
|
1151
1101
|
assert_eq!(
|
|
1152
1102
|
main_root_rows.len(),
|
|
1153
|
-
|
|
1154
|
-
"
|
|
1103
|
+
1,
|
|
1104
|
+
"empty commit root should contain only its derived lix_commit row"
|
|
1155
1105
|
);
|
|
1106
|
+
assert_eq!(main_root_rows[0].schema_key, "lix_commit");
|
|
1156
1107
|
}
|
|
1157
1108
|
|
|
1158
1109
|
#[tokio::test]
|
|
1159
|
-
async fn
|
|
1160
|
-
let
|
|
1161
|
-
let storage = StorageContext::new(Arc::clone(&backend));
|
|
1110
|
+
async fn load_row_prefers_requested_branch_over_global() {
|
|
1111
|
+
let storage = StorageContext::new(InMemoryStorageBackend::new());
|
|
1162
1112
|
let live_state = live_state_context();
|
|
1163
1113
|
|
|
1164
|
-
let
|
|
1165
|
-
.
|
|
1166
|
-
.
|
|
1167
|
-
.expect("transaction should open");
|
|
1114
|
+
let read = storage
|
|
1115
|
+
.begin_read(StorageReadOptions::default())
|
|
1116
|
+
.expect("read should open");
|
|
1168
1117
|
{
|
|
1169
1118
|
let rows = [
|
|
1170
1119
|
tracked_row_with_commit("global-tracked", Some("change-global"), "commit-global"),
|
|
1171
1120
|
tracked_row_at_with_commit(
|
|
1172
|
-
"
|
|
1173
|
-
"
|
|
1174
|
-
Some("change-
|
|
1175
|
-
"commit-
|
|
1121
|
+
"branch-a",
|
|
1122
|
+
"branch-tracked",
|
|
1123
|
+
Some("change-branch"),
|
|
1124
|
+
"commit-branch",
|
|
1176
1125
|
),
|
|
1177
1126
|
];
|
|
1178
1127
|
let mut writes = StorageWriteSet::new();
|
|
1179
1128
|
let mut json_writer = JsonStoreContext::new().writer();
|
|
1180
1129
|
{
|
|
1181
|
-
stage_materialized_live_rows(
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
&mut json_writer,
|
|
1185
|
-
&rows,
|
|
1186
|
-
)
|
|
1187
|
-
.await
|
|
1188
|
-
.expect("tracked rows should stage");
|
|
1130
|
+
stage_materialized_live_rows(&read, &mut writes, &mut json_writer, &rows)
|
|
1131
|
+
.await
|
|
1132
|
+
.expect("tracked rows should stage");
|
|
1189
1133
|
}
|
|
1190
|
-
|
|
1191
|
-
.
|
|
1192
|
-
.
|
|
1193
|
-
.expect("tracked rows should apply");
|
|
1134
|
+
storage
|
|
1135
|
+
.commit_write_set(writes, StorageWriteOptions::default())
|
|
1136
|
+
.expect("writes should commit");
|
|
1194
1137
|
}
|
|
1195
1138
|
write_untracked_rows_to_store(
|
|
1196
|
-
|
|
1139
|
+
&storage,
|
|
1140
|
+
&read,
|
|
1197
1141
|
&[
|
|
1198
|
-
|
|
1199
|
-
|
|
1142
|
+
branch_ref_row("global", "commit-global"),
|
|
1143
|
+
branch_ref_row("branch-a", "commit-branch"),
|
|
1200
1144
|
],
|
|
1201
1145
|
)
|
|
1202
1146
|
.await;
|
|
1203
|
-
transaction.commit().await.expect("commit should persist");
|
|
1204
1147
|
|
|
1205
|
-
let loaded = load_selected_tab_at(&live_state, storage
|
|
1148
|
+
let loaded = load_selected_tab_at(&live_state, &storage, "branch-a")
|
|
1206
1149
|
.await
|
|
1207
1150
|
.expect("load should succeed")
|
|
1208
|
-
.expect("
|
|
1151
|
+
.expect("branch row should be visible");
|
|
1209
1152
|
|
|
1210
|
-
assert_eq!(loaded.
|
|
1153
|
+
assert_eq!(loaded.branch_id, "branch-a");
|
|
1211
1154
|
assert!(!loaded.untracked);
|
|
1212
1155
|
assert_eq!(
|
|
1213
1156
|
loaded.snapshot_content.as_deref(),
|
|
1214
|
-
Some("{\"value\":\"
|
|
1157
|
+
Some("{\"value\":\"branch-tracked\"}")
|
|
1215
1158
|
);
|
|
1216
1159
|
}
|
|
1217
1160
|
|
|
1218
1161
|
#[tokio::test]
|
|
1219
1162
|
async fn main_override_hides_global_row() {
|
|
1220
|
-
let
|
|
1221
|
-
let storage = StorageContext::new(Arc::clone(&backend));
|
|
1163
|
+
let storage = StorageContext::new(InMemoryStorageBackend::new());
|
|
1222
1164
|
let live_state = live_state_context();
|
|
1223
1165
|
|
|
1224
|
-
let
|
|
1225
|
-
.
|
|
1226
|
-
.
|
|
1227
|
-
.expect("transaction should open");
|
|
1166
|
+
let read = storage
|
|
1167
|
+
.begin_read(StorageReadOptions::default())
|
|
1168
|
+
.expect("read should open");
|
|
1228
1169
|
{
|
|
1229
1170
|
let rows = [
|
|
1230
1171
|
tracked_row_with_commit("global-tracked", Some("change-global"), "commit-global"),
|
|
@@ -1238,36 +1179,30 @@ mod tests {
|
|
|
1238
1179
|
let mut writes = StorageWriteSet::new();
|
|
1239
1180
|
let mut json_writer = JsonStoreContext::new().writer();
|
|
1240
1181
|
{
|
|
1241
|
-
stage_materialized_live_rows(
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
&mut json_writer,
|
|
1245
|
-
&rows,
|
|
1246
|
-
)
|
|
1247
|
-
.await
|
|
1248
|
-
.expect("tracked rows should stage");
|
|
1182
|
+
stage_materialized_live_rows(&read, &mut writes, &mut json_writer, &rows)
|
|
1183
|
+
.await
|
|
1184
|
+
.expect("tracked rows should stage");
|
|
1249
1185
|
}
|
|
1250
|
-
|
|
1251
|
-
.
|
|
1252
|
-
.
|
|
1253
|
-
.expect("tracked rows should apply");
|
|
1186
|
+
storage
|
|
1187
|
+
.commit_write_set(writes, StorageWriteOptions::default())
|
|
1188
|
+
.expect("writes should commit");
|
|
1254
1189
|
}
|
|
1255
1190
|
write_untracked_rows_to_store(
|
|
1256
|
-
|
|
1191
|
+
&storage,
|
|
1192
|
+
&read,
|
|
1257
1193
|
&[
|
|
1258
|
-
|
|
1259
|
-
|
|
1194
|
+
branch_ref_row("global", "commit-global"),
|
|
1195
|
+
branch_ref_row("main", "commit-main"),
|
|
1260
1196
|
],
|
|
1261
1197
|
)
|
|
1262
1198
|
.await;
|
|
1263
|
-
transaction.commit().await.expect("commit should persist");
|
|
1264
1199
|
|
|
1265
|
-
let loaded = load_selected_tab_at(&live_state, storage
|
|
1200
|
+
let loaded = load_selected_tab_at(&live_state, &storage, "main")
|
|
1266
1201
|
.await
|
|
1267
1202
|
.expect("load should succeed")
|
|
1268
1203
|
.expect("main row should be visible");
|
|
1269
1204
|
|
|
1270
|
-
assert_eq!(loaded.
|
|
1205
|
+
assert_eq!(loaded.branch_id, "main");
|
|
1271
1206
|
assert!(!loaded.global);
|
|
1272
1207
|
assert_eq!(
|
|
1273
1208
|
loaded.snapshot_content.as_deref(),
|
|
@@ -1277,135 +1212,117 @@ mod tests {
|
|
|
1277
1212
|
|
|
1278
1213
|
#[tokio::test]
|
|
1279
1214
|
async fn load_row_prefers_requested_untracked_over_requested_tracked_and_global_rows() {
|
|
1280
|
-
let
|
|
1281
|
-
let storage = StorageContext::new(Arc::clone(&backend));
|
|
1215
|
+
let storage = StorageContext::new(InMemoryStorageBackend::new());
|
|
1282
1216
|
let live_state = live_state_context();
|
|
1283
1217
|
|
|
1284
|
-
let
|
|
1285
|
-
.
|
|
1286
|
-
.
|
|
1287
|
-
.expect("transaction should open");
|
|
1218
|
+
let read = storage
|
|
1219
|
+
.begin_read(StorageReadOptions::default())
|
|
1220
|
+
.expect("read should open");
|
|
1288
1221
|
{
|
|
1289
1222
|
let rows = [
|
|
1290
1223
|
tracked_row_with_commit("global-tracked", Some("change-global"), "commit-global"),
|
|
1291
1224
|
tracked_row_at_with_commit(
|
|
1292
|
-
"
|
|
1293
|
-
"
|
|
1294
|
-
Some("change-
|
|
1295
|
-
"commit-
|
|
1225
|
+
"branch-a",
|
|
1226
|
+
"branch-tracked",
|
|
1227
|
+
Some("change-branch"),
|
|
1228
|
+
"commit-branch",
|
|
1296
1229
|
),
|
|
1297
1230
|
];
|
|
1298
1231
|
let mut writes = StorageWriteSet::new();
|
|
1299
1232
|
let mut json_writer = JsonStoreContext::new().writer();
|
|
1300
1233
|
{
|
|
1301
|
-
stage_materialized_live_rows(
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
&mut json_writer,
|
|
1305
|
-
&rows,
|
|
1306
|
-
)
|
|
1307
|
-
.await
|
|
1308
|
-
.expect("tracked rows should stage");
|
|
1234
|
+
stage_materialized_live_rows(&read, &mut writes, &mut json_writer, &rows)
|
|
1235
|
+
.await
|
|
1236
|
+
.expect("tracked rows should stage");
|
|
1309
1237
|
}
|
|
1310
|
-
|
|
1311
|
-
.
|
|
1312
|
-
.
|
|
1313
|
-
.expect("tracked rows should apply");
|
|
1238
|
+
storage
|
|
1239
|
+
.commit_write_set(writes, StorageWriteOptions::default())
|
|
1240
|
+
.expect("writes should commit");
|
|
1314
1241
|
}
|
|
1315
1242
|
write_untracked_rows_to_store(
|
|
1316
|
-
|
|
1243
|
+
&storage,
|
|
1244
|
+
&read,
|
|
1317
1245
|
&[
|
|
1318
|
-
|
|
1319
|
-
|
|
1246
|
+
branch_ref_row("global", "commit-global"),
|
|
1247
|
+
branch_ref_row("branch-a", "commit-branch"),
|
|
1320
1248
|
untracked_row_at("global", "global-untracked"),
|
|
1321
|
-
untracked_row_at("
|
|
1249
|
+
untracked_row_at("branch-a", "branch-untracked"),
|
|
1322
1250
|
],
|
|
1323
1251
|
)
|
|
1324
1252
|
.await;
|
|
1325
|
-
transaction.commit().await.expect("commit should persist");
|
|
1326
1253
|
|
|
1327
|
-
let loaded = load_selected_tab_at(&live_state, storage
|
|
1254
|
+
let loaded = load_selected_tab_at(&live_state, &storage, "branch-a")
|
|
1328
1255
|
.await
|
|
1329
1256
|
.expect("load should succeed")
|
|
1330
|
-
.expect("
|
|
1257
|
+
.expect("branch untracked row should be visible");
|
|
1331
1258
|
|
|
1332
|
-
assert_eq!(loaded.
|
|
1259
|
+
assert_eq!(loaded.branch_id, "branch-a");
|
|
1333
1260
|
assert!(loaded.untracked);
|
|
1334
1261
|
assert_eq!(
|
|
1335
1262
|
loaded.snapshot_content.as_deref(),
|
|
1336
|
-
Some("{\"value\":\"
|
|
1263
|
+
Some("{\"value\":\"branch-untracked\"}")
|
|
1337
1264
|
);
|
|
1338
1265
|
}
|
|
1339
1266
|
|
|
1340
1267
|
#[tokio::test]
|
|
1341
|
-
async fn
|
|
1342
|
-
let
|
|
1343
|
-
let storage = StorageContext::new(Arc::clone(&backend));
|
|
1268
|
+
async fn scan_rows_overlays_requested_branch_over_global() {
|
|
1269
|
+
let storage = StorageContext::new(InMemoryStorageBackend::new());
|
|
1344
1270
|
let live_state = live_state_context();
|
|
1345
1271
|
|
|
1346
|
-
let
|
|
1347
|
-
.
|
|
1348
|
-
.
|
|
1349
|
-
.expect("transaction should open");
|
|
1272
|
+
let read = storage
|
|
1273
|
+
.begin_read(StorageReadOptions::default())
|
|
1274
|
+
.expect("read should open");
|
|
1350
1275
|
{
|
|
1351
1276
|
let rows = [
|
|
1352
1277
|
tracked_row_with_commit("global-tracked", Some("change-global"), "commit-global"),
|
|
1353
1278
|
tracked_row_at_with_commit(
|
|
1354
|
-
"
|
|
1355
|
-
"
|
|
1356
|
-
Some("change-
|
|
1357
|
-
"commit-
|
|
1279
|
+
"branch-a",
|
|
1280
|
+
"branch-tracked",
|
|
1281
|
+
Some("change-branch"),
|
|
1282
|
+
"commit-branch",
|
|
1358
1283
|
),
|
|
1359
1284
|
];
|
|
1360
1285
|
let mut writes = StorageWriteSet::new();
|
|
1361
1286
|
let mut json_writer = JsonStoreContext::new().writer();
|
|
1362
1287
|
{
|
|
1363
|
-
stage_materialized_live_rows(
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
&mut json_writer,
|
|
1367
|
-
&rows,
|
|
1368
|
-
)
|
|
1369
|
-
.await
|
|
1370
|
-
.expect("rows should stage");
|
|
1288
|
+
stage_materialized_live_rows(&read, &mut writes, &mut json_writer, &rows)
|
|
1289
|
+
.await
|
|
1290
|
+
.expect("rows should stage");
|
|
1371
1291
|
}
|
|
1372
|
-
|
|
1373
|
-
.
|
|
1374
|
-
.
|
|
1375
|
-
.expect("rows should apply");
|
|
1292
|
+
storage
|
|
1293
|
+
.commit_write_set(writes, StorageWriteOptions::default())
|
|
1294
|
+
.expect("writes should commit");
|
|
1376
1295
|
}
|
|
1377
1296
|
write_untracked_rows_to_store(
|
|
1378
|
-
|
|
1297
|
+
&storage,
|
|
1298
|
+
&read,
|
|
1379
1299
|
&[
|
|
1380
|
-
|
|
1381
|
-
|
|
1300
|
+
branch_ref_row("global", "commit-global"),
|
|
1301
|
+
branch_ref_row("branch-a", "commit-branch"),
|
|
1382
1302
|
],
|
|
1383
1303
|
)
|
|
1384
1304
|
.await;
|
|
1385
|
-
transaction.commit().await.expect("commit should persist");
|
|
1386
1305
|
|
|
1387
|
-
let rows = scan_selected_tab_at(&live_state, storage
|
|
1306
|
+
let rows = scan_selected_tab_at(&live_state, &storage, "branch-a", false)
|
|
1388
1307
|
.await
|
|
1389
1308
|
.expect("scan should succeed");
|
|
1390
1309
|
|
|
1391
1310
|
assert_eq!(rows.len(), 1);
|
|
1392
|
-
assert_eq!(rows[0].
|
|
1311
|
+
assert_eq!(rows[0].branch_id, "branch-a");
|
|
1393
1312
|
assert_eq!(
|
|
1394
1313
|
rows[0].snapshot_content.as_deref(),
|
|
1395
|
-
Some("{\"value\":\"
|
|
1314
|
+
Some("{\"value\":\"branch-tracked\"}")
|
|
1396
1315
|
);
|
|
1397
1316
|
}
|
|
1398
1317
|
|
|
1399
1318
|
#[tokio::test]
|
|
1400
|
-
async fn
|
|
1401
|
-
let
|
|
1402
|
-
let storage = StorageContext::new(Arc::clone(&backend));
|
|
1319
|
+
async fn scan_rows_projects_global_row_into_requested_branch() {
|
|
1320
|
+
let storage = StorageContext::new(InMemoryStorageBackend::new());
|
|
1403
1321
|
let live_state = live_state_context();
|
|
1404
1322
|
|
|
1405
|
-
let
|
|
1406
|
-
.
|
|
1407
|
-
.
|
|
1408
|
-
.expect("transaction should open");
|
|
1323
|
+
let read = storage
|
|
1324
|
+
.begin_read(StorageReadOptions::default())
|
|
1325
|
+
.expect("read should open");
|
|
1409
1326
|
{
|
|
1410
1327
|
let rows = [tracked_row_with_commit(
|
|
1411
1328
|
"global-tracked",
|
|
@@ -1415,37 +1332,31 @@ mod tests {
|
|
|
1415
1332
|
let mut writes = StorageWriteSet::new();
|
|
1416
1333
|
let mut json_writer = JsonStoreContext::new().writer();
|
|
1417
1334
|
{
|
|
1418
|
-
stage_materialized_live_rows(
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
&mut json_writer,
|
|
1422
|
-
&rows,
|
|
1423
|
-
)
|
|
1424
|
-
.await
|
|
1425
|
-
.expect("rows should stage");
|
|
1335
|
+
stage_materialized_live_rows(&read, &mut writes, &mut json_writer, &rows)
|
|
1336
|
+
.await
|
|
1337
|
+
.expect("rows should stage");
|
|
1426
1338
|
}
|
|
1427
|
-
|
|
1428
|
-
.
|
|
1429
|
-
.
|
|
1430
|
-
.expect("rows should apply");
|
|
1339
|
+
storage
|
|
1340
|
+
.commit_write_set(writes, StorageWriteOptions::default())
|
|
1341
|
+
.expect("writes should commit");
|
|
1431
1342
|
}
|
|
1432
1343
|
write_untracked_rows_to_store(
|
|
1433
|
-
|
|
1344
|
+
&storage,
|
|
1345
|
+
&read,
|
|
1434
1346
|
&[
|
|
1435
|
-
|
|
1436
|
-
|
|
1347
|
+
branch_ref_row("global", "commit-global"),
|
|
1348
|
+
branch_ref_row("branch-a", "commit-branch-a"),
|
|
1437
1349
|
],
|
|
1438
1350
|
)
|
|
1439
1351
|
.await;
|
|
1440
|
-
write_empty_commits_to_store(
|
|
1441
|
-
transaction.commit().await.expect("commit should persist");
|
|
1352
|
+
write_empty_commits_to_store(&storage, &read, &["commit-branch-a"]).await;
|
|
1442
1353
|
|
|
1443
|
-
let rows = scan_selected_tab_at(&live_state, storage
|
|
1354
|
+
let rows = scan_selected_tab_at(&live_state, &storage, "branch-a", false)
|
|
1444
1355
|
.await
|
|
1445
1356
|
.expect("scan should succeed");
|
|
1446
1357
|
|
|
1447
1358
|
assert_eq!(rows.len(), 1);
|
|
1448
|
-
assert_eq!(rows[0].
|
|
1359
|
+
assert_eq!(rows[0].branch_id, "branch-a");
|
|
1449
1360
|
assert!(rows[0].global);
|
|
1450
1361
|
assert_eq!(
|
|
1451
1362
|
rows[0].snapshot_content.as_deref(),
|
|
@@ -1454,15 +1365,13 @@ mod tests {
|
|
|
1454
1365
|
}
|
|
1455
1366
|
|
|
1456
1367
|
#[tokio::test]
|
|
1457
|
-
async fn
|
|
1458
|
-
let
|
|
1459
|
-
let storage = StorageContext::new(Arc::clone(&backend));
|
|
1368
|
+
async fn scan_rows_does_not_project_global_rows_into_missing_branch() {
|
|
1369
|
+
let storage = StorageContext::new(InMemoryStorageBackend::new());
|
|
1460
1370
|
let live_state = live_state_context();
|
|
1461
1371
|
|
|
1462
|
-
let
|
|
1463
|
-
.
|
|
1464
|
-
.
|
|
1465
|
-
.expect("transaction should open");
|
|
1372
|
+
let read = storage
|
|
1373
|
+
.begin_read(StorageReadOptions::default())
|
|
1374
|
+
.expect("read should open");
|
|
1466
1375
|
{
|
|
1467
1376
|
let rows = [tracked_row_with_commit(
|
|
1468
1377
|
"global-tracked",
|
|
@@ -1472,107 +1381,91 @@ mod tests {
|
|
|
1472
1381
|
let mut writes = StorageWriteSet::new();
|
|
1473
1382
|
let mut json_writer = JsonStoreContext::new().writer();
|
|
1474
1383
|
{
|
|
1475
|
-
stage_materialized_live_rows(
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
&mut json_writer,
|
|
1479
|
-
&rows,
|
|
1480
|
-
)
|
|
1481
|
-
.await
|
|
1482
|
-
.expect("tracked row should stage");
|
|
1384
|
+
stage_materialized_live_rows(&read, &mut writes, &mut json_writer, &rows)
|
|
1385
|
+
.await
|
|
1386
|
+
.expect("tracked row should stage");
|
|
1483
1387
|
}
|
|
1484
|
-
|
|
1485
|
-
.
|
|
1486
|
-
.
|
|
1487
|
-
.expect("tracked row should apply");
|
|
1388
|
+
storage
|
|
1389
|
+
.commit_write_set(writes, StorageWriteOptions::default())
|
|
1390
|
+
.expect("writes should commit");
|
|
1488
1391
|
}
|
|
1489
1392
|
write_untracked_rows_to_store(
|
|
1490
|
-
|
|
1491
|
-
&
|
|
1393
|
+
&storage,
|
|
1394
|
+
&read,
|
|
1395
|
+
&[branch_ref_row("global", "commit-global")],
|
|
1492
1396
|
)
|
|
1493
1397
|
.await;
|
|
1494
|
-
transaction.commit().await.expect("commit should persist");
|
|
1495
1398
|
|
|
1496
|
-
let rows = scan_selected_tab_at(&live_state, storage
|
|
1399
|
+
let rows = scan_selected_tab_at(&live_state, &storage, "missing-branch", false)
|
|
1497
1400
|
.await
|
|
1498
1401
|
.expect("scan should succeed");
|
|
1499
1402
|
|
|
1500
1403
|
assert_eq!(
|
|
1501
1404
|
rows.len(),
|
|
1502
1405
|
0,
|
|
1503
|
-
"global rows must not be projected into a missing
|
|
1406
|
+
"global rows must not be projected into a missing branch scope"
|
|
1504
1407
|
);
|
|
1505
1408
|
}
|
|
1506
1409
|
|
|
1507
1410
|
#[tokio::test]
|
|
1508
1411
|
async fn winning_tombstone_hides_row_unless_tombstones_are_included() {
|
|
1509
|
-
let
|
|
1510
|
-
let storage = StorageContext::new(Arc::clone(&backend));
|
|
1412
|
+
let storage = StorageContext::new(InMemoryStorageBackend::new());
|
|
1511
1413
|
let live_state = live_state_context();
|
|
1512
1414
|
|
|
1513
|
-
let
|
|
1514
|
-
.
|
|
1515
|
-
.
|
|
1516
|
-
.expect("transaction should open");
|
|
1415
|
+
let read = storage
|
|
1416
|
+
.begin_read(StorageReadOptions::default())
|
|
1417
|
+
.expect("read should open");
|
|
1517
1418
|
{
|
|
1518
1419
|
let rows = [
|
|
1519
1420
|
tracked_row_with_commit("global-tracked", Some("change-global"), "commit-global"),
|
|
1520
1421
|
tombstone_tracked_row_at_with_commit(
|
|
1521
|
-
"
|
|
1422
|
+
"branch-a",
|
|
1522
1423
|
Some("change-tombstone"),
|
|
1523
|
-
"commit-
|
|
1424
|
+
"commit-branch",
|
|
1524
1425
|
),
|
|
1525
1426
|
];
|
|
1526
1427
|
let mut writes = StorageWriteSet::new();
|
|
1527
1428
|
let mut json_writer = JsonStoreContext::new().writer();
|
|
1528
1429
|
{
|
|
1529
|
-
stage_materialized_live_rows(
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
&mut json_writer,
|
|
1533
|
-
&rows,
|
|
1534
|
-
)
|
|
1535
|
-
.await
|
|
1536
|
-
.expect("rows should stage");
|
|
1430
|
+
stage_materialized_live_rows(&read, &mut writes, &mut json_writer, &rows)
|
|
1431
|
+
.await
|
|
1432
|
+
.expect("rows should stage");
|
|
1537
1433
|
}
|
|
1538
|
-
|
|
1539
|
-
.
|
|
1540
|
-
.
|
|
1541
|
-
.expect("rows should apply");
|
|
1434
|
+
storage
|
|
1435
|
+
.commit_write_set(writes, StorageWriteOptions::default())
|
|
1436
|
+
.expect("writes should commit");
|
|
1542
1437
|
}
|
|
1543
1438
|
write_untracked_rows_to_store(
|
|
1544
|
-
|
|
1439
|
+
&storage,
|
|
1440
|
+
&read,
|
|
1545
1441
|
&[
|
|
1546
|
-
|
|
1547
|
-
|
|
1442
|
+
branch_ref_row("global", "commit-global"),
|
|
1443
|
+
branch_ref_row("branch-a", "commit-branch"),
|
|
1548
1444
|
],
|
|
1549
1445
|
)
|
|
1550
1446
|
.await;
|
|
1551
|
-
transaction.commit().await.expect("commit should persist");
|
|
1552
1447
|
|
|
1553
|
-
let hidden = scan_selected_tab_at(&live_state, storage
|
|
1448
|
+
let hidden = scan_selected_tab_at(&live_state, &storage, "branch-a", false)
|
|
1554
1449
|
.await
|
|
1555
1450
|
.expect("scan should succeed");
|
|
1556
1451
|
assert_eq!(hidden.len(), 0);
|
|
1557
1452
|
|
|
1558
|
-
let with_tombstone = scan_selected_tab_at(&live_state, storage
|
|
1453
|
+
let with_tombstone = scan_selected_tab_at(&live_state, &storage, "branch-a", true)
|
|
1559
1454
|
.await
|
|
1560
1455
|
.expect("scan should succeed");
|
|
1561
1456
|
assert_eq!(with_tombstone.len(), 1);
|
|
1562
|
-
assert_eq!(with_tombstone[0].
|
|
1457
|
+
assert_eq!(with_tombstone[0].branch_id, "branch-a");
|
|
1563
1458
|
assert_eq!(with_tombstone[0].snapshot_content, None);
|
|
1564
1459
|
}
|
|
1565
1460
|
|
|
1566
1461
|
#[tokio::test]
|
|
1567
1462
|
async fn main_tombstone_hides_global_row() {
|
|
1568
|
-
let
|
|
1569
|
-
let storage = StorageContext::new(Arc::clone(&backend));
|
|
1463
|
+
let storage = StorageContext::new(InMemoryStorageBackend::new());
|
|
1570
1464
|
let live_state = live_state_context();
|
|
1571
1465
|
|
|
1572
|
-
let
|
|
1573
|
-
.
|
|
1574
|
-
.
|
|
1575
|
-
.expect("transaction should open");
|
|
1466
|
+
let read = storage
|
|
1467
|
+
.begin_read(StorageReadOptions::default())
|
|
1468
|
+
.expect("read should open");
|
|
1576
1469
|
{
|
|
1577
1470
|
let rows = [
|
|
1578
1471
|
tracked_row_with_commit("global-tracked", Some("change-global"), "commit-global"),
|
|
@@ -1585,149 +1478,111 @@ mod tests {
|
|
|
1585
1478
|
let mut writes = StorageWriteSet::new();
|
|
1586
1479
|
let mut json_writer = JsonStoreContext::new().writer();
|
|
1587
1480
|
{
|
|
1588
|
-
stage_materialized_live_rows(
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
&mut json_writer,
|
|
1592
|
-
&rows,
|
|
1593
|
-
)
|
|
1594
|
-
.await
|
|
1595
|
-
.expect("tracked rows should stage");
|
|
1481
|
+
stage_materialized_live_rows(&read, &mut writes, &mut json_writer, &rows)
|
|
1482
|
+
.await
|
|
1483
|
+
.expect("tracked rows should stage");
|
|
1596
1484
|
}
|
|
1597
|
-
|
|
1598
|
-
.
|
|
1599
|
-
.
|
|
1600
|
-
.expect("tracked rows should apply");
|
|
1485
|
+
storage
|
|
1486
|
+
.commit_write_set(writes, StorageWriteOptions::default())
|
|
1487
|
+
.expect("writes should commit");
|
|
1601
1488
|
}
|
|
1602
1489
|
write_untracked_rows_to_store(
|
|
1603
|
-
|
|
1490
|
+
&storage,
|
|
1491
|
+
&read,
|
|
1604
1492
|
&[
|
|
1605
|
-
|
|
1606
|
-
|
|
1493
|
+
branch_ref_row("global", "commit-global"),
|
|
1494
|
+
branch_ref_row("main", "commit-main"),
|
|
1607
1495
|
],
|
|
1608
1496
|
)
|
|
1609
1497
|
.await;
|
|
1610
|
-
transaction.commit().await.expect("commit should persist");
|
|
1611
1498
|
|
|
1612
|
-
let hidden = scan_selected_tab_at(&live_state, storage
|
|
1499
|
+
let hidden = scan_selected_tab_at(&live_state, &storage, "main", false)
|
|
1613
1500
|
.await
|
|
1614
1501
|
.expect("scan should succeed");
|
|
1615
1502
|
assert_eq!(hidden.len(), 0);
|
|
1616
1503
|
|
|
1617
|
-
let tombstones = scan_selected_tab_at(&live_state, storage
|
|
1504
|
+
let tombstones = scan_selected_tab_at(&live_state, &storage, "main", true)
|
|
1618
1505
|
.await
|
|
1619
1506
|
.expect("scan should succeed");
|
|
1620
1507
|
assert_eq!(tombstones.len(), 1);
|
|
1621
|
-
assert_eq!(tombstones[0].
|
|
1508
|
+
assert_eq!(tombstones[0].branch_id, "main");
|
|
1622
1509
|
assert!(!tombstones[0].global);
|
|
1623
1510
|
assert_eq!(tombstones[0].snapshot_content, None);
|
|
1624
1511
|
}
|
|
1625
1512
|
|
|
1626
1513
|
#[tokio::test]
|
|
1627
|
-
async fn
|
|
1628
|
-
let
|
|
1629
|
-
let storage = StorageContext::new(Arc::clone(&backend));
|
|
1514
|
+
async fn writer_allows_commit_fact_to_share_the_touched_branch_commit_id() {
|
|
1515
|
+
let storage = StorageContext::new(InMemoryStorageBackend::new());
|
|
1630
1516
|
let live_state = live_state_context();
|
|
1631
|
-
let
|
|
1632
|
-
.
|
|
1633
|
-
.
|
|
1634
|
-
.expect("transaction should open");
|
|
1517
|
+
let read = storage
|
|
1518
|
+
.begin_read(StorageReadOptions::default())
|
|
1519
|
+
.expect("read should open");
|
|
1635
1520
|
|
|
1636
1521
|
{
|
|
1637
1522
|
let rows = [
|
|
1638
1523
|
tracked_row_at_with_commit(
|
|
1639
|
-
"
|
|
1640
|
-
"
|
|
1641
|
-
Some("change-
|
|
1642
|
-
"commit-
|
|
1524
|
+
"branch-a",
|
|
1525
|
+
"branch-row",
|
|
1526
|
+
Some("change-branch"),
|
|
1527
|
+
"commit-branch",
|
|
1643
1528
|
),
|
|
1644
|
-
commit_live_state_row("commit-
|
|
1529
|
+
commit_live_state_row("commit-branch"),
|
|
1645
1530
|
];
|
|
1646
1531
|
let mut writes = StorageWriteSet::new();
|
|
1647
1532
|
let mut json_writer = JsonStoreContext::new().writer();
|
|
1648
1533
|
{
|
|
1649
|
-
stage_materialized_live_rows(
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
&mut json_writer,
|
|
1653
|
-
&rows,
|
|
1654
|
-
)
|
|
1655
|
-
.await
|
|
1656
|
-
.expect("commit facts are changelog projections, not root-local rows");
|
|
1534
|
+
stage_materialized_live_rows(&read, &mut writes, &mut json_writer, &rows)
|
|
1535
|
+
.await
|
|
1536
|
+
.expect("commit facts are changelog projections, not root-local rows");
|
|
1657
1537
|
}
|
|
1658
|
-
|
|
1659
|
-
.
|
|
1660
|
-
.
|
|
1661
|
-
.expect("commit fact rows should apply");
|
|
1538
|
+
storage
|
|
1539
|
+
.commit_write_set(writes, StorageWriteOptions::default())
|
|
1540
|
+
.expect("writes should commit");
|
|
1662
1541
|
}
|
|
1663
1542
|
write_untracked_rows_to_store(
|
|
1664
|
-
|
|
1665
|
-
&
|
|
1543
|
+
&storage,
|
|
1544
|
+
&read,
|
|
1545
|
+
&[branch_ref_row("branch-a", "commit-branch")],
|
|
1666
1546
|
)
|
|
1667
1547
|
.await;
|
|
1668
|
-
transaction.commit().await.expect("commit should persist");
|
|
1669
1548
|
|
|
1670
|
-
let loaded = load_selected_tab_at(&live_state, storage
|
|
1549
|
+
let loaded = load_selected_tab_at(&live_state, &storage, "branch-a")
|
|
1671
1550
|
.await
|
|
1672
1551
|
.expect("load should succeed")
|
|
1673
|
-
.expect("
|
|
1552
|
+
.expect("branch row should be visible");
|
|
1674
1553
|
assert_eq!(
|
|
1675
1554
|
loaded.snapshot_content.as_deref(),
|
|
1676
|
-
Some("{\"value\":\"
|
|
1555
|
+
Some("{\"value\":\"branch-row\"}")
|
|
1677
1556
|
);
|
|
1678
1557
|
}
|
|
1679
1558
|
|
|
1680
1559
|
#[tokio::test]
|
|
1681
1560
|
async fn writer_uses_first_parent_as_merge_root_base() {
|
|
1682
|
-
let
|
|
1683
|
-
let
|
|
1684
|
-
|
|
1685
|
-
.
|
|
1686
|
-
|
|
1687
|
-
.expect("seed transaction should open");
|
|
1561
|
+
let storage = StorageContext::new(InMemoryStorageBackend::new());
|
|
1562
|
+
let read = storage
|
|
1563
|
+
.begin_read(StorageReadOptions::default())
|
|
1564
|
+
.expect("read should open");
|
|
1565
|
+
write_empty_commits_to_store(&storage, &read, &["parent-left"]).await;
|
|
1688
1566
|
let mut writes = StorageWriteSet::new();
|
|
1689
|
-
|
|
1690
|
-
|
|
1691
|
-
|
|
1692
|
-
.stage_commit_draft(
|
|
1693
|
-
CommitDraftRef {
|
|
1694
|
-
id: "parent-left",
|
|
1695
|
-
change_id: "parent-left:commit",
|
|
1696
|
-
parent_ids: &[],
|
|
1697
|
-
author_account_ids: &[],
|
|
1698
|
-
created_at: "1970-01-01T00:00:00.000Z",
|
|
1699
|
-
},
|
|
1700
|
-
Vec::new(),
|
|
1701
|
-
Vec::new(),
|
|
1702
|
-
)
|
|
1703
|
-
.await
|
|
1704
|
-
.expect("first parent commit should stage");
|
|
1705
|
-
TrackedStateContext::new()
|
|
1706
|
-
.writer(&mut seed_transaction.as_mut(), &mut writes)
|
|
1707
|
-
.stage_delta("parent-left", None, &[])
|
|
1708
|
-
.await
|
|
1709
|
-
.expect("first parent root should exist");
|
|
1710
|
-
}
|
|
1711
|
-
writes
|
|
1712
|
-
.apply(&mut seed_transaction.as_mut())
|
|
1567
|
+
TrackedStateContext::new()
|
|
1568
|
+
.writer(&read, &mut writes)
|
|
1569
|
+
.stage_commit_root("parent-left", None, [])
|
|
1713
1570
|
.await
|
|
1714
|
-
.expect("first parent root should
|
|
1715
|
-
|
|
1716
|
-
.
|
|
1717
|
-
.
|
|
1718
|
-
.expect("seed transaction should commit");
|
|
1571
|
+
.expect("first parent tracked root should stage");
|
|
1572
|
+
storage
|
|
1573
|
+
.commit_write_set(writes, StorageWriteOptions::default())
|
|
1574
|
+
.expect("first parent tracked root should commit");
|
|
1719
1575
|
|
|
1720
|
-
let
|
|
1721
|
-
.
|
|
1722
|
-
.
|
|
1723
|
-
.expect("transaction should open");
|
|
1576
|
+
let read = storage
|
|
1577
|
+
.begin_read(StorageReadOptions::default())
|
|
1578
|
+
.expect("read should open");
|
|
1724
1579
|
|
|
1725
1580
|
{
|
|
1726
1581
|
let rows = [
|
|
1727
1582
|
tracked_row_at_with_commit(
|
|
1728
|
-
"
|
|
1729
|
-
"
|
|
1730
|
-
Some("change-
|
|
1583
|
+
"branch-a",
|
|
1584
|
+
"branch-row",
|
|
1585
|
+
Some("change-branch"),
|
|
1731
1586
|
"commit-merge",
|
|
1732
1587
|
),
|
|
1733
1588
|
commit_live_state_row_with_parents(
|
|
@@ -1738,31 +1593,23 @@ mod tests {
|
|
|
1738
1593
|
let mut writes = StorageWriteSet::new();
|
|
1739
1594
|
let mut json_writer = JsonStoreContext::new().writer();
|
|
1740
1595
|
{
|
|
1741
|
-
stage_materialized_live_rows(
|
|
1742
|
-
|
|
1743
|
-
|
|
1744
|
-
&mut json_writer,
|
|
1745
|
-
&rows,
|
|
1746
|
-
)
|
|
1747
|
-
.await
|
|
1748
|
-
.expect("merge commit should use first parent as tracked-root base");
|
|
1596
|
+
stage_materialized_live_rows(&read, &mut writes, &mut json_writer, &rows)
|
|
1597
|
+
.await
|
|
1598
|
+
.expect("merge commit should use first parent as tracked-root base");
|
|
1749
1599
|
}
|
|
1750
|
-
|
|
1751
|
-
.
|
|
1752
|
-
.
|
|
1753
|
-
.expect("merge commit rows should apply");
|
|
1600
|
+
storage
|
|
1601
|
+
.commit_write_set(writes, StorageWriteOptions::default())
|
|
1602
|
+
.expect("writes should commit");
|
|
1754
1603
|
}
|
|
1755
1604
|
}
|
|
1756
1605
|
|
|
1757
1606
|
#[tokio::test]
|
|
1758
1607
|
async fn non_global_root_does_not_store_global_rows() {
|
|
1759
|
-
let
|
|
1760
|
-
let storage = StorageContext::new(Arc::clone(&backend));
|
|
1608
|
+
let storage = StorageContext::new(InMemoryStorageBackend::new());
|
|
1761
1609
|
let tracked_state = TrackedStateContext::new();
|
|
1762
|
-
let
|
|
1763
|
-
.
|
|
1764
|
-
.
|
|
1765
|
-
.expect("transaction should open");
|
|
1610
|
+
let read = storage
|
|
1611
|
+
.begin_read(StorageReadOptions::default())
|
|
1612
|
+
.expect("read should open");
|
|
1766
1613
|
|
|
1767
1614
|
{
|
|
1768
1615
|
let rows = [
|
|
@@ -1777,49 +1624,56 @@ mod tests {
|
|
|
1777
1624
|
let mut writes = StorageWriteSet::new();
|
|
1778
1625
|
let mut json_writer = JsonStoreContext::new().writer();
|
|
1779
1626
|
{
|
|
1780
|
-
stage_materialized_live_rows(
|
|
1781
|
-
|
|
1782
|
-
|
|
1783
|
-
&mut json_writer,
|
|
1784
|
-
&rows,
|
|
1785
|
-
)
|
|
1786
|
-
.await
|
|
1787
|
-
.expect("tracked rows should stage");
|
|
1627
|
+
stage_materialized_live_rows(&read, &mut writes, &mut json_writer, &rows)
|
|
1628
|
+
.await
|
|
1629
|
+
.expect("tracked rows should stage");
|
|
1788
1630
|
}
|
|
1789
|
-
|
|
1790
|
-
.
|
|
1791
|
-
.
|
|
1792
|
-
.expect("tracked rows should apply");
|
|
1631
|
+
storage
|
|
1632
|
+
.commit_write_set(writes, StorageWriteOptions::default())
|
|
1633
|
+
.expect("writes should commit");
|
|
1793
1634
|
}
|
|
1794
|
-
transaction.commit().await.expect("commit should persist");
|
|
1795
1635
|
|
|
1796
|
-
let global_root_rows =
|
|
1797
|
-
|
|
1798
|
-
|
|
1636
|
+
let global_root_rows = scan_tracked_root(&tracked_state, &storage, "commit-global").await;
|
|
1637
|
+
assert_eq!(global_root_rows.len(), 2);
|
|
1638
|
+
let Some(global_row) = global_root_rows
|
|
1639
|
+
.iter()
|
|
1640
|
+
.find(|row| row.schema_key == "lix_key_value")
|
|
1641
|
+
else {
|
|
1642
|
+
panic!("global root should contain the explicit global tracked row");
|
|
1643
|
+
};
|
|
1799
1644
|
assert_eq!(
|
|
1800
|
-
|
|
1645
|
+
global_row.snapshot_content.as_deref(),
|
|
1801
1646
|
Some("{\"value\":\"global-tracked\"}")
|
|
1802
1647
|
);
|
|
1803
1648
|
|
|
1804
|
-
let main_root_rows =
|
|
1805
|
-
|
|
1806
|
-
|
|
1649
|
+
let main_root_rows = scan_tracked_root(&tracked_state, &storage, "commit-main").await;
|
|
1650
|
+
assert_eq!(main_root_rows.len(), 2);
|
|
1651
|
+
let Some(main_row) = main_root_rows
|
|
1652
|
+
.iter()
|
|
1653
|
+
.find(|row| row.schema_key == "lix_key_value")
|
|
1654
|
+
else {
|
|
1655
|
+
panic!("main root should contain the explicit main tracked row");
|
|
1656
|
+
};
|
|
1807
1657
|
assert_eq!(
|
|
1808
|
-
|
|
1658
|
+
main_row.snapshot_content.as_deref(),
|
|
1809
1659
|
Some("{\"value\":\"main-tracked\"}")
|
|
1810
1660
|
);
|
|
1811
1661
|
}
|
|
1812
1662
|
|
|
1813
1663
|
async fn load_selected_tab(
|
|
1814
1664
|
live_state: &LiveStateContext,
|
|
1815
|
-
storage: StorageContext,
|
|
1665
|
+
storage: &StorageContext,
|
|
1816
1666
|
) -> Result<Option<MaterializedLiveStateRow>, LixError> {
|
|
1817
1667
|
live_state
|
|
1818
|
-
.reader(
|
|
1668
|
+
.reader(
|
|
1669
|
+
storage
|
|
1670
|
+
.begin_read(StorageReadOptions::default())
|
|
1671
|
+
.expect("read should open"),
|
|
1672
|
+
)
|
|
1819
1673
|
.load_row(&LiveStateRowRequest {
|
|
1820
1674
|
schema_key: "lix_key_value".to_string(),
|
|
1821
|
-
|
|
1822
|
-
|
|
1675
|
+
branch_id: "global".to_string(),
|
|
1676
|
+
entity_pk: crate::entity_pk::EntityPk::single("selected-tab"),
|
|
1823
1677
|
file_id: NullableKeyFilter::Null,
|
|
1824
1678
|
})
|
|
1825
1679
|
.await
|
|
@@ -1827,15 +1681,19 @@ mod tests {
|
|
|
1827
1681
|
|
|
1828
1682
|
async fn load_selected_tab_at(
|
|
1829
1683
|
live_state: &LiveStateContext,
|
|
1830
|
-
storage: StorageContext,
|
|
1831
|
-
|
|
1684
|
+
storage: &StorageContext,
|
|
1685
|
+
branch_id: &str,
|
|
1832
1686
|
) -> Result<Option<MaterializedLiveStateRow>, LixError> {
|
|
1833
1687
|
live_state
|
|
1834
|
-
.reader(
|
|
1688
|
+
.reader(
|
|
1689
|
+
storage
|
|
1690
|
+
.begin_read(StorageReadOptions::default())
|
|
1691
|
+
.expect("read should open"),
|
|
1692
|
+
)
|
|
1835
1693
|
.load_row(&LiveStateRowRequest {
|
|
1836
1694
|
schema_key: "lix_key_value".to_string(),
|
|
1837
|
-
|
|
1838
|
-
|
|
1695
|
+
branch_id: branch_id.to_string(),
|
|
1696
|
+
entity_pk: crate::entity_pk::EntityPk::single("selected-tab"),
|
|
1839
1697
|
file_id: NullableKeyFilter::Null,
|
|
1840
1698
|
})
|
|
1841
1699
|
.await
|
|
@@ -1843,19 +1701,21 @@ mod tests {
|
|
|
1843
1701
|
|
|
1844
1702
|
async fn scan_selected_tab_at(
|
|
1845
1703
|
live_state: &LiveStateContext,
|
|
1846
|
-
storage: StorageContext,
|
|
1847
|
-
|
|
1704
|
+
storage: &StorageContext,
|
|
1705
|
+
branch_id: &str,
|
|
1848
1706
|
include_tombstones: bool,
|
|
1849
1707
|
) -> Result<Vec<MaterializedLiveStateRow>, LixError> {
|
|
1850
1708
|
live_state
|
|
1851
|
-
.reader(
|
|
1709
|
+
.reader(
|
|
1710
|
+
storage
|
|
1711
|
+
.begin_read(StorageReadOptions::default())
|
|
1712
|
+
.expect("read should open"),
|
|
1713
|
+
)
|
|
1852
1714
|
.scan_rows(&LiveStateScanRequest {
|
|
1853
1715
|
filter: LiveStateFilter {
|
|
1854
1716
|
schema_keys: vec!["lix_key_value".to_string()],
|
|
1855
|
-
|
|
1856
|
-
|
|
1857
|
-
)],
|
|
1858
|
-
version_ids: vec![version_id.to_string()],
|
|
1717
|
+
entity_pks: vec![crate::entity_pk::EntityPk::single("selected-tab")],
|
|
1718
|
+
branch_ids: vec![branch_id.to_string()],
|
|
1859
1719
|
file_ids: vec![NullableKeyFilter::Null],
|
|
1860
1720
|
include_tombstones,
|
|
1861
1721
|
..LiveStateFilter::default()
|
|
@@ -1867,11 +1727,15 @@ mod tests {
|
|
|
1867
1727
|
|
|
1868
1728
|
async fn scan_tracked_root(
|
|
1869
1729
|
tracked_state: &TrackedStateContext,
|
|
1870
|
-
storage: StorageContext,
|
|
1730
|
+
storage: &StorageContext,
|
|
1871
1731
|
commit_id: &str,
|
|
1872
1732
|
) -> Vec<MaterializedTrackedStateRow> {
|
|
1873
1733
|
tracked_state
|
|
1874
|
-
.reader(
|
|
1734
|
+
.reader(
|
|
1735
|
+
storage
|
|
1736
|
+
.begin_read(StorageReadOptions::default())
|
|
1737
|
+
.expect("read should open"),
|
|
1738
|
+
)
|
|
1875
1739
|
.scan_rows_at_commit(
|
|
1876
1740
|
commit_id,
|
|
1877
1741
|
&TrackedStateScanRequest {
|
|
@@ -1895,13 +1759,13 @@ mod tests {
|
|
|
1895
1759
|
}
|
|
1896
1760
|
|
|
1897
1761
|
fn tracked_row_at_with_commit(
|
|
1898
|
-
|
|
1762
|
+
branch_id: &str,
|
|
1899
1763
|
value: &str,
|
|
1900
1764
|
change_id: Option<&str>,
|
|
1901
1765
|
commit_id: &str,
|
|
1902
1766
|
) -> MaterializedLiveStateRow {
|
|
1903
1767
|
MaterializedLiveStateRow {
|
|
1904
|
-
|
|
1768
|
+
entity_pk: identity("selected-tab"),
|
|
1905
1769
|
schema_key: "lix_key_value".to_string(),
|
|
1906
1770
|
file_id: None,
|
|
1907
1771
|
snapshot_content: Some(format!("{{\"value\":\"{value}\"}}")),
|
|
@@ -1909,23 +1773,23 @@ mod tests {
|
|
|
1909
1773
|
deleted: false,
|
|
1910
1774
|
created_at: "2026-01-01T00:00:00Z".to_string(),
|
|
1911
1775
|
updated_at: "2026-01-01T00:00:00Z".to_string(),
|
|
1912
|
-
global:
|
|
1776
|
+
global: branch_id == "global",
|
|
1913
1777
|
change_id: change_id.map(str::to_string),
|
|
1914
1778
|
commit_id: Some(commit_id.to_string()),
|
|
1915
1779
|
untracked: false,
|
|
1916
|
-
|
|
1780
|
+
branch_id: branch_id.to_string(),
|
|
1917
1781
|
}
|
|
1918
1782
|
}
|
|
1919
1783
|
|
|
1920
1784
|
fn tombstone_tracked_row_at_with_commit(
|
|
1921
|
-
|
|
1785
|
+
branch_id: &str,
|
|
1922
1786
|
change_id: Option<&str>,
|
|
1923
1787
|
commit_id: &str,
|
|
1924
1788
|
) -> MaterializedLiveStateRow {
|
|
1925
1789
|
MaterializedLiveStateRow {
|
|
1926
1790
|
snapshot_content: None,
|
|
1927
1791
|
deleted: true,
|
|
1928
|
-
..tracked_row_at_with_commit(
|
|
1792
|
+
..tracked_row_at_with_commit(branch_id, "ignored", change_id, commit_id)
|
|
1929
1793
|
}
|
|
1930
1794
|
}
|
|
1931
1795
|
|
|
@@ -1933,9 +1797,9 @@ mod tests {
|
|
|
1933
1797
|
untracked_row_at("global", value)
|
|
1934
1798
|
}
|
|
1935
1799
|
|
|
1936
|
-
fn untracked_row_at(
|
|
1800
|
+
fn untracked_row_at(branch_id: &str, value: &str) -> MaterializedUntrackedStateRow {
|
|
1937
1801
|
MaterializedUntrackedStateRow {
|
|
1938
|
-
|
|
1802
|
+
entity_pk: identity("selected-tab"),
|
|
1939
1803
|
schema_key: "lix_key_value".to_string(),
|
|
1940
1804
|
file_id: None,
|
|
1941
1805
|
snapshot_content: Some(format!("{{\"value\":\"{value}\"}}")),
|
|
@@ -1943,29 +1807,29 @@ mod tests {
|
|
|
1943
1807
|
deleted: false,
|
|
1944
1808
|
created_at: "2026-01-01T00:00:00Z".to_string(),
|
|
1945
1809
|
updated_at: "2026-01-01T00:00:00Z".to_string(),
|
|
1946
|
-
global:
|
|
1947
|
-
|
|
1810
|
+
global: branch_id == "global",
|
|
1811
|
+
branch_id: branch_id.to_string(),
|
|
1948
1812
|
}
|
|
1949
1813
|
}
|
|
1950
1814
|
|
|
1951
|
-
fn
|
|
1815
|
+
fn branch_ref_row(branch_id: &str, commit_id: &str) -> MaterializedUntrackedStateRow {
|
|
1952
1816
|
MaterializedUntrackedStateRow {
|
|
1953
|
-
|
|
1954
|
-
schema_key: "
|
|
1817
|
+
entity_pk: identity(branch_id),
|
|
1818
|
+
schema_key: "lix_branch_ref".to_string(),
|
|
1955
1819
|
file_id: None,
|
|
1956
1820
|
snapshot_content: Some(
|
|
1957
1821
|
serde_json::to_string(&json!({
|
|
1958
|
-
"id":
|
|
1822
|
+
"id": branch_id,
|
|
1959
1823
|
"commit_id": commit_id,
|
|
1960
1824
|
}))
|
|
1961
|
-
.expect("
|
|
1825
|
+
.expect("branch ref should serialize"),
|
|
1962
1826
|
),
|
|
1963
1827
|
metadata: None,
|
|
1964
1828
|
deleted: false,
|
|
1965
1829
|
created_at: "2026-01-01T00:00:00Z".to_string(),
|
|
1966
1830
|
updated_at: "2026-01-01T00:00:00Z".to_string(),
|
|
1967
1831
|
global: true,
|
|
1968
|
-
|
|
1832
|
+
branch_id: "global".to_string(),
|
|
1969
1833
|
}
|
|
1970
1834
|
}
|
|
1971
1835
|
|
|
@@ -1995,7 +1859,7 @@ mod tests {
|
|
|
1995
1859
|
snapshot: serde_json::Value,
|
|
1996
1860
|
) -> MaterializedLiveStateRow {
|
|
1997
1861
|
MaterializedLiveStateRow {
|
|
1998
|
-
|
|
1862
|
+
entity_pk: identity(commit_id),
|
|
1999
1863
|
schema_key: COMMIT_SCHEMA_KEY.to_string(),
|
|
2000
1864
|
file_id: None,
|
|
2001
1865
|
snapshot_content: Some(
|
|
@@ -2009,11 +1873,11 @@ mod tests {
|
|
|
2009
1873
|
change_id: Some(format!("change-{commit_id}")),
|
|
2010
1874
|
commit_id: Some(commit_id.to_string()),
|
|
2011
1875
|
untracked: false,
|
|
2012
|
-
|
|
1876
|
+
branch_id: "global".to_string(),
|
|
2013
1877
|
}
|
|
2014
1878
|
}
|
|
2015
1879
|
|
|
2016
|
-
fn identity(
|
|
2017
|
-
|
|
1880
|
+
fn identity(entity_pk: &str) -> EntityPk {
|
|
1881
|
+
EntityPk::single(entity_pk)
|
|
2018
1882
|
}
|
|
2019
1883
|
}
|