@lix-js/sdk 0.6.0-preview.5 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (274) hide show
  1. package/README.md +76 -4
  2. package/dist/errors.d.ts +7 -0
  3. package/dist/errors.js +19 -0
  4. package/dist/index.d.ts +4 -5
  5. package/dist/index.js +3 -3
  6. package/dist/native.d.ts +1 -0
  7. package/dist/native.js +47 -0
  8. package/dist/open-lix.d.ts +38 -207
  9. package/dist/open-lix.js +59 -284
  10. package/dist/result.d.ts +18 -0
  11. package/dist/result.js +48 -0
  12. package/dist/types.d.ts +114 -1
  13. package/dist/value.d.ts +28 -0
  14. package/dist/value.js +245 -0
  15. package/package.json +38 -71
  16. package/SKILL.md +0 -507
  17. package/dist/builtin-schemas.d.ts +0 -1
  18. package/dist/builtin-schemas.js +0 -1
  19. package/dist/engine-wasm/index.d.ts +0 -87
  20. package/dist/engine-wasm/index.js +0 -339
  21. package/dist/engine-wasm/wasm/lix_engine.d.ts +0 -79
  22. package/dist/engine-wasm/wasm/lix_engine.js +0 -833
  23. package/dist/engine-wasm/wasm/lix_engine.wasm +0 -0
  24. package/dist/engine-wasm/wasm/lix_engine.wasm.d.ts +0 -27
  25. package/dist/generated/builtin-schemas.d.ts +0 -427
  26. package/dist/generated/builtin-schemas.js +0 -643
  27. package/dist/sqlite/index.d.ts +0 -12
  28. package/dist/sqlite/index.js +0 -359
  29. package/dist-engine-src/README.md +0 -18
  30. package/dist-engine-src/src/backend/capabilities.rs +0 -67
  31. package/dist-engine-src/src/backend/conformance/baseline.rs +0 -1127
  32. package/dist-engine-src/src/backend/conformance/factory.rs +0 -93
  33. package/dist-engine-src/src/backend/conformance/failure_tests.rs +0 -608
  34. package/dist-engine-src/src/backend/conformance/fixtures.rs +0 -26
  35. package/dist-engine-src/src/backend/conformance/mod.rs +0 -75
  36. package/dist-engine-src/src/backend/conformance/model.rs +0 -28
  37. package/dist-engine-src/src/backend/conformance/model_based.rs +0 -257
  38. package/dist-engine-src/src/backend/conformance/persistence.rs +0 -204
  39. package/dist-engine-src/src/backend/conformance/projection.rs +0 -21
  40. package/dist-engine-src/src/backend/conformance/pushdown.rs +0 -24
  41. package/dist-engine-src/src/backend/conformance/runner.rs +0 -90
  42. package/dist-engine-src/src/backend/conformance/scan.rs +0 -24
  43. package/dist-engine-src/src/backend/conformance/write.rs +0 -16
  44. package/dist-engine-src/src/backend/error.rs +0 -94
  45. package/dist-engine-src/src/backend/in_memory.rs +0 -670
  46. package/dist-engine-src/src/backend/mod.rs +0 -39
  47. package/dist-engine-src/src/backend/predicate.rs +0 -80
  48. package/dist-engine-src/src/backend/traits.rs +0 -260
  49. package/dist-engine-src/src/backend/types.rs +0 -239
  50. package/dist-engine-src/src/binary_cas/chunking.rs +0 -31
  51. package/dist-engine-src/src/binary_cas/codec.rs +0 -346
  52. package/dist-engine-src/src/binary_cas/context.rs +0 -139
  53. package/dist-engine-src/src/binary_cas/kv.rs +0 -1038
  54. package/dist-engine-src/src/binary_cas/mod.rs +0 -11
  55. package/dist-engine-src/src/binary_cas/types.rs +0 -121
  56. package/dist-engine-src/src/branch/context.rs +0 -40
  57. package/dist-engine-src/src/branch/lifecycle.rs +0 -221
  58. package/dist-engine-src/src/branch/mod.rs +0 -13
  59. package/dist-engine-src/src/branch/refs.rs +0 -321
  60. package/dist-engine-src/src/branch/stage_rows.rs +0 -67
  61. package/dist-engine-src/src/branch/types.rs +0 -21
  62. package/dist-engine-src/src/catalog/context.rs +0 -412
  63. package/dist-engine-src/src/catalog/mod.rs +0 -10
  64. package/dist-engine-src/src/catalog/schema.rs +0 -4
  65. package/dist-engine-src/src/catalog/snapshot.rs +0 -1114
  66. package/dist-engine-src/src/cel/context.rs +0 -86
  67. package/dist-engine-src/src/cel/error.rs +0 -19
  68. package/dist-engine-src/src/cel/mod.rs +0 -8
  69. package/dist-engine-src/src/cel/provider.rs +0 -9
  70. package/dist-engine-src/src/cel/runtime.rs +0 -167
  71. package/dist-engine-src/src/cel/value.rs +0 -50
  72. package/dist-engine-src/src/changelog/bench_support.rs +0 -785
  73. package/dist-engine-src/src/changelog/change.rs +0 -1
  74. package/dist-engine-src/src/changelog/codec.rs +0 -497
  75. package/dist-engine-src/src/changelog/commit.rs +0 -1
  76. package/dist-engine-src/src/changelog/context.rs +0 -1614
  77. package/dist-engine-src/src/changelog/mod.rs +0 -29
  78. package/dist-engine-src/src/changelog/store.rs +0 -163
  79. package/dist-engine-src/src/changelog/test_support.rs +0 -54
  80. package/dist-engine-src/src/changelog/types.rs +0 -213
  81. package/dist-engine-src/src/commit_graph/context.rs +0 -944
  82. package/dist-engine-src/src/commit_graph/mod.rs +0 -9
  83. package/dist-engine-src/src/commit_graph/types.rs +0 -89
  84. package/dist-engine-src/src/commit_graph/walker.rs +0 -786
  85. package/dist-engine-src/src/common/error.rs +0 -347
  86. package/dist-engine-src/src/common/fingerprint.rs +0 -3
  87. package/dist-engine-src/src/common/fs_path.rs +0 -1336
  88. package/dist-engine-src/src/common/identity.rs +0 -145
  89. package/dist-engine-src/src/common/json_pointer.rs +0 -67
  90. package/dist-engine-src/src/common/metadata.rs +0 -40
  91. package/dist-engine-src/src/common/mod.rs +0 -23
  92. package/dist-engine-src/src/common/types.rs +0 -105
  93. package/dist-engine-src/src/common/wire.rs +0 -222
  94. package/dist-engine-src/src/domain.rs +0 -320
  95. package/dist-engine-src/src/engine.rs +0 -203
  96. package/dist-engine-src/src/entity_pk.rs +0 -402
  97. package/dist-engine-src/src/functions/context.rs +0 -296
  98. package/dist-engine-src/src/functions/deterministic.rs +0 -113
  99. package/dist-engine-src/src/functions/mod.rs +0 -18
  100. package/dist-engine-src/src/functions/provider.rs +0 -130
  101. package/dist-engine-src/src/functions/state.rs +0 -335
  102. package/dist-engine-src/src/functions/types.rs +0 -37
  103. package/dist-engine-src/src/init.rs +0 -692
  104. package/dist-engine-src/src/json_store/compression.rs +0 -77
  105. package/dist-engine-src/src/json_store/context.rs +0 -172
  106. package/dist-engine-src/src/json_store/encoded.rs +0 -15
  107. package/dist-engine-src/src/json_store/mod.rs +0 -38
  108. package/dist-engine-src/src/json_store/store.rs +0 -494
  109. package/dist-engine-src/src/json_store/types.rs +0 -212
  110. package/dist-engine-src/src/lib.rs +0 -92
  111. package/dist-engine-src/src/live_state/context.rs +0 -1883
  112. package/dist-engine-src/src/live_state/mod.rs +0 -21
  113. package/dist-engine-src/src/live_state/overlay.rs +0 -75
  114. package/dist-engine-src/src/live_state/reader.rs +0 -23
  115. package/dist-engine-src/src/live_state/types.rs +0 -231
  116. package/dist-engine-src/src/live_state/visibility.rs +0 -666
  117. package/dist-engine-src/src/plugin/archive.rs +0 -438
  118. package/dist-engine-src/src/plugin/component.rs +0 -183
  119. package/dist-engine-src/src/plugin/install.rs +0 -619
  120. package/dist-engine-src/src/plugin/manifest.rs +0 -516
  121. package/dist-engine-src/src/plugin/materializer.rs +0 -202
  122. package/dist-engine-src/src/plugin/mod.rs +0 -33
  123. package/dist-engine-src/src/plugin/plugin_manifest.json +0 -119
  124. package/dist-engine-src/src/plugin/storage.rs +0 -74
  125. package/dist-engine-src/src/schema/annotations/defaults.rs +0 -275
  126. package/dist-engine-src/src/schema/annotations/mod.rs +0 -1
  127. package/dist-engine-src/src/schema/builtin/lix_account.json +0 -21
  128. package/dist-engine-src/src/schema/builtin/lix_active_account.json +0 -29
  129. package/dist-engine-src/src/schema/builtin/lix_binary_blob_ref.json +0 -29
  130. package/dist-engine-src/src/schema/builtin/lix_branch_descriptor.json +0 -34
  131. package/dist-engine-src/src/schema/builtin/lix_branch_ref.json +0 -48
  132. package/dist-engine-src/src/schema/builtin/lix_change.json +0 -63
  133. package/dist-engine-src/src/schema/builtin/lix_change_author.json +0 -45
  134. package/dist-engine-src/src/schema/builtin/lix_commit.json +0 -24
  135. package/dist-engine-src/src/schema/builtin/lix_commit_edge.json +0 -53
  136. package/dist-engine-src/src/schema/builtin/lix_directory_descriptor.json +0 -52
  137. package/dist-engine-src/src/schema/builtin/lix_file_descriptor.json +0 -52
  138. package/dist-engine-src/src/schema/builtin/lix_key_value.json +0 -40
  139. package/dist-engine-src/src/schema/builtin/lix_label.json +0 -29
  140. package/dist-engine-src/src/schema/builtin/lix_label_assignment.json +0 -74
  141. package/dist-engine-src/src/schema/builtin/lix_registered_schema.json +0 -25
  142. package/dist-engine-src/src/schema/builtin/mod.rs +0 -220
  143. package/dist-engine-src/src/schema/compatibility.rs +0 -787
  144. package/dist-engine-src/src/schema/definition.json +0 -187
  145. package/dist-engine-src/src/schema/definition.rs +0 -742
  146. package/dist-engine-src/src/schema/key.rs +0 -138
  147. package/dist-engine-src/src/schema/mod.rs +0 -20
  148. package/dist-engine-src/src/schema/seed.rs +0 -14
  149. package/dist-engine-src/src/schema/tests.rs +0 -780
  150. package/dist-engine-src/src/session/context.rs +0 -1059
  151. package/dist-engine-src/src/session/create_branch.rs +0 -94
  152. package/dist-engine-src/src/session/execute.rs +0 -681
  153. package/dist-engine-src/src/session/merge/analysis.rs +0 -108
  154. package/dist-engine-src/src/session/merge/branch.rs +0 -417
  155. package/dist-engine-src/src/session/merge/conflicts.rs +0 -63
  156. package/dist-engine-src/src/session/merge/mod.rs +0 -10
  157. package/dist-engine-src/src/session/merge/stats.rs +0 -61
  158. package/dist-engine-src/src/session/mod.rs +0 -30
  159. package/dist-engine-src/src/session/switch_branch.rs +0 -113
  160. package/dist-engine-src/src/session/transaction.rs +0 -557
  161. package/dist-engine-src/src/sql2/bind/classify.rs +0 -102
  162. package/dist-engine-src/src/sql2/bind/error.rs +0 -5
  163. package/dist-engine-src/src/sql2/bind/expr.rs +0 -29
  164. package/dist-engine-src/src/sql2/bind/mod.rs +0 -12
  165. package/dist-engine-src/src/sql2/bind/public_udf.rs +0 -306
  166. package/dist-engine-src/src/sql2/bind/read.rs +0 -65
  167. package/dist-engine-src/src/sql2/bind/statement.rs +0 -2236
  168. package/dist-engine-src/src/sql2/bind/table.rs +0 -273
  169. package/dist-engine-src/src/sql2/bind/write.rs +0 -86
  170. package/dist-engine-src/src/sql2/branch_scope.rs +0 -436
  171. package/dist-engine-src/src/sql2/catalog/capability.rs +0 -20
  172. package/dist-engine-src/src/sql2/catalog/entity_surface.rs +0 -296
  173. package/dist-engine-src/src/sql2/catalog/mod.rs +0 -15
  174. package/dist-engine-src/src/sql2/catalog/registry.rs +0 -556
  175. package/dist-engine-src/src/sql2/catalog/schema.rs +0 -88
  176. package/dist-engine-src/src/sql2/catalog/surface.rs +0 -41
  177. package/dist-engine-src/src/sql2/change_materialization.rs +0 -122
  178. package/dist-engine-src/src/sql2/context.rs +0 -317
  179. package/dist-engine-src/src/sql2/dml.rs +0 -148
  180. package/dist-engine-src/src/sql2/error.rs +0 -215
  181. package/dist-engine-src/src/sql2/exec/bound_public_write.rs +0 -1593
  182. package/dist-engine-src/src/sql2/exec/datafusion.rs +0 -5266
  183. package/dist-engine-src/src/sql2/exec/fast_write.rs +0 -82
  184. package/dist-engine-src/src/sql2/exec/mod.rs +0 -24
  185. package/dist-engine-src/src/sql2/exec/write.rs +0 -661
  186. package/dist-engine-src/src/sql2/filesystem_planner.rs +0 -1485
  187. package/dist-engine-src/src/sql2/filesystem_predicates.rs +0 -159
  188. package/dist-engine-src/src/sql2/filesystem_visibility.rs +0 -383
  189. package/dist-engine-src/src/sql2/history_projection.rs +0 -56
  190. package/dist-engine-src/src/sql2/history_route.rs +0 -661
  191. package/dist-engine-src/src/sql2/mod.rs +0 -52
  192. package/dist-engine-src/src/sql2/optimize/datafusion.rs +0 -1
  193. package/dist-engine-src/src/sql2/optimize/mod.rs +0 -2
  194. package/dist-engine-src/src/sql2/optimize/simple_write.rs +0 -116
  195. package/dist-engine-src/src/sql2/parse/mod.rs +0 -69
  196. package/dist-engine-src/src/sql2/parse/normalize.rs +0 -1
  197. package/dist-engine-src/src/sql2/plan/branch_scope.rs +0 -24
  198. package/dist-engine-src/src/sql2/plan/mod.rs +0 -5
  199. package/dist-engine-src/src/sql2/plan/predicate.rs +0 -22
  200. package/dist-engine-src/src/sql2/plan/write.rs +0 -147
  201. package/dist-engine-src/src/sql2/predicate_typecheck.rs +0 -504
  202. package/dist-engine-src/src/sql2/providers/branch.rs +0 -1206
  203. package/dist-engine-src/src/sql2/providers/change.rs +0 -445
  204. package/dist-engine-src/src/sql2/providers/directory.rs +0 -2422
  205. package/dist-engine-src/src/sql2/providers/directory_history.rs +0 -645
  206. package/dist-engine-src/src/sql2/providers/entity.rs +0 -1484
  207. package/dist-engine-src/src/sql2/providers/entity_history.rs +0 -452
  208. package/dist-engine-src/src/sql2/providers/file.rs +0 -3686
  209. package/dist-engine-src/src/sql2/providers/file_history.rs +0 -924
  210. package/dist-engine-src/src/sql2/providers/history.rs +0 -426
  211. package/dist-engine-src/src/sql2/providers/lix_state.rs +0 -2542
  212. package/dist-engine-src/src/sql2/providers/mod.rs +0 -508
  213. package/dist-engine-src/src/sql2/read_only.rs +0 -63
  214. package/dist-engine-src/src/sql2/record_batch.rs +0 -17
  215. package/dist-engine-src/src/sql2/result_metadata.rs +0 -29
  216. package/dist-engine-src/src/sql2/runtime.rs +0 -60
  217. package/dist-engine-src/src/sql2/session.rs +0 -83
  218. package/dist-engine-src/src/sql2/storage/constraints.rs +0 -1
  219. package/dist-engine-src/src/sql2/storage/mod.rs +0 -1
  220. package/dist-engine-src/src/sql2/test_support/differential.rs +0 -712
  221. package/dist-engine-src/src/sql2/test_support/generators.rs +0 -354
  222. package/dist-engine-src/src/sql2/test_support/mod.rs +0 -2
  223. package/dist-engine-src/src/sql2/udfs/common.rs +0 -295
  224. package/dist-engine-src/src/sql2/udfs/lix_active_branch_commit_id.rs +0 -53
  225. package/dist-engine-src/src/sql2/udfs/lix_empty_blob.rs +0 -47
  226. package/dist-engine-src/src/sql2/udfs/lix_json.rs +0 -100
  227. package/dist-engine-src/src/sql2/udfs/lix_json_get.rs +0 -99
  228. package/dist-engine-src/src/sql2/udfs/lix_json_get_text.rs +0 -99
  229. package/dist-engine-src/src/sql2/udfs/lix_text_decode.rs +0 -82
  230. package/dist-engine-src/src/sql2/udfs/lix_text_encode.rs +0 -85
  231. package/dist-engine-src/src/sql2/udfs/lix_timestamp.rs +0 -76
  232. package/dist-engine-src/src/sql2/udfs/lix_uuid_v7.rs +0 -76
  233. package/dist-engine-src/src/sql2/udfs/mod.rs +0 -86
  234. package/dist-engine-src/src/sql2/write_normalization.rs +0 -368
  235. package/dist-engine-src/src/storage/conformance.rs +0 -399
  236. package/dist-engine-src/src/storage/context.rs +0 -620
  237. package/dist-engine-src/src/storage/mod.rs +0 -52
  238. package/dist-engine-src/src/storage/point.rs +0 -440
  239. package/dist-engine-src/src/storage/read_scope.rs +0 -67
  240. package/dist-engine-src/src/storage/reader.rs +0 -867
  241. package/dist-engine-src/src/storage/scan.rs +0 -784
  242. package/dist-engine-src/src/storage/spaces.rs +0 -236
  243. package/dist-engine-src/src/storage/stats.rs +0 -80
  244. package/dist-engine-src/src/storage/write_set.rs +0 -962
  245. package/dist-engine-src/src/storage_bench.rs +0 -171
  246. package/dist-engine-src/src/test_support.rs +0 -450
  247. package/dist-engine-src/src/tracked_state/bench_support.rs +0 -394
  248. package/dist-engine-src/src/tracked_state/codec.rs +0 -1183
  249. package/dist-engine-src/src/tracked_state/commit_root_rebuild.rs +0 -358
  250. package/dist-engine-src/src/tracked_state/context.rs +0 -2801
  251. package/dist-engine-src/src/tracked_state/diff.rs +0 -2140
  252. package/dist-engine-src/src/tracked_state/merge.rs +0 -478
  253. package/dist-engine-src/src/tracked_state/mod.rs +0 -35
  254. package/dist-engine-src/src/tracked_state/row_materialization.rs +0 -275
  255. package/dist-engine-src/src/tracked_state/storage.rs +0 -427
  256. package/dist-engine-src/src/tracked_state/tree.rs +0 -3063
  257. package/dist-engine-src/src/tracked_state/types.rs +0 -238
  258. package/dist-engine-src/src/transaction/bench_support.rs +0 -407
  259. package/dist-engine-src/src/transaction/commit.rs +0 -1592
  260. package/dist-engine-src/src/transaction/context.rs +0 -1653
  261. package/dist-engine-src/src/transaction/mod.rs +0 -24
  262. package/dist-engine-src/src/transaction/normalization.rs +0 -877
  263. package/dist-engine-src/src/transaction/prep.rs +0 -37
  264. package/dist-engine-src/src/transaction/schema_resolver.rs +0 -163
  265. package/dist-engine-src/src/transaction/staging.rs +0 -1525
  266. package/dist-engine-src/src/transaction/types.rs +0 -403
  267. package/dist-engine-src/src/transaction/validation.rs +0 -5766
  268. package/dist-engine-src/src/untracked_state/codec.rs +0 -615
  269. package/dist-engine-src/src/untracked_state/context.rs +0 -98
  270. package/dist-engine-src/src/untracked_state/materialization.rs +0 -63
  271. package/dist-engine-src/src/untracked_state/mod.rs +0 -15
  272. package/dist-engine-src/src/untracked_state/storage.rs +0 -898
  273. package/dist-engine-src/src/untracked_state/types.rs +0 -146
  274. package/dist-engine-src/src/wasm/mod.rs +0 -60
@@ -1,867 +0,0 @@
1
- #[cfg(test)]
2
- mod tests {
3
- use std::cell::RefCell;
4
- use std::ops::Bound;
5
-
6
- use bytes::Bytes;
7
-
8
- use crate::backend::{
9
- BackendError, BackendRangeScan, BackendRead, BufferedRangeScan, CoreProjection, GetOptions,
10
- InMemoryBackend, Key, KeyRange, KeyRef, PointVisitor, Prefix, ProjectedValue,
11
- ProjectedValueRef, ReadOptions, ScanOptions, ScanResult, ScanVisitor, SpaceId, StoredValue,
12
- WriteOptions,
13
- };
14
- use crate::storage::{
15
- PointReadBuffer, PointReadPlan, ScanBuffer, ScanPlan, StorageContext, StorageRead,
16
- StorageSpace,
17
- };
18
-
19
- fn key(bytes: &'static str) -> Key {
20
- Key(Bytes::from_static(bytes.as_bytes()))
21
- }
22
-
23
- fn key_bytes(bytes: &'static [u8]) -> Key {
24
- Key(Bytes::from_static(bytes))
25
- }
26
-
27
- fn value(bytes: &'static str) -> StoredValue {
28
- StoredValue {
29
- bytes: Bytes::from_static(bytes.as_bytes()),
30
- }
31
- }
32
-
33
- fn space(id: u32) -> StorageSpace {
34
- match id {
35
- 1 => StorageSpace::new(SpaceId(1), "test.space.one"),
36
- _ => StorageSpace::new(SpaceId(id), "test.space.other"),
37
- }
38
- }
39
-
40
- #[derive(Default)]
41
- struct SpyRead {
42
- get_many_keys: RefCell<Vec<Key>>,
43
- scan_range: RefCell<Option<KeyRange>>,
44
- scan_range_calls: RefCell<u64>,
45
- }
46
-
47
- impl BackendRead for SpyRead {
48
- type RangeScan<'a> = BufferedRangeScan;
49
-
50
- fn visit_keys<V>(
51
- &self,
52
- keys: &[Key],
53
- opts: GetOptions<'_>,
54
- visitor: &mut V,
55
- ) -> Result<(), BackendError>
56
- where
57
- V: PointVisitor + ?Sized,
58
- {
59
- self.get_many_keys.replace(keys.to_vec());
60
- for (index, key) in keys.iter().enumerate() {
61
- let value = match opts.projection {
62
- CoreProjection::KeyOnly => ProjectedValueRef::KeyOnly,
63
- CoreProjection::FullValue => ProjectedValueRef::FullValue(key.0.as_ref()),
64
- };
65
- visitor.visit(index, key, Some(value))?;
66
- }
67
- Ok(())
68
- }
69
-
70
- fn with_range_scan<T, F>(
71
- &self,
72
- range: KeyRange,
73
- _opts: ScanOptions<'_>,
74
- f: F,
75
- ) -> Result<T, BackendError>
76
- where
77
- F: FnOnce(&mut Self::RangeScan<'_>) -> Result<T, BackendError>,
78
- {
79
- *self.scan_range_calls.borrow_mut() += 1;
80
- self.scan_range.replace(Some(range));
81
- let mut cursor = BufferedRangeScan::default();
82
- f(&mut cursor)
83
- }
84
- }
85
-
86
- #[derive(Default)]
87
- struct RequestedOrderRead {
88
- get_many_keys: RefCell<Vec<Key>>,
89
- }
90
-
91
- impl BackendRead for RequestedOrderRead {
92
- type RangeScan<'a> = BufferedRangeScan;
93
-
94
- fn visit_keys<V>(
95
- &self,
96
- keys: &[Key],
97
- _opts: GetOptions<'_>,
98
- visitor: &mut V,
99
- ) -> Result<(), BackendError>
100
- where
101
- V: PointVisitor + ?Sized,
102
- {
103
- self.get_many_keys.replace(keys.to_vec());
104
- for (index, key) in keys.iter().enumerate() {
105
- let value = (!key.0.ends_with(b"missing"))
106
- .then_some(ProjectedValueRef::FullValue(key.0.as_ref()));
107
- visitor.visit(index, key, value)?;
108
- }
109
- Ok(())
110
- }
111
-
112
- fn with_range_scan<T, F>(
113
- &self,
114
- _range: KeyRange,
115
- _opts: ScanOptions<'_>,
116
- _f: F,
117
- ) -> Result<T, BackendError>
118
- where
119
- F: FnOnce(&mut Self::RangeScan<'_>) -> Result<T, BackendError>,
120
- {
121
- unreachable!("requested-order point-read test does not scan")
122
- }
123
- }
124
-
125
- #[test]
126
- fn point_reads_reconstruct_caller_order_duplicates_and_missing() {
127
- let storage = StorageContext::new(InMemoryBackend::new());
128
- let mut writes = storage.new_write_set();
129
- writes.put(space(1), key("a"), value("A"));
130
- writes.put(space(1), key("b"), value("B"));
131
- storage
132
- .commit_write_set(writes, WriteOptions::default())
133
- .expect("seed");
134
- let read = storage
135
- .begin_read(ReadOptions::default())
136
- .expect("begin read");
137
-
138
- let result = PointReadPlan::new(space(1), &[key("b"), key("missing"), key("a"), key("b")])
139
- .materialize(&read, GetOptions::default())
140
- .expect("caller order");
141
-
142
- assert_eq!(
143
- result.value[0],
144
- Some(ProjectedValue::FullValue(Bytes::from_static(b"B")))
145
- );
146
- assert_eq!(result.value[1], None);
147
- assert_eq!(
148
- result.value[2],
149
- Some(ProjectedValue::FullValue(Bytes::from_static(b"A")))
150
- );
151
- assert_eq!(
152
- result.value[3],
153
- Some(ProjectedValue::FullValue(Bytes::from_static(b"B")))
154
- );
155
- }
156
-
157
- #[test]
158
- fn point_reads_dedupe_before_backend_call() {
159
- let read = crate::storage::StorageReadScope::new(SpyRead::default());
160
- let result = PointReadPlan::new(
161
- space(1),
162
- &[key("b"), key("a"), key("b"), key("missing"), key("missing")],
163
- )
164
- .materialize(&read, GetOptions::default())
165
- .expect("caller order");
166
-
167
- assert_eq!(
168
- read.backend_read().get_many_keys.borrow().as_slice(),
169
- [
170
- space(1).encode_key(&key("b")),
171
- space(1).encode_key(&key("a")),
172
- space(1).encode_key(&key("missing"))
173
- ]
174
- );
175
- assert_eq!(
176
- result.value,
177
- vec![
178
- Some(ProjectedValue::FullValue(space(1).encode_key(&key("b")).0)),
179
- Some(ProjectedValue::FullValue(space(1).encode_key(&key("a")).0)),
180
- Some(ProjectedValue::FullValue(space(1).encode_key(&key("b")).0)),
181
- Some(ProjectedValue::FullValue(
182
- space(1).encode_key(&key("missing")).0
183
- )),
184
- Some(ProjectedValue::FullValue(
185
- space(1).encode_key(&key("missing")).0
186
- )),
187
- ]
188
- );
189
- }
190
-
191
- #[test]
192
- fn point_reads_can_return_values_without_echoing_keys() {
193
- let storage = StorageContext::new(InMemoryBackend::new());
194
- let mut writes = storage.new_write_set();
195
- writes.put(space(1), key("a"), value("A"));
196
- writes.put(space(1), key("b"), value("B"));
197
- storage
198
- .commit_write_set(writes, WriteOptions::default())
199
- .expect("seed");
200
- let read = storage
201
- .begin_read(ReadOptions::default())
202
- .expect("begin read");
203
-
204
- let values = PointReadPlan::new(space(1), &[key("b"), key("missing"), key("a"), key("b")])
205
- .materialize(&read, GetOptions::default())
206
- .expect("caller order values");
207
-
208
- assert_eq!(
209
- values.value,
210
- vec![
211
- Some(ProjectedValue::FullValue(Bytes::from_static(b"B"))),
212
- None,
213
- Some(ProjectedValue::FullValue(Bytes::from_static(b"A"))),
214
- Some(ProjectedValue::FullValue(Bytes::from_static(b"B"))),
215
- ]
216
- );
217
- }
218
-
219
- #[test]
220
- fn point_reads_can_return_indexed_values_without_duplicate_value_clones() {
221
- let storage = StorageContext::new(InMemoryBackend::new());
222
- let mut writes = storage.new_write_set();
223
- writes.put(space(1), key("a"), value("A"));
224
- writes.put(space(1), key("b"), value("B"));
225
- storage
226
- .commit_write_set(writes, WriteOptions::default())
227
- .expect("seed");
228
- let read = storage
229
- .begin_read(ReadOptions::default())
230
- .expect("begin read");
231
-
232
- let plan = PointReadPlan::new(space(1), &[key("b"), key("missing"), key("a"), key("b")]);
233
- let indexed = plan
234
- .collect(&read, GetOptions::default())
235
- .expect("indexed caller order values")
236
- .value;
237
-
238
- assert_eq!(indexed.len(), 4);
239
- assert_eq!(indexed.unique_values.len(), 3);
240
- assert_eq!(indexed.requested_to_unique.to_vec(), vec![0, 1, 2, 0]);
241
- assert_eq!(
242
- indexed.value_at(0),
243
- Some(&ProjectedValue::FullValue(Bytes::from_static(b"B")))
244
- );
245
- assert_eq!(indexed.value_at(1), None);
246
- assert_eq!(
247
- indexed.value_at(2),
248
- Some(&ProjectedValue::FullValue(Bytes::from_static(b"A")))
249
- );
250
- assert_eq!(
251
- indexed.materialize_caller_order(),
252
- vec![
253
- Some(ProjectedValue::FullValue(Bytes::from_static(b"B"))),
254
- None,
255
- Some(ProjectedValue::FullValue(Bytes::from_static(b"A"))),
256
- Some(ProjectedValue::FullValue(Bytes::from_static(b"B"))),
257
- ]
258
- );
259
- }
260
-
261
- #[test]
262
- fn point_request_plan_can_be_reused_for_indexed_reads() {
263
- let storage = StorageContext::new(InMemoryBackend::new());
264
- let mut writes = storage.new_write_set();
265
- writes.put(space(1), key("a"), value("A"));
266
- writes.put(space(1), key("b"), value("B"));
267
- storage
268
- .commit_write_set(writes, WriteOptions::default())
269
- .expect("seed");
270
- let read = storage
271
- .begin_read(ReadOptions::default())
272
- .expect("begin read");
273
- let plan = PointReadPlan::new(space(1), &[key("b"), key("missing"), key("a"), key("b")]);
274
-
275
- assert_eq!(plan.len(), 4);
276
- assert_eq!(
277
- plan.logical_unique_keys,
278
- vec![key("b"), key("missing"), key("a")]
279
- );
280
- assert_eq!(plan.requested_to_unique().to_vec(), vec![0, 1, 2, 0]);
281
-
282
- let result = plan
283
- .collect(&read, GetOptions::default())
284
- .expect("planned indexed read");
285
-
286
- assert_eq!(result.stats.requested_keys, 4);
287
- assert_eq!(result.stats.unique_backend_keys, 3);
288
- assert_eq!(result.stats.backend_calls, 1);
289
- assert_eq!(result.value.requested_to_unique.to_vec(), vec![0, 1, 2, 0]);
290
- assert_eq!(
291
- result.value.value_at(0),
292
- Some(&ProjectedValue::FullValue(Bytes::from_static(b"B")))
293
- );
294
- assert_eq!(result.value.value_at(1), None);
295
- assert_eq!(
296
- result.value.value_at(2),
297
- Some(&ProjectedValue::FullValue(Bytes::from_static(b"A")))
298
- );
299
-
300
- let borrowed = plan
301
- .collect(&read, GetOptions::default())
302
- .expect("borrowed planned indexed read");
303
-
304
- assert_eq!(borrowed.stats.requested_keys, 4);
305
- assert_eq!(
306
- borrowed.value.requested_to_unique,
307
- plan.requested_to_unique()
308
- );
309
- assert_eq!(
310
- borrowed.value.value_at(0),
311
- Some(&ProjectedValue::FullValue(Bytes::from_static(b"B")))
312
- );
313
- assert_eq!(borrowed.value.value_at(1), None);
314
- }
315
-
316
- #[test]
317
- fn planned_point_reads_can_reuse_value_buffer() {
318
- let storage = StorageContext::new(InMemoryBackend::new());
319
- let mut writes = storage.new_write_set();
320
- writes.put(space(1), key("a"), value("A"));
321
- writes.put(space(1), key("b"), value("B"));
322
- writes.put(space(1), key("c"), value("C"));
323
- storage
324
- .commit_write_set(writes, WriteOptions::default())
325
- .expect("seed");
326
- let read = storage
327
- .begin_read(ReadOptions::default())
328
- .expect("begin read");
329
- let first_plan =
330
- PointReadPlan::new(space(1), &[key("b"), key("missing"), key("a"), key("b")]);
331
- let second_plan = PointReadPlan::new(space(1), &[key("c")]);
332
- let mut buffer = PointReadBuffer::new();
333
-
334
- let first = first_plan
335
- .collect_into(&read, GetOptions::default(), &mut buffer)
336
- .expect("first buffered planned indexed read");
337
-
338
- assert_eq!(first.stats.requested_keys, 4);
339
- assert_eq!(first.stats.unique_backend_keys, 3);
340
- assert_eq!(first.value.len(), 4);
341
- assert_eq!(first.value.unique_values.len(), 3);
342
- assert_eq!(
343
- first.value.value_at(0),
344
- Some(&ProjectedValue::FullValue(Bytes::from_static(b"B")))
345
- );
346
- assert_eq!(first.value.value_at(1), None);
347
- assert_eq!(
348
- first.value.value_at(2),
349
- Some(&ProjectedValue::FullValue(Bytes::from_static(b"A")))
350
- );
351
- drop(first);
352
-
353
- let capacity_after_first = buffer.capacity();
354
- let second = second_plan
355
- .collect_into(&read, GetOptions::default(), &mut buffer)
356
- .expect("second buffered planned indexed read");
357
-
358
- assert_eq!(second.stats.requested_keys, 1);
359
- assert_eq!(second.stats.unique_backend_keys, 1);
360
- assert_eq!(second.value.unique_values.len(), 1);
361
- assert_eq!(
362
- second.value.value_at(0),
363
- Some(&ProjectedValue::FullValue(Bytes::from_static(b"C")))
364
- );
365
- drop(second);
366
- assert!(
367
- buffer.capacity() >= capacity_after_first,
368
- "buffer allocation should be retained for reuse"
369
- );
370
- }
371
-
372
- #[test]
373
- fn point_request_plan_can_be_built_from_known_unique_keys() {
374
- let plan = PointReadPlan::from_unique_keys(space(1), vec![key("a"), key("b"), key("c")]);
375
-
376
- assert_eq!(plan.len(), 3);
377
- assert_eq!(plan.logical_unique_keys, vec![key("a"), key("b"), key("c")]);
378
- assert_eq!(plan.requested_to_unique().to_vec(), vec![0, 1, 2]);
379
- }
380
-
381
- #[test]
382
- fn planned_point_reads_use_backend_requested_order_slots() {
383
- let read = crate::storage::StorageReadScope::new(RequestedOrderRead::default());
384
- let plan = PointReadPlan::new(space(1), &[key("b"), key("missing"), key("a"), key("b")]);
385
-
386
- let result = plan
387
- .collect(&read, GetOptions::default())
388
- .expect("borrowed planned indexed read");
389
-
390
- assert_eq!(
391
- read.backend_read().get_many_keys.borrow().as_slice(),
392
- [
393
- space(1).encode_key(&key("b")),
394
- space(1).encode_key(&key("missing")),
395
- space(1).encode_key(&key("a"))
396
- ]
397
- );
398
- assert_eq!(result.stats.requested_keys, 4);
399
- assert_eq!(result.stats.unique_backend_keys, 3);
400
- assert_eq!(result.stats.backend_calls, 1);
401
- assert_eq!(
402
- result.value.value_at(0),
403
- Some(&ProjectedValue::FullValue(Bytes::from_static(
404
- b"\0\0\0\x01b"
405
- )))
406
- );
407
- assert_eq!(result.value.value_at(1), None);
408
- assert_eq!(
409
- result.value.value_at(2),
410
- Some(&ProjectedValue::FullValue(Bytes::from_static(
411
- b"\0\0\0\x01a"
412
- )))
413
- );
414
- }
415
-
416
- #[test]
417
- fn physical_point_request_plan_reuses_encoded_backend_keys() {
418
- let read = crate::storage::StorageReadScope::new(RequestedOrderRead::default());
419
- let plan = PointReadPlan::new(space(1), &[key("b"), key("missing"), key("a"), key("b")]);
420
-
421
- assert_eq!(
422
- plan.logical_unique_keys,
423
- vec![key("b"), key("missing"), key("a")]
424
- );
425
- assert_eq!(
426
- plan.physical_unique_keys,
427
- vec![
428
- space(1).encode_key(&key("b")),
429
- space(1).encode_key(&key("missing")),
430
- space(1).encode_key(&key("a")),
431
- ]
432
- );
433
-
434
- let result = plan
435
- .collect(&read, GetOptions::default())
436
- .expect("borrowed physical planned indexed read");
437
-
438
- assert_eq!(
439
- read.backend_read().get_many_keys.borrow().as_slice(),
440
- plan.physical_unique_keys.as_slice()
441
- );
442
- assert_eq!(result.stats.requested_keys, 4);
443
- assert_eq!(result.stats.unique_backend_keys, 3);
444
- assert_eq!(result.stats.backend_calls, 1);
445
- assert_eq!(
446
- result.value.value_at(0),
447
- Some(&ProjectedValue::FullValue(Bytes::from_static(
448
- b"\0\0\0\x01b"
449
- )))
450
- );
451
- assert_eq!(result.value.value_at(1), None);
452
- }
453
-
454
- #[test]
455
- fn planned_point_reads_can_visit_unique_values_without_materializing_indexed_result() {
456
- let storage = StorageContext::new(InMemoryBackend::new());
457
- let mut writes = storage.new_write_set();
458
- writes.put(space(1), key("a"), value("A"));
459
- writes.put(space(1), key("b"), value("B"));
460
- storage
461
- .commit_write_set(writes, WriteOptions::default())
462
- .expect("seed");
463
- let read = storage
464
- .begin_read(ReadOptions::default())
465
- .expect("begin read");
466
- let plan = PointReadPlan::new(space(1), &[key("b"), key("missing"), key("a"), key("b")]);
467
-
468
- let mut visited = Vec::new();
469
- let stats = plan
470
- .visit(&read, GetOptions::default(), &mut |unique_index: usize,
471
- key: &Key,
472
- value: Option<
473
- ProjectedValueRef<'_>,
474
- >| {
475
- visited.push((
476
- unique_index,
477
- key.clone(),
478
- value.map(|value| value.to_owned()),
479
- ));
480
- Ok(())
481
- })
482
- .expect("visit unique point values");
483
-
484
- assert_eq!(stats.requested_keys, 4);
485
- assert_eq!(stats.unique_backend_keys, 3);
486
- assert_eq!(stats.backend_calls, 1);
487
- assert_eq!(
488
- visited,
489
- vec![
490
- (
491
- 0,
492
- key("b"),
493
- Some(ProjectedValue::FullValue(Bytes::from_static(b"B")))
494
- ),
495
- (1, key("missing"), None),
496
- (
497
- 2,
498
- key("a"),
499
- Some(ProjectedValue::FullValue(Bytes::from_static(b"A")))
500
- ),
501
- ]
502
- );
503
- }
504
-
505
- #[test]
506
- fn point_reads_report_shape_stats() {
507
- let read = crate::storage::StorageReadScope::new(SpyRead::default());
508
- let result = PointReadPlan::new(space(1), &[key("b"), key("a"), key("b"), key("missing")])
509
- .materialize(&read, GetOptions::default())
510
- .expect("caller order");
511
-
512
- assert_eq!(result.value.len(), 4);
513
- assert_eq!(result.stats.requested_keys, 4);
514
- assert_eq!(result.stats.unique_backend_keys, 3);
515
- assert_eq!(result.stats.backend_calls, 1);
516
- assert_eq!(result.stats.prefix_lowered, 0);
517
- assert_eq!(result.stats.range_scan_chunks, 0);
518
- assert_eq!(result.stats.prefix_scan_chunks, 0);
519
- assert_eq!(result.stats.scan_key_only_chunks, 0);
520
- assert_eq!(result.stats.scan_full_value_chunks, 0);
521
- assert_eq!(result.stats.scan_rows, 0);
522
- assert_eq!(result.stats.scan_has_more, 0);
523
- assert_eq!(result.stats.scan_resume_after, 0);
524
- assert_eq!(result.stats.scan_limit_rows_total, 0);
525
- assert_eq!(result.stats.scan_limit_rows_max, 0);
526
- }
527
-
528
- #[test]
529
- fn prefix_scan_lowers_to_range_and_respects_key_only_projection() {
530
- let storage = StorageContext::new(InMemoryBackend::new());
531
- let mut writes = storage.new_write_set();
532
- writes.put(space(1), key("aa"), value("AA"));
533
- writes.put(space(1), key("ab"), value("AB"));
534
- writes.put(space(1), key("b"), value("B"));
535
- storage
536
- .commit_write_set(writes, WriteOptions::default())
537
- .expect("seed");
538
-
539
- let read = storage
540
- .begin_read(ReadOptions::default())
541
- .expect("begin read");
542
- let chunk = ScanPlan::prefix(
543
- space(1),
544
- Prefix {
545
- bytes: Bytes::from_static(b"a"),
546
- },
547
- )
548
- .collect(
549
- &read,
550
- ScanOptions {
551
- projection: CoreProjection::KeyOnly,
552
- limit_rows: 10,
553
- resume_after: None,
554
- },
555
- )
556
- .expect("prefix scan");
557
-
558
- assert_eq!(
559
- chunk
560
- .value
561
- .entries
562
- .into_iter()
563
- .map(|entry| (entry.key, entry.value))
564
- .collect::<Vec<_>>(),
565
- vec![
566
- (key("aa"), ProjectedValue::KeyOnly),
567
- (key("ab"), ProjectedValue::KeyOnly),
568
- ]
569
- );
570
- assert!(!chunk.value.has_more);
571
- }
572
-
573
- #[test]
574
- fn scan_range_into_reuses_storage_buffer() {
575
- let storage = StorageContext::new(InMemoryBackend::new());
576
- let mut writes = storage.new_write_set();
577
- writes.put(space(1), key("aa"), value("AA"));
578
- writes.put(space(1), key("ab"), value("AB"));
579
- writes.put(space(1), key("b"), value("B"));
580
- storage
581
- .commit_write_set(writes, WriteOptions::default())
582
- .expect("seed");
583
-
584
- let read = storage
585
- .begin_read(ReadOptions::default())
586
- .expect("begin read");
587
- let mut buffer = ScanBuffer::with_capacity(8);
588
-
589
- {
590
- let chunk = ScanPlan::range(
591
- space(1),
592
- KeyRange {
593
- lower: Bound::Included(key("a")),
594
- upper: Bound::Excluded(key("b")),
595
- },
596
- )
597
- .collect_into(
598
- &read,
599
- ScanOptions {
600
- projection: CoreProjection::KeyOnly,
601
- limit_rows: 10,
602
- resume_after: None,
603
- },
604
- &mut buffer,
605
- )
606
- .expect("scan range into");
607
-
608
- assert_eq!(
609
- chunk
610
- .value
611
- .entries
612
- .iter()
613
- .map(|entry| (&entry.key, &entry.value))
614
- .collect::<Vec<_>>(),
615
- vec![
616
- (&key("aa"), &ProjectedValue::KeyOnly),
617
- (&key("ab"), &ProjectedValue::KeyOnly),
618
- ]
619
- );
620
- assert!(!chunk.value.has_more);
621
- }
622
-
623
- let capacity_after_first_scan = buffer.capacity();
624
- assert!(capacity_after_first_scan >= 8);
625
-
626
- {
627
- let chunk = ScanPlan::prefix(
628
- space(1),
629
- Prefix {
630
- bytes: Bytes::from_static(b"a"),
631
- },
632
- )
633
- .collect_into(
634
- &read,
635
- ScanOptions {
636
- projection: CoreProjection::FullValue,
637
- limit_rows: 10,
638
- resume_after: None,
639
- },
640
- &mut buffer,
641
- )
642
- .expect("scan prefix into");
643
-
644
- assert_eq!(
645
- chunk
646
- .value
647
- .entries
648
- .iter()
649
- .map(|entry| (&entry.key, &entry.value))
650
- .collect::<Vec<_>>(),
651
- vec![
652
- (
653
- &key("aa"),
654
- &ProjectedValue::FullValue(Bytes::from_static(b"AA"))
655
- ),
656
- (
657
- &key("ab"),
658
- &ProjectedValue::FullValue(Bytes::from_static(b"AB"))
659
- ),
660
- ]
661
- );
662
- assert!(!chunk.value.has_more);
663
- }
664
-
665
- assert_eq!(buffer.capacity(), capacity_after_first_scan);
666
- }
667
-
668
- #[test]
669
- fn visit_scan_prefix_lowers_without_materializing_entries() {
670
- let storage = StorageContext::new(InMemoryBackend::new());
671
- let mut writes = storage.new_write_set();
672
- writes.put(space(1), key("aa"), value("AA"));
673
- writes.put(space(1), key("ab"), value("AB"));
674
- writes.put(space(1), key("b"), value("B"));
675
- storage
676
- .commit_write_set(writes, WriteOptions::default())
677
- .expect("seed");
678
-
679
- let read = storage
680
- .begin_read(ReadOptions::default())
681
- .expect("begin read");
682
- let mut visited = Vec::new();
683
- let result = ScanPlan::prefix(
684
- space(1),
685
- Prefix {
686
- bytes: Bytes::from_static(b"a"),
687
- },
688
- )
689
- .visit(
690
- &read,
691
- ScanOptions {
692
- projection: CoreProjection::FullValue,
693
- limit_rows: 10,
694
- resume_after: None,
695
- },
696
- &mut |key: KeyRef<'_>, value: ProjectedValueRef<'_>| {
697
- visited.push((key.to_owned_key(), value.to_owned()));
698
- Ok(())
699
- },
700
- )
701
- .expect("visit scan prefix");
702
-
703
- assert_eq!(result.value.emitted, 2);
704
- assert!(!result.value.has_more);
705
- assert_eq!(
706
- visited,
707
- vec![
708
- (
709
- key("aa"),
710
- ProjectedValue::FullValue(Bytes::from_static(b"AA"))
711
- ),
712
- (
713
- key("ab"),
714
- ProjectedValue::FullValue(Bytes::from_static(b"AB"))
715
- ),
716
- ]
717
- );
718
- }
719
-
720
- #[test]
721
- fn prefix_scan_lowers_expected_range() {
722
- let read = crate::storage::StorageReadScope::new(SpyRead::default());
723
- ScanPlan::prefix(
724
- space(1),
725
- Prefix {
726
- bytes: Bytes::from_static(b"a\xff"),
727
- },
728
- )
729
- .collect(&read, ScanOptions::default())
730
- .expect("prefix scan");
731
-
732
- let range = read
733
- .backend_read()
734
- .scan_range
735
- .borrow()
736
- .clone()
737
- .expect("range captured");
738
- assert_eq!(
739
- range.lower,
740
- Bound::Included(space(1).encode_key(&key_bytes(b"a\xff")))
741
- );
742
- assert_eq!(range.upper, Bound::Excluded(space(1).encode_key(&key("b"))));
743
- }
744
-
745
- #[test]
746
- fn scan_range_reports_shape_stats() {
747
- let read = crate::storage::StorageReadScope::new(SpyRead::default());
748
- let result = ScanPlan::range(
749
- space(1),
750
- KeyRange {
751
- lower: Bound::Included(key("a")),
752
- upper: Bound::Excluded(key("z")),
753
- },
754
- )
755
- .collect(&read, ScanOptions::default())
756
- .expect("scan range");
757
-
758
- assert_eq!(result.stats.requested_keys, 0);
759
- assert_eq!(result.stats.unique_backend_keys, 0);
760
- assert_eq!(result.stats.backend_calls, 1);
761
- assert_eq!(result.stats.prefix_lowered, 0);
762
- assert_eq!(result.stats.range_scan_chunks, 1);
763
- assert_eq!(result.stats.prefix_scan_chunks, 0);
764
- assert_eq!(result.stats.scan_key_only_chunks, 0);
765
- assert_eq!(result.stats.scan_full_value_chunks, 1);
766
- assert_eq!(result.stats.scan_rows, 0);
767
- assert_eq!(result.stats.scan_has_more, 0);
768
- assert_eq!(result.stats.scan_resume_after, 0);
769
- assert_eq!(result.stats.scan_limit_rows_total, 1024);
770
- assert_eq!(result.stats.scan_limit_rows_max, 1024);
771
- }
772
-
773
- #[test]
774
- fn prefix_scan_reports_shape_stats() {
775
- let read = crate::storage::StorageReadScope::new(SpyRead::default());
776
- let result = ScanPlan::prefix(
777
- space(1),
778
- Prefix {
779
- bytes: Bytes::from_static(b"a"),
780
- },
781
- )
782
- .collect(&read, ScanOptions::default())
783
- .expect("prefix scan");
784
-
785
- assert_eq!(result.stats.requested_keys, 0);
786
- assert_eq!(result.stats.unique_backend_keys, 0);
787
- assert_eq!(result.stats.backend_calls, 1);
788
- assert_eq!(result.stats.prefix_lowered, 1);
789
- assert_eq!(result.stats.range_scan_chunks, 0);
790
- assert_eq!(result.stats.prefix_scan_chunks, 1);
791
- assert_eq!(result.stats.scan_full_value_chunks, 1);
792
- assert_eq!(*read.backend_read().scan_range_calls.borrow(), 1);
793
- }
794
-
795
- #[test]
796
- fn visit_scan_reports_trace_stats() {
797
- let storage = StorageContext::new(InMemoryBackend::new());
798
- let mut writes = storage.new_write_set();
799
- writes.put(space(1), key("aa"), value("AA"));
800
- writes.put(space(1), key("ab"), value("AB"));
801
- writes.put(space(1), key("ac"), value("AC"));
802
- storage
803
- .commit_write_set(writes, WriteOptions::default())
804
- .expect("seed");
805
- let read = storage
806
- .begin_read(ReadOptions::default())
807
- .expect("begin read");
808
-
809
- let result = ScanPlan::prefix(
810
- space(1),
811
- Prefix {
812
- bytes: Bytes::from_static(b"a"),
813
- },
814
- )
815
- .visit(
816
- &read,
817
- ScanOptions {
818
- projection: CoreProjection::KeyOnly,
819
- limit_rows: 2,
820
- resume_after: Some(&key("aa")),
821
- },
822
- &mut |_key: KeyRef<'_>, value: ProjectedValueRef<'_>| {
823
- assert!(matches!(value, ProjectedValueRef::KeyOnly));
824
- Ok(())
825
- },
826
- )
827
- .expect("visit scan prefix with stats");
828
-
829
- assert_eq!(result.value.emitted, 2);
830
- assert!(!result.value.has_more);
831
- assert_eq!(result.stats.backend_calls, 1);
832
- assert_eq!(result.stats.prefix_lowered, 1);
833
- assert_eq!(result.stats.range_scan_chunks, 0);
834
- assert_eq!(result.stats.prefix_scan_chunks, 1);
835
- assert_eq!(result.stats.scan_key_only_chunks, 1);
836
- assert_eq!(result.stats.scan_full_value_chunks, 0);
837
- assert_eq!(result.stats.scan_rows, 2);
838
- assert_eq!(result.stats.scan_has_more, 0);
839
- assert_eq!(result.stats.scan_resume_after, 1);
840
- assert_eq!(result.stats.scan_limit_rows_total, 2);
841
- assert_eq!(result.stats.scan_limit_rows_max, 2);
842
- }
843
-
844
- #[test]
845
- fn prefix_scan_limit_zero_reports_no_backend_call() {
846
- let read = crate::storage::StorageReadScope::new(SpyRead::default());
847
- let result = ScanPlan::prefix(
848
- space(1),
849
- Prefix {
850
- bytes: Bytes::from_static(b"a"),
851
- },
852
- )
853
- .collect(
854
- &read,
855
- ScanOptions {
856
- limit_rows: 0,
857
- ..ScanOptions::default()
858
- },
859
- )
860
- .expect("prefix scan");
861
-
862
- assert!(result.value.entries.is_empty());
863
- assert_eq!(result.stats.backend_calls, 0);
864
- assert_eq!(result.stats.prefix_lowered, 1);
865
- assert_eq!(*read.backend_read().scan_range_calls.borrow(), 0);
866
- }
867
- }