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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (234) hide show
  1. package/README.md +1 -1
  2. package/SKILL.md +65 -64
  3. package/dist/engine-wasm/index.js +4 -4
  4. package/dist/engine-wasm/wasm/lix_engine.d.ts +5 -5
  5. package/dist/engine-wasm/wasm/lix_engine.js +130 -118
  6. package/dist/engine-wasm/wasm/lix_engine.wasm +0 -0
  7. package/dist/engine-wasm/wasm/lix_engine.wasm.d.ts +9 -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 +33 -26
  11. package/dist/open-lix.js +10 -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 +803 -148
  94. package/dist-engine-src/src/session/create_branch.rs +94 -0
  95. package/dist-engine-src/src/session/execute.rs +223 -83
  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 +15 -12
  102. package/dist-engine-src/src/session/switch_branch.rs +113 -0
  103. package/dist-engine-src/src/session/transaction.rs +495 -14
  104. package/dist-engine-src/src/sql2/{classify.rs → bind/classify.rs} +3 -75
  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} +71 -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 +1 -1
  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 +28 -23
  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 -110
  218. package/dist-engine-src/src/sql2/entity_provider.rs +0 -3211
  219. package/dist-engine-src/src/sql2/execute.rs +0 -3533
  220. package/dist-engine-src/src/sql2/public_bind/assignment.rs +0 -46
  221. package/dist-engine-src/src/sql2/public_bind/capability.rs +0 -41
  222. package/dist-engine-src/src/sql2/public_bind/dml.rs +0 -172
  223. package/dist-engine-src/src/sql2/public_bind/mod.rs +0 -26
  224. package/dist-engine-src/src/sql2/public_bind/table.rs +0 -168
  225. package/dist-engine-src/src/sql2/version_scope.rs +0 -394
  226. package/dist-engine-src/src/storage/types.rs +0 -501
  227. package/dist-engine-src/src/tracked_state/by_file_index.rs +0 -98
  228. package/dist-engine-src/src/tracked_state/materializer.rs +0 -488
  229. package/dist-engine-src/src/transaction/live_state_overlay.rs +0 -35
  230. package/dist-engine-src/src/version/lifecycle.rs +0 -221
  231. package/dist-engine-src/src/version/mod.rs +0 -13
  232. package/dist-engine-src/src/version/refs.rs +0 -330
  233. package/dist-engine-src/src/version/stage_rows.rs +0 -67
  234. package/dist-engine-src/src/version/types.rs +0 -21
@@ -10,7 +10,7 @@ use datafusion::arrow::compute::{and, filter_record_batch};
10
10
  use datafusion::arrow::datatypes::{DataType, Field, Schema, SchemaRef};
11
11
  use datafusion::arrow::record_batch::RecordBatch;
12
12
  use datafusion::catalog::{Session, TableProvider};
13
- use datafusion::common::{not_impl_err, DFSchema, DataFusionError, Result, ScalarValue};
13
+ use datafusion::common::{not_impl_err, DFSchema, DataFusionError, Result, ScalarValue, SchemaExt};
14
14
  use datafusion::datasource::TableType;
15
15
  use datafusion::execution::TaskContext;
16
16
  use datafusion::logical_expr::dml::InsertOp;
@@ -25,90 +25,108 @@ use datafusion::prelude::SessionContext;
25
25
  use futures_util::{stream, TryStreamExt};
26
26
  use serde::Deserialize;
27
27
 
28
+ use crate::branch::BranchRefReader;
28
29
  use crate::functions::FunctionProviderHandle;
29
30
  use crate::live_state::MaterializedLiveStateRow;
30
31
  use crate::live_state::{
31
32
  LiveStateFilter, LiveStateProjection, LiveStateReader, LiveStateScanRequest,
32
33
  };
34
+ use crate::sql2::branch_scope::{
35
+ explicit_branch_ids_from_dml_filters, resolve_provider_branch_ids, resolve_write_branch_scope,
36
+ BranchBinding,
37
+ };
33
38
  use crate::sql2::dml::{InsertExec, InsertSink};
34
39
  use crate::sql2::filesystem_predicates::{
35
40
  canonicalize_filesystem_path_filters, FilesystemPathKind,
36
41
  };
37
- use crate::sql2::predicate_typecheck::validate_json_predicate_filters;
38
- use crate::sql2::version_scope::{
39
- explicit_version_ids_from_dml_filters, resolve_provider_version_ids,
40
- resolve_write_version_scope, VersionBinding,
42
+ use crate::sql2::predicate_typecheck::{
43
+ canonicalize_json_identity_text_filters, validate_json_predicate_filters,
41
44
  };
42
45
  use crate::sql2::write_normalization::{InsertCell, SqlCell, UpdateAssignmentValues};
43
46
  use crate::transaction::types::{
44
47
  LogicalPrimaryKey, TransactionJson, TransactionWriteOperation, TransactionWriteOrigin,
45
48
  TransactionWriteRow,
46
49
  };
47
- use crate::version::VersionRefReader;
48
50
  use crate::{parse_row_metadata_value, serialize_row_metadata, LixError};
49
51
 
50
- use super::filesystem_planner::{
52
+ use crate::sql2::filesystem_planner::{
51
53
  directory_descriptor_write_row, directory_path_resolvers_from_state_rows,
52
54
  filesystem_storage_scope_key, plan_recursive_directory_delete, DirectoryDescriptorWriteIntent,
53
55
  DirectoryPathResolver, FilesystemDeletePlan, FilesystemRowContext,
54
56
  };
55
- use super::filesystem_visibility::VisibleFilesystem;
56
- use super::result_metadata::json_field;
57
+ use crate::sql2::filesystem_visibility::VisibleFilesystem;
58
+ use crate::sql2::result_metadata::json_field;
57
59
  use crate::sql2::{
58
- SqlWriteContext, WriteAccess, WriteContextLiveStateReader, WriteContextVersionRefReader,
60
+ SqlWriteContext, WriteAccess, WriteContextBranchRefReader, WriteContextLiveStateReader,
59
61
  };
60
62
  use crate::transaction::types::{TransactionWrite, TransactionWriteMode};
61
63
 
62
64
  const DIRECTORY_SCHEMA_KEY: &str = "lix_directory_descriptor";
63
65
  const FILE_DESCRIPTOR_SCHEMA_KEY: &str = "lix_file_descriptor";
64
66
 
65
- pub(crate) async fn register_lix_directory_providers(
67
+ pub(super) async fn register_lix_directory_active_provider(
66
68
  session: &SessionContext,
67
- active_version_id: &str,
69
+ surface_name: &str,
70
+ active_branch_id: &str,
68
71
  live_state: Arc<dyn LiveStateReader>,
69
- version_ref: Arc<dyn VersionRefReader>,
72
+ branch_ref: Arc<dyn BranchRefReader>,
70
73
  functions: FunctionProviderHandle,
71
74
  ) -> Result<(), LixError> {
72
75
  session
73
76
  .register_table(
74
- "lix_directory_by_version",
75
- Arc::new(LixDirectoryProvider::by_version(
76
- Arc::clone(&live_state),
77
- Arc::clone(&version_ref),
78
- functions.clone(),
77
+ surface_name,
78
+ Arc::new(LixDirectoryProvider::active_branch(
79
+ active_branch_id,
80
+ live_state,
81
+ branch_ref,
82
+ functions,
79
83
  )),
80
84
  )
81
85
  .map_err(datafusion_error_to_lix_error)?;
86
+ Ok(())
87
+ }
88
+
89
+ pub(super) async fn register_lix_directory_by_branch_provider(
90
+ session: &SessionContext,
91
+ surface_name: &str,
92
+ live_state: Arc<dyn LiveStateReader>,
93
+ branch_ref: Arc<dyn BranchRefReader>,
94
+ functions: FunctionProviderHandle,
95
+ ) -> Result<(), LixError> {
82
96
  session
83
97
  .register_table(
84
- "lix_directory",
85
- Arc::new(LixDirectoryProvider::active_version(
86
- active_version_id,
87
- live_state,
88
- version_ref,
89
- functions,
98
+ surface_name,
99
+ Arc::new(LixDirectoryProvider::by_branch(
100
+ live_state, branch_ref, functions,
90
101
  )),
91
102
  )
92
103
  .map_err(datafusion_error_to_lix_error)?;
93
104
  Ok(())
94
105
  }
95
106
 
96
- pub(crate) async fn register_lix_directory_write_providers(
107
+ pub(super) async fn register_by_branch_write_provider(
97
108
  session: &SessionContext,
109
+ surface_name: &str,
98
110
  write_ctx: SqlWriteContext,
99
111
  ) -> Result<(), LixError> {
100
112
  session
101
113
  .register_table(
102
- "lix_directory_by_version",
103
- Arc::new(LixDirectoryProvider::by_version_with_write(
104
- write_ctx.clone(),
105
- )),
114
+ surface_name,
115
+ Arc::new(LixDirectoryProvider::by_branch_with_write(write_ctx)),
106
116
  )
107
117
  .map_err(datafusion_error_to_lix_error)?;
118
+ Ok(())
119
+ }
120
+
121
+ pub(super) async fn register_active_write_provider(
122
+ session: &SessionContext,
123
+ surface_name: &str,
124
+ write_ctx: SqlWriteContext,
125
+ ) -> Result<(), LixError> {
108
126
  session
109
127
  .register_table(
110
- "lix_directory",
111
- Arc::new(LixDirectoryProvider::active_version_with_write(write_ctx)),
128
+ surface_name,
129
+ Arc::new(LixDirectoryProvider::active_branch_with_write(write_ctx)),
112
130
  )
113
131
  .map_err(datafusion_error_to_lix_error)?;
114
132
  Ok(())
@@ -117,10 +135,10 @@ pub(crate) async fn register_lix_directory_write_providers(
117
135
  pub(crate) struct LixDirectoryProvider {
118
136
  schema: SchemaRef,
119
137
  live_state: Arc<dyn LiveStateReader>,
120
- version_ref: Arc<dyn VersionRefReader>,
138
+ branch_ref: Arc<dyn BranchRefReader>,
121
139
  write_access: WriteAccess,
122
140
  functions: FunctionProviderHandle,
123
- version_binding: VersionBinding,
141
+ branch_binding: BranchBinding,
124
142
  }
125
143
 
126
144
  impl std::fmt::Debug for LixDirectoryProvider {
@@ -130,63 +148,63 @@ impl std::fmt::Debug for LixDirectoryProvider {
130
148
  }
131
149
 
132
150
  impl LixDirectoryProvider {
133
- fn active_version(
134
- active_version_id: impl Into<String>,
151
+ fn active_branch(
152
+ active_branch_id: impl Into<String>,
135
153
  live_state: Arc<dyn LiveStateReader>,
136
- version_ref: Arc<dyn VersionRefReader>,
154
+ branch_ref: Arc<dyn BranchRefReader>,
137
155
  functions: FunctionProviderHandle,
138
156
  ) -> Self {
139
157
  Self {
140
158
  schema: lix_directory_schema(),
141
159
  live_state,
142
- version_ref,
160
+ branch_ref,
143
161
  write_access: WriteAccess::read_only(),
144
162
  functions,
145
- version_binding: VersionBinding::active(active_version_id),
163
+ branch_binding: BranchBinding::active(active_branch_id),
146
164
  }
147
165
  }
148
166
 
149
- fn active_version_with_write(write_ctx: SqlWriteContext) -> Self {
150
- let active_version_id = write_ctx.active_version_id();
167
+ fn active_branch_with_write(write_ctx: SqlWriteContext) -> Self {
168
+ let active_branch_id = write_ctx.active_branch_id();
151
169
  let functions = write_ctx.functions();
152
170
  let live_state = Arc::new(WriteContextLiveStateReader::new(write_ctx.clone()));
153
- let version_ref = Arc::new(WriteContextVersionRefReader::new(write_ctx.clone()));
171
+ let branch_ref = Arc::new(WriteContextBranchRefReader::new(write_ctx.clone()));
154
172
  Self {
155
173
  schema: lix_directory_schema(),
156
174
  live_state,
157
- version_ref,
175
+ branch_ref,
158
176
  write_access: WriteAccess::write(write_ctx),
159
177
  functions,
160
- version_binding: VersionBinding::active(active_version_id),
178
+ branch_binding: BranchBinding::active(active_branch_id),
161
179
  }
162
180
  }
163
181
 
164
- fn by_version(
182
+ fn by_branch(
165
183
  live_state: Arc<dyn LiveStateReader>,
166
- version_ref: Arc<dyn VersionRefReader>,
184
+ branch_ref: Arc<dyn BranchRefReader>,
167
185
  functions: FunctionProviderHandle,
168
186
  ) -> Self {
169
187
  Self {
170
- schema: lix_directory_by_version_schema(),
188
+ schema: lix_directory_by_branch_schema(),
171
189
  live_state,
172
- version_ref,
190
+ branch_ref,
173
191
  write_access: WriteAccess::read_only(),
174
192
  functions,
175
- version_binding: VersionBinding::explicit(),
193
+ branch_binding: BranchBinding::explicit(),
176
194
  }
177
195
  }
178
196
 
179
- fn by_version_with_write(write_ctx: SqlWriteContext) -> Self {
197
+ fn by_branch_with_write(write_ctx: SqlWriteContext) -> Self {
180
198
  let functions = write_ctx.functions();
181
199
  let live_state = Arc::new(WriteContextLiveStateReader::new(write_ctx.clone()));
182
- let version_ref = Arc::new(WriteContextVersionRefReader::new(write_ctx.clone()));
200
+ let branch_ref = Arc::new(WriteContextBranchRefReader::new(write_ctx.clone()));
183
201
  Self {
184
- schema: lix_directory_by_version_schema(),
202
+ schema: lix_directory_by_branch_schema(),
185
203
  live_state,
186
- version_ref,
204
+ branch_ref,
187
205
  write_access: WriteAccess::write(write_ctx),
188
206
  functions,
189
- version_binding: VersionBinding::explicit(),
207
+ branch_binding: BranchBinding::explicit(),
190
208
  }
191
209
  }
192
210
  }
@@ -225,24 +243,14 @@ impl TableProvider for LixDirectoryProvider {
225
243
  let projected_schema = projected_schema(&self.schema, projection)?;
226
244
  let scan_limit = if filters.is_empty() { limit } else { None };
227
245
  let mut request = lix_directory_scan_request(
228
- self.version_binding.active_version_id(),
246
+ self.branch_binding.active_branch_id(),
229
247
  Some(projected_schema.as_ref()),
230
248
  scan_limit,
231
249
  );
232
- if self.write_access.is_write() && matches!(self.version_binding, VersionBinding::Explicit)
233
- {
234
- request.filter.version_ids = explicit_version_ids_from_dml_filters(filters);
235
- if request.filter.version_ids.is_empty() {
236
- return Err(DataFusionError::Plan(
237
- "DELETE FROM lix_directory_by_version requires an explicit lixcol_version_id predicate"
238
- .to_string(),
239
- ));
240
- }
241
- }
242
- request.filter.version_ids = resolve_provider_version_ids(
243
- self.version_ref.as_ref(),
244
- &self.version_binding,
245
- request.filter.version_ids,
250
+ request.filter.branch_ids = resolve_provider_branch_ids(
251
+ self.branch_ref.as_ref(),
252
+ &self.branch_binding,
253
+ request.filter.branch_ids,
246
254
  )
247
255
  .await
248
256
  .map_err(lix_error_to_datafusion_error)?;
@@ -273,16 +281,15 @@ impl TableProvider for LixDirectoryProvider {
273
281
  if insert_op != InsertOp::Append {
274
282
  return not_impl_err!("{insert_op} not implemented for lix_directory yet");
275
283
  }
276
-
277
284
  let write_ctx = self
278
285
  .write_access
279
286
  .require_write("INSERT into lix_directory")?;
280
-
287
+ self.schema
288
+ .logically_equivalent_names_and_types(&input.schema())?;
281
289
  let sink = LixDirectoryInsertSink::new(
282
- input.schema(),
283
- write_ctx.clone(),
290
+ write_ctx,
284
291
  self.functions.clone(),
285
- self.version_binding.clone(),
292
+ self.branch_binding.clone(),
286
293
  );
287
294
  Ok(Arc::new(InsertExec::new(input, Arc::new(sink))))
288
295
  }
@@ -295,31 +302,29 @@ impl TableProvider for LixDirectoryProvider {
295
302
  let write_ctx = self
296
303
  .write_access
297
304
  .require_write("DELETE FROM lix_directory")?;
298
-
299
- let df_schema = DFSchema::try_from(Arc::clone(&self.schema))?;
300
305
  let filters =
301
306
  canonicalize_filesystem_path_filters(&filters, FilesystemPathKind::Directory)?;
307
+ let filters = canonicalize_json_identity_text_filters(self.schema.as_ref(), &filters)?;
308
+ let df_schema = DFSchema::try_from(Arc::clone(&self.schema))?;
302
309
  validate_json_predicate_filters(self.schema.as_ref(), &filters)?;
303
310
  let physical_filters = filters
304
311
  .iter()
305
312
  .map(|expr| create_physical_expr(expr, &df_schema, state.execution_props()))
306
313
  .collect::<Result<Vec<_>>>()?;
307
314
  let mut request =
308
- lix_directory_scan_request(self.version_binding.active_version_id(), None, None);
309
- if matches!(self.version_binding, VersionBinding::Explicit) {
310
- request.filter.version_ids = explicit_version_ids_from_dml_filters(&filters);
311
- if request.filter.version_ids.is_empty() {
312
- return Err(DataFusionError::Plan(
313
- "DELETE FROM lix_directory_by_version requires an explicit lixcol_version_id predicate"
314
- .to_string(),
315
- ));
316
- }
317
- }
318
-
315
+ lix_directory_scan_request(self.branch_binding.active_branch_id(), None, None);
316
+ request.filter.branch_ids = explicit_branch_ids_from_dml_filters(&filters);
317
+ request.filter.branch_ids = resolve_provider_branch_ids(
318
+ self.branch_ref.as_ref(),
319
+ &self.branch_binding,
320
+ request.filter.branch_ids,
321
+ )
322
+ .await
323
+ .map_err(lix_error_to_datafusion_error)?;
319
324
  Ok(Arc::new(LixDirectoryDeleteExec::new(
320
- write_ctx.clone(),
325
+ write_ctx,
321
326
  Arc::clone(&self.schema),
322
- self.version_binding.clone(),
327
+ self.branch_binding.clone(),
323
328
  request,
324
329
  physical_filters,
325
330
  )))
@@ -332,10 +337,12 @@ impl TableProvider for LixDirectoryProvider {
332
337
  filters: Vec<Expr>,
333
338
  ) -> Result<Arc<dyn ExecutionPlan>> {
334
339
  let write_ctx = self.write_access.require_write("UPDATE lix_directory")?;
335
-
336
340
  validate_lix_directory_update_assignments(&self.schema, &assignments)?;
337
-
341
+ let filters =
342
+ canonicalize_filesystem_path_filters(&filters, FilesystemPathKind::Directory)?;
343
+ let filters = canonicalize_json_identity_text_filters(self.schema.as_ref(), &filters)?;
338
344
  let df_schema = DFSchema::try_from(Arc::clone(&self.schema))?;
345
+ validate_json_predicate_filters(self.schema.as_ref(), &filters)?;
339
346
  let physical_assignments = assignments
340
347
  .iter()
341
348
  .map(|(column_name, expr)| {
@@ -345,20 +352,24 @@ impl TableProvider for LixDirectoryProvider {
345
352
  ))
346
353
  })
347
354
  .collect::<Result<Vec<_>>>()?;
348
- let filters =
349
- canonicalize_filesystem_path_filters(&filters, FilesystemPathKind::Directory)?;
350
- validate_json_predicate_filters(self.schema.as_ref(), &filters)?;
351
355
  let physical_filters = filters
352
356
  .iter()
353
357
  .map(|expr| create_physical_expr(expr, &df_schema, state.execution_props()))
354
358
  .collect::<Result<Vec<_>>>()?;
355
- let request =
356
- lix_directory_scan_request(self.version_binding.active_version_id(), None, None);
357
-
359
+ let mut request =
360
+ lix_directory_scan_request(self.branch_binding.active_branch_id(), None, None);
361
+ request.filter.branch_ids = explicit_branch_ids_from_dml_filters(&filters);
362
+ request.filter.branch_ids = resolve_provider_branch_ids(
363
+ self.branch_ref.as_ref(),
364
+ &self.branch_binding,
365
+ request.filter.branch_ids,
366
+ )
367
+ .await
368
+ .map_err(lix_error_to_datafusion_error)?;
358
369
  Ok(Arc::new(LixDirectoryUpdateExec::new(
359
- write_ctx.clone(),
370
+ write_ctx,
360
371
  Arc::clone(&self.schema),
361
- self.version_binding.clone(),
372
+ self.branch_binding.clone(),
362
373
  request,
363
374
  physical_assignments,
364
375
  physical_filters,
@@ -369,7 +380,7 @@ impl TableProvider for LixDirectoryProvider {
369
380
  struct LixDirectoryInsertSink {
370
381
  write_ctx: SqlWriteContext,
371
382
  functions: FunctionProviderHandle,
372
- version_binding: VersionBinding,
383
+ branch_binding: BranchBinding,
373
384
  surface_name: &'static str,
374
385
  }
375
386
 
@@ -381,16 +392,15 @@ impl std::fmt::Debug for LixDirectoryInsertSink {
381
392
 
382
393
  impl LixDirectoryInsertSink {
383
394
  fn new(
384
- _schema: SchemaRef,
385
395
  write_ctx: SqlWriteContext,
386
396
  functions: FunctionProviderHandle,
387
- version_binding: VersionBinding,
397
+ branch_binding: BranchBinding,
388
398
  ) -> Self {
389
- let surface_name = lix_directory_surface_name(&version_binding);
399
+ let surface_name = lix_directory_surface_name(&branch_binding);
390
400
  Self {
391
401
  write_ctx,
392
402
  functions,
393
- version_binding,
403
+ branch_binding,
394
404
  surface_name,
395
405
  }
396
406
  }
@@ -422,7 +432,7 @@ impl InsertSink for LixDirectoryInsertSink {
422
432
  path_resolvers = Some(
423
433
  directory_path_resolvers_from_live_state(
424
434
  Arc::new(WriteContextLiveStateReader::new(self.write_ctx.clone())),
425
- self.version_binding.active_version_id(),
435
+ self.branch_binding.active_branch_id(),
426
436
  )
427
437
  .await
428
438
  .map_err(lix_error_to_datafusion_error)?,
@@ -438,7 +448,7 @@ impl InsertSink for LixDirectoryInsertSink {
438
448
  if record_batch_has_non_null_column(&batch, "path")? {
439
449
  rows.extend(lix_directory_write_rows_from_batch_with_path_resolvers(
440
450
  &batch,
441
- self.version_binding.active_version_id(),
451
+ self.branch_binding.active_branch_id(),
442
452
  self.surface_name,
443
453
  path_resolvers
444
454
  .as_mut()
@@ -449,7 +459,7 @@ impl InsertSink for LixDirectoryInsertSink {
449
459
  rows.extend(
450
460
  lix_directory_write_rows_from_batch_with_options_and_path_resolvers(
451
461
  &batch,
452
- self.version_binding.active_version_id(),
462
+ self.branch_binding.active_branch_id(),
453
463
  self.surface_name,
454
464
  true,
455
465
  path_resolvers.as_mut(),
@@ -471,10 +481,10 @@ impl InsertSink for LixDirectoryInsertSink {
471
481
  }
472
482
  }
473
483
 
474
- fn lix_directory_surface_name(version_binding: &VersionBinding) -> &'static str {
475
- match version_binding {
476
- VersionBinding::Active { .. } => "lix_directory",
477
- VersionBinding::Explicit => "lix_directory_by_version",
484
+ fn lix_directory_surface_name(branch_binding: &BranchBinding) -> &'static str {
485
+ match branch_binding {
486
+ BranchBinding::Active { .. } => "lix_directory",
487
+ BranchBinding::Explicit => "lix_directory_by_branch",
478
488
  }
479
489
  }
480
490
 
@@ -482,7 +492,7 @@ fn lix_directory_surface_name(version_binding: &VersionBinding) -> &'static str
482
492
  struct LixDirectoryDeleteExec {
483
493
  write_ctx: SqlWriteContext,
484
494
  table_schema: SchemaRef,
485
- version_binding: VersionBinding,
495
+ branch_binding: BranchBinding,
486
496
  request: LiveStateScanRequest,
487
497
  filters: Vec<Arc<dyn PhysicalExpr>>,
488
498
  result_schema: SchemaRef,
@@ -499,7 +509,7 @@ impl LixDirectoryDeleteExec {
499
509
  fn new(
500
510
  write_ctx: SqlWriteContext,
501
511
  table_schema: SchemaRef,
502
- version_binding: VersionBinding,
512
+ branch_binding: BranchBinding,
503
513
  request: LiveStateScanRequest,
504
514
  filters: Vec<Arc<dyn PhysicalExpr>>,
505
515
  ) -> Self {
@@ -513,7 +523,7 @@ impl LixDirectoryDeleteExec {
513
523
  Self {
514
524
  write_ctx,
515
525
  table_schema,
516
- version_binding,
526
+ branch_binding,
517
527
  request,
518
528
  filters,
519
529
  result_schema,
@@ -574,7 +584,7 @@ impl ExecutionPlan for LixDirectoryDeleteExec {
574
584
  }
575
585
  let write_ctx = self.write_ctx.clone();
576
586
  let table_schema = Arc::clone(&self.table_schema);
577
- let version_binding = self.version_binding.clone();
587
+ let branch_binding = self.branch_binding.clone();
578
588
  let request = self.request.clone();
579
589
  let filters = self.filters.clone();
580
590
  let result_schema = Arc::clone(&self.result_schema);
@@ -588,17 +598,15 @@ impl ExecutionPlan for LixDirectoryDeleteExec {
588
598
  let source_batch = lix_directory_record_batch(&table_schema, rows)
589
599
  .map_err(lix_error_to_datafusion_error)?;
590
600
  let matched_batch = filter_lix_directory_batch(source_batch, &filters)?;
591
- let version_ids = directory_version_ids_from_batch(
592
- &matched_batch,
593
- version_binding.active_version_id(),
594
- )?;
601
+ let branch_ids =
602
+ directory_branch_ids_from_batch(&matched_batch, branch_binding.active_branch_id())?;
595
603
  let mut visible_filesystems = BTreeMap::new();
596
- for version_id in version_ids {
604
+ for branch_id in branch_ids {
597
605
  visible_filesystems.insert(
598
- version_id.clone(),
606
+ branch_id.clone(),
599
607
  VisibleFilesystem::load(
600
608
  Arc::new(WriteContextLiveStateReader::new(write_ctx.clone())),
601
- &version_id,
609
+ &branch_id,
602
610
  )
603
611
  .await
604
612
  .map_err(lix_error_to_datafusion_error)?,
@@ -606,7 +614,7 @@ impl ExecutionPlan for LixDirectoryDeleteExec {
606
614
  }
607
615
  let (write_rows, count) = lix_directory_recursive_delete_rows_from_batch(
608
616
  &matched_batch,
609
- version_binding.active_version_id(),
617
+ branch_binding.active_branch_id(),
610
618
  &visible_filesystems,
611
619
  )?;
612
620
 
@@ -637,7 +645,7 @@ impl ExecutionPlan for LixDirectoryDeleteExec {
637
645
  struct LixDirectoryUpdateExec {
638
646
  write_ctx: SqlWriteContext,
639
647
  table_schema: SchemaRef,
640
- version_binding: VersionBinding,
648
+ branch_binding: BranchBinding,
641
649
  request: LiveStateScanRequest,
642
650
  assignments: Vec<(String, Arc<dyn PhysicalExpr>)>,
643
651
  filters: Vec<Arc<dyn PhysicalExpr>>,
@@ -655,7 +663,7 @@ impl LixDirectoryUpdateExec {
655
663
  fn new(
656
664
  write_ctx: SqlWriteContext,
657
665
  table_schema: SchemaRef,
658
- version_binding: VersionBinding,
666
+ branch_binding: BranchBinding,
659
667
  request: LiveStateScanRequest,
660
668
  assignments: Vec<(String, Arc<dyn PhysicalExpr>)>,
661
669
  filters: Vec<Arc<dyn PhysicalExpr>>,
@@ -670,7 +678,7 @@ impl LixDirectoryUpdateExec {
670
678
  Self {
671
679
  write_ctx,
672
680
  table_schema,
673
- version_binding,
681
+ branch_binding,
674
682
  request,
675
683
  assignments,
676
684
  filters,
@@ -737,7 +745,7 @@ impl ExecutionPlan for LixDirectoryUpdateExec {
737
745
  }
738
746
  let write_ctx = self.write_ctx.clone();
739
747
  let table_schema = Arc::clone(&self.table_schema);
740
- let version_binding = self.version_binding.clone();
748
+ let branch_binding = self.branch_binding.clone();
741
749
  let request = self.request.clone();
742
750
  let assignments = self.assignments.clone();
743
751
  let filters = self.filters.clone();
@@ -754,14 +762,14 @@ impl ExecutionPlan for LixDirectoryUpdateExec {
754
762
  let matched_batch = filter_lix_directory_batch(source_batch, &filters)?;
755
763
  let mut path_resolvers = directory_path_resolvers_from_live_state(
756
764
  Arc::new(WriteContextLiveStateReader::new(write_ctx.clone())),
757
- version_binding.active_version_id(),
765
+ branch_binding.active_branch_id(),
758
766
  )
759
767
  .await
760
768
  .map_err(lix_error_to_datafusion_error)?;
761
769
  let write_rows = lix_directory_update_write_rows_from_batch(
762
770
  &matched_batch,
763
771
  &assignments,
764
- version_binding.active_version_id(),
772
+ branch_binding.active_branch_id(),
765
773
  &mut path_resolvers,
766
774
  )?;
767
775
  let count = u64::try_from(write_rows.len()).map_err(|_| {
@@ -942,21 +950,21 @@ struct DirectoryDescriptorSnapshot {
942
950
  #[cfg(test)]
943
951
  fn lix_directory_write_rows_from_batch(
944
952
  batch: &RecordBatch,
945
- version_binding: Option<&str>,
953
+ branch_binding: Option<&str>,
946
954
  ) -> Result<Vec<TransactionWriteRow>> {
947
- lix_directory_write_rows_from_batch_with_options(batch, version_binding, "lix_directory", true)
955
+ lix_directory_write_rows_from_batch_with_options(batch, branch_binding, "lix_directory", true)
948
956
  }
949
957
 
950
958
  fn lix_directory_write_rows_from_batch_with_path_resolvers(
951
959
  batch: &RecordBatch,
952
- version_binding: Option<&str>,
960
+ branch_binding: Option<&str>,
953
961
  surface_name: &str,
954
962
  path_resolvers: &mut BTreeMap<String, DirectoryPathResolver>,
955
963
  generate_directory_id: &mut dyn FnMut() -> String,
956
964
  ) -> Result<Vec<TransactionWriteRow>> {
957
965
  lix_directory_write_rows_from_batch_with_options_and_path_resolvers(
958
966
  batch,
959
- version_binding,
967
+ branch_binding,
960
968
  surface_name,
961
969
  true,
962
970
  Some(path_resolvers),
@@ -967,7 +975,7 @@ fn lix_directory_write_rows_from_batch_with_path_resolvers(
967
975
  fn lix_directory_update_write_rows_from_batch(
968
976
  batch: &RecordBatch,
969
977
  assignments: &[(String, Arc<dyn PhysicalExpr>)],
970
- version_binding: Option<&str>,
978
+ branch_binding: Option<&str>,
971
979
  path_resolvers: &mut BTreeMap<String, DirectoryPathResolver>,
972
980
  ) -> Result<Vec<TransactionWriteRow>> {
973
981
  let assignment_values = UpdateAssignmentValues::evaluate(batch, assignments)?;
@@ -978,7 +986,7 @@ fn lix_directory_update_write_rows_from_batch(
978
986
  batch,
979
987
  &assignment_values,
980
988
  row_index,
981
- version_binding,
989
+ branch_binding,
982
990
  )?;
983
991
  let parent_id =
984
992
  update_optional_string_value(batch, &assignment_values, row_index, "parent_id")?;
@@ -1004,22 +1012,21 @@ fn lix_directory_update_write_rows_from_batch(
1004
1012
  Ok(rows)
1005
1013
  }
1006
1014
 
1007
- fn directory_version_ids_from_batch(
1015
+ fn directory_branch_ids_from_batch(
1008
1016
  batch: &RecordBatch,
1009
- version_binding: Option<&str>,
1017
+ branch_binding: Option<&str>,
1010
1018
  ) -> Result<BTreeSet<String>> {
1011
- let mut version_ids = BTreeSet::new();
1019
+ let mut branch_ids = BTreeSet::new();
1012
1020
  for row_index in 0..batch.num_rows() {
1013
- version_ids.insert(
1014
- directory_row_context_from_batch(batch, row_index, version_binding)?.version_id,
1015
- );
1021
+ branch_ids
1022
+ .insert(directory_row_context_from_batch(batch, row_index, branch_binding)?.branch_id);
1016
1023
  }
1017
- Ok(version_ids)
1024
+ Ok(branch_ids)
1018
1025
  }
1019
1026
 
1020
1027
  fn lix_directory_recursive_delete_rows_from_batch(
1021
1028
  batch: &RecordBatch,
1022
- version_binding: Option<&str>,
1029
+ branch_binding: Option<&str>,
1023
1030
  visible_filesystems: &BTreeMap<String, VisibleFilesystem>,
1024
1031
  ) -> Result<(Vec<TransactionWriteRow>, u64)> {
1025
1032
  let mut rows = Vec::new();
@@ -1027,15 +1034,13 @@ fn lix_directory_recursive_delete_rows_from_batch(
1027
1034
  let mut count = 0u64;
1028
1035
  for row_index in 0..batch.num_rows() {
1029
1036
  let directory_id = required_string_value(batch, row_index, "id")?;
1030
- let context = directory_row_context_from_batch(batch, row_index, version_binding)?;
1031
- let visible_filesystem = visible_filesystems
1032
- .get(&context.version_id)
1033
- .ok_or_else(|| {
1034
- DataFusionError::Execution(format!(
1035
- "DELETE FROM lix_directory missing visible filesystem for version '{}'",
1036
- context.version_id
1037
- ))
1038
- })?;
1037
+ let context = directory_row_context_from_batch(batch, row_index, branch_binding)?;
1038
+ let visible_filesystem = visible_filesystems.get(&context.branch_id).ok_or_else(|| {
1039
+ DataFusionError::Execution(format!(
1040
+ "DELETE FROM lix_directory missing visible filesystem for branch '{}'",
1041
+ context.branch_id
1042
+ ))
1043
+ })?;
1039
1044
  append_deduped_delete_plan(
1040
1045
  &mut rows,
1041
1046
  &mut seen,
@@ -1071,10 +1076,10 @@ fn is_user_visible_filesystem_delete_row(row: &TransactionWriteRow) -> bool {
1071
1076
 
1072
1077
  #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
1073
1078
  struct StateRowDedupeKey {
1074
- entity_id: String,
1079
+ entity_pk: String,
1075
1080
  schema_key: String,
1076
1081
  file_id: Option<String>,
1077
- version_id: String,
1082
+ branch_id: String,
1078
1083
  global: bool,
1079
1084
  untracked: bool,
1080
1085
  }
@@ -1082,15 +1087,15 @@ struct StateRowDedupeKey {
1082
1087
  impl From<&TransactionWriteRow> for StateRowDedupeKey {
1083
1088
  fn from(row: &TransactionWriteRow) -> Self {
1084
1089
  Self {
1085
- entity_id: row
1086
- .entity_id
1090
+ entity_pk: row
1091
+ .entity_pk
1087
1092
  .as_ref()
1088
- .expect("directory provider staged row should carry entity_id")
1093
+ .expect("directory provider staged row should carry entity_pk")
1089
1094
  .as_single_string_owned()
1090
- .expect("directory provider staged row entity identity should project"),
1095
+ .expect("directory provider staged row entity primary key should project"),
1091
1096
  schema_key: row.schema_key.clone(),
1092
1097
  file_id: row.file_id.clone(),
1093
- version_id: row.version_id.clone(),
1098
+ branch_id: row.branch_id.clone(),
1094
1099
  global: row.global,
1095
1100
  untracked: row.untracked,
1096
1101
  }
@@ -1100,13 +1105,13 @@ impl From<&TransactionWriteRow> for StateRowDedupeKey {
1100
1105
  #[cfg(test)]
1101
1106
  fn lix_directory_write_rows_from_batch_with_options(
1102
1107
  batch: &RecordBatch,
1103
- version_binding: Option<&str>,
1108
+ branch_binding: Option<&str>,
1104
1109
  surface_name: &str,
1105
1110
  reject_read_only_fields: bool,
1106
1111
  ) -> Result<Vec<TransactionWriteRow>> {
1107
1112
  lix_directory_write_rows_from_batch_with_options_and_path_resolvers(
1108
1113
  batch,
1109
- version_binding,
1114
+ branch_binding,
1110
1115
  surface_name,
1111
1116
  reject_read_only_fields,
1112
1117
  None,
@@ -1116,7 +1121,7 @@ fn lix_directory_write_rows_from_batch_with_options(
1116
1121
 
1117
1122
  fn lix_directory_write_rows_from_batch_with_options_and_path_resolvers(
1118
1123
  batch: &RecordBatch,
1119
- version_binding: Option<&str>,
1124
+ branch_binding: Option<&str>,
1120
1125
  surface_name: &str,
1121
1126
  reject_read_only_fields: bool,
1122
1127
  mut path_resolvers: Option<&mut BTreeMap<String, DirectoryPathResolver>>,
@@ -1125,7 +1130,7 @@ fn lix_directory_write_rows_from_batch_with_options_and_path_resolvers(
1125
1130
  let mut rows = Vec::new();
1126
1131
  for row_index in 0..batch.num_rows() {
1127
1132
  if reject_read_only_fields {
1128
- reject_read_only_lix_directory_insert_field(batch, row_index, "lixcol_entity_id")?;
1133
+ reject_read_only_lix_directory_insert_field(batch, row_index, "lixcol_entity_pk")?;
1129
1134
  reject_read_only_lix_directory_insert_field(batch, row_index, "lixcol_schema_key")?;
1130
1135
  reject_read_only_lix_directory_insert_field(batch, row_index, "lixcol_change_id")?;
1131
1136
  reject_read_only_lix_directory_insert_field(batch, row_index, "lixcol_created_at")?;
@@ -1136,7 +1141,7 @@ fn lix_directory_write_rows_from_batch_with_options_and_path_resolvers(
1136
1141
  let path = optional_string_value(batch, row_index, "path")?;
1137
1142
  let id = optional_string_value(batch, row_index, "id")?;
1138
1143
  let hidden = optional_bool_value(batch, row_index, "hidden")?;
1139
- let context = directory_row_context_from_batch(batch, row_index, version_binding)?;
1144
+ let context = directory_row_context_from_batch(batch, row_index, branch_binding)?;
1140
1145
 
1141
1146
  if let Some(path) = path.filter(|_| reject_read_only_fields) {
1142
1147
  reject_read_only_lix_directory_insert_field(batch, row_index, "parent_id")?;
@@ -1209,14 +1214,14 @@ fn attach_lix_directory_insert_origin(
1209
1214
  if row.schema_key != DIRECTORY_SCHEMA_KEY {
1210
1215
  continue;
1211
1216
  }
1212
- let Some(entity_id) = row
1213
- .entity_id
1217
+ let Some(entity_pk) = row
1218
+ .entity_pk
1214
1219
  .as_ref()
1215
- .and_then(|entity_id| entity_id.as_single_string_owned().ok())
1220
+ .and_then(|entity_pk| entity_pk.as_single_string_owned().ok())
1216
1221
  else {
1217
1222
  continue;
1218
1223
  };
1219
- if entity_id == directory_id {
1224
+ if entity_pk == directory_id {
1220
1225
  row.origin = Some(origin.clone());
1221
1226
  }
1222
1227
  }
@@ -1236,18 +1241,18 @@ fn lix_directory_insert_origin(surface_name: &str, directory_id: &str) -> Transa
1236
1241
  fn directory_row_context_from_batch(
1237
1242
  batch: &RecordBatch,
1238
1243
  row_index: usize,
1239
- version_binding: Option<&str>,
1244
+ branch_binding: Option<&str>,
1240
1245
  ) -> Result<FilesystemRowContext> {
1241
- let scope = resolve_write_version_scope(
1246
+ let scope = resolve_write_branch_scope(
1242
1247
  optional_bool_value(batch, row_index, "lixcol_global")?,
1243
- optional_string_value(batch, row_index, "lixcol_version_id")?,
1244
- version_binding,
1245
- "INSERT into lix_directory_by_version",
1248
+ optional_string_value(batch, row_index, "lixcol_branch_id")?,
1249
+ branch_binding,
1250
+ "INSERT into lix_directory_by_branch",
1246
1251
  "lix_directory",
1247
1252
  )?;
1248
1253
 
1249
1254
  Ok(FilesystemRowContext {
1250
- version_id: scope.version_id,
1255
+ branch_id: scope.branch_id,
1251
1256
  global: scope.global,
1252
1257
  untracked: optional_bool_value(batch, row_index, "lixcol_untracked")?.unwrap_or(false),
1253
1258
  file_id: optional_string_value(batch, row_index, "lixcol_file_id")?,
@@ -1259,18 +1264,18 @@ fn directory_row_context_from_update(
1259
1264
  batch: &RecordBatch,
1260
1265
  assignment_values: &UpdateAssignmentValues,
1261
1266
  row_index: usize,
1262
- version_binding: Option<&str>,
1267
+ branch_binding: Option<&str>,
1263
1268
  ) -> Result<FilesystemRowContext> {
1264
- let scope = resolve_write_version_scope(
1269
+ let scope = resolve_write_branch_scope(
1265
1270
  optional_bool_value(batch, row_index, "lixcol_global")?,
1266
- optional_string_value(batch, row_index, "lixcol_version_id")?,
1267
- version_binding,
1268
- "UPDATE into lix_directory_by_version",
1271
+ optional_string_value(batch, row_index, "lixcol_branch_id")?,
1272
+ branch_binding,
1273
+ "UPDATE into lix_directory_by_branch",
1269
1274
  "lix_directory",
1270
1275
  )?;
1271
1276
 
1272
1277
  Ok(FilesystemRowContext {
1273
- version_id: scope.version_id,
1278
+ branch_id: scope.branch_id,
1274
1279
  global: scope.global,
1275
1280
  untracked: optional_bool_value(batch, row_index, "lixcol_untracked")?.unwrap_or(false),
1276
1281
  file_id: optional_string_value(batch, row_index, "lixcol_file_id")?,
@@ -1286,7 +1291,7 @@ fn directory_row_context_from_update(
1286
1291
 
1287
1292
  fn directory_path_resolver_key(context: &FilesystemRowContext) -> String {
1288
1293
  filesystem_storage_scope_key(
1289
- &context.version_id,
1294
+ &context.branch_id,
1290
1295
  context.global,
1291
1296
  context.untracked,
1292
1297
  context.file_id.as_deref(),
@@ -1295,7 +1300,7 @@ fn directory_path_resolver_key(context: &FilesystemRowContext) -> String {
1295
1300
 
1296
1301
  async fn directory_path_resolvers_from_live_state(
1297
1302
  live_state: Arc<dyn LiveStateReader>,
1298
- version_binding: Option<&str>,
1303
+ branch_binding: Option<&str>,
1299
1304
  ) -> std::result::Result<BTreeMap<String, DirectoryPathResolver>, LixError> {
1300
1305
  let rows = live_state
1301
1306
  .scan_rows(&LiveStateScanRequest {
@@ -1304,8 +1309,8 @@ async fn directory_path_resolvers_from_live_state(
1304
1309
  DIRECTORY_SCHEMA_KEY.to_string(),
1305
1310
  FILE_DESCRIPTOR_SCHEMA_KEY.to_string(),
1306
1311
  ],
1307
- version_ids: version_binding
1308
- .map(|version_id| vec![version_id.to_string()])
1312
+ branch_ids: branch_binding
1313
+ .map(|branch_id| vec![branch_id.to_string()])
1309
1314
  .unwrap_or_default(),
1310
1315
  ..Default::default()
1311
1316
  },
@@ -1313,8 +1318,8 @@ async fn directory_path_resolvers_from_live_state(
1313
1318
  })
1314
1319
  .await?;
1315
1320
  let mut resolvers = directory_path_resolvers_from_state_rows(rows)?;
1316
- if let Some(version_id) = version_binding {
1317
- let key = filesystem_storage_scope_key(version_id, false, false, None);
1321
+ if let Some(branch_id) = branch_binding {
1322
+ let key = filesystem_storage_scope_key(branch_id, false, false, None);
1318
1323
  resolvers
1319
1324
  .entry(key)
1320
1325
  .or_insert_with(DirectoryPathResolver::default);
@@ -1357,7 +1362,7 @@ fn lix_directory_record_batch(
1357
1362
  let mut parent_ids = Vec::new();
1358
1363
  let mut names = Vec::new();
1359
1364
  let mut hiddens = Vec::new();
1360
- let mut entity_ids = Vec::new();
1365
+ let mut entity_pks = Vec::new();
1361
1366
  let mut schema_keys = Vec::new();
1362
1367
  let mut file_ids = Vec::new();
1363
1368
  let mut globals = Vec::new();
@@ -1367,19 +1372,19 @@ fn lix_directory_record_batch(
1367
1372
  let mut commit_ids = Vec::new();
1368
1373
  let mut untracked_values = Vec::new();
1369
1374
  let mut metadata_values = Vec::new();
1370
- let mut version_ids = Vec::new();
1375
+ let mut branch_ids = Vec::new();
1371
1376
 
1372
1377
  for directory in directory_rows {
1373
1378
  ids.push(Some(directory.id.clone()));
1374
1379
  paths.push(
1375
1380
  directory_paths
1376
- .get(&(directory.live.version_id.clone(), directory.id.clone()))
1381
+ .get(&(directory.live.branch_id.clone(), directory.id.clone()))
1377
1382
  .cloned(),
1378
1383
  );
1379
1384
  parent_ids.push(directory.parent_id);
1380
1385
  names.push(Some(directory.name));
1381
1386
  hiddens.push(Some(directory.hidden));
1382
- entity_ids.push(Some(directory.live.entity_id.as_json_array_text()?));
1387
+ entity_pks.push(Some(directory.live.entity_pk.as_json_array_text()?));
1383
1388
  schema_keys.push(Some(directory.live.schema_key));
1384
1389
  file_ids.push(directory.live.file_id);
1385
1390
  globals.push(Some(directory.live.global));
@@ -1389,7 +1394,7 @@ fn lix_directory_record_batch(
1389
1394
  commit_ids.push(directory.live.commit_id);
1390
1395
  untracked_values.push(Some(directory.live.untracked));
1391
1396
  metadata_values.push(directory.live.metadata.as_ref().map(serialize_row_metadata));
1392
- version_ids.push(Some(directory.live.version_id));
1397
+ branch_ids.push(Some(directory.live.branch_id));
1393
1398
  }
1394
1399
 
1395
1400
  let mut columns = Vec::<ArrayRef>::with_capacity(schema.fields().len());
@@ -1400,7 +1405,7 @@ fn lix_directory_record_batch(
1400
1405
  "parent_id" => Arc::new(StringArray::from(parent_ids.clone())),
1401
1406
  "name" => Arc::new(StringArray::from(names.clone())),
1402
1407
  "hidden" => Arc::new(BooleanArray::from(hiddens.clone())),
1403
- "lixcol_entity_id" => Arc::new(StringArray::from(entity_ids.clone())),
1408
+ "lixcol_entity_pk" => Arc::new(StringArray::from(entity_pks.clone())),
1404
1409
  "lixcol_schema_key" => Arc::new(StringArray::from(schema_keys.clone())),
1405
1410
  "lixcol_file_id" => Arc::new(StringArray::from(file_ids.clone())),
1406
1411
  "lixcol_global" => Arc::new(BooleanArray::from(globals.clone())),
@@ -1410,7 +1415,7 @@ fn lix_directory_record_batch(
1410
1415
  "lixcol_commit_id" => Arc::new(StringArray::from(commit_ids.clone())),
1411
1416
  "lixcol_untracked" => Arc::new(BooleanArray::from(untracked_values.clone())),
1412
1417
  "lixcol_metadata" => Arc::new(StringArray::from(metadata_values.clone())),
1413
- "lixcol_version_id" => Arc::new(StringArray::from(version_ids.clone())),
1418
+ "lixcol_branch_id" => Arc::new(StringArray::from(branch_ids.clone())),
1414
1419
  other => {
1415
1420
  return Err(LixError::new(
1416
1421
  "LIX_ERROR_UNKNOWN",
@@ -1435,19 +1440,19 @@ fn lix_directory_record_batch(
1435
1440
  fn derive_directory_paths(
1436
1441
  rows: &[DirectoryDescriptorRecord],
1437
1442
  ) -> std::result::Result<BTreeMap<(String, String), String>, LixError> {
1438
- let mut by_version = BTreeMap::<String, BTreeMap<String, &DirectoryDescriptorRecord>>::new();
1443
+ let mut by_branch = BTreeMap::<String, BTreeMap<String, &DirectoryDescriptorRecord>>::new();
1439
1444
  for row in rows {
1440
- by_version
1441
- .entry(row.live.version_id.clone())
1445
+ by_branch
1446
+ .entry(row.live.branch_id.clone())
1442
1447
  .or_default()
1443
1448
  .insert(row.id.clone(), row);
1444
1449
  }
1445
1450
 
1446
1451
  let mut paths = BTreeMap::<(String, String), String>::new();
1447
- for (version_id, records) in by_version {
1452
+ for (branch_id, records) in by_branch {
1448
1453
  for directory_id in records.keys() {
1449
1454
  derive_directory_path_for(
1450
- &version_id,
1455
+ &branch_id,
1451
1456
  directory_id,
1452
1457
  &records,
1453
1458
  &mut paths,
@@ -1459,17 +1464,17 @@ fn derive_directory_paths(
1459
1464
  }
1460
1465
 
1461
1466
  fn derive_directory_path_for(
1462
- version_id: &str,
1467
+ branch_id: &str,
1463
1468
  directory_id: &str,
1464
1469
  records: &BTreeMap<String, &DirectoryDescriptorRecord>,
1465
1470
  paths: &mut BTreeMap<(String, String), String>,
1466
1471
  visiting: &mut BTreeSet<String>,
1467
1472
  ) -> std::result::Result<Option<String>, LixError> {
1468
- if let Some(path) = paths.get(&(version_id.to_string(), directory_id.to_string())) {
1473
+ if let Some(path) = paths.get(&(branch_id.to_string(), directory_id.to_string())) {
1469
1474
  return Ok(Some(path.clone()));
1470
1475
  }
1471
1476
  if !visiting.insert(directory_id.to_string()) {
1472
- return Err(directory_parent_cycle_error(version_id, directory_id));
1477
+ return Err(directory_parent_cycle_error(branch_id, directory_id));
1473
1478
  }
1474
1479
  let Some(row) = records.get(directory_id) else {
1475
1480
  visiting.remove(directory_id);
@@ -1478,7 +1483,7 @@ fn derive_directory_path_for(
1478
1483
  let path = match row.parent_id.as_deref() {
1479
1484
  Some(parent_id) => {
1480
1485
  let Some(parent_path) =
1481
- derive_directory_path_for(version_id, parent_id, records, paths, visiting)?
1486
+ derive_directory_path_for(branch_id, parent_id, records, paths, visiting)?
1482
1487
  else {
1483
1488
  visiting.remove(directory_id);
1484
1489
  return Ok(None);
@@ -1489,17 +1494,17 @@ fn derive_directory_path_for(
1489
1494
  };
1490
1495
  visiting.remove(directory_id);
1491
1496
  paths.insert(
1492
- (version_id.to_string(), directory_id.to_string()),
1497
+ (branch_id.to_string(), directory_id.to_string()),
1493
1498
  path.clone(),
1494
1499
  );
1495
1500
  Ok(Some(path))
1496
1501
  }
1497
1502
 
1498
- fn directory_parent_cycle_error(version_id: &str, directory_id: &str) -> LixError {
1503
+ fn directory_parent_cycle_error(branch_id: &str, directory_id: &str) -> LixError {
1499
1504
  LixError::new(
1500
1505
  LixError::CODE_CONSTRAINT_VIOLATION,
1501
1506
  format!(
1502
- "lix_directory_descriptor parent_id cycle in version '{version_id}' while resolving directory '{directory_id}'"
1507
+ "lix_directory_descriptor parent_id cycle in branch '{branch_id}' while resolving directory '{directory_id}'"
1503
1508
  ),
1504
1509
  )
1505
1510
  }
@@ -1520,15 +1525,15 @@ fn projected_schema(base_schema: &SchemaRef, projection: Option<&Vec<usize>>) ->
1520
1525
  }
1521
1526
 
1522
1527
  fn lix_directory_scan_request(
1523
- version_binding: Option<&str>,
1528
+ branch_binding: Option<&str>,
1524
1529
  projected_schema: Option<&Schema>,
1525
1530
  limit: Option<usize>,
1526
1531
  ) -> LiveStateScanRequest {
1527
1532
  LiveStateScanRequest {
1528
1533
  filter: LiveStateFilter {
1529
1534
  schema_keys: vec![DIRECTORY_SCHEMA_KEY.to_string()],
1530
- version_ids: version_binding
1531
- .map(|version_id| vec![version_id.to_string()])
1535
+ branch_ids: branch_binding
1536
+ .map(|branch_id| vec![branch_id.to_string()])
1532
1537
  .unwrap_or_default(),
1533
1538
  ..LiveStateFilter::default()
1534
1539
  },
@@ -1717,9 +1722,9 @@ fn update_optional_metadata_value(
1717
1722
  update_optional_string_value(batch, assignment_values, row_index, column_name)?
1718
1723
  .map(|value| {
1719
1724
  let metadata = parse_row_metadata_value(&value, context)
1720
- .map_err(super::error::lix_error_to_datafusion_error)?;
1725
+ .map_err(crate::sql2::error::lix_error_to_datafusion_error)?;
1721
1726
  TransactionJson::from_value(metadata, &format!("{context} metadata"))
1722
- .map_err(super::error::lix_error_to_datafusion_error)
1727
+ .map_err(crate::sql2::error::lix_error_to_datafusion_error)
1723
1728
  })
1724
1729
  .transpose()
1725
1730
  }
@@ -1768,9 +1773,9 @@ fn optional_metadata_value(
1768
1773
  optional_string_value(batch, row_index, column_name)?
1769
1774
  .map(|value| {
1770
1775
  let metadata = parse_row_metadata_value(&value, context)
1771
- .map_err(super::error::lix_error_to_datafusion_error)?;
1776
+ .map_err(crate::sql2::error::lix_error_to_datafusion_error)?;
1772
1777
  TransactionJson::from_value(metadata, &format!("{context} metadata"))
1773
- .map_err(super::error::lix_error_to_datafusion_error)
1778
+ .map_err(crate::sql2::error::lix_error_to_datafusion_error)
1774
1779
  })
1775
1780
  .transpose()
1776
1781
  }
@@ -1814,14 +1819,14 @@ fn optional_scalar_value(
1814
1819
  })
1815
1820
  }
1816
1821
 
1817
- fn lix_directory_schema() -> SchemaRef {
1822
+ pub(super) fn lix_directory_schema() -> SchemaRef {
1818
1823
  Arc::new(Schema::new(vec![
1819
1824
  Field::new("id", DataType::Utf8, true),
1820
1825
  Field::new("path", DataType::Utf8, true),
1821
1826
  Field::new("parent_id", DataType::Utf8, true),
1822
1827
  Field::new("name", DataType::Utf8, false),
1823
1828
  Field::new("hidden", DataType::Boolean, true),
1824
- json_field("lixcol_entity_id", false),
1829
+ json_field("lixcol_entity_pk", false),
1825
1830
  Field::new("lixcol_schema_key", DataType::Utf8, false),
1826
1831
  Field::new("lixcol_file_id", DataType::Utf8, true),
1827
1832
  Field::new("lixcol_global", DataType::Boolean, true),
@@ -1834,22 +1839,22 @@ fn lix_directory_schema() -> SchemaRef {
1834
1839
  ]))
1835
1840
  }
1836
1841
 
1837
- fn lix_directory_by_version_schema() -> SchemaRef {
1842
+ pub(super) fn lix_directory_by_branch_schema() -> SchemaRef {
1838
1843
  let mut fields = lix_directory_schema()
1839
1844
  .fields()
1840
1845
  .iter()
1841
1846
  .map(|field| field.as_ref().clone())
1842
1847
  .collect::<Vec<_>>();
1843
- fields.push(Field::new("lixcol_version_id", DataType::Utf8, false));
1848
+ fields.push(Field::new("lixcol_branch_id", DataType::Utf8, false));
1844
1849
  Arc::new(Schema::new(fields))
1845
1850
  }
1846
1851
 
1847
1852
  fn datafusion_error_to_lix_error(error: DataFusionError) -> LixError {
1848
- super::error::datafusion_error_to_lix_error(error)
1853
+ crate::sql2::error::datafusion_error_to_lix_error(error)
1849
1854
  }
1850
1855
 
1851
1856
  fn lix_error_to_datafusion_error(error: LixError) -> DataFusionError {
1852
- super::error::lix_error_to_datafusion_error(error)
1857
+ crate::sql2::error::lix_error_to_datafusion_error(error)
1853
1858
  }
1854
1859
 
1855
1860
  #[cfg(test)]
@@ -1868,9 +1873,7 @@ mod tests {
1868
1873
  use crate::functions::{
1869
1874
  FunctionProvider, FunctionProviderHandle, SharedFunctionProvider, SystemFunctionProvider,
1870
1875
  };
1871
- use crate::live_state::{
1872
- LiveStateReader, LiveStateRowRequest, LiveStateScanRequest, MaterializedLiveStateRow,
1873
- };
1876
+ use crate::live_state::{LiveStateScanRequest, MaterializedLiveStateRow};
1874
1877
  use crate::sql2::dml::InsertSink;
1875
1878
  use crate::sql2::{SqlWriteContext, SqlWriteExecutionContext};
1876
1879
  use crate::transaction::types::{
@@ -1881,10 +1884,10 @@ mod tests {
1881
1884
 
1882
1885
  use super::{
1883
1886
  derive_directory_path_for, directory_path_resolvers_from_state_rows,
1884
- lix_directory_by_version_schema, lix_directory_insert_origin, lix_directory_record_batch,
1887
+ lix_directory_by_branch_schema, lix_directory_insert_origin, lix_directory_record_batch,
1885
1888
  lix_directory_recursive_delete_rows_from_batch, lix_directory_write_rows_from_batch,
1886
- lix_directory_write_rows_from_batch_with_path_resolvers, DirectoryDescriptorRecord,
1887
- LixDirectoryInsertSink, VersionBinding,
1889
+ lix_directory_write_rows_from_batch_with_path_resolvers, BranchBinding,
1890
+ DirectoryDescriptorRecord, LixDirectoryInsertSink,
1888
1891
  };
1889
1892
  use crate::sql2::filesystem_visibility::VisibleFilesystem;
1890
1893
 
@@ -1920,8 +1923,8 @@ mod tests {
1920
1923
 
1921
1924
  #[async_trait]
1922
1925
  impl SqlWriteExecutionContext for CapturingWriteContext {
1923
- fn active_version_id(&self) -> &str {
1924
- "version-a"
1926
+ fn active_branch_id(&self) -> &str {
1927
+ "branch-a"
1925
1928
  }
1926
1929
 
1927
1930
  fn functions(&self) -> FunctionProviderHandle {
@@ -1946,14 +1949,11 @@ mod tests {
1946
1949
  Ok(self.rows.clone())
1947
1950
  }
1948
1951
 
1949
- async fn load_version_head(
1950
- &mut self,
1951
- version_id: &str,
1952
- ) -> Result<Option<String>, LixError> {
1953
- if version_id == "ghost-version" {
1952
+ async fn load_branch_head(&mut self, branch_id: &str) -> Result<Option<String>, LixError> {
1953
+ if branch_id == "ghost-branch" {
1954
1954
  return Ok(None);
1955
1955
  }
1956
- Ok(Some(format!("commit-{version_id}")))
1956
+ Ok(Some(format!("commit-{branch_id}")))
1957
1957
  }
1958
1958
 
1959
1959
  async fn stage_write(
@@ -1965,60 +1965,37 @@ mod tests {
1965
1965
  }
1966
1966
  }
1967
1967
 
1968
- #[derive(Default)]
1969
- #[allow(dead_code)]
1970
- struct RowsLiveStateReader {
1971
- rows: Vec<MaterializedLiveStateRow>,
1972
- }
1973
-
1974
- #[async_trait]
1975
- impl LiveStateReader for RowsLiveStateReader {
1976
- async fn scan_rows(
1977
- &self,
1978
- _request: &LiveStateScanRequest,
1979
- ) -> Result<Vec<MaterializedLiveStateRow>, LixError> {
1980
- Ok(self.rows.clone())
1981
- }
1982
-
1983
- async fn load_row(
1984
- &self,
1985
- _request: &LiveStateRowRequest,
1986
- ) -> Result<Option<MaterializedLiveStateRow>, LixError> {
1987
- Ok(None)
1988
- }
1989
- }
1990
-
1991
1968
  fn live_row(
1992
- entity_id: &str,
1993
- version_id: &str,
1969
+ entity_pk: &str,
1970
+ branch_id: &str,
1994
1971
  snapshot_content: &str,
1995
1972
  ) -> MaterializedLiveStateRow {
1996
1973
  live_filesystem_row(
1997
- entity_id,
1974
+ entity_pk,
1998
1975
  super::DIRECTORY_SCHEMA_KEY,
1999
1976
  None,
2000
- version_id,
1977
+ branch_id,
2001
1978
  snapshot_content,
2002
1979
  )
2003
1980
  }
2004
1981
 
2005
1982
  fn live_filesystem_row(
2006
- entity_id: &str,
1983
+ entity_pk: &str,
2007
1984
  schema_key: &str,
2008
1985
  file_id: Option<&str>,
2009
- version_id: &str,
1986
+ branch_id: &str,
2010
1987
  snapshot_content: &str,
2011
1988
  ) -> MaterializedLiveStateRow {
2012
1989
  MaterializedLiveStateRow {
2013
- entity_id: crate::entity_identity::EntityIdentity::single(entity_id),
1990
+ entity_pk: crate::entity_pk::EntityPk::single(entity_pk),
2014
1991
  schema_key: schema_key.to_string(),
2015
1992
  file_id: file_id.map(ToOwned::to_owned),
2016
1993
  snapshot_content: Some(snapshot_content.to_string()),
2017
1994
  metadata: Some(json!({"source": "test"}).to_string()),
2018
1995
  deleted: false,
2019
- version_id: version_id.to_string(),
2020
- change_id: Some(format!("change-{entity_id}")),
2021
- commit_id: Some(format!("commit-{entity_id}")),
1996
+ branch_id: branch_id.to_string(),
1997
+ change_id: Some(format!("change-{entity_pk}")),
1998
+ commit_id: Some(format!("commit-{entity_pk}")),
2022
1999
  global: false,
2023
2000
  untracked: false,
2024
2001
  created_at: "2026-04-23T00:00:00Z".to_string(),
@@ -2032,35 +2009,35 @@ mod tests {
2032
2009
  "dir-docs",
2033
2010
  "lix_directory_descriptor",
2034
2011
  None,
2035
- "version-a",
2012
+ "branch-a",
2036
2013
  r#"{"id":"dir-docs","parent_id":null,"name":"docs","hidden":false}"#,
2037
2014
  ),
2038
2015
  live_filesystem_row(
2039
2016
  "dir-guides",
2040
2017
  "lix_directory_descriptor",
2041
2018
  None,
2042
- "version-a",
2019
+ "branch-a",
2043
2020
  r#"{"id":"dir-guides","parent_id":"dir-docs","name":"guides","hidden":false}"#,
2044
2021
  ),
2045
2022
  live_filesystem_row(
2046
2023
  "file-index",
2047
2024
  "lix_file_descriptor",
2048
2025
  None,
2049
- "version-a",
2026
+ "branch-a",
2050
2027
  r#"{"id":"file-index","directory_id":"dir-docs","name":"index.md","hidden":false}"#,
2051
2028
  ),
2052
2029
  live_filesystem_row(
2053
2030
  "file-readme",
2054
2031
  "lix_file_descriptor",
2055
2032
  None,
2056
- "version-a",
2033
+ "branch-a",
2057
2034
  r#"{"id":"file-readme","directory_id":"dir-guides","name":"readme.md","hidden":false}"#,
2058
2035
  ),
2059
2036
  live_filesystem_row(
2060
2037
  "file-readme",
2061
2038
  "lix_binary_blob_ref",
2062
2039
  Some("file-readme"),
2063
- "version-a",
2040
+ "branch-a",
2064
2041
  r#"{"id":"file-readme","blob_hash":"abc123","size_bytes":5}"#,
2065
2042
  ),
2066
2043
  ]
@@ -2070,7 +2047,7 @@ mod tests {
2070
2047
  Arc::new(StringArray::from(values)) as ArrayRef
2071
2048
  }
2072
2049
 
2073
- fn directory_insert_batch(include_version: bool, global: bool) -> RecordBatch {
2050
+ fn directory_insert_batch(include_branch: bool, global: bool) -> RecordBatch {
2074
2051
  let mut fields = vec![
2075
2052
  Field::new("id", DataType::Utf8, false),
2076
2053
  Field::new("parent_id", DataType::Utf8, true),
@@ -2087,9 +2064,9 @@ mod tests {
2087
2064
  Arc::new(BooleanArray::from(vec![global])) as ArrayRef,
2088
2065
  string_column(vec![Some("{\"source\":\"directory\"}")]),
2089
2066
  ];
2090
- if include_version {
2091
- fields.push(Field::new("lixcol_version_id", DataType::Utf8, false));
2092
- columns.push(string_column(vec![Some("version-a")]));
2067
+ if include_branch {
2068
+ fields.push(Field::new("lixcol_branch_id", DataType::Utf8, false));
2069
+ columns.push(string_column(vec![Some("branch-a")]));
2093
2070
  }
2094
2071
  RecordBatch::try_new(Arc::new(Schema::new(fields)), columns)
2095
2072
  .expect("directory insert batch should build")
@@ -2101,13 +2078,13 @@ mod tests {
2101
2078
  Field::new("id", DataType::Utf8, false),
2102
2079
  Field::new("path", DataType::Utf8, true),
2103
2080
  Field::new("hidden", DataType::Boolean, false),
2104
- Field::new("lixcol_version_id", DataType::Utf8, false),
2081
+ Field::new("lixcol_branch_id", DataType::Utf8, false),
2105
2082
  ])),
2106
2083
  vec![
2107
2084
  string_column(vec![Some("dir-nested")]),
2108
2085
  string_column(vec![Some(path)]),
2109
2086
  Arc::new(BooleanArray::from(vec![false])) as ArrayRef,
2110
- string_column(vec![Some("version-a")]),
2087
+ string_column(vec![Some("branch-a")]),
2111
2088
  ],
2112
2089
  )
2113
2090
  .expect("directory path insert batch should build")
@@ -2117,11 +2094,11 @@ mod tests {
2117
2094
  RecordBatch::try_new(
2118
2095
  Arc::new(Schema::new(vec![
2119
2096
  Field::new("id", DataType::Utf8, false),
2120
- Field::new("lixcol_version_id", DataType::Utf8, false),
2097
+ Field::new("lixcol_branch_id", DataType::Utf8, false),
2121
2098
  ])),
2122
2099
  vec![
2123
2100
  string_column(ids.iter().copied().map(Some).collect::<Vec<_>>()),
2124
- string_column(vec![Some("version-a"); ids.len()]),
2101
+ string_column(vec![Some("branch-a"); ids.len()]),
2125
2102
  ],
2126
2103
  )
2127
2104
  .expect("directory delete batch should build")
@@ -2136,7 +2113,7 @@ mod tests {
2136
2113
  hidden: false,
2137
2114
  live: live_row(
2138
2115
  "dir-docs",
2139
- "version-a",
2116
+ "branch-a",
2140
2117
  "{\"id\":\"dir-docs\",\"parent_id\":null,\"name\":\"docs\",\"hidden\":false}",
2141
2118
  ),
2142
2119
  };
@@ -2147,7 +2124,7 @@ mod tests {
2147
2124
  hidden: false,
2148
2125
  live: live_row(
2149
2126
  "dir-guides",
2150
- "version-a",
2127
+ "branch-a",
2151
2128
  "{\"id\":\"dir-guides\",\"parent_id\":\"dir-docs\",\"name\":\"guides\",\"hidden\":false}",
2152
2129
  ),
2153
2130
  };
@@ -2158,7 +2135,7 @@ mod tests {
2158
2135
 
2159
2136
  assert_eq!(
2160
2137
  derive_directory_path_for(
2161
- "version-a",
2138
+ "branch-a",
2162
2139
  "dir-guides",
2163
2140
  &records,
2164
2141
  &mut paths,
@@ -2174,17 +2151,17 @@ mod tests {
2174
2151
  let rows = vec![
2175
2152
  live_row(
2176
2153
  "dir-docs",
2177
- "version-a",
2154
+ "branch-a",
2178
2155
  "{\"id\":\"dir-docs\",\"parent_id\":null,\"name\":\"docs\",\"hidden\":false}",
2179
2156
  ),
2180
2157
  live_row(
2181
2158
  "dir-guides",
2182
- "version-a",
2159
+ "branch-a",
2183
2160
  "{\"id\":\"dir-guides\",\"parent_id\":\"dir-docs\",\"name\":\"guides\",\"hidden\":true}",
2184
2161
  ),
2185
2162
  ];
2186
2163
 
2187
- let batch = lix_directory_record_batch(&lix_directory_by_version_schema(), rows)
2164
+ let batch = lix_directory_record_batch(&lix_directory_by_branch_schema(), rows)
2188
2165
  .expect("directory batch should build");
2189
2166
 
2190
2167
  assert_eq!(batch.num_rows(), 2);
@@ -2200,13 +2177,13 @@ mod tests {
2200
2177
  );
2201
2178
  assert_eq!(
2202
2179
  batch
2203
- .column_by_name("lixcol_version_id")
2204
- .expect("version column")
2180
+ .column_by_name("lixcol_branch_id")
2181
+ .expect("branch column")
2205
2182
  .as_any()
2206
2183
  .downcast_ref::<StringArray>()
2207
- .expect("version is string")
2184
+ .expect("branch is string")
2208
2185
  .value(1),
2209
- "version-a"
2186
+ "branch-a"
2210
2187
  );
2211
2188
  }
2212
2189
 
@@ -2218,7 +2195,7 @@ mod tests {
2218
2195
  assert_eq!(
2219
2196
  rows,
2220
2197
  vec![TransactionWriteRow {
2221
- entity_id: Some(crate::entity_identity::EntityIdentity::single("dir-docs")),
2198
+ entity_pk: Some(crate::entity_pk::EntityPk::single("dir-docs")),
2222
2199
  schema_key: super::DIRECTORY_SCHEMA_KEY.to_string(),
2223
2200
  file_id: None,
2224
2201
  snapshot: Some(TransactionJson::from_value_for_test(
@@ -2234,43 +2211,43 @@ mod tests {
2234
2211
  change_id: None,
2235
2212
  commit_id: None,
2236
2213
  untracked: false,
2237
- version_id: "version-a".to_string(),
2214
+ branch_id: "branch-a".to_string(),
2238
2215
  }]
2239
2216
  );
2240
2217
  }
2241
2218
 
2242
2219
  #[test]
2243
- fn active_directory_insert_defaults_version_id() {
2220
+ fn active_directory_insert_defaults_branch_id() {
2244
2221
  let rows = lix_directory_write_rows_from_batch(
2245
2222
  &directory_insert_batch(false, false),
2246
- Some("version-active"),
2223
+ Some("branch-active"),
2247
2224
  )
2248
2225
  .expect("active directory batch should decode");
2249
2226
 
2250
- assert_eq!(rows[0].version_id, "version-active");
2227
+ assert_eq!(rows[0].branch_id, "branch-active");
2251
2228
  }
2252
2229
 
2253
2230
  #[test]
2254
- fn by_version_directory_insert_requires_version_id_for_non_global_rows() {
2231
+ fn by_branch_directory_insert_requires_branch_id_for_non_global_rows() {
2255
2232
  let error =
2256
2233
  lix_directory_write_rows_from_batch(&directory_insert_batch(false, false), None)
2257
- .expect_err("by-version insert should require version id");
2234
+ .expect_err("by-branch insert should require branch id");
2258
2235
 
2259
2236
  assert!(
2260
- error.to_string().contains("requires lixcol_version_id"),
2237
+ error.to_string().contains("requires lixcol_branch_id"),
2261
2238
  "unexpected error: {error}"
2262
2239
  );
2263
2240
  }
2264
2241
 
2265
2242
  #[test]
2266
- fn directory_insert_rejects_global_with_non_global_version_id() {
2243
+ fn directory_insert_rejects_global_with_non_global_branch_id() {
2267
2244
  let error = lix_directory_write_rows_from_batch(&directory_insert_batch(true, true), None)
2268
- .expect_err("global directory write should reject conflicting version id");
2245
+ .expect_err("global directory write should reject conflicting branch id");
2269
2246
 
2270
2247
  assert!(
2271
2248
  error
2272
2249
  .to_string()
2273
- .contains("cannot set lixcol_global=true with non-global lixcol_version_id"),
2250
+ .contains("cannot set lixcol_global=true with non-global lixcol_branch_id"),
2274
2251
  "unexpected error: {error}"
2275
2252
  );
2276
2253
  }
@@ -2279,7 +2256,7 @@ mod tests {
2279
2256
  fn directory_path_insert_reuses_existing_parent_descriptor() {
2280
2257
  let existing_rows = vec![live_row(
2281
2258
  "dir-docs",
2282
- "version-a",
2259
+ "branch-a",
2283
2260
  "{\"id\":\"dir-docs\",\"parent_id\":null,\"name\":\"docs\",\"hidden\":false}",
2284
2261
  )];
2285
2262
  let mut resolvers = directory_path_resolvers_from_state_rows(existing_rows)
@@ -2306,7 +2283,7 @@ mod tests {
2306
2283
  let visible_filesystem = VisibleFilesystem::from_live_rows(filesystem_rows())
2307
2284
  .expect("visible filesystem should build");
2308
2285
  let mut visible_filesystems = BTreeMap::new();
2309
- visible_filesystems.insert("version-a".to_string(), visible_filesystem);
2286
+ visible_filesystems.insert("branch-a".to_string(), visible_filesystem);
2310
2287
 
2311
2288
  let (rows, count) = lix_directory_recursive_delete_rows_from_batch(
2312
2289
  &directory_delete_batch(&["dir-docs"]),
@@ -2321,11 +2298,11 @@ mod tests {
2321
2298
  .map(|row| {
2322
2299
  (
2323
2300
  row.schema_key.as_str(),
2324
- row.entity_id
2301
+ row.entity_pk
2325
2302
  .as_ref()
2326
- .expect("planned delete row should carry entity_id")
2303
+ .expect("planned delete row should carry entity_pk")
2327
2304
  .as_single_string_owned()
2328
- .expect("planned delete row should project entity_id"),
2305
+ .expect("planned delete row should project entity_pk"),
2329
2306
  )
2330
2307
  })
2331
2308
  .collect::<Vec<_>>(),
@@ -2345,7 +2322,7 @@ mod tests {
2345
2322
  let visible_filesystem = VisibleFilesystem::from_live_rows(filesystem_rows())
2346
2323
  .expect("visible filesystem should build");
2347
2324
  let mut visible_filesystems = BTreeMap::new();
2348
- visible_filesystems.insert("version-a".to_string(), visible_filesystem);
2325
+ visible_filesystems.insert("branch-a".to_string(), visible_filesystem);
2349
2326
 
2350
2327
  let (rows, count) = lix_directory_recursive_delete_rows_from_batch(
2351
2328
  &directory_delete_batch(&["dir-docs", "dir-guides"]),
@@ -2360,9 +2337,9 @@ mod tests {
2360
2337
  .map(|row| {
2361
2338
  (
2362
2339
  row.schema_key.clone(),
2363
- row.entity_id.clone(),
2340
+ row.entity_pk.clone(),
2364
2341
  row.file_id.clone(),
2365
- row.version_id.clone(),
2342
+ row.branch_id.clone(),
2366
2343
  )
2367
2344
  })
2368
2345
  .collect::<std::collections::BTreeSet<_>>();
@@ -2375,12 +2352,8 @@ mod tests {
2375
2352
  let mut write_context = CapturingWriteContext::default();
2376
2353
  let write_ctx = SqlWriteContext::new(&mut write_context);
2377
2354
  let batch = directory_insert_batch(true, false);
2378
- let sink = LixDirectoryInsertSink::new(
2379
- batch.schema(),
2380
- write_ctx,
2381
- test_functions(),
2382
- VersionBinding::explicit(),
2383
- );
2355
+ let sink =
2356
+ LixDirectoryInsertSink::new(write_ctx, test_functions(), BranchBinding::explicit());
2384
2357
  let count = sink
2385
2358
  .write_batches(vec![batch], &Arc::new(TaskContext::default()))
2386
2359
  .await
@@ -2392,7 +2365,7 @@ mod tests {
2392
2365
  &[TransactionWrite::Rows {
2393
2366
  mode: TransactionWriteMode::Insert,
2394
2367
  rows: vec![TransactionWriteRow {
2395
- entity_id: Some(crate::entity_identity::EntityIdentity::single("dir-docs")),
2368
+ entity_pk: Some(crate::entity_pk::EntityPk::single("dir-docs")),
2396
2369
  schema_key: super::DIRECTORY_SCHEMA_KEY.to_string(),
2397
2370
  file_id: None,
2398
2371
  snapshot: Some(TransactionJson::from_value_for_test(
@@ -2402,7 +2375,7 @@ mod tests {
2402
2375
  json!({"source": "directory"})
2403
2376
  )),
2404
2377
  origin: Some(lix_directory_insert_origin(
2405
- "lix_directory_by_version",
2378
+ "lix_directory_by_branch",
2406
2379
  "dir-docs"
2407
2380
  )),
2408
2381
  created_at: None,
@@ -2411,7 +2384,7 @@ mod tests {
2411
2384
  change_id: None,
2412
2385
  commit_id: None,
2413
2386
  untracked: false,
2414
- version_id: "version-a".to_string(),
2387
+ branch_id: "branch-a".to_string(),
2415
2388
  }]
2416
2389
  }]
2417
2390
  );
@@ -2422,19 +2395,15 @@ mod tests {
2422
2395
  let mut write_context = CapturingWriteContext {
2423
2396
  rows: vec![live_row(
2424
2397
  "dir-docs",
2425
- "version-a",
2398
+ "branch-a",
2426
2399
  "{\"id\":\"dir-docs\",\"parent_id\":null,\"name\":\"docs\",\"hidden\":false}",
2427
2400
  )],
2428
2401
  writes: Vec::new(),
2429
2402
  };
2430
2403
  let write_ctx = SqlWriteContext::new(&mut write_context);
2431
2404
  let batch = directory_path_insert_batch("/docs/nested/");
2432
- let sink = LixDirectoryInsertSink::new(
2433
- batch.schema(),
2434
- write_ctx,
2435
- test_functions(),
2436
- VersionBinding::explicit(),
2437
- );
2405
+ let sink =
2406
+ LixDirectoryInsertSink::new(write_ctx, test_functions(), BranchBinding::explicit());
2438
2407
  let count = sink
2439
2408
  .write_batches(vec![batch], &Arc::new(TaskContext::default()))
2440
2409
  .await