@lix-js/sdk 0.6.0-preview.3 → 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.
Files changed (235) hide show
  1. package/README.md +1 -1
  2. package/SKILL.md +105 -65
  3. package/dist/engine-wasm/index.js +4 -4
  4. package/dist/engine-wasm/wasm/lix_engine.d.ts +30 -6
  5. package/dist/engine-wasm/wasm/lix_engine.js +187 -117
  6. package/dist/engine-wasm/wasm/lix_engine.wasm +0 -0
  7. package/dist/engine-wasm/wasm/lix_engine.wasm.d.ts +14 -8
  8. package/dist/generated/builtin-schemas.d.ts +69 -69
  9. package/dist/generated/builtin-schemas.js +94 -94
  10. package/dist/open-lix.d.ts +42 -28
  11. package/dist/open-lix.js +49 -10
  12. package/dist/sqlite/index.js +86 -30
  13. package/dist-engine-src/README.md +3 -3
  14. package/dist-engine-src/src/backend/capabilities.rs +67 -0
  15. package/dist-engine-src/src/backend/conformance/baseline.rs +1127 -0
  16. package/dist-engine-src/src/backend/conformance/factory.rs +93 -0
  17. package/dist-engine-src/src/backend/conformance/failure_tests.rs +608 -0
  18. package/dist-engine-src/src/backend/conformance/fixtures.rs +26 -0
  19. package/dist-engine-src/src/backend/conformance/mod.rs +75 -0
  20. package/dist-engine-src/src/backend/conformance/model.rs +28 -0
  21. package/dist-engine-src/src/backend/conformance/model_based.rs +257 -0
  22. package/dist-engine-src/src/backend/conformance/persistence.rs +204 -0
  23. package/dist-engine-src/src/backend/conformance/projection.rs +21 -0
  24. package/dist-engine-src/src/backend/conformance/pushdown.rs +24 -0
  25. package/dist-engine-src/src/backend/conformance/runner.rs +90 -0
  26. package/dist-engine-src/src/backend/conformance/scan.rs +24 -0
  27. package/dist-engine-src/src/backend/conformance/write.rs +16 -0
  28. package/dist-engine-src/src/backend/error.rs +94 -0
  29. package/dist-engine-src/src/backend/in_memory.rs +670 -0
  30. package/dist-engine-src/src/backend/mod.rs +36 -9
  31. package/dist-engine-src/src/backend/predicate.rs +80 -0
  32. package/dist-engine-src/src/backend/traits.rs +260 -0
  33. package/dist-engine-src/src/backend/types.rs +224 -81
  34. package/dist-engine-src/src/binary_cas/context.rs +8 -8
  35. package/dist-engine-src/src/binary_cas/kv.rs +234 -259
  36. package/dist-engine-src/src/{version → branch}/context.rs +12 -12
  37. package/dist-engine-src/src/branch/lifecycle.rs +221 -0
  38. package/dist-engine-src/src/branch/mod.rs +13 -0
  39. package/dist-engine-src/src/branch/refs.rs +321 -0
  40. package/dist-engine-src/src/branch/stage_rows.rs +67 -0
  41. package/dist-engine-src/src/branch/types.rs +21 -0
  42. package/dist-engine-src/src/catalog/context.rs +18 -18
  43. package/dist-engine-src/src/catalog/snapshot.rs +8 -8
  44. package/dist-engine-src/src/changelog/bench_support.rs +785 -0
  45. package/dist-engine-src/src/changelog/change.rs +1 -0
  46. package/dist-engine-src/src/changelog/codec.rs +497 -0
  47. package/dist-engine-src/src/changelog/commit.rs +1 -0
  48. package/dist-engine-src/src/changelog/context.rs +1614 -0
  49. package/dist-engine-src/src/changelog/mod.rs +29 -0
  50. package/dist-engine-src/src/changelog/store.rs +163 -0
  51. package/dist-engine-src/src/changelog/test_support.rs +54 -0
  52. package/dist-engine-src/src/changelog/types.rs +213 -0
  53. package/dist-engine-src/src/commit_graph/context.rs +317 -274
  54. package/dist-engine-src/src/commit_graph/mod.rs +2 -4
  55. package/dist-engine-src/src/commit_graph/types.rs +22 -42
  56. package/dist-engine-src/src/commit_graph/walker.rs +133 -103
  57. package/dist-engine-src/src/common/error.rs +52 -18
  58. package/dist-engine-src/src/common/identity.rs +2 -2
  59. package/dist-engine-src/src/common/mod.rs +1 -1
  60. package/dist-engine-src/src/domain.rs +42 -46
  61. package/dist-engine-src/src/engine.rs +74 -96
  62. package/dist-engine-src/src/{entity_identity.rs → entity_pk.rs} +89 -92
  63. package/dist-engine-src/src/functions/context.rs +56 -52
  64. package/dist-engine-src/src/functions/state.rs +51 -52
  65. package/dist-engine-src/src/init.rs +288 -154
  66. package/dist-engine-src/src/json_store/context.rs +15 -266
  67. package/dist-engine-src/src/json_store/mod.rs +26 -0
  68. package/dist-engine-src/src/json_store/store.rs +103 -718
  69. package/dist-engine-src/src/json_store/types.rs +4 -9
  70. package/dist-engine-src/src/lib.rs +49 -19
  71. package/dist-engine-src/src/live_state/context.rs +654 -790
  72. package/dist-engine-src/src/live_state/mod.rs +9 -3
  73. package/dist-engine-src/src/live_state/overlay.rs +4 -4
  74. package/dist-engine-src/src/live_state/types.rs +30 -21
  75. package/dist-engine-src/src/live_state/visibility.rs +514 -71
  76. package/dist-engine-src/src/plugin/install.rs +48 -48
  77. package/dist-engine-src/src/plugin/manifest.rs +7 -7
  78. package/dist-engine-src/src/plugin/materializer.rs +0 -275
  79. package/dist-engine-src/src/plugin/plugin_manifest.json +4 -3
  80. package/dist-engine-src/src/schema/builtin/lix_binary_blob_ref.json +2 -2
  81. package/dist-engine-src/src/schema/builtin/lix_branch_descriptor.json +34 -0
  82. package/dist-engine-src/src/schema/builtin/lix_branch_ref.json +48 -0
  83. package/dist-engine-src/src/schema/builtin/lix_change.json +3 -3
  84. package/dist-engine-src/src/schema/builtin/lix_commit.json +1 -1
  85. package/dist-engine-src/src/schema/builtin/lix_label_assignment.json +6 -6
  86. package/dist-engine-src/src/schema/builtin/mod.rs +18 -20
  87. package/dist-engine-src/src/schema/compatibility.rs +11 -11
  88. package/dist-engine-src/src/schema/definition.json +2 -2
  89. package/dist-engine-src/src/schema/definition.rs +5 -5
  90. package/dist-engine-src/src/schema/key.rs +3 -3
  91. package/dist-engine-src/src/schema/mod.rs +1 -1
  92. package/dist-engine-src/src/schema/tests.rs +18 -18
  93. package/dist-engine-src/src/session/context.rs +819 -124
  94. package/dist-engine-src/src/session/create_branch.rs +94 -0
  95. package/dist-engine-src/src/session/execute.rs +260 -57
  96. package/dist-engine-src/src/session/merge/analysis.rs +9 -3
  97. package/dist-engine-src/src/session/merge/{version.rs → branch.rs} +119 -129
  98. package/dist-engine-src/src/session/merge/conflicts.rs +2 -2
  99. package/dist-engine-src/src/session/merge/mod.rs +5 -6
  100. package/dist-engine-src/src/session/merge/stats.rs +7 -11
  101. package/dist-engine-src/src/session/mod.rs +19 -16
  102. package/dist-engine-src/src/session/switch_branch.rs +113 -0
  103. package/dist-engine-src/src/session/transaction.rs +557 -0
  104. package/dist-engine-src/src/sql2/bind/classify.rs +102 -0
  105. package/dist-engine-src/src/sql2/bind/error.rs +5 -0
  106. package/dist-engine-src/src/sql2/bind/expr.rs +29 -0
  107. package/dist-engine-src/src/sql2/bind/mod.rs +12 -0
  108. package/dist-engine-src/src/sql2/{udfs/public_call.rs → bind/public_udf.rs} +98 -3
  109. package/dist-engine-src/src/sql2/bind/read.rs +65 -0
  110. package/dist-engine-src/src/sql2/bind/statement.rs +2236 -0
  111. package/dist-engine-src/src/sql2/bind/table.rs +273 -0
  112. package/dist-engine-src/src/sql2/bind/write.rs +86 -0
  113. package/dist-engine-src/src/sql2/branch_scope.rs +436 -0
  114. package/dist-engine-src/src/sql2/catalog/capability.rs +20 -0
  115. package/dist-engine-src/src/sql2/catalog/entity_surface.rs +296 -0
  116. package/dist-engine-src/src/sql2/catalog/mod.rs +15 -0
  117. package/dist-engine-src/src/sql2/catalog/registry.rs +556 -0
  118. package/dist-engine-src/src/sql2/catalog/schema.rs +88 -0
  119. package/dist-engine-src/src/sql2/catalog/surface.rs +41 -0
  120. package/dist-engine-src/src/sql2/change_materialization.rs +122 -0
  121. package/dist-engine-src/src/sql2/context.rs +36 -30
  122. package/dist-engine-src/src/sql2/error.rs +4 -5
  123. package/dist-engine-src/src/sql2/exec/bound_public_write.rs +1593 -0
  124. package/dist-engine-src/src/sql2/exec/datafusion.rs +5266 -0
  125. package/dist-engine-src/src/sql2/exec/fast_write.rs +82 -0
  126. package/dist-engine-src/src/sql2/exec/mod.rs +24 -0
  127. package/dist-engine-src/src/sql2/exec/write.rs +661 -0
  128. package/dist-engine-src/src/sql2/filesystem_planner.rs +72 -77
  129. package/dist-engine-src/src/sql2/filesystem_visibility.rs +21 -21
  130. package/dist-engine-src/src/sql2/history_projection.rs +8 -8
  131. package/dist-engine-src/src/sql2/history_route.rs +35 -31
  132. package/dist-engine-src/src/sql2/mod.rs +30 -24
  133. package/dist-engine-src/src/sql2/optimize/datafusion.rs +1 -0
  134. package/dist-engine-src/src/sql2/optimize/mod.rs +2 -0
  135. package/dist-engine-src/src/sql2/optimize/simple_write.rs +116 -0
  136. package/dist-engine-src/src/sql2/parse/mod.rs +69 -0
  137. package/dist-engine-src/src/sql2/parse/normalize.rs +1 -0
  138. package/dist-engine-src/src/sql2/plan/branch_scope.rs +24 -0
  139. package/dist-engine-src/src/sql2/plan/mod.rs +5 -0
  140. package/dist-engine-src/src/sql2/plan/predicate.rs +22 -0
  141. package/dist-engine-src/src/sql2/plan/write.rs +147 -0
  142. package/dist-engine-src/src/sql2/predicate_typecheck.rs +258 -0
  143. package/dist-engine-src/src/sql2/{version_provider.rs → providers/branch.rs} +218 -214
  144. package/dist-engine-src/src/sql2/{change_provider.rs → providers/change.rs} +156 -42
  145. package/dist-engine-src/src/sql2/{directory_provider.rs → providers/directory.rs} +291 -322
  146. package/dist-engine-src/src/sql2/{directory_history_provider.rs → providers/directory_history.rs} +56 -42
  147. package/dist-engine-src/src/sql2/providers/entity.rs +1484 -0
  148. package/dist-engine-src/src/sql2/{entity_history_provider.rs → providers/entity_history.rs} +43 -31
  149. package/dist-engine-src/src/sql2/{file_provider.rs → providers/file.rs} +323 -316
  150. package/dist-engine-src/src/sql2/{file_history_provider.rs → providers/file_history.rs} +60 -46
  151. package/dist-engine-src/src/sql2/{history_provider.rs → providers/history.rs} +46 -32
  152. package/dist-engine-src/src/sql2/{lix_state_provider.rs → providers/lix_state.rs} +359 -329
  153. package/dist-engine-src/src/sql2/providers/mod.rs +508 -0
  154. package/dist-engine-src/src/sql2/read_only.rs +2 -2
  155. package/dist-engine-src/src/sql2/session.rs +47 -96
  156. package/dist-engine-src/src/sql2/storage/constraints.rs +1 -0
  157. package/dist-engine-src/src/sql2/storage/mod.rs +1 -0
  158. package/dist-engine-src/src/sql2/test_support/differential.rs +712 -0
  159. package/dist-engine-src/src/sql2/test_support/generators.rs +354 -0
  160. package/dist-engine-src/src/sql2/test_support/mod.rs +2 -0
  161. package/dist-engine-src/src/sql2/udfs/{lix_active_version_commit_id.rs → lix_active_branch_commit_id.rs} +7 -7
  162. package/dist-engine-src/src/sql2/udfs/mod.rs +3 -6
  163. package/dist-engine-src/src/sql2/write_normalization.rs +45 -22
  164. package/dist-engine-src/src/storage/conformance.rs +399 -0
  165. package/dist-engine-src/src/storage/context.rs +552 -288
  166. package/dist-engine-src/src/storage/mod.rs +48 -10
  167. package/dist-engine-src/src/storage/point.rs +440 -0
  168. package/dist-engine-src/src/storage/read_scope.rs +43 -64
  169. package/dist-engine-src/src/storage/reader.rs +867 -0
  170. package/dist-engine-src/src/storage/scan.rs +784 -0
  171. package/dist-engine-src/src/storage/spaces.rs +236 -0
  172. package/dist-engine-src/src/storage/stats.rs +80 -0
  173. package/dist-engine-src/src/storage/write_set.rs +962 -0
  174. package/dist-engine-src/src/storage_bench.rs +136 -4828
  175. package/dist-engine-src/src/test_support.rs +360 -138
  176. package/dist-engine-src/src/tracked_state/bench_support.rs +394 -0
  177. package/dist-engine-src/src/tracked_state/codec.rs +155 -1057
  178. package/dist-engine-src/src/tracked_state/commit_root_rebuild.rs +358 -0
  179. package/dist-engine-src/src/tracked_state/context.rs +1927 -993
  180. package/dist-engine-src/src/tracked_state/diff.rs +1715 -261
  181. package/dist-engine-src/src/tracked_state/merge.rs +74 -88
  182. package/dist-engine-src/src/tracked_state/mod.rs +19 -16
  183. package/dist-engine-src/src/tracked_state/{materialization.rs → row_materialization.rs} +50 -178
  184. package/dist-engine-src/src/tracked_state/storage.rs +243 -191
  185. package/dist-engine-src/src/tracked_state/tree.rs +247 -371
  186. package/dist-engine-src/src/tracked_state/types.rs +49 -42
  187. package/dist-engine-src/src/transaction/bench_support.rs +407 -0
  188. package/dist-engine-src/src/transaction/commit.rs +821 -713
  189. package/dist-engine-src/src/transaction/context.rs +705 -600
  190. package/dist-engine-src/src/transaction/mod.rs +13 -2
  191. package/dist-engine-src/src/transaction/normalization.rs +63 -76
  192. package/dist-engine-src/src/transaction/prep.rs +13 -13
  193. package/dist-engine-src/src/transaction/schema_resolver.rs +19 -5
  194. package/dist-engine-src/src/transaction/staging.rs +228 -434
  195. package/dist-engine-src/src/transaction/types.rs +41 -98
  196. package/dist-engine-src/src/transaction/validation.rs +382 -446
  197. package/dist-engine-src/src/untracked_state/codec.rs +337 -29
  198. package/dist-engine-src/src/untracked_state/context.rs +7 -7
  199. package/dist-engine-src/src/untracked_state/materialization.rs +2 -2
  200. package/dist-engine-src/src/untracked_state/mod.rs +1 -1
  201. package/dist-engine-src/src/untracked_state/storage.rs +659 -157
  202. package/dist-engine-src/src/untracked_state/types.rs +21 -21
  203. package/package.json +71 -68
  204. package/dist-engine-src/src/backend/kv.rs +0 -358
  205. package/dist-engine-src/src/backend/testing.rs +0 -658
  206. package/dist-engine-src/src/commit_store/codec.rs +0 -887
  207. package/dist-engine-src/src/commit_store/context.rs +0 -944
  208. package/dist-engine-src/src/commit_store/materialization.rs +0 -84
  209. package/dist-engine-src/src/commit_store/mod.rs +0 -16
  210. package/dist-engine-src/src/commit_store/storage.rs +0 -600
  211. package/dist-engine-src/src/commit_store/types.rs +0 -215
  212. package/dist-engine-src/src/schema/builtin/lix_version_descriptor.json +0 -34
  213. package/dist-engine-src/src/schema/builtin/lix_version_ref.json +0 -48
  214. package/dist-engine-src/src/session/create_version.rs +0 -88
  215. package/dist-engine-src/src/session/merge/apply.rs +0 -23
  216. package/dist-engine-src/src/session/optimization9_sql2_bench.rs +0 -100
  217. package/dist-engine-src/src/session/switch_version.rs +0 -109
  218. package/dist-engine-src/src/sql2/classify.rs +0 -182
  219. package/dist-engine-src/src/sql2/entity_provider.rs +0 -3211
  220. package/dist-engine-src/src/sql2/execute.rs +0 -3440
  221. package/dist-engine-src/src/sql2/public_bind/assignment.rs +0 -46
  222. package/dist-engine-src/src/sql2/public_bind/capability.rs +0 -41
  223. package/dist-engine-src/src/sql2/public_bind/dml.rs +0 -166
  224. package/dist-engine-src/src/sql2/public_bind/mod.rs +0 -25
  225. package/dist-engine-src/src/sql2/public_bind/table.rs +0 -168
  226. package/dist-engine-src/src/sql2/version_scope.rs +0 -394
  227. package/dist-engine-src/src/storage/types.rs +0 -501
  228. package/dist-engine-src/src/tracked_state/by_file_index.rs +0 -98
  229. package/dist-engine-src/src/tracked_state/materializer.rs +0 -488
  230. package/dist-engine-src/src/transaction/live_state_overlay.rs +0 -35
  231. package/dist-engine-src/src/version/lifecycle.rs +0 -221
  232. package/dist-engine-src/src/version/mod.rs +0 -13
  233. package/dist-engine-src/src/version/refs.rs +0 -330
  234. package/dist-engine-src/src/version/stage_rows.rs +0 -67
  235. package/dist-engine-src/src/version/types.rs +0 -21
@@ -1,236 +1,97 @@
1
1
  use std::collections::HashMap;
2
2
 
3
- use crate::json_store::JsonStoreContext;
4
- use crate::storage::{KvGetGroup, KvGetRequest, StorageReader, StorageWriteSet};
3
+ use crate::storage::{PointReadPlan, StorageRead, StorageSpace, StorageWriteSet};
4
+ use crate::storage::{
5
+ StorageGetOptions, StorageKey, StorageProjectedValue, StorageSpaceId, StorageValue,
6
+ };
5
7
  use crate::tracked_state::codec::PendingChunkWrite;
6
8
  use crate::tracked_state::types::{
7
- TrackedStateDeltaEntry, TrackedStateDeltaRef, TrackedStateRootId, TRACKED_STATE_HASH_BYTES,
9
+ TrackedStateCommitRoot, TrackedStateCommitRootParent, TrackedStateRootId,
10
+ TRACKED_STATE_HASH_BYTES,
8
11
  };
9
12
  use crate::LixError;
13
+ use bytes::Bytes;
14
+
15
+ pub(crate) const TRACKED_STATE_TREE_CHUNK_NAMESPACE: &'static str = "tracked_state.tree_chunk";
16
+ pub(crate) const TRACKED_STATE_COMMIT_ROOT_NAMESPACE: &'static str = "tracked_state.commit_root";
17
+ pub(crate) const TRACKED_STATE_TREE_CHUNK_SPACE: StorageSpace = StorageSpace::new(
18
+ StorageSpaceId(0x0004_0001),
19
+ TRACKED_STATE_TREE_CHUNK_NAMESPACE,
20
+ );
21
+ pub(crate) const TRACKED_STATE_COMMIT_ROOT_SPACE: StorageSpace = StorageSpace::new(
22
+ StorageSpaceId(0x0004_0004),
23
+ TRACKED_STATE_COMMIT_ROOT_NAMESPACE,
24
+ );
10
25
 
11
- pub(crate) const TRACKED_STATE_CHUNK_NAMESPACE: &'static str = "tracked_state.tree.chunk";
12
- pub(crate) const TRACKED_STATE_ROOT_NAMESPACE: &'static str = "tracked_state.tree.root";
13
- pub(crate) const TRACKED_STATE_BY_FILE_ROOT_NAMESPACE: &'static str =
14
- "tracked_state.tree.root.by_file";
15
- pub(crate) const TRACKED_STATE_DELTA_PACK_NAMESPACE: &'static str = "tracked_state.delta_pack";
26
+ const COMMIT_ROOT_MAGIC: &[u8; 5] = b"LXTR1";
16
27
 
17
28
  async fn get_one(
18
- store: &mut (impl StorageReader + ?Sized),
19
- namespace: &str,
29
+ store: &(impl StorageRead + ?Sized),
30
+ space: StorageSpace,
20
31
  key: Vec<u8>,
21
32
  ) -> Result<Option<Vec<u8>>, LixError> {
22
- Ok(store
23
- .get_values(KvGetRequest {
24
- groups: vec![KvGetGroup {
25
- namespace: namespace.to_string(),
26
- keys: vec![key],
27
- }],
28
- })
29
- .await?
30
- .groups
33
+ let result = PointReadPlan::new(space, &[StorageKey(Bytes::from(key))])
34
+ .materialize(store, StorageGetOptions::default())?;
35
+ Ok(result
36
+ .value
31
37
  .into_iter()
32
38
  .next()
33
- .and_then(|group| group.single_value_owned()))
39
+ .flatten()
40
+ .and_then(full_value))
34
41
  }
35
42
 
36
43
  pub(crate) async fn load_root(
37
- store: &mut (impl StorageReader + ?Sized),
44
+ store: &(impl StorageRead + ?Sized),
38
45
  commit_id: &str,
39
46
  ) -> Result<Option<TrackedStateRootId>, LixError> {
40
- let Some(bytes) = get_one(
41
- store,
42
- TRACKED_STATE_ROOT_NAMESPACE,
43
- commit_id.as_bytes().to_vec(),
44
- )
45
- .await?
46
- else {
47
- return Ok(None);
48
- };
49
- TrackedStateRootId::from_slice(&bytes).map(Some)
50
- }
51
-
52
- pub(crate) fn stage_root(
53
- writes: &mut StorageWriteSet,
54
- commit_id: &str,
55
- root_id: &TrackedStateRootId,
56
- ) {
57
- writes.put(
58
- TRACKED_STATE_ROOT_NAMESPACE,
59
- commit_id.as_bytes().to_vec(),
60
- root_id.as_bytes().to_vec(),
61
- );
47
+ Ok(load_commit_root(store, commit_id)
48
+ .await?
49
+ .map(|metadata| metadata.root_id))
62
50
  }
63
51
 
64
- pub(crate) async fn load_by_file_root(
65
- store: &mut (impl StorageReader + ?Sized),
52
+ pub(crate) async fn load_commit_root(
53
+ store: &(impl StorageRead + ?Sized),
66
54
  commit_id: &str,
67
- ) -> Result<Option<TrackedStateRootId>, LixError> {
55
+ ) -> Result<Option<TrackedStateCommitRoot>, LixError> {
68
56
  let Some(bytes) = get_one(
69
57
  store,
70
- TRACKED_STATE_BY_FILE_ROOT_NAMESPACE,
58
+ TRACKED_STATE_COMMIT_ROOT_SPACE,
71
59
  commit_id.as_bytes().to_vec(),
72
60
  )
73
61
  .await?
74
62
  else {
75
63
  return Ok(None);
76
64
  };
77
- TrackedStateRootId::from_slice(&bytes).map(Some)
78
- }
79
-
80
- pub(crate) fn stage_by_file_root(
81
- writes: &mut StorageWriteSet,
82
- commit_id: &str,
83
- root_id: &TrackedStateRootId,
84
- ) {
85
- writes.put(
86
- TRACKED_STATE_BY_FILE_ROOT_NAMESPACE,
87
- commit_id.as_bytes().to_vec(),
88
- root_id.as_bytes().to_vec(),
89
- );
90
- }
91
-
92
- pub(crate) async fn load_delta_pack(
93
- store: &mut (impl StorageReader + ?Sized),
94
- commit_id: &str,
95
- ) -> Result<Option<Vec<TrackedStateDeltaEntry>>, LixError> {
96
- let json_store = JsonStoreContext::new();
97
- let result = store
98
- .get_values(KvGetRequest {
99
- groups: vec![
100
- KvGetGroup {
101
- namespace: TRACKED_STATE_DELTA_PACK_NAMESPACE.to_string(),
102
- keys: vec![commit_id.as_bytes().to_vec()],
103
- },
104
- json_store.commit_pack_get_group(commit_id, 0),
105
- ],
106
- })
107
- .await?;
108
- let mut groups = result.groups.into_iter();
109
- let delta_group = groups.next().ok_or_else(|| {
110
- LixError::new(
111
- LixError::CODE_INTERNAL_ERROR,
112
- "tracked-state delta pack load returned no delta result group",
113
- )
114
- })?;
115
- let json_pack_group = groups.next().ok_or_else(|| {
116
- LixError::new(
117
- LixError::CODE_INTERNAL_ERROR,
118
- "tracked-state delta pack load returned no JSON pack result group",
119
- )
120
- })?;
121
- let Some(bytes) = delta_group.single_value_owned() else {
122
- return Ok(None);
123
- };
124
- let pack_refs = if crate::tracked_state::codec::delta_pack_uses_json_pack_indexes(&bytes)? {
125
- json_pack_group
126
- .single_value_owned()
127
- .map(|bytes| json_store.decode_pack_refs(&bytes))
128
- .transpose()?
129
- } else {
130
- None
131
- };
132
- let (stored_commit_id, entries) =
133
- crate::tracked_state::codec::decode_delta_pack(&bytes, pack_refs.as_deref())?;
134
- if stored_commit_id != commit_id {
65
+ let metadata = decode_commit_root(&bytes)?;
66
+ if metadata.commit_id != commit_id {
135
67
  return Err(LixError::new(
136
68
  LixError::CODE_INTERNAL_ERROR,
137
69
  format!(
138
- "tracked-state delta pack identity mismatch: expected '{commit_id}', got '{stored_commit_id}'"
70
+ "tracked_state commit_root key for commit '{commit_id}' contains root metadata for commit '{}'",
71
+ metadata.commit_id
139
72
  ),
140
73
  ));
141
74
  }
142
- Ok(Some(entries))
75
+ Ok(Some(metadata))
143
76
  }
144
77
 
145
- pub(crate) async fn delta_pack_exists(
146
- store: &mut (impl StorageReader + ?Sized),
147
- commit_id: &str,
148
- ) -> Result<bool, LixError> {
149
- let result = store
150
- .exists_many(KvGetRequest {
151
- groups: vec![KvGetGroup {
152
- namespace: TRACKED_STATE_DELTA_PACK_NAMESPACE.to_string(),
153
- keys: vec![commit_id.as_bytes().to_vec()],
154
- }],
155
- })
156
- .await?;
157
- let group = result.groups.into_iter().next().ok_or_else(|| {
158
- LixError::new(
159
- LixError::CODE_INTERNAL_ERROR,
160
- "tracked-state delta pack existence check returned no result group",
161
- )
162
- })?;
163
- group.exists.into_iter().next().ok_or_else(|| {
164
- LixError::new(
165
- LixError::CODE_INTERNAL_ERROR,
166
- "tracked-state delta pack existence check returned no result",
167
- )
168
- })
169
- }
170
-
171
- pub(crate) fn stage_delta_pack_refs(
172
- writes: &mut StorageWriteSet,
173
- commit_id: &str,
174
- deltas: &[TrackedStateDeltaRef<'_>],
175
- ) -> Result<(), LixError> {
176
- writes.put(
177
- TRACKED_STATE_DELTA_PACK_NAMESPACE,
178
- commit_id.as_bytes().to_vec(),
179
- crate::tracked_state::codec::encode_delta_pack_refs(commit_id, deltas)?,
180
- );
181
- Ok(())
182
- }
183
-
184
- pub(crate) struct DeltaJsonPackIndexesRef<'a> {
185
- pub(crate) commit_id: &'a str,
186
- pub(crate) pack_id: u32,
187
- pub(crate) indexes: &'a std::collections::HashMap<[u8; TRACKED_STATE_HASH_BYTES], usize>,
188
- }
189
-
190
- pub(crate) fn stage_delta_pack_refs_with_json_pack_indexes(
78
+ pub(crate) fn stage_commit_root(
191
79
  writes: &mut StorageWriteSet,
192
- commit_id: &str,
193
- deltas: &[TrackedStateDeltaRef<'_>],
194
- json_pack_indexes: DeltaJsonPackIndexesRef<'_>,
80
+ metadata: &TrackedStateCommitRoot,
195
81
  ) -> Result<(), LixError> {
196
- if json_pack_indexes.commit_id != commit_id {
197
- return Err(LixError::new(
198
- LixError::CODE_INTERNAL_ERROR,
199
- format!(
200
- "tracked-state delta JSON pack indexes for '{}' cannot encode delta pack '{}'",
201
- json_pack_indexes.commit_id, commit_id
202
- ),
203
- ));
204
- }
205
- if json_pack_indexes.pack_id != 0 {
206
- return Err(LixError::new(
207
- LixError::CODE_INTERNAL_ERROR,
208
- format!(
209
- "tracked-state delta JSON pack indexes only support pack 0, got pack {}",
210
- json_pack_indexes.pack_id
211
- ),
212
- ));
213
- }
214
- if json_pack_indexes.indexes.is_empty() {
215
- return stage_delta_pack_refs(writes, commit_id, deltas);
216
- }
217
82
  writes.put(
218
- TRACKED_STATE_DELTA_PACK_NAMESPACE,
219
- commit_id.as_bytes().to_vec(),
220
- crate::tracked_state::codec::encode_delta_pack_refs_with_json_pack_indexes(
221
- commit_id,
222
- deltas,
223
- Some(json_pack_indexes.indexes),
224
- )?,
83
+ TRACKED_STATE_COMMIT_ROOT_SPACE,
84
+ key(metadata.commit_id.as_bytes().to_vec()),
85
+ value(encode_commit_root(metadata)?),
225
86
  );
226
87
  Ok(())
227
88
  }
228
89
 
229
90
  pub(crate) async fn read_chunk(
230
- store: &mut (impl StorageReader + ?Sized),
91
+ store: &(impl StorageRead + ?Sized),
231
92
  hash: &[u8; TRACKED_STATE_HASH_BYTES],
232
93
  ) -> Result<Option<Vec<u8>>, LixError> {
233
- get_one(store, TRACKED_STATE_CHUNK_NAMESPACE, hash.to_vec()).await
94
+ get_one(store, TRACKED_STATE_TREE_CHUNK_SPACE, hash.to_vec()).await
234
95
  }
235
96
 
236
97
  pub(crate) fn verify_chunk_hash(
@@ -250,14 +111,13 @@ pub(crate) fn verify_chunk_hash(
250
111
  pub(crate) fn stage_chunks(writes: &mut StorageWriteSet, chunks: &[PendingChunkWrite]) {
251
112
  for chunk in chunks {
252
113
  writes.put(
253
- TRACKED_STATE_CHUNK_NAMESPACE,
254
- chunk.hash.to_vec(),
255
- chunk.data.clone(),
114
+ TRACKED_STATE_TREE_CHUNK_SPACE,
115
+ key(chunk.hash.to_vec()),
116
+ value(chunk.data.clone()),
256
117
  );
257
118
  }
258
119
  }
259
120
 
260
- #[allow(dead_code)]
261
121
  #[derive(Debug, Default)]
262
122
  pub(crate) struct TrackedStateChunkOverlay {
263
123
  chunks: HashMap<[u8; TRACKED_STATE_HASH_BYTES], Vec<u8>>,
@@ -270,7 +130,7 @@ impl TrackedStateChunkOverlay {
270
130
 
271
131
  pub(crate) async fn read_chunk(
272
132
  &self,
273
- store: &mut (impl StorageReader + ?Sized),
133
+ store: &(impl StorageRead + ?Sized),
274
134
  hash: &[u8; TRACKED_STATE_HASH_BYTES],
275
135
  ) -> Result<Option<Vec<u8>>, LixError> {
276
136
  if let Some(bytes) = self.chunks.get(hash) {
@@ -291,11 +151,203 @@ impl TrackedStateChunkOverlay {
291
151
  }
292
152
  }
293
153
 
154
+ fn key(bytes: Vec<u8>) -> StorageKey {
155
+ StorageKey(Bytes::from(bytes))
156
+ }
157
+
158
+ fn value(bytes: Vec<u8>) -> StorageValue {
159
+ StorageValue {
160
+ bytes: Bytes::from(bytes),
161
+ }
162
+ }
163
+
164
+ fn full_value(value: StorageProjectedValue) -> Option<Vec<u8>> {
165
+ match value {
166
+ StorageProjectedValue::FullValue(bytes) => Some(bytes.to_vec()),
167
+ StorageProjectedValue::KeyOnly => None,
168
+ }
169
+ }
170
+
171
+ fn encode_commit_root(metadata: &TrackedStateCommitRoot) -> Result<Vec<u8>, LixError> {
172
+ let mut out = Vec::new();
173
+ out.extend_from_slice(COMMIT_ROOT_MAGIC);
174
+ write_string(&mut out, &metadata.commit_id)?;
175
+ out.extend_from_slice(metadata.root_id.as_bytes());
176
+ write_u32(&mut out, metadata.parent_roots.len(), "parent root count")?;
177
+ for parent in &metadata.parent_roots {
178
+ write_string(&mut out, &parent.commit_id)?;
179
+ out.extend_from_slice(parent.root_id.as_bytes());
180
+ }
181
+ out.extend_from_slice(&metadata.changed_key_count.to_le_bytes());
182
+ out.extend_from_slice(&metadata.row_count_estimate.to_le_bytes());
183
+ out.extend_from_slice(&metadata.tree_height.to_le_bytes());
184
+ out.extend_from_slice(&metadata.primary_chunk_count.to_le_bytes());
185
+ out.extend_from_slice(&metadata.primary_chunk_bytes.to_le_bytes());
186
+ Ok(out)
187
+ }
188
+
189
+ fn decode_commit_root(bytes: &[u8]) -> Result<TrackedStateCommitRoot, LixError> {
190
+ let mut cursor = CommitRootCursor { bytes, offset: 0 };
191
+ cursor.expect_magic()?;
192
+ let commit_id = cursor.read_string("commit_id")?;
193
+ let root_id = cursor.read_root_id("root_id")?;
194
+ let parent_count = cursor.read_u32("parent root count")? as usize;
195
+ let mut parent_roots = Vec::with_capacity(parent_count);
196
+ for _ in 0..parent_count {
197
+ parent_roots.push(TrackedStateCommitRootParent {
198
+ commit_id: cursor.read_string("parent commit_id")?,
199
+ root_id: cursor.read_root_id("parent root_id")?,
200
+ });
201
+ }
202
+ let changed_key_count = cursor.read_u64("changed_key_count")?;
203
+ let row_count_estimate = cursor.read_u64("row_count_estimate")?;
204
+ let tree_height = cursor.read_u32("tree_height")?;
205
+ let primary_chunk_count = cursor.read_u64("primary_chunk_count")?;
206
+ let primary_chunk_bytes = cursor.read_u64("primary_chunk_bytes")?;
207
+ cursor.expect_end()?;
208
+ Ok(TrackedStateCommitRoot {
209
+ commit_id,
210
+ root_id,
211
+ parent_roots,
212
+ changed_key_count,
213
+ row_count_estimate,
214
+ tree_height,
215
+ primary_chunk_count,
216
+ primary_chunk_bytes,
217
+ })
218
+ }
219
+
220
+ fn write_string(out: &mut Vec<u8>, value: &str) -> Result<(), LixError> {
221
+ write_u32(out, value.len(), "string length")?;
222
+ out.extend_from_slice(value.as_bytes());
223
+ Ok(())
224
+ }
225
+
226
+ fn write_u32(out: &mut Vec<u8>, value: usize, field: &str) -> Result<(), LixError> {
227
+ let value = u32::try_from(value).map_err(|_| {
228
+ LixError::new(
229
+ LixError::CODE_INTERNAL_ERROR,
230
+ format!("tracked_state commit root metadata {field} exceeds u32"),
231
+ )
232
+ })?;
233
+ out.extend_from_slice(&value.to_le_bytes());
234
+ Ok(())
235
+ }
236
+
237
+ struct CommitRootCursor<'a> {
238
+ bytes: &'a [u8],
239
+ offset: usize,
240
+ }
241
+
242
+ impl CommitRootCursor<'_> {
243
+ fn expect_magic(&mut self) -> Result<(), LixError> {
244
+ let magic = self.read_exact(COMMIT_ROOT_MAGIC.len(), "magic")?;
245
+ if magic != COMMIT_ROOT_MAGIC {
246
+ return Err(LixError::new(
247
+ LixError::CODE_INTERNAL_ERROR,
248
+ "failed to decode tracked_state commit_root: bad magic",
249
+ ));
250
+ }
251
+ Ok(())
252
+ }
253
+
254
+ fn expect_end(&self) -> Result<(), LixError> {
255
+ if self.offset != self.bytes.len() {
256
+ return Err(LixError::new(
257
+ LixError::CODE_INTERNAL_ERROR,
258
+ "failed to decode tracked_state commit_root: trailing bytes",
259
+ ));
260
+ }
261
+ Ok(())
262
+ }
263
+
264
+ fn read_string(&mut self, field: &str) -> Result<String, LixError> {
265
+ let len = self.read_u32(field)? as usize;
266
+ let bytes = self.read_exact(len, field)?;
267
+ String::from_utf8(bytes.to_vec()).map_err(|error| {
268
+ LixError::new(
269
+ LixError::CODE_INTERNAL_ERROR,
270
+ format!("failed to decode tracked_state commit_root {field}: {error}"),
271
+ )
272
+ })
273
+ }
274
+
275
+ fn read_root_id(&mut self, field: &str) -> Result<TrackedStateRootId, LixError> {
276
+ let bytes = self.read_exact(TRACKED_STATE_HASH_BYTES, field)?;
277
+ TrackedStateRootId::from_slice(bytes)
278
+ }
279
+
280
+ fn read_u32(&mut self, field: &str) -> Result<u32, LixError> {
281
+ let bytes = self.read_exact(std::mem::size_of::<u32>(), field)?;
282
+ Ok(u32::from_le_bytes(bytes.try_into().expect("fixed u32")))
283
+ }
284
+
285
+ fn read_u64(&mut self, field: &str) -> Result<u64, LixError> {
286
+ let bytes = self.read_exact(std::mem::size_of::<u64>(), field)?;
287
+ Ok(u64::from_le_bytes(bytes.try_into().expect("fixed u64")))
288
+ }
289
+
290
+ fn read_exact(&mut self, len: usize, field: &str) -> Result<&[u8], LixError> {
291
+ let end = self.offset.checked_add(len).ok_or_else(|| {
292
+ LixError::new(
293
+ LixError::CODE_INTERNAL_ERROR,
294
+ format!("failed to decode tracked_state commit_root {field}: overflow"),
295
+ )
296
+ })?;
297
+ if end > self.bytes.len() {
298
+ return Err(LixError::new(
299
+ LixError::CODE_INTERNAL_ERROR,
300
+ format!("failed to decode tracked_state commit_root {field}: truncated"),
301
+ ));
302
+ }
303
+ let out = &self.bytes[self.offset..end];
304
+ self.offset = end;
305
+ Ok(out)
306
+ }
307
+ }
308
+
294
309
  #[cfg(test)]
295
310
  mod tests {
311
+ use std::collections::BTreeMap;
296
312
  use std::fs;
297
313
  use std::path::{Path, PathBuf};
298
314
 
315
+ use crate::binary_cas::kv::{
316
+ BINARY_CAS_CHUNK_SPACE, BINARY_CAS_MANIFEST_CHUNK_SPACE, BINARY_CAS_MANIFEST_SPACE,
317
+ };
318
+ use crate::changelog::{CHANGE_SPACE, COMMIT_CHANGE_REF_CHUNK_SPACE, COMMIT_SPACE};
319
+ use crate::json_store::store::JSON_SPACE;
320
+ use crate::untracked_state::storage::UNTRACKED_STATE_ROW_SPACE;
321
+
322
+ use super::{TRACKED_STATE_COMMIT_ROOT_SPACE, TRACKED_STATE_TREE_CHUNK_SPACE};
323
+
324
+ #[test]
325
+ fn native_storage_space_ids_are_unique_across_owner_layouts() {
326
+ let spaces = [
327
+ UNTRACKED_STATE_ROW_SPACE,
328
+ JSON_SPACE,
329
+ TRACKED_STATE_TREE_CHUNK_SPACE,
330
+ TRACKED_STATE_COMMIT_ROOT_SPACE,
331
+ BINARY_CAS_MANIFEST_SPACE,
332
+ BINARY_CAS_MANIFEST_CHUNK_SPACE,
333
+ BINARY_CAS_CHUNK_SPACE,
334
+ COMMIT_SPACE,
335
+ CHANGE_SPACE,
336
+ COMMIT_CHANGE_REF_CHUNK_SPACE,
337
+ ];
338
+ let mut seen = BTreeMap::new();
339
+ for space in spaces {
340
+ assert_eq!(
341
+ seen.insert(space.id, space.name),
342
+ None,
343
+ "storage space id {:?} is reused by {} and {}",
344
+ space.id,
345
+ seen.get(&space.id).copied().unwrap_or(space.name),
346
+ space.name
347
+ );
348
+ }
349
+ }
350
+
299
351
  #[test]
300
352
  fn production_tracked_state_sources_do_not_call_storage_batch_writer() {
301
353
  let tracked_state_dir = Path::new(env!("CARGO_MANIFEST_DIR")).join("src/tracked_state");