@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
@@ -1,30 +1,41 @@
1
1
  use std::sync::Arc;
2
2
 
3
3
  use crate::functions::FunctionContext;
4
- use crate::transaction::{open_transaction, Transaction};
4
+ use crate::storage::{InMemoryStorageBackend, StorageBackend};
5
+ use tokio::sync::Notify;
6
+
7
+ use crate::transaction::{
8
+ open_transaction, CommitBoundaryGuard, CommitBoundaryState, Transaction,
9
+ TransactionCommitBoundary,
10
+ };
5
11
  use crate::LixError;
6
12
 
7
- use super::context::SessionTransactionGuard;
13
+ use super::context::{closed_error, SessionWriteAccess};
8
14
  use super::SessionContext;
9
15
 
10
- pub struct SessionTransaction {
11
- pub(super) transaction: Option<Transaction>,
16
+ pub struct SessionTransaction<B: StorageBackend = InMemoryStorageBackend> {
17
+ pub(super) transaction: Option<Transaction<B>>,
12
18
  pub(super) runtime_functions: FunctionContext,
13
- _transaction_guard: SessionTransactionGuard,
19
+ transaction_manager: SessionTransactionManager,
20
+ _write_access: SessionWriteAccess,
14
21
  }
15
22
 
16
- impl SessionContext {
17
- pub async fn begin_transaction(&self) -> Result<SessionTransaction, LixError> {
23
+ impl<B> SessionContext<B>
24
+ where
25
+ B: StorageBackend + Clone + Send + Sync + 'static,
26
+ for<'backend> B::Read<'backend>: Clone + Send + Sync + 'static,
27
+ for<'backend> B::Write<'backend>: Send,
28
+ {
29
+ pub async fn begin_transaction(&self) -> Result<SessionTransaction<B>, LixError> {
18
30
  self.ensure_open()?;
19
- let transaction_guard = self.reserve_session_transaction()?;
20
- let opened = match open_transaction(
31
+ let write_access = self.begin_explicit_session_write_access().await?;
32
+ let mut opened = match open_transaction(
21
33
  &self.mode,
22
34
  self.storage.clone(),
23
35
  Arc::clone(&self.live_state),
24
36
  Arc::clone(&self.tracked_state),
25
37
  Arc::clone(&self.binary_cas),
26
- Arc::clone(&self.commit_store),
27
- Arc::clone(&self.version_ctx),
38
+ Arc::clone(&self.branch_ctx),
28
39
  Arc::clone(&self.catalog_context),
29
40
  )
30
41
  .await
@@ -34,30 +45,55 @@ impl SessionContext {
34
45
  return Err(error);
35
46
  }
36
47
  };
48
+ self.ensure_open()?;
49
+ opened
50
+ .transaction
51
+ .attach_commit_boundary(self.transaction_commit_boundary());
52
+ self.transaction_manager()
53
+ .mark_explicit_transaction_open()?;
37
54
  Ok(SessionTransaction {
38
55
  transaction: Some(opened.transaction),
39
56
  runtime_functions: opened.runtime_functions,
40
- _transaction_guard: transaction_guard,
57
+ transaction_manager: self.transaction_manager(),
58
+ _write_access: write_access,
41
59
  })
42
60
  }
43
61
  }
44
62
 
45
- impl SessionTransaction {
46
- pub(super) fn transaction_mut(&mut self) -> Result<&mut Transaction, LixError> {
63
+ impl<B> SessionTransaction<B>
64
+ where
65
+ B: StorageBackend + Clone + Send + Sync + 'static,
66
+ for<'backend> B::Read<'backend>: Clone + Send + Sync + 'static,
67
+ for<'backend> B::Write<'backend>: Send,
68
+ {
69
+ pub(super) fn transaction_mut(&mut self) -> Result<&mut Transaction<B>, LixError> {
70
+ self.ensure_session_open()?;
47
71
  self.transaction
48
72
  .as_mut()
49
73
  .ok_or_else(|| transaction_state_error("Lix transaction is closed"))
50
74
  }
51
75
 
76
+ pub fn active_branch_id(&self) -> Result<&str, LixError> {
77
+ self.ensure_session_open()?;
78
+ self.transaction
79
+ .as_ref()
80
+ .map(|transaction| transaction.active_branch_id())
81
+ .ok_or_else(|| transaction_state_error("Lix transaction is closed"))
82
+ }
83
+
52
84
  pub async fn commit(mut self) -> Result<(), LixError> {
85
+ let operation_guard = self.begin_session_commit_operation()?;
53
86
  let transaction = self
54
87
  .transaction
55
88
  .take()
56
89
  .ok_or_else(|| transaction_state_error("Lix transaction is closed"))?;
90
+ let queued_commit_guard = self.transaction_manager.begin_commit();
57
91
  let result = transaction
58
92
  .commit(&self.runtime_functions)
59
93
  .await
60
94
  .map(|_| ());
95
+ drop(queued_commit_guard);
96
+ drop(operation_guard);
61
97
  result
62
98
  }
63
99
 
@@ -69,8 +105,453 @@ impl SessionTransaction {
69
105
  let result = transaction.rollback().await;
70
106
  result
71
107
  }
108
+
109
+ pub(super) fn ensure_session_open(&self) -> Result<(), LixError> {
110
+ self.transaction_manager.ensure_open()
111
+ }
112
+
113
+ pub(super) fn begin_session_operation(&self) -> Result<SessionOperationGuard, LixError> {
114
+ self.transaction_manager.begin_transaction_operation()
115
+ }
116
+
117
+ fn begin_session_commit_operation(&self) -> Result<SessionOperationGuard, LixError> {
118
+ self.transaction_manager
119
+ .begin_transaction_commit_operation()
120
+ }
72
121
  }
73
122
 
74
123
  pub(crate) fn transaction_state_error(message: impl Into<String>) -> LixError {
75
124
  LixError::new("LIX_INVALID_TRANSACTION_STATE", message)
76
125
  }
126
+
127
+ #[derive(Clone)]
128
+ pub(super) struct SessionTransactionManager {
129
+ inner: Arc<SessionTransactionManagerInner>,
130
+ }
131
+
132
+ struct SessionTransactionManagerInner {
133
+ state: std::sync::Mutex<SessionTransactionState>,
134
+ state_changed: Notify,
135
+ commit_boundary: CommitBoundaryState,
136
+ }
137
+
138
+ #[derive(Debug, Default)]
139
+ enum SessionTransactionState {
140
+ #[default]
141
+ OpenIdle,
142
+ OpenOperation {
143
+ active_operations: usize,
144
+ },
145
+ OpenTransaction {
146
+ active_operations: usize,
147
+ owner: TransactionOwner,
148
+ },
149
+ Closing {
150
+ active_operations: usize,
151
+ },
152
+ Closed,
153
+ }
154
+
155
+ #[derive(Clone, Copy, Debug, Eq, PartialEq)]
156
+ enum TransactionOwner {
157
+ Automatic,
158
+ ExplicitOpening,
159
+ Explicit,
160
+ ExplicitCommitting,
161
+ }
162
+
163
+ impl SessionTransactionManager {
164
+ pub(super) fn new() -> Self {
165
+ Self {
166
+ inner: Arc::new(SessionTransactionManagerInner {
167
+ state: std::sync::Mutex::new(SessionTransactionState::default()),
168
+ state_changed: Notify::new(),
169
+ commit_boundary: CommitBoundaryState::new(),
170
+ }),
171
+ }
172
+ }
173
+
174
+ pub(super) async fn close(&self) -> Result<(), LixError> {
175
+ let mut commit_rx = self.inner.commit_boundary.subscribe();
176
+ loop {
177
+ if let Some(_commit_gate) = self.inner.commit_boundary.try_lock_durable_commit() {
178
+ {
179
+ let mut state = self.lock_state();
180
+ if state.has_explicit_transaction() {
181
+ return Err(active_transaction_error());
182
+ }
183
+ let active_operations = state.active_operations();
184
+ *state = if active_operations == 0 {
185
+ SessionTransactionState::Closed
186
+ } else {
187
+ SessionTransactionState::Closing { active_operations }
188
+ };
189
+ }
190
+ self.inner.state_changed.notify_waiters();
191
+ break;
192
+ }
193
+
194
+ let notified = self.inner.state_changed.notified();
195
+ tokio::select! {
196
+ _ = notified => {}
197
+ result = commit_rx.changed() => {
198
+ if result.is_err() {
199
+ self.inner.state_changed.notify_waiters();
200
+ }
201
+ }
202
+ }
203
+ }
204
+
205
+ loop {
206
+ let notified = self.inner.state_changed.notified();
207
+ {
208
+ let mut state = self.lock_state();
209
+ let commit_count = *commit_rx.borrow_and_update();
210
+ if state.active_operations() == 0
211
+ && commit_count == 0
212
+ && !self.inner.commit_boundary.is_active()
213
+ {
214
+ *state = SessionTransactionState::Closed;
215
+ break;
216
+ }
217
+ }
218
+ tokio::select! {
219
+ _ = notified => {}
220
+ result = commit_rx.changed() => {
221
+ if result.is_err() {
222
+ self.inner.state_changed.notify_waiters();
223
+ }
224
+ }
225
+ }
226
+ }
227
+ Ok(())
228
+ }
229
+
230
+ pub(super) fn is_closed(&self) -> bool {
231
+ self.lock_state().is_closed()
232
+ }
233
+
234
+ pub(super) fn ensure_open(&self) -> Result<(), LixError> {
235
+ if self.is_closed() {
236
+ return Err(closed_error());
237
+ }
238
+ Ok(())
239
+ }
240
+
241
+ pub(super) fn begin_session_operation(&self) -> Result<SessionOperationGuard, LixError> {
242
+ self.begin_operation(SessionOperationScope::Session)
243
+ }
244
+
245
+ pub(super) fn begin_transaction_operation(&self) -> Result<SessionOperationGuard, LixError> {
246
+ self.begin_operation(SessionOperationScope::Transaction)
247
+ }
248
+
249
+ pub(super) fn begin_transaction_commit_operation(
250
+ &self,
251
+ ) -> Result<SessionOperationGuard, LixError> {
252
+ self.begin_operation(SessionOperationScope::TransactionCommit)
253
+ }
254
+
255
+ fn begin_operation(
256
+ &self,
257
+ scope: SessionOperationScope,
258
+ ) -> Result<SessionOperationGuard, LixError> {
259
+ {
260
+ let mut state = self.lock_state();
261
+ state.begin_operation(scope)?;
262
+ }
263
+ self.inner.state_changed.notify_waiters();
264
+
265
+ if let Err(error) = self.ensure_open() {
266
+ self.finish_operation();
267
+ return Err(error);
268
+ }
269
+
270
+ Ok(SessionOperationGuard {
271
+ manager: self.clone(),
272
+ })
273
+ }
274
+
275
+ pub(super) fn begin_write_lease(&self) -> Result<SessionWriteLease, LixError> {
276
+ self.begin_write_lease_for(TransactionOwner::Automatic)
277
+ }
278
+
279
+ pub(super) fn begin_explicit_write_lease(&self) -> Result<SessionWriteLease, LixError> {
280
+ self.begin_write_lease_for(TransactionOwner::ExplicitOpening)
281
+ }
282
+
283
+ fn begin_write_lease_for(
284
+ &self,
285
+ owner: TransactionOwner,
286
+ ) -> Result<SessionWriteLease, LixError> {
287
+ {
288
+ let mut state = self.lock_state();
289
+ state.begin_write_lease(owner)?;
290
+ }
291
+ self.inner.state_changed.notify_waiters();
292
+
293
+ let operation_guard = SessionOperationGuard {
294
+ manager: self.clone(),
295
+ };
296
+ if let Err(error) = self.ensure_open() {
297
+ drop(operation_guard);
298
+ self.finish_transaction();
299
+ return Err(error);
300
+ }
301
+ let transaction_guard = SessionTransactionGuard {
302
+ manager: self.clone(),
303
+ };
304
+ Ok(SessionWriteLease {
305
+ _transaction_guard: transaction_guard,
306
+ _operation_guard: operation_guard,
307
+ })
308
+ }
309
+
310
+ pub(super) fn begin_commit(&self) -> CommitBoundaryGuard {
311
+ self.inner.commit_boundary.begin()
312
+ }
313
+
314
+ pub(super) fn mark_explicit_transaction_open(&self) -> Result<(), LixError> {
315
+ {
316
+ let mut state = self.lock_state();
317
+ state.mark_explicit_transaction_open()?;
318
+ }
319
+ self.inner.state_changed.notify_waiters();
320
+ Ok(())
321
+ }
322
+
323
+ pub(super) fn transaction_commit_boundary(&self) -> TransactionCommitBoundary {
324
+ let manager = self.clone();
325
+ TransactionCommitBoundary::new(
326
+ self.inner.commit_boundary.clone(),
327
+ Arc::new(move || manager.ensure_open()),
328
+ )
329
+ }
330
+
331
+ fn finish_operation(&self) {
332
+ {
333
+ let mut state = self.lock_state();
334
+ state.finish_operation();
335
+ }
336
+ self.inner.state_changed.notify_waiters();
337
+ }
338
+
339
+ fn finish_transaction(&self) {
340
+ {
341
+ let mut state = self.lock_state();
342
+ state.finish_transaction();
343
+ }
344
+ self.inner.state_changed.notify_waiters();
345
+ }
346
+
347
+ fn lock_state(&self) -> std::sync::MutexGuard<'_, SessionTransactionState> {
348
+ self.inner
349
+ .state
350
+ .lock()
351
+ .expect("session transaction manager lock should not poison")
352
+ }
353
+
354
+ #[cfg(test)]
355
+ pub(super) fn operation_count_for_test(&self) -> usize {
356
+ self.lock_state().active_operations()
357
+ }
358
+
359
+ #[cfg(test)]
360
+ pub(super) fn commit_in_progress_for_test(&self) -> bool {
361
+ self.inner.commit_boundary.is_active()
362
+ }
363
+
364
+ #[cfg(test)]
365
+ pub(super) fn active_transaction_for_test(&self) -> bool {
366
+ matches!(
367
+ *self.lock_state(),
368
+ SessionTransactionState::OpenTransaction { .. }
369
+ )
370
+ }
371
+ }
372
+
373
+ impl SessionTransactionState {
374
+ fn is_closed(&self) -> bool {
375
+ matches!(self, Self::Closing { .. } | Self::Closed)
376
+ }
377
+
378
+ fn active_operations(&self) -> usize {
379
+ match self {
380
+ Self::OpenIdle | Self::Closed => 0,
381
+ Self::OpenOperation { active_operations }
382
+ | Self::OpenTransaction {
383
+ active_operations, ..
384
+ }
385
+ | Self::Closing { active_operations } => *active_operations,
386
+ }
387
+ }
388
+
389
+ fn has_explicit_transaction(&self) -> bool {
390
+ matches!(
391
+ self,
392
+ Self::OpenTransaction {
393
+ owner: TransactionOwner::Explicit,
394
+ ..
395
+ }
396
+ )
397
+ }
398
+
399
+ fn begin_operation(&mut self, scope: SessionOperationScope) -> Result<(), LixError> {
400
+ match self {
401
+ Self::OpenIdle => {
402
+ if matches!(scope, SessionOperationScope::Session) {
403
+ *self = Self::OpenOperation {
404
+ active_operations: 1,
405
+ };
406
+ Ok(())
407
+ } else {
408
+ Err(active_transaction_error())
409
+ }
410
+ }
411
+ Self::OpenOperation { active_operations } => {
412
+ if matches!(scope, SessionOperationScope::Session) {
413
+ *active_operations += 1;
414
+ Ok(())
415
+ } else {
416
+ Err(active_transaction_error())
417
+ }
418
+ }
419
+ Self::OpenTransaction {
420
+ active_operations,
421
+ owner,
422
+ } => match scope {
423
+ SessionOperationScope::Session => Err(active_transaction_error()),
424
+ SessionOperationScope::Transaction => {
425
+ *active_operations += 1;
426
+ Ok(())
427
+ }
428
+ SessionOperationScope::TransactionCommit => {
429
+ if *owner != TransactionOwner::Explicit {
430
+ return Err(active_transaction_error());
431
+ }
432
+ *owner = TransactionOwner::ExplicitCommitting;
433
+ *active_operations += 1;
434
+ Ok(())
435
+ }
436
+ },
437
+ Self::Closing { .. } | Self::Closed => Err(closed_error()),
438
+ }
439
+ }
440
+
441
+ fn begin_write_lease(&mut self, owner: TransactionOwner) -> Result<(), LixError> {
442
+ match self {
443
+ Self::OpenIdle => {
444
+ *self = Self::OpenTransaction {
445
+ active_operations: 1,
446
+ owner,
447
+ };
448
+ Ok(())
449
+ }
450
+ Self::OpenOperation { .. } | Self::OpenTransaction { .. } => {
451
+ Err(active_transaction_error())
452
+ }
453
+ Self::Closing { .. } | Self::Closed => Err(closed_error()),
454
+ }
455
+ }
456
+
457
+ fn mark_explicit_transaction_open(&mut self) -> Result<(), LixError> {
458
+ match self {
459
+ Self::OpenTransaction {
460
+ active_operations: 1,
461
+ owner,
462
+ } if *owner == TransactionOwner::ExplicitOpening => {
463
+ *owner = TransactionOwner::Explicit;
464
+ Ok(())
465
+ }
466
+ Self::Closing { .. } | Self::Closed => Err(closed_error()),
467
+ _ => {
468
+ panic!("explicit transaction should be opening before it is marked open");
469
+ }
470
+ }
471
+ }
472
+
473
+ fn finish_operation(&mut self) {
474
+ match self {
475
+ Self::OpenOperation { active_operations } => {
476
+ *active_operations = active_operations
477
+ .checked_sub(1)
478
+ .expect("session operation count should not underflow");
479
+ if *active_operations == 0 {
480
+ *self = Self::OpenIdle;
481
+ }
482
+ }
483
+ Self::OpenTransaction {
484
+ active_operations, ..
485
+ } => {
486
+ *active_operations = active_operations
487
+ .checked_sub(1)
488
+ .expect("session operation count should not underflow");
489
+ }
490
+ Self::Closing { active_operations } => {
491
+ *active_operations = active_operations
492
+ .checked_sub(1)
493
+ .expect("session operation count should not underflow");
494
+ if *active_operations == 0 {
495
+ *self = Self::Closed;
496
+ }
497
+ }
498
+ Self::OpenIdle | Self::Closed => {
499
+ panic!("session operation count should not underflow");
500
+ }
501
+ }
502
+ }
503
+
504
+ fn finish_transaction(&mut self) {
505
+ match self {
506
+ Self::OpenTransaction {
507
+ active_operations: 0,
508
+ ..
509
+ } => {
510
+ *self = Self::OpenIdle;
511
+ }
512
+ Self::OpenTransaction { .. } => {}
513
+ Self::Closing { .. } | Self::Closed => {}
514
+ Self::OpenIdle | Self::OpenOperation { .. } => {
515
+ panic!("session transaction should be active before it is finished");
516
+ }
517
+ }
518
+ }
519
+ }
520
+
521
+ #[derive(Clone, Copy)]
522
+ enum SessionOperationScope {
523
+ Session,
524
+ Transaction,
525
+ TransactionCommit,
526
+ }
527
+
528
+ fn active_transaction_error() -> LixError {
529
+ transaction_state_error(
530
+ "Lix handle has an active transaction; use the transaction handle for reads and writes until it is committed or rolled back",
531
+ )
532
+ }
533
+
534
+ pub(super) struct SessionWriteLease {
535
+ _operation_guard: SessionOperationGuard,
536
+ _transaction_guard: SessionTransactionGuard,
537
+ }
538
+
539
+ pub(super) struct SessionTransactionGuard {
540
+ manager: SessionTransactionManager,
541
+ }
542
+
543
+ impl Drop for SessionTransactionGuard {
544
+ fn drop(&mut self) {
545
+ self.manager.finish_transaction();
546
+ }
547
+ }
548
+
549
+ pub(super) struct SessionOperationGuard {
550
+ manager: SessionTransactionManager,
551
+ }
552
+
553
+ impl Drop for SessionOperationGuard {
554
+ fn drop(&mut self) {
555
+ self.manager.finish_operation();
556
+ }
557
+ }
@@ -1,8 +1,5 @@
1
1
  use datafusion::sql::parser::Statement as DataFusionStatement;
2
- use datafusion::sql::sqlparser::ast::{
3
- FromTable, ObjectName, Query, SetExpr, Statement as SqlStatement, TableFactor, TableObject,
4
- TableWithJoins,
5
- };
2
+ use datafusion::sql::sqlparser::ast::{Query, SetExpr, Statement as SqlStatement};
6
3
 
7
4
  use crate::LixError;
8
5
 
@@ -36,82 +33,13 @@ pub(crate) fn validate_supported_datafusion_statement_ast(
36
33
  pub(crate) fn classify_datafusion_statement(statement: &DataFusionStatement) -> SqlStatementKind {
37
34
  match statement {
38
35
  DataFusionStatement::Statement(statement) => classify_ast_statement(statement),
39
- DataFusionStatement::Explain(_) => SqlStatementKind::Read,
40
- _ => SqlStatementKind::Other,
41
- }
42
- }
43
-
44
- pub(crate) fn datafusion_statement_dml_target_table_names(
45
- statement: &DataFusionStatement,
46
- ) -> Vec<String> {
47
- let mut targets = Vec::new();
48
- collect_datafusion_statement_dml_target_table_names(statement, &mut targets);
49
- targets
50
- }
51
-
52
- fn collect_datafusion_statement_dml_target_table_names(
53
- statement: &DataFusionStatement,
54
- targets: &mut Vec<String>,
55
- ) {
56
- match statement {
57
- DataFusionStatement::Statement(statement) => {
58
- collect_dml_target_table_names(statement, targets);
59
- }
60
36
  DataFusionStatement::Explain(explain) => {
61
- collect_datafusion_statement_dml_target_table_names(
62
- explain.statement.as_ref(),
63
- targets,
64
- );
65
- }
66
- _ => {}
67
- }
68
- }
69
-
70
- fn collect_dml_target_table_names(statement: &SqlStatement, targets: &mut Vec<String>) {
71
- match statement {
72
- SqlStatement::Insert(insert) => {
73
- if let TableObject::TableName(name) = &insert.table {
74
- if let Some(table_name) = object_name_table_part(name) {
75
- targets.push(table_name);
76
- }
77
- }
78
- }
79
- SqlStatement::Update(update) => {
80
- collect_table_with_joins_target(&update.table, targets);
81
- }
82
- SqlStatement::Delete(delete) => {
83
- let tables = match &delete.from {
84
- FromTable::WithFromKeyword(tables) | FromTable::WithoutKeyword(tables) => tables,
85
- };
86
- for table in tables {
87
- collect_table_with_joins_target(table, targets);
88
- }
89
- }
90
- SqlStatement::Explain { statement, .. } => {
91
- collect_dml_target_table_names(statement.as_ref(), targets);
92
- }
93
- _ => {}
94
- }
95
- }
96
-
97
- fn collect_table_with_joins_target(table: &TableWithJoins, targets: &mut Vec<String>) {
98
- if let TableFactor::Table { name, .. } = &table.relation {
99
- if let Some(table_name) = object_name_table_part(name) {
100
- targets.push(table_name);
37
+ classify_datafusion_statement(explain.statement.as_ref())
101
38
  }
39
+ _ => SqlStatementKind::Other,
102
40
  }
103
41
  }
104
42
 
105
- fn object_name_table_part(name: &ObjectName) -> Option<String> {
106
- name.0.last().and_then(|part| part.as_ident()).map(|ident| {
107
- if ident.quote_style.is_some() {
108
- ident.value.clone()
109
- } else {
110
- ident.value.to_ascii_lowercase()
111
- }
112
- })
113
- }
114
-
115
43
  fn classify_ast_statement(statement: &SqlStatement) -> SqlStatementKind {
116
44
  match statement {
117
45
  SqlStatement::Insert(_) | SqlStatement::Update(_) | SqlStatement::Delete(_) => {
@@ -0,0 +1,5 @@
1
+ use crate::LixError;
2
+
3
+ pub(crate) fn unsupported(message: impl Into<String>) -> LixError {
4
+ LixError::new(LixError::CODE_UNSUPPORTED_SQL, message.into())
5
+ }
@@ -0,0 +1,29 @@
1
+ #[derive(Clone, Debug, Eq, PartialEq)]
2
+ pub(crate) enum BoundExpr {
3
+ Column(BoundColumnRef),
4
+ Param(BoundParamRef),
5
+ Literal(BoundLiteral),
6
+ Function { name: String, args: Vec<BoundExpr> },
7
+ }
8
+
9
+ #[derive(Clone, Debug, Eq, PartialEq)]
10
+ pub(crate) enum BoundLiteral {
11
+ Null,
12
+ Bool(bool),
13
+ Integer(i64),
14
+ Text(String),
15
+ Json(serde_json::Value),
16
+ Blob(Vec<u8>),
17
+ }
18
+
19
+ #[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd)]
20
+ pub(crate) struct BoundColumnRef {
21
+ pub(crate) table: String,
22
+ pub(crate) column_id: usize,
23
+ pub(crate) name: String,
24
+ }
25
+
26
+ #[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd)]
27
+ pub(crate) struct BoundParamRef {
28
+ pub(crate) index: usize,
29
+ }
@@ -0,0 +1,12 @@
1
+ pub(crate) mod classify;
2
+ pub(crate) mod error;
3
+ pub(crate) mod expr;
4
+ mod public_udf;
5
+ pub(crate) mod read;
6
+ pub(crate) mod statement;
7
+ pub(crate) mod table;
8
+ pub(crate) mod write;
9
+
10
+ pub(crate) use public_udf::statement_has_durable_runtime_function;
11
+ pub(crate) use read::{bind_read_statement, bind_statement_route, BoundStatementRoute};
12
+ pub(crate) use statement::bind_statement;