@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,14 +1,52 @@
1
+ //! Primary storage adapter.
2
+ //!
3
+ //! This module is the Lix-neutral layer between domain stores and
4
+ //! `backend`. Domain stores own schemas and key layouts; storage owns
5
+ //! shared scopes, batching, lowering, cursors, and adapter stats.
6
+ //!
7
+ //! Storage is intentionally below the session transaction lifecycle. Direct
8
+ //! users of `StorageContext` or `StorageWriteSet` bypass session close/commit
9
+ //! accounting and must serialize durable writes themselves.
10
+
1
11
  mod context;
12
+ mod point;
2
13
  mod read_scope;
3
- mod types;
4
-
5
- pub(crate) use context::StorageContext;
6
- pub(crate) use read_scope::{ScopedStorageReader, StorageReadScope};
7
- pub(crate) use types::{
8
- KvEntryPage, KvExistsBatch, KvExistsGroup, KvGetGroup, KvGetRequest, KvKeyPage, KvScanRange,
9
- KvScanRequest, KvValueBatch, KvValueGroup, KvValuePage, KvWriteStats, StorageReadTransaction,
10
- StorageReader, StorageWriteSet, StorageWriteTransaction,
14
+ #[cfg(test)]
15
+ mod reader;
16
+ mod scan;
17
+ mod spaces;
18
+ mod stats;
19
+ mod write_set;
20
+
21
+ #[cfg(test)]
22
+ mod conformance;
23
+
24
+ pub trait StorageBackend: crate::backend::Backend {}
25
+ impl<T> StorageBackend for T where T: crate::backend::Backend {}
26
+ pub trait StorageBackendRead: crate::backend::BackendRead {}
27
+ impl<T> StorageBackendRead for T where T: crate::backend::BackendRead {}
28
+ pub type StorageBackendReadOf<'a, B> = <B as crate::backend::Backend>::Read<'a>;
29
+
30
+ pub use crate::backend::{
31
+ BackendError as StorageBackendError, CoreProjection as StorageCoreProjection,
32
+ DurableWriteGuard, DurableWriteLock, GetOptions as StorageGetOptions,
33
+ InMemoryBackend as InMemoryStorageBackend, InMemoryRead as InMemoryStorageRead,
34
+ InMemoryWrite as InMemoryStorageWrite, Key as StorageKey, KeyRange as StorageKeyRange,
35
+ Prefix as StoragePrefix, ProjectedValue as StorageProjectedValue,
36
+ ReadOptions as StorageReadOptions, ScanOptions as StorageScanOptions,
37
+ SpaceId as StorageSpaceId, StoredValue as StorageValue, WriteOptions as StorageWriteOptions,
11
38
  };
12
39
 
13
- #[cfg(feature = "storage-benches")]
14
- pub(crate) use types::{KvWriteBatch, KvWriteGroup};
40
+ pub use context::StorageContext;
41
+ pub use point::{
42
+ PointReadBuffer, PointReadPlan, PointValues, PointValuesRef, RequestedToUnique,
43
+ RequestedToUniqueRef,
44
+ };
45
+ pub use read_scope::{StorageRead, StorageReadScope};
46
+ pub use scan::{ScanBuffer, ScanChunkRef, ScanCursor, ScanPlan};
47
+ pub(crate) use spaces::decode_logical_key_ref;
48
+ pub use spaces::StorageSpace;
49
+ pub use stats::{
50
+ StorageReadResult, StorageReadStats, StorageReadStatsCollector, StorageWriteSetStats,
51
+ };
52
+ pub use write_set::{StorageWriteSet, StorageWriteSetError};
@@ -0,0 +1,440 @@
1
+ use std::collections::HashMap;
2
+
3
+ use ahash::RandomState;
4
+
5
+ use crate::backend::{
6
+ BackendError, BackendRead, GetOptions, Key, PointVisitor, ProjectedValue, ProjectedValueRef,
7
+ };
8
+ use crate::storage::{StorageRead, StorageReadResult, StorageReadStats, StorageSpace};
9
+
10
+ type FastHashBuilder = RandomState;
11
+
12
+ #[derive(Clone, Debug)]
13
+ pub struct PointReadPlan {
14
+ pub logical_unique_keys: Vec<Key>,
15
+ pub physical_unique_keys: Vec<Key>,
16
+ pub requested_to_unique: RequestedToUnique,
17
+ }
18
+
19
+ #[derive(Debug, PartialEq, Eq)]
20
+ pub struct PointValues<'plan> {
21
+ pub unique_values: Vec<Option<ProjectedValue>>,
22
+ pub requested_to_unique: RequestedToUniqueRef<'plan>,
23
+ }
24
+
25
+ #[derive(Debug, Default)]
26
+ pub struct PointReadBuffer {
27
+ unique_values: Vec<Option<ProjectedValue>>,
28
+ }
29
+
30
+ #[derive(Debug, PartialEq, Eq)]
31
+ pub struct PointValuesRef<'plan, 'buf> {
32
+ pub unique_values: &'buf [Option<ProjectedValue>],
33
+ pub requested_to_unique: RequestedToUniqueRef<'plan>,
34
+ }
35
+
36
+ #[derive(Clone, Debug, PartialEq, Eq)]
37
+ pub enum RequestedToUnique {
38
+ Identity { len: usize },
39
+ Indexes(Vec<usize>),
40
+ }
41
+
42
+ #[derive(Clone, Copy, Debug, PartialEq, Eq)]
43
+ pub enum RequestedToUniqueRef<'a> {
44
+ Identity { len: usize },
45
+ Indexes(&'a [usize]),
46
+ }
47
+
48
+ impl PointReadPlan {
49
+ pub fn new(space: StorageSpace, keys: &[Key]) -> Self {
50
+ let mut unique_index_by_key =
51
+ HashMap::<Key, usize, FastHashBuilder>::with_capacity_and_hasher(
52
+ keys.len(),
53
+ FastHashBuilder::with_seeds(0, 0, 0, 0),
54
+ );
55
+ let mut logical_unique_keys = Vec::with_capacity(keys.len());
56
+ let mut requested_to_unique = Vec::with_capacity(keys.len());
57
+ for key in keys {
58
+ if let Some(&unique_index) = unique_index_by_key.get(key) {
59
+ requested_to_unique.push(unique_index);
60
+ continue;
61
+ }
62
+
63
+ let unique_index = logical_unique_keys.len();
64
+ unique_index_by_key.insert(key.clone(), unique_index);
65
+ logical_unique_keys.push(key.clone());
66
+ requested_to_unique.push(unique_index);
67
+ }
68
+
69
+ Self::from_parts(
70
+ space,
71
+ logical_unique_keys,
72
+ requested_to_unique_mapping(requested_to_unique, keys.len()),
73
+ )
74
+ }
75
+
76
+ pub fn from_unique_keys(space: StorageSpace, unique_keys: Vec<Key>) -> Self {
77
+ debug_assert!(
78
+ keys_are_unique(&unique_keys),
79
+ "PointReadPlan::from_unique_keys requires unique keys"
80
+ );
81
+ let len = unique_keys.len();
82
+ Self::from_parts(space, unique_keys, RequestedToUnique::Identity { len })
83
+ }
84
+
85
+ pub fn len(&self) -> usize {
86
+ self.requested_to_unique.len()
87
+ }
88
+
89
+ pub fn is_empty(&self) -> bool {
90
+ self.requested_to_unique.is_empty()
91
+ }
92
+
93
+ pub fn requested_to_unique(&self) -> RequestedToUniqueRef<'_> {
94
+ self.requested_to_unique.as_ref()
95
+ }
96
+
97
+ pub fn collect<R>(
98
+ &self,
99
+ read: &R,
100
+ opts: GetOptions<'_>,
101
+ ) -> Result<StorageReadResult<PointValues<'_>>, BackendError>
102
+ where
103
+ R: StorageRead + ?Sized,
104
+ {
105
+ let unique_values =
106
+ collect_physical_unique_values(read.backend_read(), &self.physical_unique_keys, opts)?;
107
+ Ok(StorageReadResult::new(
108
+ PointValues {
109
+ unique_values,
110
+ requested_to_unique: self.requested_to_unique.as_ref(),
111
+ },
112
+ self.stats(),
113
+ ))
114
+ }
115
+
116
+ pub fn materialize<R>(
117
+ &self,
118
+ read: &R,
119
+ opts: GetOptions<'_>,
120
+ ) -> Result<StorageReadResult<Vec<Option<ProjectedValue>>>, BackendError>
121
+ where
122
+ R: StorageRead + ?Sized,
123
+ {
124
+ let result = self.collect(read, opts)?;
125
+ Ok(StorageReadResult::new(
126
+ result.value.materialize_caller_order(),
127
+ result.stats,
128
+ ))
129
+ }
130
+
131
+ pub fn collect_into<'plan, 'buf, R>(
132
+ &'plan self,
133
+ read: &R,
134
+ opts: GetOptions<'_>,
135
+ buffer: &'buf mut PointReadBuffer,
136
+ ) -> Result<StorageReadResult<PointValuesRef<'plan, 'buf>>, BackendError>
137
+ where
138
+ R: StorageRead + ?Sized,
139
+ {
140
+ collect_physical_unique_values_into(
141
+ read.backend_read(),
142
+ &self.physical_unique_keys,
143
+ opts,
144
+ buffer,
145
+ )?;
146
+
147
+ Ok(StorageReadResult::new(
148
+ PointValuesRef {
149
+ unique_values: buffer.unique_values.as_slice(),
150
+ requested_to_unique: self.requested_to_unique.as_ref(),
151
+ },
152
+ self.stats(),
153
+ ))
154
+ }
155
+
156
+ pub fn visit<R, V>(
157
+ &self,
158
+ read: &R,
159
+ opts: GetOptions<'_>,
160
+ visitor: &mut V,
161
+ ) -> Result<StorageReadStats, BackendError>
162
+ where
163
+ R: StorageRead + ?Sized,
164
+ V: PointVisitor + ?Sized,
165
+ {
166
+ struct LogicalPointVisitor<'a, V: ?Sized> {
167
+ logical_keys: &'a [Key],
168
+ inner: &'a mut V,
169
+ }
170
+
171
+ impl<V> PointVisitor for LogicalPointVisitor<'_, V>
172
+ where
173
+ V: PointVisitor + ?Sized,
174
+ {
175
+ fn visit(
176
+ &mut self,
177
+ index: usize,
178
+ _key: &Key,
179
+ value: Option<ProjectedValueRef<'_>>,
180
+ ) -> Result<(), BackendError> {
181
+ let Some(logical_key) = self.logical_keys.get(index) else {
182
+ return Ok(());
183
+ };
184
+ self.inner.visit(index, logical_key, value)
185
+ }
186
+ }
187
+
188
+ read.backend_read().visit_keys(
189
+ &self.physical_unique_keys,
190
+ opts,
191
+ &mut LogicalPointVisitor {
192
+ logical_keys: &self.logical_unique_keys,
193
+ inner: visitor,
194
+ },
195
+ )?;
196
+ Ok(self.stats())
197
+ }
198
+
199
+ fn from_parts(
200
+ space: StorageSpace,
201
+ logical_unique_keys: Vec<Key>,
202
+ requested_to_unique: RequestedToUnique,
203
+ ) -> Self {
204
+ let physical_unique_keys = logical_unique_keys
205
+ .iter()
206
+ .map(|key| space.encode_key(key))
207
+ .collect();
208
+ Self {
209
+ logical_unique_keys,
210
+ physical_unique_keys,
211
+ requested_to_unique,
212
+ }
213
+ }
214
+
215
+ fn stats(&self) -> StorageReadStats {
216
+ StorageReadStats {
217
+ requested_keys: self.requested_to_unique.len() as u64,
218
+ unique_backend_keys: self.logical_unique_keys.len() as u64,
219
+ backend_calls: 1,
220
+ prefix_lowered: 0,
221
+ ..StorageReadStats::default()
222
+ }
223
+ }
224
+ }
225
+
226
+ impl RequestedToUnique {
227
+ pub fn len(&self) -> usize {
228
+ match self {
229
+ Self::Identity { len } => *len,
230
+ Self::Indexes(indexes) => indexes.len(),
231
+ }
232
+ }
233
+
234
+ pub fn is_empty(&self) -> bool {
235
+ self.len() == 0
236
+ }
237
+
238
+ pub fn unique_index(&self, requested_index: usize) -> Option<usize> {
239
+ match self {
240
+ Self::Identity { len } => (requested_index < *len).then_some(requested_index),
241
+ Self::Indexes(indexes) => indexes.get(requested_index).copied(),
242
+ }
243
+ }
244
+
245
+ pub fn as_ref(&self) -> RequestedToUniqueRef<'_> {
246
+ match self {
247
+ Self::Identity { len } => RequestedToUniqueRef::Identity { len: *len },
248
+ Self::Indexes(indexes) => RequestedToUniqueRef::Indexes(indexes),
249
+ }
250
+ }
251
+
252
+ pub fn to_vec(&self) -> Vec<usize> {
253
+ match self {
254
+ Self::Identity { len } => (0..*len).collect(),
255
+ Self::Indexes(indexes) => indexes.clone(),
256
+ }
257
+ }
258
+ }
259
+
260
+ impl<'a> RequestedToUniqueRef<'a> {
261
+ pub fn len(&self) -> usize {
262
+ match self {
263
+ Self::Identity { len } => *len,
264
+ Self::Indexes(indexes) => indexes.len(),
265
+ }
266
+ }
267
+
268
+ pub fn is_empty(&self) -> bool {
269
+ self.len() == 0
270
+ }
271
+
272
+ pub fn unique_index(&self, requested_index: usize) -> Option<usize> {
273
+ match self {
274
+ Self::Identity { len } => (requested_index < *len).then_some(requested_index),
275
+ Self::Indexes(indexes) => indexes.get(requested_index).copied(),
276
+ }
277
+ }
278
+
279
+ pub fn to_vec(self) -> Vec<usize> {
280
+ match self {
281
+ Self::Identity { len } => (0..len).collect(),
282
+ Self::Indexes(indexes) => indexes.to_vec(),
283
+ }
284
+ }
285
+ }
286
+
287
+ impl<'plan> PointValues<'plan> {
288
+ pub fn len(&self) -> usize {
289
+ self.requested_to_unique.len()
290
+ }
291
+
292
+ pub fn is_empty(&self) -> bool {
293
+ self.requested_to_unique.is_empty()
294
+ }
295
+
296
+ pub fn value_at(&self, requested_index: usize) -> Option<&ProjectedValue> {
297
+ let unique_index = self.requested_to_unique.unique_index(requested_index)?;
298
+ self.unique_values.get(unique_index)?.as_ref()
299
+ }
300
+
301
+ pub fn materialize_caller_order(self) -> Vec<Option<ProjectedValue>> {
302
+ materialize_caller_order(self.unique_values, self.requested_to_unique)
303
+ }
304
+ }
305
+
306
+ impl PointReadBuffer {
307
+ pub fn new() -> Self {
308
+ Self::default()
309
+ }
310
+
311
+ pub fn capacity(&self) -> usize {
312
+ self.unique_values.capacity()
313
+ }
314
+
315
+ pub fn clear(&mut self) {
316
+ self.unique_values.clear();
317
+ }
318
+
319
+ fn reset_for_len(&mut self, len: usize) {
320
+ self.unique_values.clear();
321
+ self.unique_values.resize_with(len, || None);
322
+ }
323
+ }
324
+
325
+ impl<'plan, 'buf> PointValuesRef<'plan, 'buf> {
326
+ pub fn len(&self) -> usize {
327
+ self.requested_to_unique.len()
328
+ }
329
+
330
+ pub fn is_empty(&self) -> bool {
331
+ self.requested_to_unique.is_empty()
332
+ }
333
+
334
+ pub fn value_at(&self, requested_index: usize) -> Option<&ProjectedValue> {
335
+ let unique_index = self.requested_to_unique.unique_index(requested_index)?;
336
+ self.unique_values.get(unique_index)?.as_ref()
337
+ }
338
+ }
339
+
340
+ fn collect_physical_unique_values<R>(
341
+ read: &R,
342
+ physical_unique_keys: &[Key],
343
+ opts: GetOptions<'_>,
344
+ ) -> Result<Vec<Option<ProjectedValue>>, BackendError>
345
+ where
346
+ R: BackendRead,
347
+ {
348
+ let mut values = vec![None; physical_unique_keys.len()];
349
+ collect_physical_unique_values_into_slice(
350
+ read,
351
+ physical_unique_keys,
352
+ opts,
353
+ values.as_mut_slice(),
354
+ )?;
355
+ Ok(values)
356
+ }
357
+
358
+ fn collect_physical_unique_values_into<R>(
359
+ read: &R,
360
+ physical_unique_keys: &[Key],
361
+ opts: GetOptions<'_>,
362
+ buffer: &mut PointReadBuffer,
363
+ ) -> Result<(), BackendError>
364
+ where
365
+ R: BackendRead,
366
+ {
367
+ buffer.reset_for_len(physical_unique_keys.len());
368
+ collect_physical_unique_values_into_slice(
369
+ read,
370
+ physical_unique_keys,
371
+ opts,
372
+ buffer.unique_values.as_mut_slice(),
373
+ )
374
+ }
375
+
376
+ fn collect_physical_unique_values_into_slice<R>(
377
+ read: &R,
378
+ physical_unique_keys: &[Key],
379
+ opts: GetOptions<'_>,
380
+ values: &mut [Option<ProjectedValue>],
381
+ ) -> Result<(), BackendError>
382
+ where
383
+ R: BackendRead,
384
+ {
385
+ struct Collector<'a> {
386
+ values: &'a mut [Option<ProjectedValue>],
387
+ }
388
+
389
+ impl PointVisitor for Collector<'_> {
390
+ fn visit(
391
+ &mut self,
392
+ index: usize,
393
+ _key: &Key,
394
+ value: Option<ProjectedValueRef<'_>>,
395
+ ) -> Result<(), BackendError> {
396
+ if let Some(slot) = self.values.get_mut(index) {
397
+ *slot = value.map(|value| value.to_owned());
398
+ }
399
+ Ok(())
400
+ }
401
+ }
402
+
403
+ read.visit_keys(physical_unique_keys, opts, &mut Collector { values })
404
+ }
405
+
406
+ fn keys_are_unique(keys: &[Key]) -> bool {
407
+ let mut seen = HashMap::<&Key, (), FastHashBuilder>::with_capacity_and_hasher(
408
+ keys.len(),
409
+ FastHashBuilder::with_seeds(0, 0, 0, 0),
410
+ );
411
+ keys.iter().all(|key| seen.insert(key, ()).is_none())
412
+ }
413
+
414
+ fn requested_to_unique_mapping(indexes: Vec<usize>, requested_len: usize) -> RequestedToUnique {
415
+ if indexes.len() == requested_len
416
+ && indexes
417
+ .iter()
418
+ .copied()
419
+ .enumerate()
420
+ .all(|(requested_index, unique_index)| requested_index == unique_index)
421
+ {
422
+ RequestedToUnique::Identity { len: requested_len }
423
+ } else {
424
+ RequestedToUnique::Indexes(indexes)
425
+ }
426
+ }
427
+
428
+ fn materialize_caller_order(
429
+ unique_values: Vec<Option<ProjectedValue>>,
430
+ requested_to_unique: RequestedToUniqueRef<'_>,
431
+ ) -> Vec<Option<ProjectedValue>> {
432
+ let mut values = Vec::with_capacity(requested_to_unique.len());
433
+ for requested_index in 0..requested_to_unique.len() {
434
+ let unique_index = requested_to_unique
435
+ .unique_index(requested_index)
436
+ .expect("requested index is inside requested_to_unique");
437
+ values.push(unique_values[unique_index].clone());
438
+ }
439
+ values
440
+ }
@@ -1,88 +1,67 @@
1
- use std::sync::Arc;
1
+ use crate::backend::{BackendError, BackendRead};
2
2
 
3
- use crate::storage::{
4
- KvEntryPage, KvExistsBatch, KvGetRequest, KvKeyPage, KvScanRequest, KvValueBatch, KvValuePage,
5
- StorageReadTransaction, StorageReader,
6
- };
7
- use crate::LixError;
8
- use tokio::sync::Mutex;
3
+ pub trait StorageRead {
4
+ type BackendRead: BackendRead;
9
5
 
10
- /// Shared read visibility over one KV store handle.
11
- ///
12
- /// This lets multiple subsystem readers share the same transaction/backend view
13
- /// even when the underlying handle itself is not cloneable.
14
- pub(crate) struct StorageReadScope<S> {
15
- store: Arc<Mutex<S>>,
6
+ fn backend_read(&self) -> &Self::BackendRead;
16
7
  }
17
8
 
18
- impl<S> StorageReadScope<S>
19
- where
20
- S: StorageReader,
21
- {
22
- pub(crate) fn new(store: S) -> Self {
23
- Self {
24
- store: Arc::new(Mutex::new(store)),
25
- }
9
+ #[derive(Clone)]
10
+ pub struct StorageReadScope<R> {
11
+ read: R,
12
+ }
13
+
14
+ impl<R> StorageReadScope<R> {
15
+ pub fn new(read: R) -> Self {
16
+ Self { read }
26
17
  }
27
18
 
28
- pub(crate) fn store(&self) -> ScopedStorageReader<S> {
29
- ScopedStorageReader {
30
- store: Arc::clone(&self.store),
31
- }
19
+ pub fn store(&self) -> Self
20
+ where
21
+ R: Clone,
22
+ {
23
+ self.clone()
32
24
  }
33
25
  }
34
26
 
35
- impl StorageReadScope<Box<dyn StorageReadTransaction + Send + Sync + 'static>> {
36
- pub(crate) async fn rollback(self) -> Result<(), LixError> {
37
- let store = Arc::try_unwrap(self.store).map_err(|_| {
38
- LixError::new(
39
- "LIX_ERROR_UNKNOWN",
40
- "cannot close storage read scope while scoped readers are still alive",
41
- )
42
- })?;
43
- store.into_inner().rollback().await
27
+ impl<R> StorageReadScope<R>
28
+ where
29
+ R: BackendRead,
30
+ {
31
+ pub fn close(self) -> Result<(), BackendError> {
32
+ self.read.close()
44
33
  }
45
34
  }
46
35
 
47
- pub(crate) struct ScopedStorageReader<S> {
48
- store: Arc<Mutex<S>>,
49
- }
36
+ impl<R> StorageRead for StorageReadScope<R>
37
+ where
38
+ R: BackendRead,
39
+ {
40
+ type BackendRead = R;
50
41
 
51
- impl<S> Clone for ScopedStorageReader<S> {
52
- fn clone(&self) -> Self {
53
- Self {
54
- store: Arc::clone(&self.store),
55
- }
42
+ fn backend_read(&self) -> &Self::BackendRead {
43
+ &self.read
56
44
  }
57
45
  }
58
46
 
59
- #[async_trait::async_trait]
60
- impl<S> StorageReader for ScopedStorageReader<S>
47
+ impl<T> StorageRead for &T
61
48
  where
62
- S: StorageReader,
49
+ T: StorageRead + ?Sized,
63
50
  {
64
- async fn get_values(&mut self, request: KvGetRequest) -> Result<KvValueBatch, LixError> {
65
- let mut store = self.store.lock().await;
66
- store.get_values(request).await
67
- }
68
-
69
- async fn exists_many(&mut self, request: KvGetRequest) -> Result<KvExistsBatch, LixError> {
70
- let mut store = self.store.lock().await;
71
- store.exists_many(request).await
72
- }
51
+ type BackendRead = T::BackendRead;
73
52
 
74
- async fn scan_keys(&mut self, request: KvScanRequest) -> Result<KvKeyPage, LixError> {
75
- let mut store = self.store.lock().await;
76
- store.scan_keys(request).await
53
+ fn backend_read(&self) -> &Self::BackendRead {
54
+ (*self).backend_read()
77
55
  }
56
+ }
78
57
 
79
- async fn scan_values(&mut self, request: KvScanRequest) -> Result<KvValuePage, LixError> {
80
- let mut store = self.store.lock().await;
81
- store.scan_values(request).await
82
- }
58
+ impl<T> StorageRead for &mut T
59
+ where
60
+ T: StorageRead + ?Sized,
61
+ {
62
+ type BackendRead = T::BackendRead;
83
63
 
84
- async fn scan_entries(&mut self, request: KvScanRequest) -> Result<KvEntryPage, LixError> {
85
- let mut store = self.store.lock().await;
86
- store.scan_entries(request).await
64
+ fn backend_read(&self) -> &Self::BackendRead {
65
+ (**self).backend_read()
87
66
  }
88
67
  }