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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (223) 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 +39 -201
  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 +20 -50
  16. package/SKILL.md +0 -506
  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 -821
  23. package/dist/engine-wasm/wasm/lix_engine.wasm +0 -0
  24. package/dist/engine-wasm/wasm/lix_engine.wasm.d.ts +0 -26
  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 -303
  29. package/dist-engine-src/README.md +0 -18
  30. package/dist-engine-src/src/backend/kv.rs +0 -358
  31. package/dist-engine-src/src/backend/mod.rs +0 -12
  32. package/dist-engine-src/src/backend/testing.rs +0 -658
  33. package/dist-engine-src/src/backend/types.rs +0 -96
  34. package/dist-engine-src/src/binary_cas/chunking.rs +0 -31
  35. package/dist-engine-src/src/binary_cas/codec.rs +0 -346
  36. package/dist-engine-src/src/binary_cas/context.rs +0 -139
  37. package/dist-engine-src/src/binary_cas/kv.rs +0 -1063
  38. package/dist-engine-src/src/binary_cas/mod.rs +0 -11
  39. package/dist-engine-src/src/binary_cas/types.rs +0 -121
  40. package/dist-engine-src/src/catalog/context.rs +0 -412
  41. package/dist-engine-src/src/catalog/mod.rs +0 -10
  42. package/dist-engine-src/src/catalog/schema.rs +0 -4
  43. package/dist-engine-src/src/catalog/snapshot.rs +0 -1114
  44. package/dist-engine-src/src/cel/context.rs +0 -86
  45. package/dist-engine-src/src/cel/error.rs +0 -19
  46. package/dist-engine-src/src/cel/mod.rs +0 -8
  47. package/dist-engine-src/src/cel/provider.rs +0 -9
  48. package/dist-engine-src/src/cel/runtime.rs +0 -167
  49. package/dist-engine-src/src/cel/value.rs +0 -50
  50. package/dist-engine-src/src/commit_graph/context.rs +0 -901
  51. package/dist-engine-src/src/commit_graph/mod.rs +0 -11
  52. package/dist-engine-src/src/commit_graph/types.rs +0 -109
  53. package/dist-engine-src/src/commit_graph/walker.rs +0 -756
  54. package/dist-engine-src/src/commit_store/codec.rs +0 -887
  55. package/dist-engine-src/src/commit_store/context.rs +0 -944
  56. package/dist-engine-src/src/commit_store/materialization.rs +0 -84
  57. package/dist-engine-src/src/commit_store/mod.rs +0 -16
  58. package/dist-engine-src/src/commit_store/storage.rs +0 -600
  59. package/dist-engine-src/src/commit_store/types.rs +0 -215
  60. package/dist-engine-src/src/common/error.rs +0 -313
  61. package/dist-engine-src/src/common/fingerprint.rs +0 -3
  62. package/dist-engine-src/src/common/fs_path.rs +0 -1336
  63. package/dist-engine-src/src/common/identity.rs +0 -145
  64. package/dist-engine-src/src/common/json_pointer.rs +0 -67
  65. package/dist-engine-src/src/common/metadata.rs +0 -40
  66. package/dist-engine-src/src/common/mod.rs +0 -23
  67. package/dist-engine-src/src/common/types.rs +0 -105
  68. package/dist-engine-src/src/common/wire.rs +0 -222
  69. package/dist-engine-src/src/domain.rs +0 -324
  70. package/dist-engine-src/src/engine.rs +0 -225
  71. package/dist-engine-src/src/entity_identity.rs +0 -405
  72. package/dist-engine-src/src/functions/context.rs +0 -292
  73. package/dist-engine-src/src/functions/deterministic.rs +0 -113
  74. package/dist-engine-src/src/functions/mod.rs +0 -18
  75. package/dist-engine-src/src/functions/provider.rs +0 -130
  76. package/dist-engine-src/src/functions/state.rs +0 -336
  77. package/dist-engine-src/src/functions/types.rs +0 -37
  78. package/dist-engine-src/src/init.rs +0 -558
  79. package/dist-engine-src/src/json_store/compression.rs +0 -77
  80. package/dist-engine-src/src/json_store/context.rs +0 -423
  81. package/dist-engine-src/src/json_store/encoded.rs +0 -15
  82. package/dist-engine-src/src/json_store/mod.rs +0 -12
  83. package/dist-engine-src/src/json_store/store.rs +0 -1109
  84. package/dist-engine-src/src/json_store/types.rs +0 -217
  85. package/dist-engine-src/src/lib.rs +0 -62
  86. package/dist-engine-src/src/live_state/context.rs +0 -2019
  87. package/dist-engine-src/src/live_state/mod.rs +0 -15
  88. package/dist-engine-src/src/live_state/overlay.rs +0 -75
  89. package/dist-engine-src/src/live_state/reader.rs +0 -23
  90. package/dist-engine-src/src/live_state/types.rs +0 -222
  91. package/dist-engine-src/src/live_state/visibility.rs +0 -223
  92. package/dist-engine-src/src/plugin/archive.rs +0 -438
  93. package/dist-engine-src/src/plugin/component.rs +0 -183
  94. package/dist-engine-src/src/plugin/install.rs +0 -619
  95. package/dist-engine-src/src/plugin/manifest.rs +0 -516
  96. package/dist-engine-src/src/plugin/materializer.rs +0 -477
  97. package/dist-engine-src/src/plugin/mod.rs +0 -33
  98. package/dist-engine-src/src/plugin/plugin_manifest.json +0 -118
  99. package/dist-engine-src/src/plugin/storage.rs +0 -74
  100. package/dist-engine-src/src/schema/annotations/defaults.rs +0 -275
  101. package/dist-engine-src/src/schema/annotations/mod.rs +0 -1
  102. package/dist-engine-src/src/schema/builtin/lix_account.json +0 -21
  103. package/dist-engine-src/src/schema/builtin/lix_active_account.json +0 -29
  104. package/dist-engine-src/src/schema/builtin/lix_binary_blob_ref.json +0 -29
  105. package/dist-engine-src/src/schema/builtin/lix_change.json +0 -63
  106. package/dist-engine-src/src/schema/builtin/lix_change_author.json +0 -45
  107. package/dist-engine-src/src/schema/builtin/lix_commit.json +0 -24
  108. package/dist-engine-src/src/schema/builtin/lix_commit_edge.json +0 -53
  109. package/dist-engine-src/src/schema/builtin/lix_directory_descriptor.json +0 -52
  110. package/dist-engine-src/src/schema/builtin/lix_file_descriptor.json +0 -52
  111. package/dist-engine-src/src/schema/builtin/lix_key_value.json +0 -40
  112. package/dist-engine-src/src/schema/builtin/lix_label.json +0 -29
  113. package/dist-engine-src/src/schema/builtin/lix_label_assignment.json +0 -74
  114. package/dist-engine-src/src/schema/builtin/lix_registered_schema.json +0 -25
  115. package/dist-engine-src/src/schema/builtin/lix_version_descriptor.json +0 -34
  116. package/dist-engine-src/src/schema/builtin/lix_version_ref.json +0 -48
  117. package/dist-engine-src/src/schema/builtin/mod.rs +0 -222
  118. package/dist-engine-src/src/schema/compatibility.rs +0 -787
  119. package/dist-engine-src/src/schema/definition.json +0 -187
  120. package/dist-engine-src/src/schema/definition.rs +0 -742
  121. package/dist-engine-src/src/schema/key.rs +0 -138
  122. package/dist-engine-src/src/schema/mod.rs +0 -20
  123. package/dist-engine-src/src/schema/seed.rs +0 -14
  124. package/dist-engine-src/src/schema/tests.rs +0 -780
  125. package/dist-engine-src/src/session/context.rs +0 -404
  126. package/dist-engine-src/src/session/create_version.rs +0 -88
  127. package/dist-engine-src/src/session/execute.rs +0 -541
  128. package/dist-engine-src/src/session/merge/analysis.rs +0 -102
  129. package/dist-engine-src/src/session/merge/apply.rs +0 -23
  130. package/dist-engine-src/src/session/merge/conflicts.rs +0 -63
  131. package/dist-engine-src/src/session/merge/mod.rs +0 -11
  132. package/dist-engine-src/src/session/merge/stats.rs +0 -65
  133. package/dist-engine-src/src/session/merge/version.rs +0 -427
  134. package/dist-engine-src/src/session/mod.rs +0 -27
  135. package/dist-engine-src/src/session/optimization9_sql2_bench.rs +0 -100
  136. package/dist-engine-src/src/session/switch_version.rs +0 -110
  137. package/dist-engine-src/src/session/transaction.rs +0 -76
  138. package/dist-engine-src/src/sql2/change_provider.rs +0 -331
  139. package/dist-engine-src/src/sql2/classify.rs +0 -174
  140. package/dist-engine-src/src/sql2/context.rs +0 -311
  141. package/dist-engine-src/src/sql2/directory_history_provider.rs +0 -631
  142. package/dist-engine-src/src/sql2/directory_provider.rs +0 -2453
  143. package/dist-engine-src/src/sql2/dml.rs +0 -148
  144. package/dist-engine-src/src/sql2/entity_history_provider.rs +0 -440
  145. package/dist-engine-src/src/sql2/entity_provider.rs +0 -3211
  146. package/dist-engine-src/src/sql2/error.rs +0 -215
  147. package/dist-engine-src/src/sql2/execute.rs +0 -3533
  148. package/dist-engine-src/src/sql2/file_history_provider.rs +0 -910
  149. package/dist-engine-src/src/sql2/file_provider.rs +0 -3679
  150. package/dist-engine-src/src/sql2/filesystem_planner.rs +0 -1490
  151. package/dist-engine-src/src/sql2/filesystem_predicates.rs +0 -159
  152. package/dist-engine-src/src/sql2/filesystem_visibility.rs +0 -383
  153. package/dist-engine-src/src/sql2/history_projection.rs +0 -56
  154. package/dist-engine-src/src/sql2/history_provider.rs +0 -412
  155. package/dist-engine-src/src/sql2/history_route.rs +0 -657
  156. package/dist-engine-src/src/sql2/lix_state_provider.rs +0 -2512
  157. package/dist-engine-src/src/sql2/mod.rs +0 -47
  158. package/dist-engine-src/src/sql2/predicate_typecheck.rs +0 -246
  159. package/dist-engine-src/src/sql2/public_bind/assignment.rs +0 -46
  160. package/dist-engine-src/src/sql2/public_bind/capability.rs +0 -41
  161. package/dist-engine-src/src/sql2/public_bind/dml.rs +0 -172
  162. package/dist-engine-src/src/sql2/public_bind/mod.rs +0 -26
  163. package/dist-engine-src/src/sql2/public_bind/table.rs +0 -168
  164. package/dist-engine-src/src/sql2/read_only.rs +0 -63
  165. package/dist-engine-src/src/sql2/record_batch.rs +0 -17
  166. package/dist-engine-src/src/sql2/result_metadata.rs +0 -29
  167. package/dist-engine-src/src/sql2/runtime.rs +0 -60
  168. package/dist-engine-src/src/sql2/session.rs +0 -132
  169. package/dist-engine-src/src/sql2/udfs/common.rs +0 -295
  170. package/dist-engine-src/src/sql2/udfs/lix_active_version_commit_id.rs +0 -53
  171. package/dist-engine-src/src/sql2/udfs/lix_empty_blob.rs +0 -47
  172. package/dist-engine-src/src/sql2/udfs/lix_json.rs +0 -100
  173. package/dist-engine-src/src/sql2/udfs/lix_json_get.rs +0 -99
  174. package/dist-engine-src/src/sql2/udfs/lix_json_get_text.rs +0 -99
  175. package/dist-engine-src/src/sql2/udfs/lix_text_decode.rs +0 -82
  176. package/dist-engine-src/src/sql2/udfs/lix_text_encode.rs +0 -85
  177. package/dist-engine-src/src/sql2/udfs/lix_timestamp.rs +0 -76
  178. package/dist-engine-src/src/sql2/udfs/lix_uuid_v7.rs +0 -76
  179. package/dist-engine-src/src/sql2/udfs/mod.rs +0 -89
  180. package/dist-engine-src/src/sql2/udfs/public_call.rs +0 -238
  181. package/dist-engine-src/src/sql2/version_provider.rs +0 -1202
  182. package/dist-engine-src/src/sql2/version_scope.rs +0 -394
  183. package/dist-engine-src/src/sql2/write_normalization.rs +0 -345
  184. package/dist-engine-src/src/storage/context.rs +0 -356
  185. package/dist-engine-src/src/storage/mod.rs +0 -14
  186. package/dist-engine-src/src/storage/read_scope.rs +0 -88
  187. package/dist-engine-src/src/storage/types.rs +0 -501
  188. package/dist-engine-src/src/storage_bench.rs +0 -4863
  189. package/dist-engine-src/src/test_support.rs +0 -228
  190. package/dist-engine-src/src/tracked_state/by_file_index.rs +0 -98
  191. package/dist-engine-src/src/tracked_state/codec.rs +0 -2085
  192. package/dist-engine-src/src/tracked_state/context.rs +0 -1867
  193. package/dist-engine-src/src/tracked_state/diff.rs +0 -686
  194. package/dist-engine-src/src/tracked_state/materialization.rs +0 -403
  195. package/dist-engine-src/src/tracked_state/materializer.rs +0 -488
  196. package/dist-engine-src/src/tracked_state/merge.rs +0 -492
  197. package/dist-engine-src/src/tracked_state/mod.rs +0 -32
  198. package/dist-engine-src/src/tracked_state/storage.rs +0 -375
  199. package/dist-engine-src/src/tracked_state/tree.rs +0 -3187
  200. package/dist-engine-src/src/tracked_state/types.rs +0 -231
  201. package/dist-engine-src/src/transaction/commit.rs +0 -1484
  202. package/dist-engine-src/src/transaction/context.rs +0 -1548
  203. package/dist-engine-src/src/transaction/live_state_overlay.rs +0 -35
  204. package/dist-engine-src/src/transaction/mod.rs +0 -13
  205. package/dist-engine-src/src/transaction/normalization.rs +0 -890
  206. package/dist-engine-src/src/transaction/prep.rs +0 -37
  207. package/dist-engine-src/src/transaction/schema_resolver.rs +0 -149
  208. package/dist-engine-src/src/transaction/staging.rs +0 -1731
  209. package/dist-engine-src/src/transaction/types.rs +0 -460
  210. package/dist-engine-src/src/transaction/validation.rs +0 -5830
  211. package/dist-engine-src/src/untracked_state/codec.rs +0 -307
  212. package/dist-engine-src/src/untracked_state/context.rs +0 -98
  213. package/dist-engine-src/src/untracked_state/materialization.rs +0 -63
  214. package/dist-engine-src/src/untracked_state/mod.rs +0 -15
  215. package/dist-engine-src/src/untracked_state/storage.rs +0 -396
  216. package/dist-engine-src/src/untracked_state/types.rs +0 -146
  217. package/dist-engine-src/src/version/context.rs +0 -40
  218. package/dist-engine-src/src/version/lifecycle.rs +0 -221
  219. package/dist-engine-src/src/version/mod.rs +0 -13
  220. package/dist-engine-src/src/version/refs.rs +0 -330
  221. package/dist-engine-src/src/version/stage_rows.rs +0 -67
  222. package/dist-engine-src/src/version/types.rs +0 -21
  223. package/dist-engine-src/src/wasm/mod.rs +0 -60
@@ -1,541 +0,0 @@
1
- use std::sync::Arc;
2
-
3
- use crate::functions::FunctionContext;
4
- use crate::sql2;
5
- use crate::storage::{StorageReadScope, StorageWriteSet};
6
- use crate::{LixError, LixNotice, SqlQueryResult, Value};
7
-
8
- use super::context::{SessionContext, SessionSqlExecutionContext};
9
- use super::transaction::SessionTransaction;
10
-
11
- /// Result of executing one SQL statement through engine.
12
- ///
13
- /// Column names live once at the result-set level. Individual rows only own
14
- /// values, which keeps the public API row-oriented without copying schema
15
- /// metadata into every row.
16
- #[derive(Debug, Clone, PartialEq)]
17
- pub struct ExecuteResult {
18
- columns: Vec<String>,
19
- rows: Vec<Row>,
20
- rows_affected: u64,
21
- notices: Vec<LixNotice>,
22
- }
23
-
24
- impl ExecuteResult {
25
- fn from_sql_query_result(result: SqlQueryResult) -> Self {
26
- Self {
27
- columns: result.columns,
28
- rows: Vec::new(),
29
- rows_affected: 0,
30
- notices: result.notices,
31
- }
32
- .with_rows(result.rows)
33
- }
34
-
35
- pub fn from_rows_affected(rows_affected: u64) -> Self {
36
- Self {
37
- columns: Vec::new(),
38
- rows: Vec::new(),
39
- rows_affected,
40
- notices: Vec::new(),
41
- }
42
- }
43
-
44
- pub fn from_rows(columns: Vec<String>, rows: Vec<Vec<Value>>) -> Self {
45
- Self {
46
- columns,
47
- rows: Vec::new(),
48
- rows_affected: 0,
49
- notices: Vec::new(),
50
- }
51
- .with_rows(rows)
52
- }
53
-
54
- fn with_rows(mut self, rows: Vec<Vec<Value>>) -> Self {
55
- let columns = Arc::<[String]>::from(self.columns.clone().into_boxed_slice());
56
- self.rows = rows
57
- .into_iter()
58
- .map(|values| Row {
59
- columns: Arc::clone(&columns),
60
- values,
61
- })
62
- .collect();
63
- self
64
- }
65
-
66
- /// Returns the result-set column names in row value order.
67
- pub fn columns(&self) -> &[String] {
68
- &self.columns
69
- }
70
-
71
- /// Returns the owned rows. Use `iter()` for name-based access.
72
- pub fn rows(&self) -> &[Row] {
73
- &self.rows
74
- }
75
-
76
- /// Iterates rows with borrowed access to the shared column metadata.
77
- pub fn iter(&self) -> impl Iterator<Item = RowRef<'_>> {
78
- self.rows.iter().map(|row| RowRef {
79
- columns: self.columns.as_slice(),
80
- values: row.values.as_slice(),
81
- })
82
- }
83
-
84
- /// Returns the number of rows in this result set.
85
- pub fn len(&self) -> usize {
86
- self.rows.len()
87
- }
88
-
89
- /// Returns true when this result set has no rows.
90
- pub fn is_empty(&self) -> bool {
91
- self.rows.is_empty()
92
- }
93
-
94
- /// Returns the number of rows affected by a mutation statement.
95
- pub fn rows_affected(&self) -> u64 {
96
- self.rows_affected
97
- }
98
-
99
- /// Returns non-fatal diagnostics produced while executing the statement.
100
- pub fn notices(&self) -> &[LixNotice] {
101
- &self.notices
102
- }
103
-
104
- /// Looks up the value for `column_name` on an owned row from this set.
105
- pub fn get<'a>(&self, row: &'a Row, column_name: &str) -> Option<&'a Value> {
106
- let index = self.column_index(column_name)?;
107
- row.get_index(index)
108
- }
109
-
110
- /// Returns the index for a column name.
111
- pub fn column_index(&self, column_name: &str) -> Option<usize> {
112
- self.columns.iter().position(|column| column == column_name)
113
- }
114
- }
115
-
116
- /// One owned row returned by a query.
117
- #[derive(Debug, Clone, PartialEq)]
118
- pub struct Row {
119
- columns: Arc<[String]>,
120
- values: Vec<Value>,
121
- }
122
-
123
- impl Row {
124
- /// Returns the values in result-set column order.
125
- pub fn values(&self) -> &[Value] {
126
- &self.values
127
- }
128
-
129
- /// Returns the value at `index`.
130
- pub fn get_index(&self, index: usize) -> Option<&Value> {
131
- self.values.get(index)
132
- }
133
-
134
- /// Returns the raw value for `column_name`, or an error when the column is absent.
135
- pub fn value(&self, column_name: &str) -> Result<&Value, LixError> {
136
- let index = self.column_index(column_name)?;
137
- self.values.get(index).ok_or_else(|| {
138
- LixError::new(
139
- LixError::CODE_COLUMN_NOT_FOUND,
140
- format!(
141
- "column '{}' points past row width {}; available columns: {}",
142
- column_name,
143
- self.values.len(),
144
- self.available_columns()
145
- ),
146
- )
147
- })
148
- }
149
-
150
- /// Converts the named column to a native Rust value.
151
- pub fn get<T>(&self, column_name: &str) -> Result<T, LixError>
152
- where
153
- T: TryFromValue,
154
- {
155
- T::try_from_value(self.value(column_name)?)
156
- }
157
-
158
- fn column_index(&self, column_name: &str) -> Result<usize, LixError> {
159
- self.columns
160
- .iter()
161
- .position(|column| column == column_name)
162
- .ok_or_else(|| {
163
- LixError::new(
164
- LixError::CODE_COLUMN_NOT_FOUND,
165
- format!(
166
- "column '{}' does not exist; available columns: {}",
167
- column_name,
168
- self.available_columns()
169
- ),
170
- )
171
- })
172
- }
173
-
174
- fn available_columns(&self) -> String {
175
- if self.columns.is_empty() {
176
- "<none>".to_string()
177
- } else {
178
- self.columns.join(", ")
179
- }
180
- }
181
- }
182
-
183
- pub trait TryFromValue: Sized {
184
- fn try_from_value(value: &Value) -> Result<Self, LixError>;
185
- }
186
-
187
- impl TryFromValue for Value {
188
- fn try_from_value(value: &Value) -> Result<Self, LixError> {
189
- Ok(value.clone())
190
- }
191
- }
192
-
193
- impl TryFromValue for String {
194
- fn try_from_value(value: &Value) -> Result<Self, LixError> {
195
- match value {
196
- Value::Text(value) => Ok(value.clone()),
197
- other => Err(value_type_error("text", other)),
198
- }
199
- }
200
- }
201
-
202
- impl TryFromValue for bool {
203
- fn try_from_value(value: &Value) -> Result<Self, LixError> {
204
- match value {
205
- Value::Boolean(value) => Ok(*value),
206
- other => Err(value_type_error("boolean", other)),
207
- }
208
- }
209
- }
210
-
211
- impl TryFromValue for i64 {
212
- fn try_from_value(value: &Value) -> Result<Self, LixError> {
213
- match value {
214
- Value::Integer(value) => Ok(*value),
215
- other => Err(value_type_error("integer", other)),
216
- }
217
- }
218
- }
219
-
220
- impl TryFromValue for f64 {
221
- fn try_from_value(value: &Value) -> Result<Self, LixError> {
222
- match value {
223
- Value::Real(value) => Ok(*value),
224
- other => Err(value_type_error("real", other)),
225
- }
226
- }
227
- }
228
-
229
- impl TryFromValue for serde_json::Value {
230
- fn try_from_value(value: &Value) -> Result<Self, LixError> {
231
- match value {
232
- Value::Json(value) => Ok(value.clone()),
233
- other => Err(value_type_error("json", other)),
234
- }
235
- }
236
- }
237
-
238
- impl TryFromValue for Vec<u8> {
239
- fn try_from_value(value: &Value) -> Result<Self, LixError> {
240
- match value {
241
- Value::Blob(value) => Ok(value.clone()),
242
- other => Err(value_type_error("blob", other)),
243
- }
244
- }
245
- }
246
-
247
- fn value_type_error(expected: &str, actual: &Value) -> LixError {
248
- LixError::new(
249
- "LIX_ERROR_VALUE_TYPE",
250
- format!("expected {expected} value, got {actual:?}"),
251
- )
252
- }
253
-
254
- /// Zero-copy row view with access to the result-set column names.
255
- ///
256
- /// This is the ergonomic path for callers that want `row.get("column")`
257
- /// without storing column metadata on every owned row.
258
- #[derive(Debug, Clone, Copy)]
259
- pub struct RowRef<'a> {
260
- columns: &'a [String],
261
- values: &'a [Value],
262
- }
263
-
264
- impl RowRef<'_> {
265
- /// Returns the result-set column names in row value order.
266
- pub fn columns(&self) -> &[String] {
267
- self.columns
268
- }
269
-
270
- /// Returns the row values in result-set column order.
271
- pub fn values(&self) -> &[Value] {
272
- self.values
273
- }
274
-
275
- /// Returns the value for `column_name`.
276
- pub fn get(&self, column_name: &str) -> Option<&Value> {
277
- let index = self
278
- .columns
279
- .iter()
280
- .position(|column| column == column_name)?;
281
- self.values.get(index)
282
- }
283
-
284
- /// Returns the value at `index`.
285
- pub fn get_index(&self, index: usize) -> Option<&Value> {
286
- self.values.get(index)
287
- }
288
- }
289
-
290
- impl SessionContext {
291
- /// Executes one DataFusion SQL statement against this Lix session.
292
- ///
293
- /// The SQL dialect is DataFusion SQL, not SQLite SQL. Positional
294
- /// placeholders use `?` or `$1`, `$2`, and so on. SQLite-specific catalog tables
295
- /// and transaction statements such as `sqlite_master`, `BEGIN`, and
296
- /// `COMMIT` are not part of this contract; use `information_schema` for
297
- /// catalog inspection. Lix owns transaction boundaries for each statement.
298
- pub async fn execute(&self, sql: &str, params: &[Value]) -> Result<ExecuteResult, LixError> {
299
- self.ensure_open()?;
300
- let _transaction_guard = self.reserve_session_transaction()?;
301
- let statement = sql2::parse_statement(sql)?;
302
- let kind = sql2::classify_datafusion_statement(&statement);
303
- if kind == sql2::SqlStatementKind::Write {
304
- let sql_for_error = sql.to_string();
305
- let params = params.to_vec();
306
- return self
307
- .with_write_transaction_reserved(|transaction| {
308
- Box::pin(async move {
309
- // Re-plan against the transaction-backed write
310
- // session so provider hooks read and stage through the
311
- // transaction-owned SQL write context.
312
- let tx_plan =
313
- sql2::create_write_logical_plan_from_parsed(transaction, statement)
314
- .await?;
315
- let result = sql2::execute_logical_plan(tx_plan, &params).await?;
316
- let affected_rows = affected_rows_from_query_result(result)?;
317
- Ok(ExecuteResult::from_rows_affected(affected_rows))
318
- })
319
- })
320
- .await
321
- .map_err(|error| normalize_sql_surface_error(error, &sql_for_error));
322
- }
323
- if kind == sql2::SqlStatementKind::Other {
324
- return Err(LixError::new(
325
- LixError::CODE_UNSUPPORTED_SQL,
326
- "SQL statement is not supported by Lix SQL",
327
- ));
328
- }
329
-
330
- let read_scope = StorageReadScope::new(self.storage.begin_read_transaction().await?);
331
- let read_result = async {
332
- let mut read_store = read_scope.store();
333
- let live_state: Arc<dyn crate::live_state::LiveStateReader> =
334
- Arc::new(self.live_state.reader(read_store.clone()));
335
- let runtime_functions = FunctionContext::prepare(live_state.as_ref()).await?;
336
- let functions = runtime_functions.provider();
337
- let active_version_id = self.active_version_id_from_reader(&mut read_store).await?;
338
- let visible_schemas = self
339
- .catalog_context
340
- .schema_jsons_for_sql_read_planning(live_state.as_ref(), &active_version_id)
341
- .await?;
342
- let ctx = SessionSqlExecutionContext {
343
- active_version_id: &active_version_id,
344
- read_store,
345
- live_state: Arc::clone(&self.live_state),
346
- binary_cas: Arc::clone(&self.binary_cas),
347
- commit_store: Arc::clone(&self.commit_store),
348
- version_ctx: Arc::clone(&self.version_ctx),
349
- visible_schemas,
350
- functions: functions.clone(),
351
- };
352
-
353
- let plan = sql2::create_logical_plan_from_parsed(&ctx, sql, statement).await?;
354
- let result = sql2::execute_logical_plan(plan, params).await?;
355
- drop(ctx);
356
- drop(live_state);
357
- Ok::<_, LixError>((runtime_functions, result))
358
- };
359
- let (runtime_functions, result) = match read_result.await {
360
- Ok(result) => {
361
- read_scope.rollback().await?;
362
- result
363
- }
364
- Err(error) => {
365
- let _ = read_scope.rollback().await;
366
- return Err(normalize_sql_surface_error(error, sql));
367
- }
368
- };
369
- self.persist_runtime_functions_if_needed(&runtime_functions)
370
- .await?;
371
- Ok(ExecuteResult::from_sql_query_result(result))
372
- }
373
-
374
- /// Persists execution-scoped runtime function state after a successful read.
375
- ///
376
- /// Reads do not otherwise own a write transaction, but SQL functions such as
377
- /// `lix_uuid_v7()` can still advance runtime state. Persisting happens only
378
- /// after successful execution so failed reads do not consume durable
379
- /// sequence state.
380
- async fn persist_runtime_functions_if_needed(
381
- &self,
382
- runtime_functions: &FunctionContext,
383
- ) -> Result<(), LixError> {
384
- let mut writes = StorageWriteSet::new();
385
- runtime_functions
386
- .stage_persist_if_needed(&mut writes)
387
- .await?;
388
- if writes.is_empty() {
389
- return Ok(());
390
- }
391
- let mut transaction = self.storage.begin_write_transaction().await?;
392
- writes.apply(&mut transaction.as_mut()).await?;
393
- transaction.commit().await
394
- }
395
- }
396
-
397
- impl SessionTransaction {
398
- /// Executes one SQL statement inside this transaction.
399
- ///
400
- /// Write statements are staged until `commit()`. Read statements use the
401
- /// transaction overlay, so they can observe writes staged by earlier calls
402
- /// on this transaction handle.
403
- pub async fn execute(
404
- &mut self,
405
- sql: &str,
406
- params: &[Value],
407
- ) -> Result<ExecuteResult, LixError> {
408
- let statement = sql2::parse_statement(sql)?;
409
- let kind = sql2::classify_datafusion_statement(&statement);
410
- let transaction = self.transaction_mut()?;
411
- match kind {
412
- sql2::SqlStatementKind::Write => {
413
- execute_transaction_write(transaction, statement, params)
414
- .await
415
- .map_err(|error| normalize_sql_surface_error(error, sql))
416
- }
417
- sql2::SqlStatementKind::Read => {
418
- let plan = sql2::create_transaction_read_logical_plan_from_parsed(
419
- transaction,
420
- sql,
421
- statement,
422
- )
423
- .await
424
- .map_err(|error| normalize_sql_surface_error(error, sql))?;
425
- let result = sql2::execute_logical_plan(plan, params)
426
- .await
427
- .map_err(|error| normalize_sql_surface_error(error, sql))?;
428
- Ok(ExecuteResult::from_sql_query_result(result))
429
- }
430
- sql2::SqlStatementKind::Other => Err(LixError::new(
431
- LixError::CODE_UNSUPPORTED_SQL,
432
- "SQL statement is not supported by Lix SQL",
433
- )),
434
- }
435
- }
436
- }
437
-
438
- async fn execute_transaction_write(
439
- transaction: &mut crate::transaction::Transaction,
440
- statement: datafusion::sql::parser::Statement,
441
- params: &[Value],
442
- ) -> Result<ExecuteResult, LixError> {
443
- let tx_plan = sql2::create_write_logical_plan_from_parsed(transaction, statement).await?;
444
- let result = sql2::execute_logical_plan(tx_plan, params).await?;
445
- let affected_rows = affected_rows_from_query_result(result)?;
446
- Ok(ExecuteResult::from_rows_affected(affected_rows))
447
- }
448
-
449
- fn normalize_sql_surface_error(error: LixError, sql: &str) -> LixError {
450
- if error.code.starts_with("LIX_ERROR_PATH_") && sql_uses_public_filesystem_path_surface(sql) {
451
- return LixError {
452
- code: LixError::CODE_INVALID_PARAM.to_string(),
453
- ..error
454
- };
455
- }
456
- if error.code == LixError::CODE_INVALID_JSON_PATH
457
- && error
458
- .message
459
- .to_ascii_lowercase()
460
- .contains("uses variadic path segments")
461
- {
462
- return LixError {
463
- code: LixError::CODE_INVALID_PARAM.to_string(),
464
- ..error
465
- };
466
- }
467
- if error.code == LixError::CODE_FOREIGN_KEY {
468
- let lower = error.message.to_ascii_lowercase();
469
- if lower.contains("schema 'lix_version_ref'") && lower.contains("target 'lix_commit.") {
470
- return LixError {
471
- code: LixError::CODE_VERSION_NOT_FOUND.to_string(),
472
- ..error
473
- };
474
- }
475
- }
476
- error
477
- }
478
-
479
- fn sql_uses_public_filesystem_path_surface(sql: &str) -> bool {
480
- let lower = sql.to_ascii_lowercase();
481
- (lower.contains("lix_file") || lower.contains("lix_directory")) && lower.contains("path")
482
- }
483
-
484
- fn affected_rows_from_query_result(result: SqlQueryResult) -> Result<u64, LixError> {
485
- let Some(first_row) = result.rows.first() else {
486
- return Ok(0);
487
- };
488
- let Some(first_value) = first_row.first() else {
489
- return Ok(0);
490
- };
491
- match first_value {
492
- Value::Integer(value) if *value >= 0 => Ok(*value as u64),
493
- Value::Text(value) => value.parse::<u64>().map_err(|error| {
494
- LixError::new(
495
- "LIX_ERROR_UNKNOWN",
496
- format!("failed to parse affected row count from SQL result: {error}"),
497
- )
498
- }),
499
- other => Err(LixError::new(
500
- "LIX_ERROR_UNKNOWN",
501
- format!("expected affected row count, got {other:?}"),
502
- )),
503
- }
504
- }
505
-
506
- #[cfg(test)]
507
- mod tests {
508
- use super::*;
509
-
510
- #[test]
511
- fn row_get_converts_native_values_and_value_keeps_wrapper() {
512
- let result = ExecuteResult::from_rows(
513
- vec!["title".to_string(), "done".to_string()],
514
- vec![vec![Value::Text("Hello".to_string()), Value::Boolean(true)]],
515
- );
516
- let row = &result.rows()[0];
517
-
518
- assert_eq!(row.get::<String>("title").unwrap(), "Hello");
519
- assert!(row.get::<bool>("done").unwrap());
520
- assert_eq!(
521
- row.value("title").unwrap(),
522
- &Value::Text("Hello".to_string())
523
- );
524
- }
525
-
526
- #[test]
527
- fn row_get_errors_on_missing_column_and_wrong_type() {
528
- let result = ExecuteResult::from_rows(
529
- vec!["title".to_string()],
530
- vec![vec![Value::Text("Hello".to_string())]],
531
- );
532
- let row = &result.rows()[0];
533
-
534
- let missing = row.get::<String>("missing").unwrap_err();
535
- assert_eq!(missing.code, LixError::CODE_COLUMN_NOT_FOUND);
536
- assert!(missing.message.contains("available columns: title"));
537
-
538
- let wrong_type = row.get::<bool>("title").unwrap_err();
539
- assert_eq!(wrong_type.code, "LIX_ERROR_VALUE_TYPE");
540
- }
541
- }
@@ -1,102 +0,0 @@
1
- use crate::storage::StorageReader;
2
- use crate::tracked_state::{
3
- plan_merge, TrackedStateDiff, TrackedStateDiffRequest, TrackedStateMergePlan,
4
- TrackedStateStoreReader,
5
- };
6
- use crate::LixError;
7
-
8
- use super::conflicts::{conflicts_from_plan, MergeConflict};
9
- use super::stats::{stats_from_diff, stats_from_plan, MergeStats};
10
-
11
- #[derive(Debug, Clone, Copy, PartialEq, Eq)]
12
- pub(crate) enum MergeOutcome {
13
- AlreadyUpToDate,
14
- FastForward,
15
- MergeCommitted,
16
- }
17
-
18
- #[derive(Debug, Clone, PartialEq, Eq)]
19
- pub(crate) struct MergeCommits {
20
- pub(crate) base_commit_id: String,
21
- pub(crate) target_commit_id: String,
22
- pub(crate) source_commit_id: String,
23
- }
24
-
25
- #[derive(Debug, Clone, PartialEq, Eq)]
26
- pub(crate) struct MergeAnalysis {
27
- pub(crate) outcome: MergeOutcome,
28
- pub(crate) commits: MergeCommits,
29
- pub(crate) source_diff: TrackedStateDiff,
30
- pub(crate) target_diff: TrackedStateDiff,
31
- pub(crate) stats: MergeStats,
32
- pub(crate) conflicts: Vec<MergeConflict>,
33
- pub(crate) merge_plan: Option<TrackedStateMergePlan>,
34
- }
35
-
36
- impl MergeAnalysis {
37
- pub(crate) fn merge_plan(&self) -> Option<&TrackedStateMergePlan> {
38
- self.merge_plan.as_ref()
39
- }
40
- }
41
-
42
- pub(crate) async fn analyze<S>(
43
- reader: &mut TrackedStateStoreReader<S>,
44
- commits: MergeCommits,
45
- ) -> Result<MergeAnalysis, LixError>
46
- where
47
- S: StorageReader,
48
- {
49
- let request = TrackedStateDiffRequest::default();
50
- let source_diff = reader
51
- .diff_commits(&commits.base_commit_id, &commits.source_commit_id, &request)
52
- .await?;
53
- let target_diff = if commits.base_commit_id == commits.source_commit_id
54
- || commits.base_commit_id == commits.target_commit_id
55
- {
56
- TrackedStateDiff::default()
57
- } else {
58
- reader
59
- .diff_commits(&commits.base_commit_id, &commits.target_commit_id, &request)
60
- .await?
61
- };
62
-
63
- let outcome = if commits.base_commit_id == commits.source_commit_id {
64
- MergeOutcome::AlreadyUpToDate
65
- } else if commits.base_commit_id == commits.target_commit_id {
66
- MergeOutcome::FastForward
67
- } else {
68
- MergeOutcome::MergeCommitted
69
- };
70
-
71
- let merge_plan = if outcome == MergeOutcome::MergeCommitted {
72
- Some(plan_merge(&target_diff, &source_diff)?)
73
- } else {
74
- None
75
- };
76
-
77
- let stats = match outcome {
78
- MergeOutcome::AlreadyUpToDate => MergeStats::default(),
79
- MergeOutcome::FastForward => stats_from_diff(&source_diff),
80
- MergeOutcome::MergeCommitted => merge_plan
81
- .as_ref()
82
- .map(|plan| stats_from_plan(plan, &source_diff))
83
- .transpose()?
84
- .unwrap_or_default(),
85
- };
86
-
87
- let conflicts = merge_plan
88
- .as_ref()
89
- .map(conflicts_from_plan)
90
- .transpose()?
91
- .unwrap_or_default();
92
-
93
- Ok(MergeAnalysis {
94
- outcome,
95
- commits,
96
- source_diff,
97
- target_diff,
98
- stats,
99
- conflicts,
100
- merge_plan,
101
- })
102
- }
@@ -1,23 +0,0 @@
1
- use crate::tracked_state::TrackedStateMergePlan;
2
- use crate::transaction::types::TransactionAdoptedChange;
3
-
4
- pub(crate) fn adopted_changes_from_merge_plan(
5
- plan: &TrackedStateMergePlan,
6
- target_version_id: &str,
7
- ) -> Vec<TransactionAdoptedChange> {
8
- plan.patches
9
- .iter()
10
- .map(|patch| stage_adopted_change_from_patch(patch, target_version_id))
11
- .collect()
12
- }
13
-
14
- fn stage_adopted_change_from_patch(
15
- patch: &crate::tracked_state::TrackedStateMergePatch,
16
- target_version_id: &str,
17
- ) -> TransactionAdoptedChange {
18
- TransactionAdoptedChange {
19
- version_id: target_version_id.to_string(),
20
- change_id: patch.change_id().to_string(),
21
- projected_row: patch.projected_row().clone(),
22
- }
23
- }