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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (274) hide show
  1. package/README.md +76 -4
  2. package/dist/errors.d.ts +7 -0
  3. package/dist/errors.js +19 -0
  4. package/dist/index.d.ts +4 -5
  5. package/dist/index.js +3 -3
  6. package/dist/native.d.ts +1 -0
  7. package/dist/native.js +47 -0
  8. package/dist/open-lix.d.ts +38 -207
  9. package/dist/open-lix.js +59 -284
  10. package/dist/result.d.ts +18 -0
  11. package/dist/result.js +48 -0
  12. package/dist/types.d.ts +114 -1
  13. package/dist/value.d.ts +28 -0
  14. package/dist/value.js +245 -0
  15. package/package.json +38 -71
  16. package/SKILL.md +0 -507
  17. package/dist/builtin-schemas.d.ts +0 -1
  18. package/dist/builtin-schemas.js +0 -1
  19. package/dist/engine-wasm/index.d.ts +0 -87
  20. package/dist/engine-wasm/index.js +0 -339
  21. package/dist/engine-wasm/wasm/lix_engine.d.ts +0 -79
  22. package/dist/engine-wasm/wasm/lix_engine.js +0 -833
  23. package/dist/engine-wasm/wasm/lix_engine.wasm +0 -0
  24. package/dist/engine-wasm/wasm/lix_engine.wasm.d.ts +0 -27
  25. package/dist/generated/builtin-schemas.d.ts +0 -427
  26. package/dist/generated/builtin-schemas.js +0 -643
  27. package/dist/sqlite/index.d.ts +0 -12
  28. package/dist/sqlite/index.js +0 -359
  29. package/dist-engine-src/README.md +0 -18
  30. package/dist-engine-src/src/backend/capabilities.rs +0 -67
  31. package/dist-engine-src/src/backend/conformance/baseline.rs +0 -1127
  32. package/dist-engine-src/src/backend/conformance/factory.rs +0 -93
  33. package/dist-engine-src/src/backend/conformance/failure_tests.rs +0 -608
  34. package/dist-engine-src/src/backend/conformance/fixtures.rs +0 -26
  35. package/dist-engine-src/src/backend/conformance/mod.rs +0 -75
  36. package/dist-engine-src/src/backend/conformance/model.rs +0 -28
  37. package/dist-engine-src/src/backend/conformance/model_based.rs +0 -257
  38. package/dist-engine-src/src/backend/conformance/persistence.rs +0 -204
  39. package/dist-engine-src/src/backend/conformance/projection.rs +0 -21
  40. package/dist-engine-src/src/backend/conformance/pushdown.rs +0 -24
  41. package/dist-engine-src/src/backend/conformance/runner.rs +0 -90
  42. package/dist-engine-src/src/backend/conformance/scan.rs +0 -24
  43. package/dist-engine-src/src/backend/conformance/write.rs +0 -16
  44. package/dist-engine-src/src/backend/error.rs +0 -94
  45. package/dist-engine-src/src/backend/in_memory.rs +0 -670
  46. package/dist-engine-src/src/backend/mod.rs +0 -39
  47. package/dist-engine-src/src/backend/predicate.rs +0 -80
  48. package/dist-engine-src/src/backend/traits.rs +0 -260
  49. package/dist-engine-src/src/backend/types.rs +0 -239
  50. package/dist-engine-src/src/binary_cas/chunking.rs +0 -31
  51. package/dist-engine-src/src/binary_cas/codec.rs +0 -346
  52. package/dist-engine-src/src/binary_cas/context.rs +0 -139
  53. package/dist-engine-src/src/binary_cas/kv.rs +0 -1038
  54. package/dist-engine-src/src/binary_cas/mod.rs +0 -11
  55. package/dist-engine-src/src/binary_cas/types.rs +0 -121
  56. package/dist-engine-src/src/branch/context.rs +0 -40
  57. package/dist-engine-src/src/branch/lifecycle.rs +0 -221
  58. package/dist-engine-src/src/branch/mod.rs +0 -13
  59. package/dist-engine-src/src/branch/refs.rs +0 -321
  60. package/dist-engine-src/src/branch/stage_rows.rs +0 -67
  61. package/dist-engine-src/src/branch/types.rs +0 -21
  62. package/dist-engine-src/src/catalog/context.rs +0 -412
  63. package/dist-engine-src/src/catalog/mod.rs +0 -10
  64. package/dist-engine-src/src/catalog/schema.rs +0 -4
  65. package/dist-engine-src/src/catalog/snapshot.rs +0 -1114
  66. package/dist-engine-src/src/cel/context.rs +0 -86
  67. package/dist-engine-src/src/cel/error.rs +0 -19
  68. package/dist-engine-src/src/cel/mod.rs +0 -8
  69. package/dist-engine-src/src/cel/provider.rs +0 -9
  70. package/dist-engine-src/src/cel/runtime.rs +0 -167
  71. package/dist-engine-src/src/cel/value.rs +0 -50
  72. package/dist-engine-src/src/changelog/bench_support.rs +0 -785
  73. package/dist-engine-src/src/changelog/change.rs +0 -1
  74. package/dist-engine-src/src/changelog/codec.rs +0 -497
  75. package/dist-engine-src/src/changelog/commit.rs +0 -1
  76. package/dist-engine-src/src/changelog/context.rs +0 -1614
  77. package/dist-engine-src/src/changelog/mod.rs +0 -29
  78. package/dist-engine-src/src/changelog/store.rs +0 -163
  79. package/dist-engine-src/src/changelog/test_support.rs +0 -54
  80. package/dist-engine-src/src/changelog/types.rs +0 -213
  81. package/dist-engine-src/src/commit_graph/context.rs +0 -944
  82. package/dist-engine-src/src/commit_graph/mod.rs +0 -9
  83. package/dist-engine-src/src/commit_graph/types.rs +0 -89
  84. package/dist-engine-src/src/commit_graph/walker.rs +0 -786
  85. package/dist-engine-src/src/common/error.rs +0 -347
  86. package/dist-engine-src/src/common/fingerprint.rs +0 -3
  87. package/dist-engine-src/src/common/fs_path.rs +0 -1336
  88. package/dist-engine-src/src/common/identity.rs +0 -145
  89. package/dist-engine-src/src/common/json_pointer.rs +0 -67
  90. package/dist-engine-src/src/common/metadata.rs +0 -40
  91. package/dist-engine-src/src/common/mod.rs +0 -23
  92. package/dist-engine-src/src/common/types.rs +0 -105
  93. package/dist-engine-src/src/common/wire.rs +0 -222
  94. package/dist-engine-src/src/domain.rs +0 -320
  95. package/dist-engine-src/src/engine.rs +0 -203
  96. package/dist-engine-src/src/entity_pk.rs +0 -402
  97. package/dist-engine-src/src/functions/context.rs +0 -296
  98. package/dist-engine-src/src/functions/deterministic.rs +0 -113
  99. package/dist-engine-src/src/functions/mod.rs +0 -18
  100. package/dist-engine-src/src/functions/provider.rs +0 -130
  101. package/dist-engine-src/src/functions/state.rs +0 -335
  102. package/dist-engine-src/src/functions/types.rs +0 -37
  103. package/dist-engine-src/src/init.rs +0 -692
  104. package/dist-engine-src/src/json_store/compression.rs +0 -77
  105. package/dist-engine-src/src/json_store/context.rs +0 -172
  106. package/dist-engine-src/src/json_store/encoded.rs +0 -15
  107. package/dist-engine-src/src/json_store/mod.rs +0 -38
  108. package/dist-engine-src/src/json_store/store.rs +0 -494
  109. package/dist-engine-src/src/json_store/types.rs +0 -212
  110. package/dist-engine-src/src/lib.rs +0 -92
  111. package/dist-engine-src/src/live_state/context.rs +0 -1883
  112. package/dist-engine-src/src/live_state/mod.rs +0 -21
  113. package/dist-engine-src/src/live_state/overlay.rs +0 -75
  114. package/dist-engine-src/src/live_state/reader.rs +0 -23
  115. package/dist-engine-src/src/live_state/types.rs +0 -231
  116. package/dist-engine-src/src/live_state/visibility.rs +0 -666
  117. package/dist-engine-src/src/plugin/archive.rs +0 -438
  118. package/dist-engine-src/src/plugin/component.rs +0 -183
  119. package/dist-engine-src/src/plugin/install.rs +0 -619
  120. package/dist-engine-src/src/plugin/manifest.rs +0 -516
  121. package/dist-engine-src/src/plugin/materializer.rs +0 -202
  122. package/dist-engine-src/src/plugin/mod.rs +0 -33
  123. package/dist-engine-src/src/plugin/plugin_manifest.json +0 -119
  124. package/dist-engine-src/src/plugin/storage.rs +0 -74
  125. package/dist-engine-src/src/schema/annotations/defaults.rs +0 -275
  126. package/dist-engine-src/src/schema/annotations/mod.rs +0 -1
  127. package/dist-engine-src/src/schema/builtin/lix_account.json +0 -21
  128. package/dist-engine-src/src/schema/builtin/lix_active_account.json +0 -29
  129. package/dist-engine-src/src/schema/builtin/lix_binary_blob_ref.json +0 -29
  130. package/dist-engine-src/src/schema/builtin/lix_branch_descriptor.json +0 -34
  131. package/dist-engine-src/src/schema/builtin/lix_branch_ref.json +0 -48
  132. package/dist-engine-src/src/schema/builtin/lix_change.json +0 -63
  133. package/dist-engine-src/src/schema/builtin/lix_change_author.json +0 -45
  134. package/dist-engine-src/src/schema/builtin/lix_commit.json +0 -24
  135. package/dist-engine-src/src/schema/builtin/lix_commit_edge.json +0 -53
  136. package/dist-engine-src/src/schema/builtin/lix_directory_descriptor.json +0 -52
  137. package/dist-engine-src/src/schema/builtin/lix_file_descriptor.json +0 -52
  138. package/dist-engine-src/src/schema/builtin/lix_key_value.json +0 -40
  139. package/dist-engine-src/src/schema/builtin/lix_label.json +0 -29
  140. package/dist-engine-src/src/schema/builtin/lix_label_assignment.json +0 -74
  141. package/dist-engine-src/src/schema/builtin/lix_registered_schema.json +0 -25
  142. package/dist-engine-src/src/schema/builtin/mod.rs +0 -220
  143. package/dist-engine-src/src/schema/compatibility.rs +0 -787
  144. package/dist-engine-src/src/schema/definition.json +0 -187
  145. package/dist-engine-src/src/schema/definition.rs +0 -742
  146. package/dist-engine-src/src/schema/key.rs +0 -138
  147. package/dist-engine-src/src/schema/mod.rs +0 -20
  148. package/dist-engine-src/src/schema/seed.rs +0 -14
  149. package/dist-engine-src/src/schema/tests.rs +0 -780
  150. package/dist-engine-src/src/session/context.rs +0 -1059
  151. package/dist-engine-src/src/session/create_branch.rs +0 -94
  152. package/dist-engine-src/src/session/execute.rs +0 -681
  153. package/dist-engine-src/src/session/merge/analysis.rs +0 -108
  154. package/dist-engine-src/src/session/merge/branch.rs +0 -417
  155. package/dist-engine-src/src/session/merge/conflicts.rs +0 -63
  156. package/dist-engine-src/src/session/merge/mod.rs +0 -10
  157. package/dist-engine-src/src/session/merge/stats.rs +0 -61
  158. package/dist-engine-src/src/session/mod.rs +0 -30
  159. package/dist-engine-src/src/session/switch_branch.rs +0 -113
  160. package/dist-engine-src/src/session/transaction.rs +0 -557
  161. package/dist-engine-src/src/sql2/bind/classify.rs +0 -102
  162. package/dist-engine-src/src/sql2/bind/error.rs +0 -5
  163. package/dist-engine-src/src/sql2/bind/expr.rs +0 -29
  164. package/dist-engine-src/src/sql2/bind/mod.rs +0 -12
  165. package/dist-engine-src/src/sql2/bind/public_udf.rs +0 -306
  166. package/dist-engine-src/src/sql2/bind/read.rs +0 -65
  167. package/dist-engine-src/src/sql2/bind/statement.rs +0 -2236
  168. package/dist-engine-src/src/sql2/bind/table.rs +0 -273
  169. package/dist-engine-src/src/sql2/bind/write.rs +0 -86
  170. package/dist-engine-src/src/sql2/branch_scope.rs +0 -436
  171. package/dist-engine-src/src/sql2/catalog/capability.rs +0 -20
  172. package/dist-engine-src/src/sql2/catalog/entity_surface.rs +0 -296
  173. package/dist-engine-src/src/sql2/catalog/mod.rs +0 -15
  174. package/dist-engine-src/src/sql2/catalog/registry.rs +0 -556
  175. package/dist-engine-src/src/sql2/catalog/schema.rs +0 -88
  176. package/dist-engine-src/src/sql2/catalog/surface.rs +0 -41
  177. package/dist-engine-src/src/sql2/change_materialization.rs +0 -122
  178. package/dist-engine-src/src/sql2/context.rs +0 -317
  179. package/dist-engine-src/src/sql2/dml.rs +0 -148
  180. package/dist-engine-src/src/sql2/error.rs +0 -215
  181. package/dist-engine-src/src/sql2/exec/bound_public_write.rs +0 -1593
  182. package/dist-engine-src/src/sql2/exec/datafusion.rs +0 -5266
  183. package/dist-engine-src/src/sql2/exec/fast_write.rs +0 -82
  184. package/dist-engine-src/src/sql2/exec/mod.rs +0 -24
  185. package/dist-engine-src/src/sql2/exec/write.rs +0 -661
  186. package/dist-engine-src/src/sql2/filesystem_planner.rs +0 -1485
  187. package/dist-engine-src/src/sql2/filesystem_predicates.rs +0 -159
  188. package/dist-engine-src/src/sql2/filesystem_visibility.rs +0 -383
  189. package/dist-engine-src/src/sql2/history_projection.rs +0 -56
  190. package/dist-engine-src/src/sql2/history_route.rs +0 -661
  191. package/dist-engine-src/src/sql2/mod.rs +0 -52
  192. package/dist-engine-src/src/sql2/optimize/datafusion.rs +0 -1
  193. package/dist-engine-src/src/sql2/optimize/mod.rs +0 -2
  194. package/dist-engine-src/src/sql2/optimize/simple_write.rs +0 -116
  195. package/dist-engine-src/src/sql2/parse/mod.rs +0 -69
  196. package/dist-engine-src/src/sql2/parse/normalize.rs +0 -1
  197. package/dist-engine-src/src/sql2/plan/branch_scope.rs +0 -24
  198. package/dist-engine-src/src/sql2/plan/mod.rs +0 -5
  199. package/dist-engine-src/src/sql2/plan/predicate.rs +0 -22
  200. package/dist-engine-src/src/sql2/plan/write.rs +0 -147
  201. package/dist-engine-src/src/sql2/predicate_typecheck.rs +0 -504
  202. package/dist-engine-src/src/sql2/providers/branch.rs +0 -1206
  203. package/dist-engine-src/src/sql2/providers/change.rs +0 -445
  204. package/dist-engine-src/src/sql2/providers/directory.rs +0 -2422
  205. package/dist-engine-src/src/sql2/providers/directory_history.rs +0 -645
  206. package/dist-engine-src/src/sql2/providers/entity.rs +0 -1484
  207. package/dist-engine-src/src/sql2/providers/entity_history.rs +0 -452
  208. package/dist-engine-src/src/sql2/providers/file.rs +0 -3686
  209. package/dist-engine-src/src/sql2/providers/file_history.rs +0 -924
  210. package/dist-engine-src/src/sql2/providers/history.rs +0 -426
  211. package/dist-engine-src/src/sql2/providers/lix_state.rs +0 -2542
  212. package/dist-engine-src/src/sql2/providers/mod.rs +0 -508
  213. package/dist-engine-src/src/sql2/read_only.rs +0 -63
  214. package/dist-engine-src/src/sql2/record_batch.rs +0 -17
  215. package/dist-engine-src/src/sql2/result_metadata.rs +0 -29
  216. package/dist-engine-src/src/sql2/runtime.rs +0 -60
  217. package/dist-engine-src/src/sql2/session.rs +0 -83
  218. package/dist-engine-src/src/sql2/storage/constraints.rs +0 -1
  219. package/dist-engine-src/src/sql2/storage/mod.rs +0 -1
  220. package/dist-engine-src/src/sql2/test_support/differential.rs +0 -712
  221. package/dist-engine-src/src/sql2/test_support/generators.rs +0 -354
  222. package/dist-engine-src/src/sql2/test_support/mod.rs +0 -2
  223. package/dist-engine-src/src/sql2/udfs/common.rs +0 -295
  224. package/dist-engine-src/src/sql2/udfs/lix_active_branch_commit_id.rs +0 -53
  225. package/dist-engine-src/src/sql2/udfs/lix_empty_blob.rs +0 -47
  226. package/dist-engine-src/src/sql2/udfs/lix_json.rs +0 -100
  227. package/dist-engine-src/src/sql2/udfs/lix_json_get.rs +0 -99
  228. package/dist-engine-src/src/sql2/udfs/lix_json_get_text.rs +0 -99
  229. package/dist-engine-src/src/sql2/udfs/lix_text_decode.rs +0 -82
  230. package/dist-engine-src/src/sql2/udfs/lix_text_encode.rs +0 -85
  231. package/dist-engine-src/src/sql2/udfs/lix_timestamp.rs +0 -76
  232. package/dist-engine-src/src/sql2/udfs/lix_uuid_v7.rs +0 -76
  233. package/dist-engine-src/src/sql2/udfs/mod.rs +0 -86
  234. package/dist-engine-src/src/sql2/write_normalization.rs +0 -368
  235. package/dist-engine-src/src/storage/conformance.rs +0 -399
  236. package/dist-engine-src/src/storage/context.rs +0 -620
  237. package/dist-engine-src/src/storage/mod.rs +0 -52
  238. package/dist-engine-src/src/storage/point.rs +0 -440
  239. package/dist-engine-src/src/storage/read_scope.rs +0 -67
  240. package/dist-engine-src/src/storage/reader.rs +0 -867
  241. package/dist-engine-src/src/storage/scan.rs +0 -784
  242. package/dist-engine-src/src/storage/spaces.rs +0 -236
  243. package/dist-engine-src/src/storage/stats.rs +0 -80
  244. package/dist-engine-src/src/storage/write_set.rs +0 -962
  245. package/dist-engine-src/src/storage_bench.rs +0 -171
  246. package/dist-engine-src/src/test_support.rs +0 -450
  247. package/dist-engine-src/src/tracked_state/bench_support.rs +0 -394
  248. package/dist-engine-src/src/tracked_state/codec.rs +0 -1183
  249. package/dist-engine-src/src/tracked_state/commit_root_rebuild.rs +0 -358
  250. package/dist-engine-src/src/tracked_state/context.rs +0 -2801
  251. package/dist-engine-src/src/tracked_state/diff.rs +0 -2140
  252. package/dist-engine-src/src/tracked_state/merge.rs +0 -478
  253. package/dist-engine-src/src/tracked_state/mod.rs +0 -35
  254. package/dist-engine-src/src/tracked_state/row_materialization.rs +0 -275
  255. package/dist-engine-src/src/tracked_state/storage.rs +0 -427
  256. package/dist-engine-src/src/tracked_state/tree.rs +0 -3063
  257. package/dist-engine-src/src/tracked_state/types.rs +0 -238
  258. package/dist-engine-src/src/transaction/bench_support.rs +0 -407
  259. package/dist-engine-src/src/transaction/commit.rs +0 -1592
  260. package/dist-engine-src/src/transaction/context.rs +0 -1653
  261. package/dist-engine-src/src/transaction/mod.rs +0 -24
  262. package/dist-engine-src/src/transaction/normalization.rs +0 -877
  263. package/dist-engine-src/src/transaction/prep.rs +0 -37
  264. package/dist-engine-src/src/transaction/schema_resolver.rs +0 -163
  265. package/dist-engine-src/src/transaction/staging.rs +0 -1525
  266. package/dist-engine-src/src/transaction/types.rs +0 -403
  267. package/dist-engine-src/src/transaction/validation.rs +0 -5766
  268. package/dist-engine-src/src/untracked_state/codec.rs +0 -615
  269. package/dist-engine-src/src/untracked_state/context.rs +0 -98
  270. package/dist-engine-src/src/untracked_state/materialization.rs +0 -63
  271. package/dist-engine-src/src/untracked_state/mod.rs +0 -15
  272. package/dist-engine-src/src/untracked_state/storage.rs +0 -898
  273. package/dist-engine-src/src/untracked_state/types.rs +0 -146
  274. package/dist-engine-src/src/wasm/mod.rs +0 -60
@@ -1,681 +0,0 @@
1
- use std::sync::Arc;
2
-
3
- use crate::functions::FunctionContext;
4
- use crate::sql2;
5
- use crate::storage::{StorageBackend, StorageReadOptions, StorageWriteOptions, StorageWriteSet};
6
- use crate::transaction::{begin_commit_boundary, commit_at_boundary};
7
- use crate::{LixError, LixNotice, SqlQueryResult, Value};
8
-
9
- use super::context::{SessionContext, SessionSqlExecutionContext, SessionWriteAccess};
10
- use super::transaction::SessionTransaction;
11
-
12
- /// Result of executing one SQL statement through engine.
13
- ///
14
- /// Column names live once at the result-set level. Individual rows only own
15
- /// values, which keeps the public API row-oriented without copying schema
16
- /// metadata into every row.
17
- #[derive(Debug, Clone, PartialEq)]
18
- pub struct ExecuteResult {
19
- columns: Vec<String>,
20
- rows: Vec<Row>,
21
- rows_affected: u64,
22
- notices: Vec<LixNotice>,
23
- }
24
-
25
- impl ExecuteResult {
26
- fn from_sql_query_result(result: SqlQueryResult) -> Self {
27
- Self {
28
- columns: result.columns,
29
- rows: Vec::new(),
30
- rows_affected: 0,
31
- notices: result.notices,
32
- }
33
- .with_rows(result.rows)
34
- }
35
-
36
- pub fn from_rows_affected(rows_affected: u64) -> Self {
37
- Self {
38
- columns: Vec::new(),
39
- rows: Vec::new(),
40
- rows_affected,
41
- notices: Vec::new(),
42
- }
43
- }
44
-
45
- pub fn from_rows(columns: Vec<String>, rows: Vec<Vec<Value>>) -> Self {
46
- Self {
47
- columns,
48
- rows: Vec::new(),
49
- rows_affected: 0,
50
- notices: Vec::new(),
51
- }
52
- .with_rows(rows)
53
- }
54
-
55
- fn with_rows(mut self, rows: Vec<Vec<Value>>) -> Self {
56
- let columns = Arc::<[String]>::from(self.columns.clone().into_boxed_slice());
57
- self.rows = rows
58
- .into_iter()
59
- .map(|values| Row {
60
- columns: Arc::clone(&columns),
61
- values,
62
- })
63
- .collect();
64
- self
65
- }
66
-
67
- /// Returns the result-set column names in row value order.
68
- pub fn columns(&self) -> &[String] {
69
- &self.columns
70
- }
71
-
72
- /// Returns the owned rows. Use `iter()` for name-based access.
73
- pub fn rows(&self) -> &[Row] {
74
- &self.rows
75
- }
76
-
77
- /// Iterates rows with borrowed access to the shared column metadata.
78
- pub fn iter(&self) -> impl Iterator<Item = RowRef<'_>> {
79
- self.rows.iter().map(|row| RowRef {
80
- columns: self.columns.as_slice(),
81
- values: row.values.as_slice(),
82
- })
83
- }
84
-
85
- /// Returns the number of rows in this result set.
86
- pub fn len(&self) -> usize {
87
- self.rows.len()
88
- }
89
-
90
- /// Returns true when this result set has no rows.
91
- pub fn is_empty(&self) -> bool {
92
- self.rows.is_empty()
93
- }
94
-
95
- /// Returns the number of rows affected by a mutation statement.
96
- pub fn rows_affected(&self) -> u64 {
97
- self.rows_affected
98
- }
99
-
100
- /// Returns non-fatal diagnostics produced while executing the statement.
101
- pub fn notices(&self) -> &[LixNotice] {
102
- &self.notices
103
- }
104
-
105
- /// Looks up the value for `column_name` on an owned row from this set.
106
- pub fn get<'a>(&self, row: &'a Row, column_name: &str) -> Option<&'a Value> {
107
- let index = self.column_index(column_name)?;
108
- row.get_index(index)
109
- }
110
-
111
- /// Returns the index for a column name.
112
- pub fn column_index(&self, column_name: &str) -> Option<usize> {
113
- self.columns.iter().position(|column| column == column_name)
114
- }
115
- }
116
-
117
- /// One owned row returned by a query.
118
- #[derive(Debug, Clone, PartialEq)]
119
- pub struct Row {
120
- columns: Arc<[String]>,
121
- values: Vec<Value>,
122
- }
123
-
124
- impl Row {
125
- /// Returns the values in result-set column order.
126
- pub fn values(&self) -> &[Value] {
127
- &self.values
128
- }
129
-
130
- /// Returns the value at `index`.
131
- pub fn get_index(&self, index: usize) -> Option<&Value> {
132
- self.values.get(index)
133
- }
134
-
135
- /// Returns the raw value for `column_name`, or an error when the column is absent.
136
- pub fn value(&self, column_name: &str) -> Result<&Value, LixError> {
137
- let index = self.column_index(column_name)?;
138
- self.values.get(index).ok_or_else(|| {
139
- LixError::new(
140
- LixError::CODE_COLUMN_NOT_FOUND,
141
- format!(
142
- "column '{}' points past row width {}; available columns: {}",
143
- column_name,
144
- self.values.len(),
145
- self.available_columns()
146
- ),
147
- )
148
- })
149
- }
150
-
151
- /// Converts the named column to a native Rust value.
152
- pub fn get<T>(&self, column_name: &str) -> Result<T, LixError>
153
- where
154
- T: TryFromValue,
155
- {
156
- T::try_from_value(self.value(column_name)?)
157
- }
158
-
159
- fn column_index(&self, column_name: &str) -> Result<usize, LixError> {
160
- self.columns
161
- .iter()
162
- .position(|column| column == column_name)
163
- .ok_or_else(|| {
164
- LixError::new(
165
- LixError::CODE_COLUMN_NOT_FOUND,
166
- format!(
167
- "column '{}' does not exist; available columns: {}",
168
- column_name,
169
- self.available_columns()
170
- ),
171
- )
172
- })
173
- }
174
-
175
- fn available_columns(&self) -> String {
176
- if self.columns.is_empty() {
177
- "<none>".to_string()
178
- } else {
179
- self.columns.join(", ")
180
- }
181
- }
182
- }
183
-
184
- pub trait TryFromValue: Sized {
185
- fn try_from_value(value: &Value) -> Result<Self, LixError>;
186
- }
187
-
188
- impl TryFromValue for Value {
189
- fn try_from_value(value: &Value) -> Result<Self, LixError> {
190
- Ok(value.clone())
191
- }
192
- }
193
-
194
- impl TryFromValue for String {
195
- fn try_from_value(value: &Value) -> Result<Self, LixError> {
196
- match value {
197
- Value::Text(value) => Ok(value.clone()),
198
- other => Err(value_type_error("text", other)),
199
- }
200
- }
201
- }
202
-
203
- impl TryFromValue for bool {
204
- fn try_from_value(value: &Value) -> Result<Self, LixError> {
205
- match value {
206
- Value::Boolean(value) => Ok(*value),
207
- other => Err(value_type_error("boolean", other)),
208
- }
209
- }
210
- }
211
-
212
- impl TryFromValue for i64 {
213
- fn try_from_value(value: &Value) -> Result<Self, LixError> {
214
- match value {
215
- Value::Integer(value) => Ok(*value),
216
- other => Err(value_type_error("integer", other)),
217
- }
218
- }
219
- }
220
-
221
- impl TryFromValue for f64 {
222
- fn try_from_value(value: &Value) -> Result<Self, LixError> {
223
- match value {
224
- Value::Real(value) => Ok(*value),
225
- other => Err(value_type_error("real", other)),
226
- }
227
- }
228
- }
229
-
230
- impl TryFromValue for serde_json::Value {
231
- fn try_from_value(value: &Value) -> Result<Self, LixError> {
232
- match value {
233
- Value::Json(value) => Ok(value.clone()),
234
- other => Err(value_type_error("json", other)),
235
- }
236
- }
237
- }
238
-
239
- impl TryFromValue for Vec<u8> {
240
- fn try_from_value(value: &Value) -> Result<Self, LixError> {
241
- match value {
242
- Value::Blob(value) => Ok(value.clone()),
243
- other => Err(value_type_error("blob", other)),
244
- }
245
- }
246
- }
247
-
248
- fn value_type_error(expected: &str, actual: &Value) -> LixError {
249
- LixError::new(
250
- "LIX_ERROR_VALUE_TYPE",
251
- format!("expected {expected} value, got {actual:?}"),
252
- )
253
- }
254
-
255
- /// Zero-copy row view with access to the result-set column names.
256
- ///
257
- /// This is the ergonomic path for callers that want `row.get("column")`
258
- /// without storing column metadata on every owned row.
259
- #[derive(Debug, Clone, Copy)]
260
- pub struct RowRef<'a> {
261
- columns: &'a [String],
262
- values: &'a [Value],
263
- }
264
-
265
- impl RowRef<'_> {
266
- /// Returns the result-set column names in row value order.
267
- pub fn columns(&self) -> &[String] {
268
- self.columns
269
- }
270
-
271
- /// Returns the row values in result-set column order.
272
- pub fn values(&self) -> &[Value] {
273
- self.values
274
- }
275
-
276
- /// Returns the value for `column_name`.
277
- pub fn get(&self, column_name: &str) -> Option<&Value> {
278
- let index = self
279
- .columns
280
- .iter()
281
- .position(|column| column == column_name)?;
282
- self.values.get(index)
283
- }
284
-
285
- /// Returns the value at `index`.
286
- pub fn get_index(&self, index: usize) -> Option<&Value> {
287
- self.values.get(index)
288
- }
289
- }
290
-
291
- impl<B> SessionContext<B>
292
- where
293
- B: StorageBackend + Clone + Send + Sync + 'static,
294
- for<'backend> B::Read<'backend>: Clone + Send + Sync + 'static,
295
- for<'backend> B::Write<'backend>: Send,
296
- {
297
- /// Executes one DataFusion SQL statement against this Lix session.
298
- ///
299
- /// The SQL dialect is DataFusion SQL, not SQLite SQL. Positional
300
- /// placeholders use `?` or `$1`, `$2`, and so on. SQLite-specific catalog tables
301
- /// and transaction statements such as `sqlite_master`, `BEGIN`, and
302
- /// `COMMIT` are not part of this contract; use `information_schema` for
303
- /// catalog inspection. Lix owns transaction boundaries for each statement.
304
- pub async fn execute(&self, sql: &str, params: &[Value]) -> Result<ExecuteResult, LixError> {
305
- self.ensure_open()?;
306
- let statement = sql2::parse_statement(sql)?;
307
- if sql2::bind_statement_route(&statement)? == sql2::BoundStatementRoute::Write {
308
- let write_access = self.begin_session_write_access().await?;
309
- let sql_for_error = sql.to_string();
310
- let params = params.to_vec();
311
- return self
312
- .with_write_transaction_reserved(write_access, |transaction| {
313
- Box::pin(async move {
314
- // Re-plan against the transaction-backed write
315
- // session so provider hooks read and stage through the
316
- // transaction-owned SQL write context.
317
- transaction.prepare_sql_visible_schemas().await?;
318
- let tx_plan =
319
- sql2::create_write_logical_plan_from_parsed(transaction, statement)
320
- .await?;
321
- let affected_rows =
322
- sql2::execute_write_logical_plan(transaction, tx_plan, &params).await?;
323
- Ok(ExecuteResult::from_rows_affected(affected_rows))
324
- })
325
- })
326
- .await
327
- .map_err(|error| normalize_sql_surface_error(error, &sql_for_error));
328
- }
329
-
330
- let runtime_write_access = if sql2::statement_has_durable_runtime_function(&statement) {
331
- Some(self.begin_session_write_access().await?)
332
- } else {
333
- None
334
- };
335
- let _operation_guard = if runtime_write_access.is_some() {
336
- None
337
- } else {
338
- Some(self.begin_session_operation()?)
339
- };
340
- let read_scope = self.storage.begin_read(StorageReadOptions::default())?;
341
- let read_result = async {
342
- let mut read_store = read_scope.store();
343
- let live_state: Arc<dyn crate::live_state::LiveStateReader> =
344
- Arc::new(self.live_state.reader(read_store.clone()));
345
- let runtime_functions = FunctionContext::prepare(live_state.as_ref()).await?;
346
- let functions = runtime_functions.provider();
347
- let active_branch_id = self.active_branch_id_from_reader(&mut read_store).await?;
348
- let visible_schemas = self
349
- .catalog_context
350
- .schema_jsons_for_sql_read_planning(live_state.as_ref(), &active_branch_id)
351
- .await?;
352
- let ctx = SessionSqlExecutionContext {
353
- active_branch_id: &active_branch_id,
354
- read_store,
355
- live_state: Arc::clone(&self.live_state),
356
- binary_cas: Arc::clone(&self.binary_cas),
357
- branch_ctx: Arc::clone(&self.branch_ctx),
358
- visible_schemas,
359
- functions: functions.clone(),
360
- };
361
-
362
- let plan = sql2::create_logical_plan_from_parsed(&ctx, sql, statement).await?;
363
- let result = sql2::execute_logical_plan(plan, params).await?;
364
- drop(ctx);
365
- drop(live_state);
366
- Ok::<_, LixError>((runtime_functions, result))
367
- };
368
- let (runtime_functions, result) = match read_result.await {
369
- Ok(result) => {
370
- read_scope.close()?;
371
- result
372
- }
373
- Err(error) => {
374
- let _ = read_scope.close();
375
- return Err(normalize_sql_surface_error(error, sql));
376
- }
377
- };
378
- self.persist_runtime_functions_if_needed(&runtime_functions, runtime_write_access.as_ref())
379
- .await?;
380
- Ok(ExecuteResult::from_sql_query_result(result))
381
- }
382
-
383
- #[cfg(test)]
384
- pub(crate) async fn execute_with_write_executor_mode(
385
- &self,
386
- sql: &str,
387
- params: &[Value],
388
- mode: sql2::WriteExecutorMode,
389
- ) -> Result<ExecuteResult, LixError> {
390
- self.ensure_open()?;
391
- let statement = sql2::parse_statement(sql)?;
392
- if sql2::bind_statement_route(&statement)? == sql2::BoundStatementRoute::Write {
393
- let write_access = self.begin_session_write_access().await?;
394
- let sql_for_error = sql.to_string();
395
- let params = params.to_vec();
396
- return self
397
- .with_write_transaction_reserved(write_access, |transaction| {
398
- Box::pin(async move {
399
- transaction.prepare_sql_visible_schemas().await?;
400
- let tx_plan =
401
- sql2::create_write_logical_plan_from_parsed(transaction, statement)
402
- .await?;
403
- let affected_rows = sql2::execute_write_logical_plan_with_mode(
404
- transaction,
405
- tx_plan,
406
- &params,
407
- mode,
408
- )
409
- .await?;
410
- Ok(ExecuteResult::from_rows_affected(affected_rows))
411
- })
412
- })
413
- .await
414
- .map_err(|error| normalize_sql_surface_error(error, &sql_for_error));
415
- }
416
- self.execute(sql, params).await
417
- }
418
-
419
- /// Persists execution-scoped runtime function state after a successful read.
420
- ///
421
- /// Reads do not otherwise own a write transaction, but SQL functions such as
422
- /// `lix_uuid_v7()` can still advance runtime state. Persisting happens only
423
- /// after successful execution so failed reads do not consume durable
424
- /// sequence state.
425
- async fn persist_runtime_functions_if_needed(
426
- &self,
427
- runtime_functions: &FunctionContext,
428
- runtime_write_access: Option<&SessionWriteAccess>,
429
- ) -> Result<(), LixError> {
430
- let mut writes = StorageWriteSet::new();
431
- runtime_functions
432
- .stage_persist_if_needed(&mut writes)
433
- .await?;
434
- if writes.is_empty() {
435
- return Ok(());
436
- }
437
- if runtime_write_access.is_none() {
438
- return Err(LixError::new(
439
- LixError::CODE_INTERNAL_ERROR,
440
- "runtime function state changed without reserved write access",
441
- ));
442
- }
443
- let commit_boundary = self.transaction_commit_boundary();
444
- let _commit_guard = begin_commit_boundary(Some(&commit_boundary));
445
- commit_at_boundary(Some(&commit_boundary), || {
446
- self.storage
447
- .commit_write_set(writes, StorageWriteOptions::default())?;
448
- Ok(())
449
- })?;
450
- Ok(())
451
- }
452
- }
453
-
454
- impl<B> SessionTransaction<B>
455
- where
456
- B: StorageBackend + Clone + Send + Sync + 'static,
457
- for<'backend> B::Read<'backend>: Clone + Send + Sync + 'static,
458
- for<'backend> B::Write<'backend>: Send,
459
- {
460
- /// Executes one SQL statement inside this transaction.
461
- ///
462
- /// Write statements are staged until `commit()`. Read statements use the
463
- /// transaction overlay, so they can observe writes staged by earlier calls
464
- /// on this transaction handle.
465
- pub async fn execute(
466
- &mut self,
467
- sql: &str,
468
- params: &[Value],
469
- ) -> Result<ExecuteResult, LixError> {
470
- let _operation_guard = self.begin_session_operation()?;
471
- let statement = sql2::parse_statement(sql)?;
472
- let transaction = self.transaction_mut()?;
473
- match sql2::bind_statement_route(&statement)? {
474
- sql2::BoundStatementRoute::Write => {
475
- execute_transaction_write_auto(transaction, statement, params)
476
- .await
477
- .map_err(|error| normalize_sql_surface_error(error, sql))
478
- }
479
- sql2::BoundStatementRoute::Read => {
480
- let read_ctx = transaction.sql_read_execution_context().await?;
481
- let read_result = async {
482
- let plan = sql2::create_transaction_read_logical_plan_from_parsed(
483
- &read_ctx,
484
- transaction,
485
- sql,
486
- statement,
487
- )
488
- .await?;
489
- sql2::execute_logical_plan(plan, params).await
490
- }
491
- .await;
492
- let close_result = read_ctx.close();
493
- let result = match read_result {
494
- Ok(result) => {
495
- close_result?;
496
- result
497
- }
498
- Err(error) => {
499
- let _ = close_result;
500
- return Err(normalize_sql_surface_error(error, sql));
501
- }
502
- };
503
- Ok(ExecuteResult::from_sql_query_result(result))
504
- }
505
- }
506
- }
507
-
508
- #[cfg(test)]
509
- pub(crate) async fn execute_with_write_executor_mode(
510
- &mut self,
511
- sql: &str,
512
- params: &[Value],
513
- mode: sql2::WriteExecutorMode,
514
- ) -> Result<ExecuteResult, LixError> {
515
- let _operation_guard = self.begin_session_operation()?;
516
- let statement = sql2::parse_statement(sql)?;
517
- let transaction = self.transaction_mut()?;
518
- match sql2::bind_statement_route(&statement)? {
519
- sql2::BoundStatementRoute::Write => {
520
- execute_transaction_write_with_mode(transaction, statement, params, mode)
521
- .await
522
- .map_err(|error| normalize_sql_surface_error(error, sql))
523
- }
524
- sql2::BoundStatementRoute::Read => self.execute(sql, params).await,
525
- }
526
- }
527
-
528
- #[cfg(test)]
529
- pub(crate) async fn execute_with_write_executor_mode_and_trace(
530
- &mut self,
531
- sql: &str,
532
- params: &[Value],
533
- mode: sql2::WriteExecutorMode,
534
- ) -> Result<(ExecuteResult, Option<sql2::WriteExecutorPath>), LixError> {
535
- let _operation_guard = self.begin_session_operation()?;
536
- let statement = sql2::parse_statement(sql)?;
537
- let transaction = self.transaction_mut()?;
538
- match sql2::bind_statement_route(&statement)? {
539
- sql2::BoundStatementRoute::Write => {
540
- execute_transaction_write_with_mode_and_trace(transaction, statement, params, mode)
541
- .await
542
- .map_err(|error| normalize_sql_surface_error(error, sql))
543
- }
544
- sql2::BoundStatementRoute::Read => {
545
- self.execute(sql, params).await.map(|result| (result, None))
546
- }
547
- }
548
- }
549
-
550
- #[cfg(test)]
551
- pub(crate) async fn scan_live_state_for_test(
552
- &mut self,
553
- request: &crate::live_state::LiveStateScanRequest,
554
- ) -> Result<Vec<crate::live_state::MaterializedLiveStateRow>, LixError> {
555
- let _operation_guard = self.begin_session_operation()?;
556
- let transaction = self.transaction_mut()?;
557
- <crate::transaction::Transaction<B> as sql2::SqlWriteExecutionContext>::scan_live_state(
558
- transaction,
559
- request,
560
- )
561
- .await
562
- }
563
- }
564
-
565
- async fn execute_transaction_write_auto<B>(
566
- transaction: &mut crate::transaction::Transaction<B>,
567
- statement: datafusion::sql::parser::Statement,
568
- params: &[Value],
569
- ) -> Result<ExecuteResult, LixError>
570
- where
571
- B: StorageBackend + Clone + Send + Sync + 'static,
572
- for<'backend> B::Read<'backend>: Clone + Send + Sync + 'static,
573
- for<'backend> B::Write<'backend>: Send,
574
- {
575
- transaction.prepare_sql_visible_schemas().await?;
576
- let tx_plan = sql2::create_write_logical_plan_from_parsed(transaction, statement).await?;
577
- let affected_rows = sql2::execute_write_logical_plan(transaction, tx_plan, params).await?;
578
- Ok(ExecuteResult::from_rows_affected(affected_rows))
579
- }
580
-
581
- #[cfg(test)]
582
- async fn execute_transaction_write_with_mode<B>(
583
- transaction: &mut crate::transaction::Transaction<B>,
584
- statement: datafusion::sql::parser::Statement,
585
- params: &[Value],
586
- mode: sql2::WriteExecutorMode,
587
- ) -> Result<ExecuteResult, LixError>
588
- where
589
- B: StorageBackend + Clone + Send + Sync + 'static,
590
- for<'backend> B::Read<'backend>: Clone + Send + Sync + 'static,
591
- for<'backend> B::Write<'backend>: Send,
592
- {
593
- transaction.prepare_sql_visible_schemas().await?;
594
- let tx_plan = sql2::create_write_logical_plan_from_parsed(transaction, statement).await?;
595
- let affected_rows =
596
- sql2::execute_write_logical_plan_with_mode(transaction, tx_plan, params, mode).await?;
597
- Ok(ExecuteResult::from_rows_affected(affected_rows))
598
- }
599
-
600
- #[cfg(test)]
601
- async fn execute_transaction_write_with_mode_and_trace<B>(
602
- transaction: &mut crate::transaction::Transaction<B>,
603
- statement: datafusion::sql::parser::Statement,
604
- params: &[Value],
605
- mode: sql2::WriteExecutorMode,
606
- ) -> Result<(ExecuteResult, Option<sql2::WriteExecutorPath>), LixError>
607
- where
608
- B: StorageBackend + Clone + Send + Sync + 'static,
609
- for<'backend> B::Read<'backend>: Clone + Send + Sync + 'static,
610
- for<'backend> B::Write<'backend>: Send,
611
- {
612
- transaction.prepare_sql_visible_schemas().await?;
613
- let tx_plan = sql2::create_write_logical_plan_from_parsed(transaction, statement).await?;
614
- let (affected_rows, path) =
615
- sql2::execute_write_logical_plan_with_mode_and_trace(transaction, tx_plan, params, mode)
616
- .await?;
617
- Ok((ExecuteResult::from_rows_affected(affected_rows), Some(path)))
618
- }
619
-
620
- fn normalize_sql_surface_error(error: LixError, sql: &str) -> LixError {
621
- if error.code.starts_with("LIX_ERROR_PATH_") && sql_uses_public_filesystem_path_surface(sql) {
622
- return LixError {
623
- code: LixError::CODE_INVALID_PARAM.to_string(),
624
- ..error
625
- };
626
- }
627
- if error.code == LixError::CODE_INVALID_JSON_PATH
628
- && error
629
- .message
630
- .to_ascii_lowercase()
631
- .contains("uses variadic path segments")
632
- {
633
- return LixError {
634
- code: LixError::CODE_INVALID_PARAM.to_string(),
635
- ..error
636
- };
637
- }
638
- error
639
- }
640
-
641
- fn sql_uses_public_filesystem_path_surface(sql: &str) -> bool {
642
- let lower = sql.to_ascii_lowercase();
643
- (lower.contains("lix_file") || lower.contains("lix_directory")) && lower.contains("path")
644
- }
645
-
646
- #[cfg(test)]
647
- mod tests {
648
- use super::*;
649
-
650
- #[test]
651
- fn row_get_converts_native_values_and_value_keeps_wrapper() {
652
- let result = ExecuteResult::from_rows(
653
- vec!["title".to_string(), "done".to_string()],
654
- vec![vec![Value::Text("Hello".to_string()), Value::Boolean(true)]],
655
- );
656
- let row = &result.rows()[0];
657
-
658
- assert_eq!(row.get::<String>("title").unwrap(), "Hello");
659
- assert!(row.get::<bool>("done").unwrap());
660
- assert_eq!(
661
- row.value("title").unwrap(),
662
- &Value::Text("Hello".to_string())
663
- );
664
- }
665
-
666
- #[test]
667
- fn row_get_errors_on_missing_column_and_wrong_type() {
668
- let result = ExecuteResult::from_rows(
669
- vec!["title".to_string()],
670
- vec![vec![Value::Text("Hello".to_string())]],
671
- );
672
- let row = &result.rows()[0];
673
-
674
- let missing = row.get::<String>("missing").unwrap_err();
675
- assert_eq!(missing.code, LixError::CODE_COLUMN_NOT_FOUND);
676
- assert!(missing.message.contains("available columns: title"));
677
-
678
- let wrong_type = row.get::<bool>("title").unwrap_err();
679
- assert_eq!(wrong_type.code, "LIX_ERROR_VALUE_TYPE");
680
- }
681
- }