@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,9 +1,9 @@
|
|
|
1
|
-
use std::collections::{BTreeMap,
|
|
1
|
+
use std::collections::{BTreeMap, HashMap};
|
|
2
2
|
use std::sync::{Arc, Mutex};
|
|
3
3
|
|
|
4
4
|
use crate::catalog::SchemaPlanId;
|
|
5
5
|
use crate::domain::{Domain, DomainRowIdentity};
|
|
6
|
-
use crate::
|
|
6
|
+
use crate::entity_pk::EntityPk;
|
|
7
7
|
use crate::functions::{FunctionProvider, FunctionProviderHandle};
|
|
8
8
|
#[cfg(test)]
|
|
9
9
|
use crate::live_state::LiveStateRowRequest;
|
|
@@ -11,11 +11,12 @@ use crate::live_state::{LiveStateScanRequest, MaterializedLiveStateRow};
|
|
|
11
11
|
#[cfg(test)]
|
|
12
12
|
use crate::transaction::types::{stage_json_from_value, TransactionJson};
|
|
13
13
|
use crate::transaction::types::{
|
|
14
|
-
LogicalPrimaryKey, PreparedTransactionWrite,
|
|
15
|
-
TransactionWriteOperation, TransactionWriteOrigin,
|
|
14
|
+
LogicalPrimaryKey, PreparedTransactionWrite, StagedCommitChangeRef, TransactionFileData,
|
|
15
|
+
TransactionWriteMode, TransactionWriteOperation, TransactionWriteOrigin,
|
|
16
|
+
TransactionWriteOutcome,
|
|
16
17
|
};
|
|
17
|
-
use crate::transaction::types::{
|
|
18
|
-
use crate::
|
|
18
|
+
use crate::transaction::types::{PreparedStateRow, StagedCommitChangeRefs};
|
|
19
|
+
use crate::GLOBAL_BRANCH_ID;
|
|
19
20
|
use crate::{LixError, NullableKeyFilter};
|
|
20
21
|
|
|
21
22
|
/// Transaction-local write buffer after transaction-boundary preparation.
|
|
@@ -27,28 +28,25 @@ use crate::{LixError, NullableKeyFilter};
|
|
|
27
28
|
pub(crate) struct TransactionWriteBuffer {
|
|
28
29
|
functions: FunctionProviderHandle,
|
|
29
30
|
rows: Mutex<Vec<Option<PreparedStateRow>>>,
|
|
30
|
-
adopted_rows: Mutex<Vec<Option<PreparedAdoptedStateRow>>>,
|
|
31
31
|
by_identity: Mutex<HashMap<PreparedStateRowIdentity, RowSlot>>,
|
|
32
32
|
insert_identities: Mutex<BTreeMap<PreparedStateRowIdentity, Option<TransactionWriteOrigin>>>,
|
|
33
|
-
|
|
34
|
-
|
|
33
|
+
commit_change_refs_by_branch: Mutex<BTreeMap<String, StagedCommitChangeRefs>>,
|
|
34
|
+
extra_commit_parents_by_branch: Mutex<BTreeMap<String, Vec<String>>>,
|
|
35
35
|
file_data_writes: Mutex<Vec<TransactionFileData>>,
|
|
36
36
|
}
|
|
37
37
|
|
|
38
38
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
|
39
39
|
pub(crate) enum RowSlot {
|
|
40
40
|
State(usize),
|
|
41
|
-
Adopted(usize),
|
|
42
41
|
}
|
|
43
42
|
|
|
44
43
|
/// Drained prepared transaction writes ready for commit.
|
|
45
44
|
pub(crate) struct PreparedWriteSet {
|
|
46
45
|
pub(crate) state_rows: Vec<PreparedStateRow>,
|
|
47
|
-
pub(crate) adopted_rows: Vec<PreparedAdoptedStateRow>,
|
|
48
46
|
pub(crate) insert_identities:
|
|
49
47
|
BTreeMap<PreparedStateRowIdentity, Option<TransactionWriteOrigin>>,
|
|
50
|
-
pub(crate)
|
|
51
|
-
pub(crate)
|
|
48
|
+
pub(crate) commit_change_refs_by_branch: BTreeMap<String, StagedCommitChangeRefs>,
|
|
49
|
+
pub(crate) extra_commit_parents_by_branch: BTreeMap<String, Vec<String>>,
|
|
52
50
|
pub(crate) file_data_writes: Vec<TransactionFileData>,
|
|
53
51
|
}
|
|
54
52
|
|
|
@@ -75,35 +73,30 @@ pub(crate) struct PreparedWriteValidationIndex<'a> {
|
|
|
75
73
|
#[derive(Clone, Copy)]
|
|
76
74
|
pub(crate) enum PreparedValidationRow<'a> {
|
|
77
75
|
State(&'a PreparedStateRow),
|
|
78
|
-
Adopted(&'a PreparedAdoptedStateRow),
|
|
79
76
|
}
|
|
80
77
|
|
|
81
78
|
impl<'a> PreparedValidationRow<'a> {
|
|
82
|
-
pub(crate) fn
|
|
79
|
+
pub(crate) fn entity_pk(&self) -> &EntityPk {
|
|
83
80
|
match self {
|
|
84
|
-
Self::State(row) => &row.
|
|
85
|
-
Self::Adopted(row) => &row.entity_id,
|
|
81
|
+
Self::State(row) => &row.entity_pk,
|
|
86
82
|
}
|
|
87
83
|
}
|
|
88
84
|
|
|
89
85
|
pub(crate) fn schema_plan_id(&self) -> SchemaPlanId {
|
|
90
86
|
match self {
|
|
91
87
|
Self::State(row) => row.schema_plan_id,
|
|
92
|
-
Self::Adopted(row) => row.schema_plan_id,
|
|
93
88
|
}
|
|
94
89
|
}
|
|
95
90
|
|
|
96
91
|
pub(crate) fn schema_key(&self) -> &str {
|
|
97
92
|
match self {
|
|
98
93
|
Self::State(row) => &row.schema_key,
|
|
99
|
-
Self::Adopted(row) => &row.schema_key,
|
|
100
94
|
}
|
|
101
95
|
}
|
|
102
96
|
|
|
103
97
|
pub(crate) fn file_id(&self) -> &Option<String> {
|
|
104
98
|
match self {
|
|
105
99
|
Self::State(row) => &row.file_id,
|
|
106
|
-
Self::Adopted(row) => &row.file_id,
|
|
107
100
|
}
|
|
108
101
|
}
|
|
109
102
|
|
|
@@ -114,10 +107,6 @@ impl<'a> PreparedValidationRow<'a> {
|
|
|
114
107
|
.snapshot
|
|
115
108
|
.as_ref()
|
|
116
109
|
.map(|snapshot| snapshot.normalized.as_ref()),
|
|
117
|
-
Self::Adopted(row) => row
|
|
118
|
-
.snapshot
|
|
119
|
-
.as_ref()
|
|
120
|
-
.map(|snapshot| snapshot.normalized.as_ref()),
|
|
121
110
|
}
|
|
122
111
|
}
|
|
123
112
|
|
|
@@ -127,10 +116,6 @@ impl<'a> PreparedValidationRow<'a> {
|
|
|
127
116
|
.snapshot
|
|
128
117
|
.as_ref()
|
|
129
118
|
.map(|snapshot| snapshot.value.as_ref()),
|
|
130
|
-
Self::Adopted(row) => row
|
|
131
|
-
.snapshot
|
|
132
|
-
.as_ref()
|
|
133
|
-
.map(|snapshot| snapshot.value.as_ref()),
|
|
134
119
|
}
|
|
135
120
|
}
|
|
136
121
|
|
|
@@ -140,30 +125,30 @@ impl<'a> PreparedValidationRow<'a> {
|
|
|
140
125
|
.metadata
|
|
141
126
|
.as_ref()
|
|
142
127
|
.map(|metadata| metadata.value.as_ref()),
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
pub(crate) fn is_tombstone(&self) -> bool {
|
|
132
|
+
match self {
|
|
133
|
+
Self::State(row) => row.snapshot.is_none(),
|
|
147
134
|
}
|
|
148
135
|
}
|
|
149
136
|
|
|
150
137
|
pub(crate) fn untracked(&self) -> bool {
|
|
151
138
|
match self {
|
|
152
139
|
Self::State(row) => row.untracked,
|
|
153
|
-
Self::Adopted(_) => false,
|
|
154
140
|
}
|
|
155
141
|
}
|
|
156
142
|
|
|
157
|
-
pub(crate) fn
|
|
143
|
+
pub(crate) fn branch_id(&self) -> &str {
|
|
158
144
|
match self {
|
|
159
|
-
Self::State(row) => &row.
|
|
160
|
-
Self::Adopted(row) => &row.version_id,
|
|
145
|
+
Self::State(row) => &row.branch_id,
|
|
161
146
|
}
|
|
162
147
|
}
|
|
163
148
|
|
|
164
149
|
pub(crate) fn domain(&self) -> Domain {
|
|
165
150
|
Domain::exact_file(
|
|
166
|
-
self.
|
|
151
|
+
self.branch_id().to_string(),
|
|
167
152
|
self.untracked(),
|
|
168
153
|
self.file_id().clone(),
|
|
169
154
|
)
|
|
@@ -173,7 +158,7 @@ impl<'a> PreparedValidationRow<'a> {
|
|
|
173
158
|
DomainRowIdentity::in_domain(
|
|
174
159
|
self.domain(),
|
|
175
160
|
self.schema_key().to_string(),
|
|
176
|
-
self.
|
|
161
|
+
self.entity_pk().clone(),
|
|
177
162
|
)
|
|
178
163
|
}
|
|
179
164
|
}
|
|
@@ -193,7 +178,7 @@ impl<'a> PreparedWriteValidationIndex<'a> {
|
|
|
193
178
|
.flat_map(|(target_scope, rows)| {
|
|
194
179
|
rows.iter().copied().filter(move |row| {
|
|
195
180
|
schema_scope.validation_scope_contains_constraint_domain(target_scope)
|
|
196
|
-
|| (row.
|
|
181
|
+
|| (row.is_tombstone()
|
|
197
182
|
&& target_scope.tombstone_domain_affects_validation_scope(schema_scope))
|
|
198
183
|
})
|
|
199
184
|
})
|
|
@@ -235,10 +220,7 @@ impl<'a> PreparedWriteValidationSet<'a> {
|
|
|
235
220
|
impl PreparedWriteSet {
|
|
236
221
|
#[cfg(test)]
|
|
237
222
|
pub(crate) fn validation_rows(&self) -> impl Iterator<Item = PreparedValidationRow<'_>> + '_ {
|
|
238
|
-
self.state_rows
|
|
239
|
-
.iter()
|
|
240
|
-
.map(PreparedValidationRow::State)
|
|
241
|
-
.chain(self.adopted_rows.iter().map(PreparedValidationRow::Adopted))
|
|
223
|
+
self.state_rows.iter().map(PreparedValidationRow::State)
|
|
242
224
|
}
|
|
243
225
|
|
|
244
226
|
pub(crate) fn validation_index(&self) -> PreparedWriteValidationIndex<'_> {
|
|
@@ -250,14 +232,6 @@ impl PreparedWriteSet {
|
|
|
250
232
|
.or_default()
|
|
251
233
|
.push(row);
|
|
252
234
|
}
|
|
253
|
-
for row in &self.adopted_rows {
|
|
254
|
-
let row = PreparedValidationRow::Adopted(row);
|
|
255
|
-
rows_by_schema_scope
|
|
256
|
-
.entry(row.domain().schema_catalog_domain())
|
|
257
|
-
.or_default()
|
|
258
|
-
.push(row);
|
|
259
|
-
}
|
|
260
|
-
|
|
261
235
|
let mut insert_identities_by_schema_scope = BTreeMap::<
|
|
262
236
|
Domain,
|
|
263
237
|
Vec<(&PreparedStateRowIdentity, Option<&TransactionWriteOrigin>)>,
|
|
@@ -296,11 +270,10 @@ impl TransactionWriteBuffer {
|
|
|
296
270
|
Self {
|
|
297
271
|
functions,
|
|
298
272
|
rows: Mutex::new(Vec::new()),
|
|
299
|
-
adopted_rows: Mutex::new(Vec::new()),
|
|
300
273
|
by_identity: Mutex::new(HashMap::new()),
|
|
301
274
|
insert_identities: Mutex::new(BTreeMap::new()),
|
|
302
|
-
|
|
303
|
-
|
|
275
|
+
commit_change_refs_by_branch: Mutex::new(BTreeMap::new()),
|
|
276
|
+
extra_commit_parents_by_branch: Mutex::new(BTreeMap::new()),
|
|
304
277
|
file_data_writes: Mutex::new(Vec::new()),
|
|
305
278
|
}
|
|
306
279
|
}
|
|
@@ -313,12 +286,6 @@ impl TransactionWriteBuffer {
|
|
|
313
286
|
"failed to acquire transaction staged writes lock",
|
|
314
287
|
)
|
|
315
288
|
})?;
|
|
316
|
-
let mut adopted_rows_guard = self.adopted_rows.lock().map_err(|_| {
|
|
317
|
-
LixError::new(
|
|
318
|
-
"LIX_ERROR_UNKNOWN",
|
|
319
|
-
"failed to acquire transaction staged adopted writes lock",
|
|
320
|
-
)
|
|
321
|
-
})?;
|
|
322
289
|
let mut by_identity_guard = self.by_identity.lock().map_err(|_| {
|
|
323
290
|
LixError::new(
|
|
324
291
|
"LIX_ERROR_UNKNOWN",
|
|
@@ -337,112 +304,87 @@ impl TransactionWriteBuffer {
|
|
|
337
304
|
"failed to acquire transaction staged insert identity lock",
|
|
338
305
|
)
|
|
339
306
|
})?;
|
|
340
|
-
let mut
|
|
341
|
-
|
|
342
|
-
"LIX_ERROR_UNKNOWN",
|
|
343
|
-
"failed to acquire transaction staged commit membership lock",
|
|
344
|
-
)
|
|
345
|
-
})?;
|
|
346
|
-
let mut extra_parents_guard =
|
|
347
|
-
self.extra_commit_parents_by_version.lock().map_err(|_| {
|
|
307
|
+
let mut commit_change_refs_guard =
|
|
308
|
+
self.commit_change_refs_by_branch.lock().map_err(|_| {
|
|
348
309
|
LixError::new(
|
|
349
310
|
"LIX_ERROR_UNKNOWN",
|
|
350
|
-
"failed to acquire transaction staged
|
|
311
|
+
"failed to acquire transaction staged commit change refs lock",
|
|
351
312
|
)
|
|
352
313
|
})?;
|
|
314
|
+
let mut extra_parents_guard = self.extra_commit_parents_by_branch.lock().map_err(|_| {
|
|
315
|
+
LixError::new(
|
|
316
|
+
"LIX_ERROR_UNKNOWN",
|
|
317
|
+
"failed to acquire transaction staged extra commit parents lock",
|
|
318
|
+
)
|
|
319
|
+
})?;
|
|
353
320
|
let result = Ok(PreparedWriteSet {
|
|
354
321
|
state_rows: std::mem::take(&mut *rows_guard)
|
|
355
322
|
.into_iter()
|
|
356
323
|
.flatten()
|
|
357
324
|
.collect(),
|
|
358
|
-
adopted_rows: std::mem::take(&mut *adopted_rows_guard)
|
|
359
|
-
.into_iter()
|
|
360
|
-
.flatten()
|
|
361
|
-
.collect(),
|
|
362
325
|
insert_identities: std::mem::take(&mut *insert_identities_guard),
|
|
363
|
-
|
|
364
|
-
|
|
326
|
+
commit_change_refs_by_branch: std::mem::take(&mut *commit_change_refs_guard),
|
|
327
|
+
extra_commit_parents_by_branch: std::mem::take(&mut *extra_parents_guard),
|
|
365
328
|
file_data_writes: std::mem::take(&mut *file_data_guard),
|
|
366
329
|
});
|
|
367
330
|
by_identity_guard.clear();
|
|
368
331
|
result
|
|
369
332
|
}
|
|
370
333
|
|
|
371
|
-
/// Records an additional parent for the commit generated for `
|
|
334
|
+
/// Records an additional parent for the commit generated for `branch_id`.
|
|
372
335
|
///
|
|
373
|
-
/// Normal writes parent the new commit to the
|
|
374
|
-
/// Merges add the source
|
|
336
|
+
/// Normal writes parent the new commit to the branch's previous head.
|
|
337
|
+
/// Merges add the source branch head as an extra parent so the commit graph
|
|
375
338
|
/// preserves branch ancestry while tracked-state roots still apply source
|
|
376
339
|
/// rows onto the target root.
|
|
377
340
|
pub(crate) fn add_commit_parent(
|
|
378
341
|
&self,
|
|
379
|
-
|
|
342
|
+
branch_id: String,
|
|
380
343
|
parent_commit_id: String,
|
|
381
344
|
) -> Result<(), LixError> {
|
|
382
|
-
let mut guard = self.
|
|
345
|
+
let mut guard = self.extra_commit_parents_by_branch.lock().map_err(|_| {
|
|
383
346
|
LixError::new(
|
|
384
347
|
"LIX_ERROR_UNKNOWN",
|
|
385
348
|
"failed to acquire transaction staged extra commit parents lock",
|
|
386
349
|
)
|
|
387
350
|
})?;
|
|
388
|
-
let parents = guard.entry(
|
|
351
|
+
let parents = guard.entry(branch_id).or_default();
|
|
389
352
|
if !parents.contains(&parent_commit_id) {
|
|
390
353
|
parents.push(parent_commit_id);
|
|
391
354
|
}
|
|
392
355
|
Ok(())
|
|
393
356
|
}
|
|
394
357
|
|
|
395
|
-
pub(crate) fn
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
)
|
|
401
|
-
})?;
|
|
402
|
-
Ok(guard
|
|
403
|
-
.get(version_id)
|
|
404
|
-
.map(|members| members.commit_id.clone()))
|
|
405
|
-
}
|
|
406
|
-
|
|
407
|
-
/// Stages a commit for `version_id` even if no tracked state rows changed.
|
|
408
|
-
///
|
|
409
|
-
/// Merge uses this to record graph ancestry for convergent merges where the
|
|
410
|
-
/// target already has the same final state as the source, but the source
|
|
411
|
-
/// head is not reachable from the target head.
|
|
412
|
-
pub(crate) fn stage_empty_commit(&self, version_id: String) -> Result<String, LixError> {
|
|
358
|
+
pub(crate) fn stage_selected_commit_change_refs(
|
|
359
|
+
&self,
|
|
360
|
+
branch_id: String,
|
|
361
|
+
selected_change_refs: impl IntoIterator<Item = StagedCommitChangeRef>,
|
|
362
|
+
) -> Result<String, LixError> {
|
|
413
363
|
let mut functions = self.functions.clone();
|
|
414
|
-
let mut guard = self.
|
|
364
|
+
let mut guard = self.commit_change_refs_by_branch.lock().map_err(|_| {
|
|
415
365
|
LixError::new(
|
|
416
366
|
"LIX_ERROR_UNKNOWN",
|
|
417
|
-
"failed to acquire transaction staged commit
|
|
367
|
+
"failed to acquire transaction staged commit change refs lock",
|
|
418
368
|
)
|
|
419
369
|
})?;
|
|
420
|
-
let
|
|
421
|
-
|
|
370
|
+
let change_refs = guard.entry(branch_id).or_insert_with(|| {
|
|
371
|
+
StagedCommitChangeRefs::new(
|
|
422
372
|
functions.uuid_v7(),
|
|
423
373
|
functions.uuid_v7(),
|
|
424
374
|
functions.timestamp(),
|
|
425
375
|
)
|
|
426
376
|
});
|
|
427
|
-
|
|
428
|
-
|
|
377
|
+
change_refs.allow_empty();
|
|
378
|
+
for change_ref in selected_change_refs {
|
|
379
|
+
change_refs.add_selected_change_ref(change_ref);
|
|
380
|
+
}
|
|
381
|
+
Ok(change_refs.commit_id.clone())
|
|
429
382
|
}
|
|
430
383
|
|
|
431
384
|
/// Builds the transaction-local read overlay from currently staged writes.
|
|
432
385
|
pub(crate) fn staging_overlay(self: &Arc<Self>) -> Result<PreparedStateRowOverlay, LixError> {
|
|
433
|
-
let by_identity_guard = self.by_identity.lock().map_err(|_| {
|
|
434
|
-
LixError::new(
|
|
435
|
-
"LIX_ERROR_UNKNOWN",
|
|
436
|
-
"failed to acquire transaction staged identity index lock",
|
|
437
|
-
)
|
|
438
|
-
})?;
|
|
439
|
-
let slots = by_identity_guard
|
|
440
|
-
.iter()
|
|
441
|
-
.map(|(identity, slot)| (identity.clone(), *slot))
|
|
442
|
-
.collect();
|
|
443
386
|
Ok(PreparedStateRowOverlay {
|
|
444
387
|
staged_writes: Arc::clone(self),
|
|
445
|
-
slots,
|
|
446
388
|
})
|
|
447
389
|
}
|
|
448
390
|
|
|
@@ -458,16 +400,9 @@ impl TransactionWriteBuffer {
|
|
|
458
400
|
let (mode, count) = match &write {
|
|
459
401
|
PreparedTransactionWrite::Rows { mode, rows } => (Some(*mode), rows.len() as u64),
|
|
460
402
|
PreparedTransactionWrite::RowsWithFileData { mode, count, .. } => (Some(*mode), *count),
|
|
461
|
-
PreparedTransactionWrite::AdoptedChanges { rows } => (None, rows.len() as u64),
|
|
462
403
|
};
|
|
463
404
|
let mut functions = self.functions.clone();
|
|
464
|
-
let (rows,
|
|
465
|
-
for row in &rows {
|
|
466
|
-
validate_commit_membership_support(row)?;
|
|
467
|
-
}
|
|
468
|
-
for row in &adopted_rows {
|
|
469
|
-
validate_adopted_commit_membership_support(row)?;
|
|
470
|
-
}
|
|
405
|
+
let (rows, file_data_writes) = self.state_rows_from_stage_write(write);
|
|
471
406
|
reject_duplicate_present_rows_in_batch(&rows)?;
|
|
472
407
|
let mut guard = self.rows.lock().map_err(|_| {
|
|
473
408
|
LixError::new(
|
|
@@ -475,24 +410,19 @@ impl TransactionWriteBuffer {
|
|
|
475
410
|
"failed to acquire transaction staged writes lock",
|
|
476
411
|
)
|
|
477
412
|
})?;
|
|
478
|
-
let mut adopted_guard = self.adopted_rows.lock().map_err(|_| {
|
|
479
|
-
LixError::new(
|
|
480
|
-
"LIX_ERROR_UNKNOWN",
|
|
481
|
-
"failed to acquire transaction staged adopted writes lock",
|
|
482
|
-
)
|
|
483
|
-
})?;
|
|
484
413
|
let mut by_identity_guard = self.by_identity.lock().map_err(|_| {
|
|
485
414
|
LixError::new(
|
|
486
415
|
"LIX_ERROR_UNKNOWN",
|
|
487
416
|
"failed to acquire transaction staged identity index lock",
|
|
488
417
|
)
|
|
489
418
|
})?;
|
|
490
|
-
let mut
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
419
|
+
let mut commit_change_refs_guard =
|
|
420
|
+
self.commit_change_refs_by_branch.lock().map_err(|_| {
|
|
421
|
+
LixError::new(
|
|
422
|
+
"LIX_ERROR_UNKNOWN",
|
|
423
|
+
"failed to acquire transaction staged commit change refs lock",
|
|
424
|
+
)
|
|
425
|
+
})?;
|
|
496
426
|
let mut insert_identities_guard = self.insert_identities.lock().map_err(|_| {
|
|
497
427
|
LixError::new(
|
|
498
428
|
"LIX_ERROR_UNKNOWN",
|
|
@@ -500,22 +430,25 @@ impl TransactionWriteBuffer {
|
|
|
500
430
|
)
|
|
501
431
|
})?;
|
|
502
432
|
for mut row in rows {
|
|
433
|
+
if row.global && row.branch_id != GLOBAL_BRANCH_ID {
|
|
434
|
+
return Err(LixError::new(
|
|
435
|
+
LixError::CODE_INVALID_PARAM,
|
|
436
|
+
"global staged rows must use the global branch id",
|
|
437
|
+
));
|
|
438
|
+
}
|
|
503
439
|
let identity = PreparedStateRowIdentity::from(&row);
|
|
504
440
|
if mode == Some(TransactionWriteMode::Insert)
|
|
505
441
|
&& by_identity_guard.contains_key(&identity)
|
|
506
442
|
{
|
|
507
443
|
return Err(duplicate_insert_identity_error(&row));
|
|
508
444
|
}
|
|
509
|
-
if matches!(by_identity_guard.get(&identity), Some(RowSlot::Adopted(_))) {
|
|
510
|
-
return Err(conflicting_adopted_identity_error(&row));
|
|
511
|
-
}
|
|
512
445
|
let existing_slot = by_identity_guard.remove(&identity);
|
|
513
446
|
if let Some(RowSlot::State(index)) = existing_slot {
|
|
514
447
|
if let Some(previous) = guard.get_mut(index).and_then(Option::take) {
|
|
515
|
-
|
|
448
|
+
remove_row_from_commit_change_refs(&mut commit_change_refs_guard, &previous);
|
|
516
449
|
}
|
|
517
450
|
}
|
|
518
|
-
|
|
451
|
+
add_row_to_commit_change_refs(&mut commit_change_refs_guard, &mut row, &mut functions);
|
|
519
452
|
let identity = PreparedStateRowIdentity::from(&row);
|
|
520
453
|
if mode == Some(TransactionWriteMode::Insert) {
|
|
521
454
|
insert_identities_guard.insert(identity.clone(), row.origin.clone());
|
|
@@ -533,17 +466,6 @@ impl TransactionWriteBuffer {
|
|
|
533
466
|
};
|
|
534
467
|
by_identity_guard.insert(identity, slot);
|
|
535
468
|
}
|
|
536
|
-
for mut row in adopted_rows {
|
|
537
|
-
let identity = PreparedStateRowIdentity::from(&row);
|
|
538
|
-
if by_identity_guard.contains_key(&identity) {
|
|
539
|
-
return Err(conflicting_adopted_projection_error(&row));
|
|
540
|
-
}
|
|
541
|
-
add_adopted_row_to_commit_members(&mut commit_members_guard, &mut row, &mut functions);
|
|
542
|
-
let identity = PreparedStateRowIdentity::from(&row);
|
|
543
|
-
let index = adopted_guard.len();
|
|
544
|
-
adopted_guard.push(Some(row));
|
|
545
|
-
by_identity_guard.insert(identity, RowSlot::Adopted(index));
|
|
546
|
-
}
|
|
547
469
|
if !file_data_writes.is_empty() {
|
|
548
470
|
self.file_data_writes
|
|
549
471
|
.lock()
|
|
@@ -561,16 +483,8 @@ impl TransactionWriteBuffer {
|
|
|
561
483
|
fn state_rows_from_stage_write(
|
|
562
484
|
&self,
|
|
563
485
|
write: PreparedTransactionWrite,
|
|
564
|
-
) ->
|
|
565
|
-
(
|
|
566
|
-
Vec<PreparedStateRow>,
|
|
567
|
-
Vec<PreparedAdoptedStateRow>,
|
|
568
|
-
Vec<TransactionFileData>,
|
|
569
|
-
),
|
|
570
|
-
LixError,
|
|
571
|
-
> {
|
|
486
|
+
) -> (Vec<PreparedStateRow>, Vec<TransactionFileData>) {
|
|
572
487
|
let mut state_rows = Vec::new();
|
|
573
|
-
let mut adopted_rows = Vec::new();
|
|
574
488
|
let mut file_data_writes = Vec::new();
|
|
575
489
|
match write {
|
|
576
490
|
PreparedTransactionWrite::Rows { rows, .. } => {
|
|
@@ -582,23 +496,19 @@ impl TransactionWriteBuffer {
|
|
|
582
496
|
state_rows.extend(rows);
|
|
583
497
|
file_data_writes.extend(file_data);
|
|
584
498
|
}
|
|
585
|
-
PreparedTransactionWrite::AdoptedChanges { rows } => {
|
|
586
|
-
adopted_rows.extend(rows);
|
|
587
|
-
}
|
|
588
499
|
}
|
|
589
|
-
|
|
500
|
+
(state_rows, file_data_writes)
|
|
590
501
|
}
|
|
591
502
|
}
|
|
592
503
|
|
|
593
504
|
/// Read overlay derived from staged transaction writes.
|
|
505
|
+
#[derive(Clone)]
|
|
594
506
|
pub(crate) struct PreparedStateRowOverlay {
|
|
595
507
|
staged_writes: Arc<TransactionWriteBuffer>,
|
|
596
|
-
slots: BTreeMap<PreparedStateRowIdentity, RowSlot>,
|
|
597
508
|
}
|
|
598
509
|
|
|
599
510
|
pub(crate) struct StagedScanParts {
|
|
600
511
|
pub(crate) rows: Vec<MaterializedLiveStateRow>,
|
|
601
|
-
pub(crate) hidden_identities: BTreeSet<PreparedStateRowIdentity>,
|
|
602
512
|
}
|
|
603
513
|
|
|
604
514
|
impl PreparedStateRowOverlay {
|
|
@@ -608,7 +518,17 @@ impl PreparedStateRowOverlay {
|
|
|
608
518
|
&self,
|
|
609
519
|
request: &LiveStateScanRequest,
|
|
610
520
|
) -> Result<Vec<MaterializedLiveStateRow>, LixError> {
|
|
611
|
-
Ok(
|
|
521
|
+
Ok(crate::live_state::resolve_visible_rows(
|
|
522
|
+
self.scan_parts(request)?.rows,
|
|
523
|
+
Vec::new(),
|
|
524
|
+
&crate::live_state::VisibilityRequest {
|
|
525
|
+
branch_scope: crate::live_state::VisibilityBranchScope::BranchIds {
|
|
526
|
+
branch_ids: request.filter.branch_ids.clone(),
|
|
527
|
+
},
|
|
528
|
+
include_tombstones: request.filter.include_tombstones,
|
|
529
|
+
limit: None,
|
|
530
|
+
},
|
|
531
|
+
))
|
|
612
532
|
}
|
|
613
533
|
|
|
614
534
|
/// Returns staged rows and base-row identities hidden by staged rows in one pass.
|
|
@@ -619,22 +539,28 @@ impl PreparedStateRowOverlay {
|
|
|
619
539
|
&self,
|
|
620
540
|
request: &LiveStateScanRequest,
|
|
621
541
|
) -> Result<StagedScanParts, LixError> {
|
|
542
|
+
if matches!(
|
|
543
|
+
request.filter.rows,
|
|
544
|
+
crate::live_state::LiveStateRowFilter::None
|
|
545
|
+
) {
|
|
546
|
+
return Ok(StagedScanParts { rows: Vec::new() });
|
|
547
|
+
}
|
|
548
|
+
|
|
622
549
|
let rows_guard = self.staged_writes.rows.lock().map_err(|_| {
|
|
623
550
|
LixError::new(
|
|
624
551
|
"LIX_ERROR_UNKNOWN",
|
|
625
552
|
"failed to acquire transaction staged writes lock",
|
|
626
553
|
)
|
|
627
554
|
})?;
|
|
628
|
-
let
|
|
555
|
+
let by_identity_guard = self.staged_writes.by_identity.lock().map_err(|_| {
|
|
629
556
|
LixError::new(
|
|
630
557
|
"LIX_ERROR_UNKNOWN",
|
|
631
|
-
"failed to acquire transaction staged
|
|
558
|
+
"failed to acquire transaction staged identity index lock",
|
|
632
559
|
)
|
|
633
560
|
})?;
|
|
634
561
|
|
|
635
562
|
let mut rows = Vec::new();
|
|
636
|
-
|
|
637
|
-
for (identity, slot) in &self.slots {
|
|
563
|
+
for slot in by_identity_guard.values() {
|
|
638
564
|
match *slot {
|
|
639
565
|
RowSlot::State(index) => {
|
|
640
566
|
let Some(row) = rows_guard.get(index).and_then(Option::as_ref) else {
|
|
@@ -643,29 +569,11 @@ impl PreparedStateRowOverlay {
|
|
|
643
569
|
if !staged_row_identity_matches_scan(row, request) {
|
|
644
570
|
continue;
|
|
645
571
|
}
|
|
646
|
-
|
|
647
|
-
if row.snapshot.is_some() || request.filter.include_tombstones {
|
|
648
|
-
rows.push(MaterializedLiveStateRow::from(row));
|
|
649
|
-
}
|
|
650
|
-
}
|
|
651
|
-
RowSlot::Adopted(index) => {
|
|
652
|
-
let Some(row) = adopted_guard.get(index).and_then(Option::as_ref) else {
|
|
653
|
-
continue;
|
|
654
|
-
};
|
|
655
|
-
if !adopted_row_identity_matches_scan(row, request) {
|
|
656
|
-
continue;
|
|
657
|
-
}
|
|
658
|
-
hidden_identities.insert(identity.clone());
|
|
659
|
-
if row.snapshot.is_some() || request.filter.include_tombstones {
|
|
660
|
-
rows.push(MaterializedLiveStateRow::from(row));
|
|
661
|
-
}
|
|
572
|
+
rows.push(MaterializedLiveStateRow::from(row));
|
|
662
573
|
}
|
|
663
574
|
}
|
|
664
575
|
}
|
|
665
|
-
Ok(StagedScanParts {
|
|
666
|
-
rows,
|
|
667
|
-
hidden_identities,
|
|
668
|
-
})
|
|
576
|
+
Ok(StagedScanParts { rows })
|
|
669
577
|
}
|
|
670
578
|
|
|
671
579
|
/// Returns a staged exact-row answer, if this transaction has one.
|
|
@@ -688,44 +596,26 @@ impl PreparedStateRowOverlay {
|
|
|
688
596
|
StagedExactRow::Row(MaterializedLiveStateRow::from(&row))
|
|
689
597
|
});
|
|
690
598
|
}
|
|
691
|
-
|
|
692
|
-
if row.snapshot.is_none() {
|
|
693
|
-
StagedExactRow::Tombstone
|
|
694
|
-
} else {
|
|
695
|
-
StagedExactRow::Row(MaterializedLiveStateRow::from(&row))
|
|
696
|
-
}
|
|
697
|
-
})
|
|
599
|
+
None
|
|
698
600
|
}
|
|
699
601
|
|
|
700
602
|
#[cfg(test)]
|
|
701
603
|
fn load_state_slot(&self, identity: &PreparedStateRowIdentity) -> Option<PreparedStateRow> {
|
|
702
|
-
let
|
|
604
|
+
let rows_guard = self.staged_writes.rows.lock().ok()?;
|
|
605
|
+
let by_identity_guard = self.staged_writes.by_identity.lock().ok()?;
|
|
606
|
+
let Some(RowSlot::State(index)) = by_identity_guard.get(identity).copied() else {
|
|
703
607
|
return None;
|
|
704
608
|
};
|
|
705
|
-
|
|
706
|
-
.rows
|
|
707
|
-
.lock()
|
|
708
|
-
.ok()?
|
|
709
|
-
.get(index)?
|
|
710
|
-
.as_ref()
|
|
711
|
-
.cloned()
|
|
609
|
+
rows_guard.get(index)?.as_ref().cloned()
|
|
712
610
|
}
|
|
611
|
+
}
|
|
713
612
|
|
|
714
|
-
|
|
715
|
-
fn
|
|
613
|
+
impl crate::live_state::StagedLiveStateRows for PreparedStateRowOverlay {
|
|
614
|
+
fn staged_rows(
|
|
716
615
|
&self,
|
|
717
|
-
|
|
718
|
-
) ->
|
|
719
|
-
|
|
720
|
-
return None;
|
|
721
|
-
};
|
|
722
|
-
self.staged_writes
|
|
723
|
-
.adopted_rows
|
|
724
|
-
.lock()
|
|
725
|
-
.ok()?
|
|
726
|
-
.get(index)?
|
|
727
|
-
.as_ref()
|
|
728
|
-
.cloned()
|
|
616
|
+
request: &LiveStateScanRequest,
|
|
617
|
+
) -> Result<Vec<MaterializedLiveStateRow>, LixError> {
|
|
618
|
+
Ok(self.scan_parts(request)?.rows)
|
|
729
619
|
}
|
|
730
620
|
}
|
|
731
621
|
|
|
@@ -739,9 +629,9 @@ pub(crate) enum StagedExactRow {
|
|
|
739
629
|
pub(crate) struct PreparedStateRowIdentity {
|
|
740
630
|
untracked: bool,
|
|
741
631
|
schema_key: String,
|
|
742
|
-
|
|
632
|
+
entity_pk: crate::entity_pk::EntityPk,
|
|
743
633
|
file_id: Option<String>,
|
|
744
|
-
|
|
634
|
+
branch_id: String,
|
|
745
635
|
}
|
|
746
636
|
|
|
747
637
|
impl PreparedStateRowIdentity {
|
|
@@ -749,9 +639,9 @@ impl PreparedStateRowIdentity {
|
|
|
749
639
|
Self {
|
|
750
640
|
untracked: row.untracked,
|
|
751
641
|
schema_key: row.schema_key.clone(),
|
|
752
|
-
|
|
642
|
+
entity_pk: row.entity_pk.clone(),
|
|
753
643
|
file_id: row.file_id.clone(),
|
|
754
|
-
|
|
644
|
+
branch_id: row.branch_id.clone(),
|
|
755
645
|
}
|
|
756
646
|
}
|
|
757
647
|
|
|
@@ -766,9 +656,9 @@ impl PreparedStateRowIdentity {
|
|
|
766
656
|
Some(Self {
|
|
767
657
|
untracked,
|
|
768
658
|
schema_key: request.schema_key.clone(),
|
|
769
|
-
|
|
659
|
+
entity_pk: request.entity_pk.clone(),
|
|
770
660
|
file_id,
|
|
771
|
-
|
|
661
|
+
branch_id: request.branch_id.clone(),
|
|
772
662
|
})
|
|
773
663
|
}
|
|
774
664
|
|
|
@@ -776,16 +666,12 @@ impl PreparedStateRowIdentity {
|
|
|
776
666
|
&self.schema_key
|
|
777
667
|
}
|
|
778
668
|
|
|
779
|
-
pub(crate) fn
|
|
780
|
-
&self.
|
|
669
|
+
pub(crate) fn entity_pk(&self) -> &crate::entity_pk::EntityPk {
|
|
670
|
+
&self.entity_pk
|
|
781
671
|
}
|
|
782
672
|
|
|
783
673
|
pub(crate) fn domain(&self) -> Domain {
|
|
784
|
-
Domain::exact_file(
|
|
785
|
-
self.version_id.clone(),
|
|
786
|
-
self.untracked,
|
|
787
|
-
self.file_id.clone(),
|
|
788
|
-
)
|
|
674
|
+
Domain::exact_file(self.branch_id.clone(), self.untracked, self.file_id.clone())
|
|
789
675
|
}
|
|
790
676
|
}
|
|
791
677
|
|
|
@@ -795,52 +681,18 @@ impl From<&PreparedStateRow> for PreparedStateRowIdentity {
|
|
|
795
681
|
}
|
|
796
682
|
}
|
|
797
683
|
|
|
798
|
-
impl From<&PreparedAdoptedStateRow> for PreparedStateRowIdentity {
|
|
799
|
-
fn from(row: &PreparedAdoptedStateRow) -> Self {
|
|
800
|
-
Self {
|
|
801
|
-
untracked: false,
|
|
802
|
-
schema_key: row.schema_key.clone(),
|
|
803
|
-
entity_id: row.entity_id.clone(),
|
|
804
|
-
file_id: row.file_id.clone(),
|
|
805
|
-
version_id: row.version_id.clone(),
|
|
806
|
-
}
|
|
807
|
-
}
|
|
808
|
-
}
|
|
809
|
-
|
|
810
684
|
impl From<&MaterializedLiveStateRow> for PreparedStateRowIdentity {
|
|
811
685
|
fn from(row: &MaterializedLiveStateRow) -> Self {
|
|
812
686
|
Self {
|
|
813
687
|
untracked: row.untracked,
|
|
814
688
|
schema_key: row.schema_key.clone(),
|
|
815
|
-
|
|
689
|
+
entity_pk: row.entity_pk.clone(),
|
|
816
690
|
file_id: row.file_id.clone(),
|
|
817
|
-
|
|
691
|
+
branch_id: row.branch_id.clone(),
|
|
818
692
|
}
|
|
819
693
|
}
|
|
820
694
|
}
|
|
821
695
|
|
|
822
|
-
fn validate_commit_membership_support(row: &PreparedStateRow) -> Result<(), LixError> {
|
|
823
|
-
if row.global && row.version_id != GLOBAL_VERSION_ID {
|
|
824
|
-
return Err(LixError::new(
|
|
825
|
-
"LIX_ERROR_UNKNOWN",
|
|
826
|
-
"engine global staged rows must use the global version id",
|
|
827
|
-
));
|
|
828
|
-
}
|
|
829
|
-
Ok(())
|
|
830
|
-
}
|
|
831
|
-
|
|
832
|
-
fn validate_adopted_commit_membership_support(
|
|
833
|
-
row: &PreparedAdoptedStateRow,
|
|
834
|
-
) -> Result<(), LixError> {
|
|
835
|
-
if row.global && row.version_id != GLOBAL_VERSION_ID {
|
|
836
|
-
return Err(LixError::new(
|
|
837
|
-
"LIX_ERROR_UNKNOWN",
|
|
838
|
-
"engine global adopted rows must use the global version id",
|
|
839
|
-
));
|
|
840
|
-
}
|
|
841
|
-
Ok(())
|
|
842
|
-
}
|
|
843
|
-
|
|
844
696
|
fn reject_duplicate_present_rows_in_batch(rows: &[PreparedStateRow]) -> Result<(), LixError> {
|
|
845
697
|
let mut pending_present_rows = BTreeMap::<PreparedStateRowIdentity, &PreparedStateRow>::new();
|
|
846
698
|
for row in rows {
|
|
@@ -863,13 +715,13 @@ fn duplicate_staged_present_row_error(
|
|
|
863
715
|
let message = logical_primary_key_violation_message(row.origin.as_ref())
|
|
864
716
|
.unwrap_or_else(|| {
|
|
865
717
|
format!(
|
|
866
|
-
"primary-key constraint violation on schema '{}': duplicate staged rows for
|
|
718
|
+
"primary-key constraint violation on schema '{}': duplicate staged rows for entity_pk '{}' in branch '{}'",
|
|
867
719
|
row.schema_key,
|
|
868
720
|
previous
|
|
869
|
-
.
|
|
721
|
+
.entity_pk
|
|
870
722
|
.as_json_array_text()
|
|
871
|
-
.unwrap_or_else(|_| "<invalid
|
|
872
|
-
row.
|
|
723
|
+
.unwrap_or_else(|_| "<invalid entity_pk>".to_string()),
|
|
724
|
+
row.branch_id
|
|
873
725
|
)
|
|
874
726
|
});
|
|
875
727
|
LixError::new(LixError::CODE_UNIQUE, message)
|
|
@@ -877,22 +729,22 @@ fn duplicate_staged_present_row_error(
|
|
|
877
729
|
|
|
878
730
|
pub(crate) fn duplicate_insert_identity_message(
|
|
879
731
|
schema_key: &str,
|
|
880
|
-
|
|
881
|
-
|
|
732
|
+
entity_pk: &crate::entity_pk::EntityPk,
|
|
733
|
+
branch_id: Option<&str>,
|
|
882
734
|
origin: Option<&TransactionWriteOrigin>,
|
|
883
735
|
) -> String {
|
|
884
736
|
if let Some(message) = logical_primary_key_violation_message(origin) {
|
|
885
737
|
return message;
|
|
886
738
|
}
|
|
887
|
-
let
|
|
739
|
+
let entity_pk = entity_pk
|
|
888
740
|
.as_json_array_text()
|
|
889
|
-
.unwrap_or_else(|_| "<invalid
|
|
890
|
-
match
|
|
891
|
-
Some(
|
|
892
|
-
"primary-key constraint violation on schema '{schema_key}': INSERT would duplicate
|
|
741
|
+
.unwrap_or_else(|_| "<invalid entity_pk>".to_string());
|
|
742
|
+
match branch_id {
|
|
743
|
+
Some(branch_id) => format!(
|
|
744
|
+
"primary-key constraint violation on schema '{schema_key}': INSERT would duplicate entity_pk '{entity_pk}' in branch '{branch_id}'"
|
|
893
745
|
),
|
|
894
746
|
None => format!(
|
|
895
|
-
"primary-key constraint violation on schema '{schema_key}': INSERT would duplicate
|
|
747
|
+
"primary-key constraint violation on schema '{schema_key}': INSERT would duplicate entity_pk '{entity_pk}'"
|
|
896
748
|
),
|
|
897
749
|
}
|
|
898
750
|
}
|
|
@@ -900,8 +752,8 @@ pub(crate) fn duplicate_insert_identity_message(
|
|
|
900
752
|
fn duplicate_insert_identity_error(row: &PreparedStateRow) -> LixError {
|
|
901
753
|
let message = duplicate_insert_identity_message(
|
|
902
754
|
&row.schema_key,
|
|
903
|
-
&row.
|
|
904
|
-
Some(&row.
|
|
755
|
+
&row.entity_pk,
|
|
756
|
+
Some(&row.branch_id),
|
|
905
757
|
row.origin.as_ref(),
|
|
906
758
|
);
|
|
907
759
|
LixError::new(LixError::CODE_UNIQUE, message)
|
|
@@ -939,36 +791,8 @@ fn format_logical_primary_key(primary_key: &LogicalPrimaryKey) -> String {
|
|
|
939
791
|
.join(", ")
|
|
940
792
|
}
|
|
941
793
|
|
|
942
|
-
fn
|
|
943
|
-
|
|
944
|
-
LixError::CODE_UNIQUE,
|
|
945
|
-
format!(
|
|
946
|
-
"transaction cannot stage a new row and an adopted projection for schema '{}' entity_id '{}' in version '{}'",
|
|
947
|
-
row.schema_key,
|
|
948
|
-
row.entity_id
|
|
949
|
-
.as_json_array_text()
|
|
950
|
-
.unwrap_or_else(|_| "<invalid entity_id>".to_string()),
|
|
951
|
-
row.version_id
|
|
952
|
-
),
|
|
953
|
-
)
|
|
954
|
-
}
|
|
955
|
-
|
|
956
|
-
fn conflicting_adopted_projection_error(row: &PreparedAdoptedStateRow) -> LixError {
|
|
957
|
-
LixError::new(
|
|
958
|
-
LixError::CODE_UNIQUE,
|
|
959
|
-
format!(
|
|
960
|
-
"transaction cannot stage duplicate adopted projections for schema '{}' entity_id '{}' in version '{}'",
|
|
961
|
-
row.schema_key,
|
|
962
|
-
row.entity_id
|
|
963
|
-
.as_json_array_text()
|
|
964
|
-
.unwrap_or_else(|_| "<invalid entity_id>".to_string()),
|
|
965
|
-
row.version_id
|
|
966
|
-
),
|
|
967
|
-
)
|
|
968
|
-
}
|
|
969
|
-
|
|
970
|
-
fn add_row_to_commit_members(
|
|
971
|
-
members_by_version: &mut BTreeMap<String, StagedCommitMembers>,
|
|
794
|
+
fn add_row_to_commit_change_refs(
|
|
795
|
+
change_refs_by_branch: &mut BTreeMap<String, StagedCommitChangeRefs>,
|
|
972
796
|
row: &mut PreparedStateRow,
|
|
973
797
|
functions: &mut dyn FunctionProvider,
|
|
974
798
|
) {
|
|
@@ -978,81 +802,39 @@ fn add_row_to_commit_members(
|
|
|
978
802
|
let change_id = row
|
|
979
803
|
.change_id
|
|
980
804
|
.clone()
|
|
981
|
-
.expect("tracked staged rows must carry change_id for commit
|
|
982
|
-
let
|
|
983
|
-
.entry(row.
|
|
984
|
-
.or_insert_with(|| {
|
|
985
|
-
StagedCommitMembers::new(
|
|
986
|
-
functions.uuid_v7(),
|
|
987
|
-
functions.uuid_v7(),
|
|
988
|
-
functions.timestamp(),
|
|
989
|
-
)
|
|
990
|
-
});
|
|
991
|
-
row.commit_id = Some(members.commit_id.clone());
|
|
992
|
-
members.add_change_id(change_id);
|
|
993
|
-
}
|
|
994
|
-
|
|
995
|
-
fn add_adopted_row_to_commit_members(
|
|
996
|
-
members_by_version: &mut BTreeMap<String, StagedCommitMembers>,
|
|
997
|
-
row: &mut PreparedAdoptedStateRow,
|
|
998
|
-
functions: &mut dyn FunctionProvider,
|
|
999
|
-
) {
|
|
1000
|
-
let members = members_by_version
|
|
1001
|
-
.entry(row.version_id.clone())
|
|
805
|
+
.expect("tracked staged rows must carry change_id for commit change refs");
|
|
806
|
+
let change_refs = change_refs_by_branch
|
|
807
|
+
.entry(row.branch_id.clone())
|
|
1002
808
|
.or_insert_with(|| {
|
|
1003
|
-
|
|
809
|
+
StagedCommitChangeRefs::new(
|
|
1004
810
|
functions.uuid_v7(),
|
|
1005
811
|
functions.uuid_v7(),
|
|
1006
812
|
functions.timestamp(),
|
|
1007
813
|
)
|
|
1008
814
|
});
|
|
1009
|
-
row.commit_id =
|
|
1010
|
-
|
|
815
|
+
row.commit_id = Some(change_refs.commit_id.clone());
|
|
816
|
+
change_refs.add_change_id(change_id);
|
|
1011
817
|
}
|
|
1012
818
|
|
|
1013
|
-
fn
|
|
1014
|
-
|
|
819
|
+
fn remove_row_from_commit_change_refs(
|
|
820
|
+
change_refs_by_branch: &mut BTreeMap<String, StagedCommitChangeRefs>,
|
|
1015
821
|
row: &PreparedStateRow,
|
|
1016
822
|
) {
|
|
1017
823
|
if row.untracked {
|
|
1018
824
|
return;
|
|
1019
825
|
}
|
|
1020
|
-
let Some(
|
|
826
|
+
let Some(change_refs) = change_refs_by_branch.get_mut(&row.branch_id) else {
|
|
1021
827
|
return;
|
|
1022
828
|
};
|
|
1023
829
|
let Some(change_id) = row.change_id.as_deref() else {
|
|
1024
830
|
return;
|
|
1025
831
|
};
|
|
1026
|
-
|
|
1027
|
-
if
|
|
1028
|
-
|
|
832
|
+
change_refs.remove_change_id(change_id);
|
|
833
|
+
if change_refs.is_empty() {
|
|
834
|
+
change_refs_by_branch.remove(&row.branch_id);
|
|
1029
835
|
}
|
|
1030
836
|
}
|
|
1031
837
|
|
|
1032
|
-
fn adopted_row_identity_matches_scan(
|
|
1033
|
-
row: &PreparedAdoptedStateRow,
|
|
1034
|
-
request: &LiveStateScanRequest,
|
|
1035
|
-
) -> bool {
|
|
1036
|
-
if !request.filter.schema_keys.is_empty()
|
|
1037
|
-
&& !request.filter.schema_keys.contains(&row.schema_key)
|
|
1038
|
-
{
|
|
1039
|
-
return false;
|
|
1040
|
-
}
|
|
1041
|
-
if !request.filter.entity_ids.is_empty() && !request.filter.entity_ids.contains(&row.entity_id)
|
|
1042
|
-
{
|
|
1043
|
-
return false;
|
|
1044
|
-
}
|
|
1045
|
-
if !request.filter.version_ids.is_empty()
|
|
1046
|
-
&& !request.filter.version_ids.contains(&row.version_id)
|
|
1047
|
-
{
|
|
1048
|
-
return false;
|
|
1049
|
-
}
|
|
1050
|
-
if request.filter.untracked == Some(true) {
|
|
1051
|
-
return false;
|
|
1052
|
-
}
|
|
1053
|
-
nullable_key_matches_filters(&row.file_id, &request.filter.file_ids)
|
|
1054
|
-
}
|
|
1055
|
-
|
|
1056
838
|
fn staged_row_identity_matches_scan(
|
|
1057
839
|
row: &PreparedStateRow,
|
|
1058
840
|
request: &LiveStateScanRequest,
|
|
@@ -1062,13 +844,11 @@ fn staged_row_identity_matches_scan(
|
|
|
1062
844
|
{
|
|
1063
845
|
return false;
|
|
1064
846
|
}
|
|
1065
|
-
if !request.filter.
|
|
847
|
+
if !request.filter.entity_pks.is_empty() && !request.filter.entity_pks.contains(&row.entity_pk)
|
|
1066
848
|
{
|
|
1067
849
|
return false;
|
|
1068
850
|
}
|
|
1069
|
-
if !request
|
|
1070
|
-
&& !request.filter.version_ids.contains(&row.version_id)
|
|
1071
|
-
{
|
|
851
|
+
if !staged_branch_matches_scan(&row.branch_id, request) {
|
|
1072
852
|
return false;
|
|
1073
853
|
}
|
|
1074
854
|
if request
|
|
@@ -1091,6 +871,21 @@ fn nullable_key_matches_filters(
|
|
|
1091
871
|
.any(|filter| nullable_key_matches_filter(value, filter))
|
|
1092
872
|
}
|
|
1093
873
|
|
|
874
|
+
fn staged_branch_matches_scan(branch_id: &str, request: &LiveStateScanRequest) -> bool {
|
|
875
|
+
request.filter.branch_ids.is_empty()
|
|
876
|
+
|| request
|
|
877
|
+
.filter
|
|
878
|
+
.branch_ids
|
|
879
|
+
.iter()
|
|
880
|
+
.any(|requested| requested == branch_id)
|
|
881
|
+
|| (branch_id == GLOBAL_BRANCH_ID
|
|
882
|
+
&& request
|
|
883
|
+
.filter
|
|
884
|
+
.branch_ids
|
|
885
|
+
.iter()
|
|
886
|
+
.any(|requested| requested != GLOBAL_BRANCH_ID))
|
|
887
|
+
}
|
|
888
|
+
|
|
1094
889
|
fn nullable_key_matches_filter(value: &Option<String>, filter: &NullableKeyFilter<String>) -> bool {
|
|
1095
890
|
match filter {
|
|
1096
891
|
NullableKeyFilter::Any => true,
|
|
@@ -1128,8 +923,8 @@ mod tests {
|
|
|
1128
923
|
let row = overlay
|
|
1129
924
|
.load_exact(&LiveStateRowRequest {
|
|
1130
925
|
schema_key: "lix_key_value".to_string(),
|
|
1131
|
-
|
|
1132
|
-
|
|
926
|
+
branch_id: "global".to_string(),
|
|
927
|
+
entity_pk: crate::entity_pk::EntityPk::single("sql2-duplicate-key"),
|
|
1133
928
|
file_id: NullableKeyFilter::Null,
|
|
1134
929
|
})
|
|
1135
930
|
.expect("staged row should be visible");
|
|
@@ -1268,7 +1063,7 @@ mod tests {
|
|
|
1268
1063
|
|
|
1269
1064
|
assert_eq!(drained.state_rows.len(), 2);
|
|
1270
1065
|
assert!(drained.state_rows.iter().any(|row| {
|
|
1271
|
-
row.
|
|
1066
|
+
row.entity_pk == crate::entity_pk::EntityPk::single("sql2-key-a")
|
|
1272
1067
|
&& row
|
|
1273
1068
|
.snapshot
|
|
1274
1069
|
.as_ref()
|
|
@@ -1276,7 +1071,7 @@ mod tests {
|
|
|
1276
1071
|
== Some("{\"key\":\"sql2-key-a\",\"value\":\"second\"}")
|
|
1277
1072
|
}));
|
|
1278
1073
|
assert!(drained.state_rows.iter().any(|row| {
|
|
1279
|
-
row.
|
|
1074
|
+
row.entity_pk == crate::entity_pk::EntityPk::single("sql2-key-b")
|
|
1280
1075
|
&& row
|
|
1281
1076
|
.snapshot
|
|
1282
1077
|
.as_ref()
|
|
@@ -1295,7 +1090,7 @@ mod tests {
|
|
|
1295
1090
|
rows: vec![state_row("file-readme", "descriptor")],
|
|
1296
1091
|
file_data: vec![TransactionFileData {
|
|
1297
1092
|
file_id: "file-readme".to_string(),
|
|
1298
|
-
|
|
1093
|
+
branch_id: "global".to_string(),
|
|
1299
1094
|
untracked: true,
|
|
1300
1095
|
data: b"hello".to_vec(),
|
|
1301
1096
|
}],
|
|
@@ -1323,12 +1118,12 @@ mod tests {
|
|
|
1323
1118
|
.expect("tracked global row should stage");
|
|
1324
1119
|
|
|
1325
1120
|
let drained = staged_writes.drain().expect("drain should succeed");
|
|
1326
|
-
let
|
|
1327
|
-
.
|
|
1121
|
+
let change_refs = drained
|
|
1122
|
+
.commit_change_refs_by_branch
|
|
1328
1123
|
.get("global")
|
|
1329
|
-
.expect("global commit
|
|
1124
|
+
.expect("global commit change_refs should exist");
|
|
1330
1125
|
assert_eq!(
|
|
1331
|
-
|
|
1126
|
+
change_refs.change_ids.iter().cloned().collect::<Vec<_>>(),
|
|
1332
1127
|
vec!["test-change-id".to_string()]
|
|
1333
1128
|
);
|
|
1334
1129
|
}
|
|
@@ -1345,7 +1140,7 @@ mod tests {
|
|
|
1345
1140
|
.expect("untracked row should stage");
|
|
1346
1141
|
|
|
1347
1142
|
let drained = staged_writes.drain().expect("drain should succeed");
|
|
1348
|
-
assert!(drained.
|
|
1143
|
+
assert!(drained.commit_change_refs_by_branch.is_empty());
|
|
1349
1144
|
}
|
|
1350
1145
|
|
|
1351
1146
|
#[tokio::test]
|
|
@@ -1370,12 +1165,12 @@ mod tests {
|
|
|
1370
1165
|
.expect("tracked overwrite should stage");
|
|
1371
1166
|
|
|
1372
1167
|
let drained = staged_writes.drain().expect("drain should succeed");
|
|
1373
|
-
let
|
|
1374
|
-
.
|
|
1168
|
+
let change_refs = drained
|
|
1169
|
+
.commit_change_refs_by_branch
|
|
1375
1170
|
.get("global")
|
|
1376
|
-
.expect("global commit
|
|
1171
|
+
.expect("global commit change_refs should exist");
|
|
1377
1172
|
assert_eq!(
|
|
1378
|
-
|
|
1173
|
+
change_refs.change_ids.iter().cloned().collect::<Vec<_>>(),
|
|
1379
1174
|
vec!["change-second".to_string()]
|
|
1380
1175
|
);
|
|
1381
1176
|
}
|
|
@@ -1407,12 +1202,12 @@ mod tests {
|
|
|
1407
1202
|
.state_rows
|
|
1408
1203
|
.iter()
|
|
1409
1204
|
.any(|row| { row.change_id.as_deref() == Some("change-untracked") && row.untracked }));
|
|
1410
|
-
let
|
|
1411
|
-
.
|
|
1205
|
+
let change_refs = drained
|
|
1206
|
+
.commit_change_refs_by_branch
|
|
1412
1207
|
.get("global")
|
|
1413
1208
|
.expect("tracked commit member should remain in tracked domain");
|
|
1414
1209
|
assert_eq!(
|
|
1415
|
-
|
|
1210
|
+
change_refs.change_ids.iter().cloned().collect::<Vec<_>>(),
|
|
1416
1211
|
vec!["change-tracked".to_string()]
|
|
1417
1212
|
);
|
|
1418
1213
|
}
|
|
@@ -1455,41 +1250,41 @@ mod tests {
|
|
|
1455
1250
|
let drained = staged_writes.drain().expect("drain should succeed");
|
|
1456
1251
|
assert_eq!(drained.state_rows.len(), 2);
|
|
1457
1252
|
assert!(drained.state_rows.iter().any(|row| {
|
|
1458
|
-
row.
|
|
1253
|
+
row.entity_pk == crate::entity_pk::EntityPk::single("shared-domain-key")
|
|
1459
1254
|
&& !row.untracked
|
|
1460
1255
|
}));
|
|
1461
1256
|
assert!(drained.state_rows.iter().any(|row| {
|
|
1462
|
-
row.
|
|
1257
|
+
row.entity_pk == crate::entity_pk::EntityPk::single("shared-domain-key")
|
|
1463
1258
|
&& row.untracked
|
|
1464
1259
|
}));
|
|
1465
1260
|
}
|
|
1466
1261
|
|
|
1467
1262
|
#[tokio::test]
|
|
1468
|
-
async fn
|
|
1263
|
+
async fn staged_writes_track_active_branch_members_separately() {
|
|
1469
1264
|
let staged_writes = test_staged_writes();
|
|
1470
1265
|
|
|
1471
1266
|
staged_writes
|
|
1472
1267
|
.stage_write(PreparedTransactionWrite::Rows {
|
|
1473
1268
|
mode: TransactionWriteMode::Replace,
|
|
1474
|
-
rows: vec![state_row("active-
|
|
1269
|
+
rows: vec![state_row("active-branch-key", "value")
|
|
1475
1270
|
.with_tracked()
|
|
1476
|
-
.
|
|
1271
|
+
.with_branch("branch-a")],
|
|
1477
1272
|
})
|
|
1478
|
-
.expect("active-
|
|
1273
|
+
.expect("active-branch tracked staging should accumulate change_refs");
|
|
1479
1274
|
|
|
1480
1275
|
let drained = staged_writes.drain().expect("drain should succeed");
|
|
1481
|
-
let
|
|
1482
|
-
.
|
|
1483
|
-
.get("
|
|
1484
|
-
.expect("active-
|
|
1276
|
+
let change_refs = drained
|
|
1277
|
+
.commit_change_refs_by_branch
|
|
1278
|
+
.get("branch-a")
|
|
1279
|
+
.expect("active-branch commit change_refs should exist");
|
|
1485
1280
|
assert_eq!(
|
|
1486
|
-
|
|
1281
|
+
change_refs.change_ids.iter().cloned().collect::<Vec<_>>(),
|
|
1487
1282
|
vec!["test-change-id".to_string()]
|
|
1488
1283
|
);
|
|
1489
1284
|
}
|
|
1490
1285
|
|
|
1491
1286
|
#[tokio::test]
|
|
1492
|
-
async fn
|
|
1287
|
+
async fn staged_writes_reject_global_rows_with_non_global_branch_id() {
|
|
1493
1288
|
let staged_writes = test_staged_writes();
|
|
1494
1289
|
|
|
1495
1290
|
let error = staged_writes
|
|
@@ -1497,15 +1292,15 @@ mod tests {
|
|
|
1497
1292
|
mode: TransactionWriteMode::Replace,
|
|
1498
1293
|
rows: vec![{
|
|
1499
1294
|
let mut row = state_row("invalid-global-key", "value");
|
|
1500
|
-
row.
|
|
1295
|
+
row.branch_id = "branch-a".to_string();
|
|
1501
1296
|
row
|
|
1502
1297
|
}],
|
|
1503
1298
|
})
|
|
1504
|
-
.expect_err("global row with non-global
|
|
1299
|
+
.expect_err("global row with non-global branch should fail");
|
|
1505
1300
|
|
|
1506
1301
|
assert!(error
|
|
1507
1302
|
.message
|
|
1508
|
-
.contains("global staged rows must use the global
|
|
1303
|
+
.contains("global staged rows must use the global branch id"));
|
|
1509
1304
|
}
|
|
1510
1305
|
|
|
1511
1306
|
#[tokio::test]
|
|
@@ -1523,7 +1318,7 @@ mod tests {
|
|
|
1523
1318
|
mode: TransactionWriteMode::Replace,
|
|
1524
1319
|
rows: vec![
|
|
1525
1320
|
state_row("shared-entity", "base"),
|
|
1526
|
-
state_row("shared-entity", "other-
|
|
1321
|
+
state_row("shared-entity", "other-branch").with_branch("branch-b"),
|
|
1527
1322
|
state_row("shared-entity", "other-schema").with_schema("other_schema"),
|
|
1528
1323
|
state_row("shared-entity", "other-file").with_file_id("file-a"),
|
|
1529
1324
|
state_row("shared-entity", "tracked").with_tracked(),
|
|
@@ -1537,9 +1332,7 @@ mod tests {
|
|
|
1537
1332
|
let rows = overlay
|
|
1538
1333
|
.scan(&LiveStateScanRequest {
|
|
1539
1334
|
filter: LiveStateFilter {
|
|
1540
|
-
|
|
1541
|
-
"shared-entity",
|
|
1542
|
-
)],
|
|
1335
|
+
entity_pks: vec![crate::entity_pk::EntityPk::single("shared-entity")],
|
|
1543
1336
|
include_tombstones: true,
|
|
1544
1337
|
..LiveStateFilter::default()
|
|
1545
1338
|
},
|
|
@@ -1547,20 +1340,21 @@ mod tests {
|
|
|
1547
1340
|
})
|
|
1548
1341
|
.expect("overlay scan should succeed");
|
|
1549
1342
|
|
|
1550
|
-
assert_eq!(rows.len(),
|
|
1343
|
+
assert_eq!(rows.len(), 4);
|
|
1551
1344
|
assert_eq!(
|
|
1552
1345
|
rows.iter()
|
|
1553
|
-
.filter(
|
|
1554
|
-
== crate::
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
|
|
1346
|
+
.filter(
|
|
1347
|
+
|row| row.entity_pk == crate::entity_pk::EntityPk::single("shared-entity")
|
|
1348
|
+
&& row.branch_id == "global"
|
|
1349
|
+
&& row.schema_key == "lix_key_value"
|
|
1350
|
+
&& row.file_id.is_none()
|
|
1351
|
+
)
|
|
1558
1352
|
.count(),
|
|
1559
|
-
|
|
1353
|
+
1
|
|
1560
1354
|
);
|
|
1561
1355
|
assert!(rows.iter().any(|row| {
|
|
1562
1356
|
row.snapshot_content.as_deref()
|
|
1563
|
-
== Some("{\"key\":\"shared-entity\",\"value\":\"
|
|
1357
|
+
== Some("{\"key\":\"shared-entity\",\"value\":\"base\"}")
|
|
1564
1358
|
}));
|
|
1565
1359
|
}
|
|
1566
1360
|
|
|
@@ -1576,13 +1370,13 @@ mod tests {
|
|
|
1576
1370
|
.expect("staging rows should succeed");
|
|
1577
1371
|
|
|
1578
1372
|
let drained = staged_writes.drain().expect("drain should succeed");
|
|
1579
|
-
let
|
|
1580
|
-
.
|
|
1373
|
+
let change_refs = drained
|
|
1374
|
+
.commit_change_refs_by_branch
|
|
1581
1375
|
.get("global")
|
|
1582
|
-
.expect("global commit
|
|
1583
|
-
assert_eq!(
|
|
1584
|
-
assert_eq!(
|
|
1585
|
-
assert_eq!(
|
|
1376
|
+
.expect("global commit change_refs should exist");
|
|
1377
|
+
assert_eq!(change_refs.commit_id, "test-uuid-1");
|
|
1378
|
+
assert_eq!(change_refs.commit_change_id, "test-uuid-2");
|
|
1379
|
+
assert_eq!(change_refs.created_at, "test-timestamp-1");
|
|
1586
1380
|
}
|
|
1587
1381
|
|
|
1588
1382
|
#[tokio::test]
|
|
@@ -1604,9 +1398,9 @@ mod tests {
|
|
|
1604
1398
|
);
|
|
1605
1399
|
assert_eq!(
|
|
1606
1400
|
drained
|
|
1607
|
-
.
|
|
1401
|
+
.commit_change_refs_by_branch
|
|
1608
1402
|
.get("global")
|
|
1609
|
-
.expect("global commit
|
|
1403
|
+
.expect("global commit change_refs should exist")
|
|
1610
1404
|
.commit_id,
|
|
1611
1405
|
"test-uuid-1"
|
|
1612
1406
|
);
|
|
@@ -1645,7 +1439,7 @@ mod tests {
|
|
|
1645
1439
|
PreparedStateRow {
|
|
1646
1440
|
schema_plan_id: SchemaPlanId::for_test(0),
|
|
1647
1441
|
facts: crate::transaction::types::PreparedRowFacts::default(),
|
|
1648
|
-
|
|
1442
|
+
entity_pk: crate::entity_pk::EntityPk::single(key),
|
|
1649
1443
|
schema_key: "lix_key_value".to_string(),
|
|
1650
1444
|
file_id: None,
|
|
1651
1445
|
snapshot: Some(snapshot),
|
|
@@ -1657,7 +1451,7 @@ mod tests {
|
|
|
1657
1451
|
change_id: None,
|
|
1658
1452
|
commit_id: None,
|
|
1659
1453
|
untracked: true,
|
|
1660
|
-
|
|
1454
|
+
branch_id: "global".to_string(),
|
|
1661
1455
|
}
|
|
1662
1456
|
}
|
|
1663
1457
|
|
|
@@ -1670,8 +1464,8 @@ mod tests {
|
|
|
1670
1464
|
fn exact_request_for_key(key: &str) -> LiveStateRowRequest {
|
|
1671
1465
|
LiveStateRowRequest {
|
|
1672
1466
|
schema_key: "lix_key_value".to_string(),
|
|
1673
|
-
|
|
1674
|
-
|
|
1467
|
+
branch_id: "global".to_string(),
|
|
1468
|
+
entity_pk: crate::entity_pk::EntityPk::single(key),
|
|
1675
1469
|
file_id: NullableKeyFilter::Null,
|
|
1676
1470
|
}
|
|
1677
1471
|
}
|
|
@@ -1680,8 +1474,8 @@ mod tests {
|
|
|
1680
1474
|
LiveStateScanRequest {
|
|
1681
1475
|
filter: LiveStateFilter {
|
|
1682
1476
|
schema_keys: vec!["lix_key_value".to_string()],
|
|
1683
|
-
|
|
1684
|
-
|
|
1477
|
+
entity_pks: vec![crate::entity_pk::EntityPk::single(key)],
|
|
1478
|
+
branch_ids: vec!["global".to_string()],
|
|
1685
1479
|
file_ids: vec![NullableKeyFilter::Null],
|
|
1686
1480
|
include_tombstones,
|
|
1687
1481
|
..LiveStateFilter::default()
|
|
@@ -1694,7 +1488,7 @@ mod tests {
|
|
|
1694
1488
|
fn with_schema(self, schema_key: &str) -> Self;
|
|
1695
1489
|
fn with_file_id(self, file_id: &str) -> Self;
|
|
1696
1490
|
fn with_tracked(self) -> Self;
|
|
1697
|
-
fn
|
|
1491
|
+
fn with_branch(self, branch_id: &str) -> Self;
|
|
1698
1492
|
fn with_change_id(self, change_id: &str) -> Self;
|
|
1699
1493
|
}
|
|
1700
1494
|
|
|
@@ -1717,9 +1511,9 @@ mod tests {
|
|
|
1717
1511
|
self
|
|
1718
1512
|
}
|
|
1719
1513
|
|
|
1720
|
-
fn
|
|
1721
|
-
self.
|
|
1722
|
-
self.global =
|
|
1514
|
+
fn with_branch(mut self, branch_id: &str) -> Self {
|
|
1515
|
+
self.branch_id = branch_id.to_string();
|
|
1516
|
+
self.global = branch_id == GLOBAL_BRANCH_ID;
|
|
1723
1517
|
self
|
|
1724
1518
|
}
|
|
1725
1519
|
|