@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,1202 +0,0 @@
1
- use std::any::Any;
2
- use std::sync::Arc;
3
-
4
- use async_trait::async_trait;
5
- use datafusion::arrow::array::{ArrayRef, BooleanArray, StringArray, UInt64Array};
6
- use datafusion::arrow::compute::{and, filter_record_batch};
7
- use datafusion::arrow::datatypes::{DataType, Field, Schema, SchemaRef};
8
- use datafusion::arrow::record_batch::RecordBatch;
9
- use datafusion::catalog::{Session, TableProvider};
10
- use datafusion::common::{not_impl_err, DFSchema, DataFusionError, Result, ScalarValue};
11
- use datafusion::datasource::TableType;
12
- use datafusion::execution::TaskContext;
13
- use datafusion::logical_expr::dml::InsertOp;
14
- use datafusion::logical_expr::{Expr, TableProviderFilterPushDown};
15
- use datafusion::physical_expr::{create_physical_expr, EquivalenceProperties, PhysicalExpr};
16
- use datafusion::physical_plan::execution_plan::{Boundedness, EmissionType, PlanProperties};
17
- use datafusion::physical_plan::stream::RecordBatchStreamAdapter;
18
- use datafusion::physical_plan::{
19
- DisplayAs, DisplayFormatType, ExecutionPlan, Partitioning, SendableRecordBatchStream,
20
- };
21
- use futures_util::{stream, TryStreamExt};
22
- use serde_json::Value as JsonValue;
23
-
24
- use crate::live_state::{
25
- LiveStateFilter, LiveStateReader, LiveStateScanRequest, MaterializedLiveStateRow,
26
- };
27
- use crate::sql2::dml::{InsertExec, InsertSink};
28
- use crate::sql2::record_batch::record_batch_with_row_count;
29
- use crate::sql2::write_normalization::{InsertCell, SqlCell, UpdateAssignmentValues};
30
- use crate::sql2::{
31
- SqlWriteContext, WriteAccess, WriteContextLiveStateReader, WriteContextVersionRefReader,
32
- };
33
- use crate::transaction::types::{
34
- LogicalPrimaryKey, TransactionWrite, TransactionWriteMode, TransactionWriteOperation,
35
- TransactionWriteOrigin, TransactionWriteRow,
36
- };
37
- use crate::version::{
38
- version_descriptor_stage_row, version_descriptor_tombstone_row, version_ref_stage_row,
39
- version_ref_tombstone_row, VersionRefReader,
40
- };
41
- use crate::LixError;
42
- use crate::GLOBAL_VERSION_ID;
43
-
44
- pub(crate) async fn register_lix_version_provider(
45
- session: &datafusion::prelude::SessionContext,
46
- live_state: Arc<dyn LiveStateReader>,
47
- version_ref: Arc<dyn VersionRefReader>,
48
- ) -> Result<(), LixError> {
49
- session
50
- .register_table(
51
- "lix_version",
52
- Arc::new(LixVersionProvider::new(live_state, version_ref)),
53
- )
54
- .map_err(datafusion_error_to_lix_error)?;
55
- Ok(())
56
- }
57
-
58
- pub(crate) async fn register_lix_version_write_provider(
59
- session: &datafusion::prelude::SessionContext,
60
- write_ctx: SqlWriteContext,
61
- ) -> Result<(), LixError> {
62
- session
63
- .register_table(
64
- "lix_version",
65
- Arc::new(LixVersionProvider::with_write(write_ctx)),
66
- )
67
- .map_err(datafusion_error_to_lix_error)?;
68
- Ok(())
69
- }
70
-
71
- struct LixVersionProvider {
72
- schema: SchemaRef,
73
- live_state: Arc<dyn LiveStateReader>,
74
- version_ref: Arc<dyn VersionRefReader>,
75
- write_access: WriteAccess,
76
- }
77
-
78
- impl std::fmt::Debug for LixVersionProvider {
79
- fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
80
- f.debug_struct("LixVersionProvider").finish()
81
- }
82
- }
83
-
84
- impl LixVersionProvider {
85
- fn new(live_state: Arc<dyn LiveStateReader>, version_ref: Arc<dyn VersionRefReader>) -> Self {
86
- Self {
87
- schema: lix_version_schema(),
88
- live_state,
89
- version_ref,
90
- write_access: WriteAccess::read_only(),
91
- }
92
- }
93
-
94
- fn with_write(write_ctx: SqlWriteContext) -> Self {
95
- let live_state = Arc::new(WriteContextLiveStateReader::new(write_ctx.clone()));
96
- let version_ref = Arc::new(WriteContextVersionRefReader::new(write_ctx.clone()));
97
- Self {
98
- schema: lix_version_schema(),
99
- live_state,
100
- version_ref,
101
- write_access: WriteAccess::write(write_ctx),
102
- }
103
- }
104
- }
105
-
106
- #[async_trait]
107
- impl TableProvider for LixVersionProvider {
108
- fn as_any(&self) -> &dyn Any {
109
- self
110
- }
111
-
112
- fn schema(&self) -> SchemaRef {
113
- Arc::clone(&self.schema)
114
- }
115
-
116
- fn table_type(&self) -> TableType {
117
- TableType::Base
118
- }
119
-
120
- fn supports_filters_pushdown(
121
- &self,
122
- filters: &[&Expr],
123
- ) -> Result<Vec<TableProviderFilterPushDown>> {
124
- Ok(filters
125
- .iter()
126
- .map(|_| TableProviderFilterPushDown::Unsupported)
127
- .collect())
128
- }
129
-
130
- async fn scan(
131
- &self,
132
- _state: &dyn Session,
133
- projection: Option<&Vec<usize>>,
134
- _filters: &[Expr],
135
- _limit: Option<usize>,
136
- ) -> Result<Arc<dyn ExecutionPlan>> {
137
- Ok(Arc::new(LixVersionScanExec::new(
138
- Arc::clone(&self.live_state),
139
- Arc::clone(&self.version_ref),
140
- projected_schema(&self.schema, projection),
141
- projection.cloned(),
142
- )))
143
- }
144
-
145
- async fn insert_into(
146
- &self,
147
- _state: &dyn Session,
148
- input: Arc<dyn ExecutionPlan>,
149
- insert_op: InsertOp,
150
- ) -> Result<Arc<dyn ExecutionPlan>> {
151
- if insert_op != InsertOp::Append {
152
- return not_impl_err!("{insert_op} not implemented for lix_version yet");
153
- }
154
-
155
- let write_ctx = self.write_access.require_write("INSERT into lix_version")?;
156
- let sink = LixVersionInsertSink::new(input.schema(), write_ctx);
157
- Ok(Arc::new(InsertExec::new(input, Arc::new(sink))))
158
- }
159
-
160
- async fn delete_from(
161
- &self,
162
- state: &dyn Session,
163
- filters: Vec<Expr>,
164
- ) -> Result<Arc<dyn ExecutionPlan>> {
165
- let write_ctx = self.write_access.require_write("DELETE FROM lix_version")?;
166
- let df_schema = DFSchema::try_from(Arc::clone(&self.schema))?;
167
- let physical_filters = filters
168
- .iter()
169
- .map(|expr| create_physical_expr(expr, &df_schema, state.execution_props()))
170
- .collect::<Result<Vec<_>>>()?;
171
-
172
- Ok(Arc::new(LixVersionDeleteExec::new(
173
- write_ctx,
174
- Arc::clone(&self.live_state),
175
- Arc::clone(&self.version_ref),
176
- Arc::clone(&self.schema),
177
- physical_filters,
178
- )))
179
- }
180
-
181
- async fn update(
182
- &self,
183
- state: &dyn Session,
184
- assignments: Vec<(String, Expr)>,
185
- filters: Vec<Expr>,
186
- ) -> Result<Arc<dyn ExecutionPlan>> {
187
- let write_ctx = self.write_access.require_write("UPDATE lix_version")?;
188
- validate_lix_version_update_assignments(&assignments)?;
189
-
190
- let df_schema = DFSchema::try_from(Arc::clone(&self.schema))?;
191
- let physical_assignments = assignments
192
- .iter()
193
- .map(|(column_name, expr)| {
194
- Ok((
195
- column_name.clone(),
196
- create_physical_expr(expr, &df_schema, state.execution_props())?,
197
- ))
198
- })
199
- .collect::<Result<Vec<_>>>()?;
200
- let physical_filters = filters
201
- .iter()
202
- .map(|expr| create_physical_expr(expr, &df_schema, state.execution_props()))
203
- .collect::<Result<Vec<_>>>()?;
204
-
205
- Ok(Arc::new(LixVersionUpdateExec::new(
206
- write_ctx,
207
- Arc::clone(&self.live_state),
208
- Arc::clone(&self.version_ref),
209
- Arc::clone(&self.schema),
210
- physical_assignments,
211
- physical_filters,
212
- )))
213
- }
214
- }
215
-
216
- struct LixVersionInsertSink {
217
- write_ctx: SqlWriteContext,
218
- }
219
-
220
- impl std::fmt::Debug for LixVersionInsertSink {
221
- fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
222
- f.debug_struct("LixVersionInsertSink").finish()
223
- }
224
- }
225
-
226
- impl LixVersionInsertSink {
227
- fn new(_schema: SchemaRef, write_ctx: SqlWriteContext) -> Self {
228
- Self { write_ctx }
229
- }
230
- }
231
-
232
- impl DisplayAs for LixVersionInsertSink {
233
- fn fmt_as(&self, t: DisplayFormatType, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
234
- match t {
235
- DisplayFormatType::Default | DisplayFormatType::Verbose => {
236
- write!(f, "LixVersionInsertSink")
237
- }
238
- DisplayFormatType::TreeRender => write!(f, "LixVersionInsertSink"),
239
- }
240
- }
241
- }
242
-
243
- #[async_trait]
244
- impl InsertSink for LixVersionInsertSink {
245
- async fn write_batches(
246
- &self,
247
- batches: Vec<RecordBatch>,
248
- _context: &Arc<TaskContext>,
249
- ) -> Result<u64> {
250
- let default_commit_id = self
251
- .write_ctx
252
- .load_version_head(&self.write_ctx.active_version_id())
253
- .await
254
- .map_err(lix_error_to_datafusion_error)?
255
- .ok_or_else(|| {
256
- DataFusionError::Execution(
257
- "INSERT into lix_version could not resolve active version head".to_string(),
258
- )
259
- })?;
260
- let mut rows = Vec::new();
261
- let mut count = 0u64;
262
- for batch in batches {
263
- let version_rows = version_insert_rows_from_batch(&batch, &default_commit_id)?;
264
- count = count
265
- .checked_add(u64::try_from(version_rows.len()).map_err(|_| {
266
- DataFusionError::Execution("INSERT row count overflow".to_string())
267
- })?)
268
- .ok_or_else(|| DataFusionError::Execution("INSERT row count overflow".into()))?;
269
- rows.extend(version_rows.into_iter().flat_map(version_insert_stage_rows));
270
- }
271
-
272
- if !rows.is_empty() {
273
- self.write_ctx
274
- .stage_write(TransactionWrite::Rows {
275
- mode: TransactionWriteMode::Insert,
276
- rows,
277
- })
278
- .await
279
- .map_err(lix_error_to_datafusion_error)?;
280
- }
281
-
282
- Ok(count)
283
- }
284
- }
285
-
286
- struct LixVersionDeleteExec {
287
- write_ctx: SqlWriteContext,
288
- active_version_id: String,
289
- live_state: Arc<dyn LiveStateReader>,
290
- version_ref: Arc<dyn VersionRefReader>,
291
- table_schema: SchemaRef,
292
- filters: Vec<Arc<dyn PhysicalExpr>>,
293
- result_schema: SchemaRef,
294
- properties: Arc<PlanProperties>,
295
- }
296
-
297
- impl std::fmt::Debug for LixVersionDeleteExec {
298
- fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
299
- f.debug_struct("LixVersionDeleteExec").finish()
300
- }
301
- }
302
-
303
- impl LixVersionDeleteExec {
304
- fn new(
305
- write_ctx: SqlWriteContext,
306
- live_state: Arc<dyn LiveStateReader>,
307
- version_ref: Arc<dyn VersionRefReader>,
308
- table_schema: SchemaRef,
309
- filters: Vec<Arc<dyn PhysicalExpr>>,
310
- ) -> Self {
311
- let result_schema = dml_count_schema();
312
- let properties = dml_plan_properties(Arc::clone(&result_schema));
313
- let active_version_id = write_ctx.active_version_id();
314
- Self {
315
- write_ctx,
316
- active_version_id,
317
- live_state,
318
- version_ref,
319
- table_schema,
320
- filters,
321
- result_schema,
322
- properties: Arc::new(properties),
323
- }
324
- }
325
- }
326
-
327
- impl DisplayAs for LixVersionDeleteExec {
328
- fn fmt_as(&self, t: DisplayFormatType, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
329
- match t {
330
- DisplayFormatType::Default | DisplayFormatType::Verbose => {
331
- write!(f, "LixVersionDeleteExec(filters={})", self.filters.len())
332
- }
333
- DisplayFormatType::TreeRender => write!(f, "LixVersionDeleteExec"),
334
- }
335
- }
336
- }
337
-
338
- impl ExecutionPlan for LixVersionDeleteExec {
339
- fn name(&self) -> &str {
340
- "LixVersionDeleteExec"
341
- }
342
-
343
- fn as_any(&self) -> &dyn Any {
344
- self
345
- }
346
-
347
- fn properties(&self) -> &Arc<PlanProperties> {
348
- &self.properties
349
- }
350
-
351
- fn children(&self) -> Vec<&Arc<dyn ExecutionPlan>> {
352
- Vec::new()
353
- }
354
-
355
- fn with_new_children(
356
- self: Arc<Self>,
357
- children: Vec<Arc<dyn ExecutionPlan>>,
358
- ) -> Result<Arc<dyn ExecutionPlan>> {
359
- if !children.is_empty() {
360
- return Err(DataFusionError::Execution(
361
- "LixVersionDeleteExec does not accept children".to_string(),
362
- ));
363
- }
364
- Ok(self)
365
- }
366
-
367
- fn execute(
368
- &self,
369
- partition: usize,
370
- _context: Arc<TaskContext>,
371
- ) -> Result<SendableRecordBatchStream> {
372
- if partition != 0 {
373
- return Err(DataFusionError::Execution(format!(
374
- "LixVersionDeleteExec only exposes one partition, got {partition}"
375
- )));
376
- }
377
- let write_ctx = self.write_ctx.clone();
378
- let active_version_id = self.active_version_id.clone();
379
- let live_state = Arc::clone(&self.live_state);
380
- let version_ref = Arc::clone(&self.version_ref);
381
- let filters = self.filters.clone();
382
- let table_schema = Arc::clone(&self.table_schema);
383
- let result_schema = Arc::clone(&self.result_schema);
384
- let stream_schema = Arc::clone(&result_schema);
385
-
386
- let stream = stream::once(async move {
387
- let rows = load_version_rows(live_state, version_ref)
388
- .await
389
- .map_err(lix_error_to_datafusion_error)?;
390
- let source_batch = version_record_batch(&version_projection_for_scan(None), &rows)?;
391
- let matched_batch = filter_version_batch(source_batch, &filters)?;
392
- let version_rows = version_rows_from_batch(&matched_batch)?;
393
- reject_protected_version_deletes(&version_rows, &active_version_id)?;
394
- let count = u64::try_from(version_rows.len())
395
- .map_err(|_| DataFusionError::Execution("DELETE row count overflow".to_string()))?;
396
- let rows = version_rows
397
- .into_iter()
398
- .flat_map(version_tombstone_rows)
399
- .collect::<Vec<_>>();
400
-
401
- if !rows.is_empty() {
402
- write_ctx
403
- .stage_write(TransactionWrite::Rows {
404
- mode: TransactionWriteMode::Replace,
405
- rows,
406
- })
407
- .await
408
- .map_err(lix_error_to_datafusion_error)?;
409
- }
410
-
411
- let _ = table_schema;
412
- Ok::<_, DataFusionError>(stream::iter(vec![Ok::<RecordBatch, DataFusionError>(
413
- dml_count_batch(Arc::clone(&stream_schema), count)?,
414
- )]))
415
- })
416
- .try_flatten();
417
- Ok(Box::pin(RecordBatchStreamAdapter::new(
418
- result_schema,
419
- stream,
420
- )))
421
- }
422
- }
423
-
424
- struct LixVersionUpdateExec {
425
- write_ctx: SqlWriteContext,
426
- live_state: Arc<dyn LiveStateReader>,
427
- version_ref: Arc<dyn VersionRefReader>,
428
- table_schema: SchemaRef,
429
- assignments: Vec<(String, Arc<dyn PhysicalExpr>)>,
430
- filters: Vec<Arc<dyn PhysicalExpr>>,
431
- result_schema: SchemaRef,
432
- properties: Arc<PlanProperties>,
433
- }
434
-
435
- impl std::fmt::Debug for LixVersionUpdateExec {
436
- fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
437
- f.debug_struct("LixVersionUpdateExec").finish()
438
- }
439
- }
440
-
441
- impl LixVersionUpdateExec {
442
- fn new(
443
- write_ctx: SqlWriteContext,
444
- live_state: Arc<dyn LiveStateReader>,
445
- version_ref: Arc<dyn VersionRefReader>,
446
- table_schema: SchemaRef,
447
- assignments: Vec<(String, Arc<dyn PhysicalExpr>)>,
448
- filters: Vec<Arc<dyn PhysicalExpr>>,
449
- ) -> Self {
450
- let result_schema = dml_count_schema();
451
- let properties = dml_plan_properties(Arc::clone(&result_schema));
452
- Self {
453
- write_ctx,
454
- live_state,
455
- version_ref,
456
- table_schema,
457
- assignments,
458
- filters,
459
- result_schema,
460
- properties: Arc::new(properties),
461
- }
462
- }
463
- }
464
-
465
- impl DisplayAs for LixVersionUpdateExec {
466
- fn fmt_as(&self, t: DisplayFormatType, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
467
- match t {
468
- DisplayFormatType::Default | DisplayFormatType::Verbose => {
469
- write!(
470
- f,
471
- "LixVersionUpdateExec(assignments={}, filters={})",
472
- self.assignments.len(),
473
- self.filters.len()
474
- )
475
- }
476
- DisplayFormatType::TreeRender => write!(f, "LixVersionUpdateExec"),
477
- }
478
- }
479
- }
480
-
481
- impl ExecutionPlan for LixVersionUpdateExec {
482
- fn name(&self) -> &str {
483
- "LixVersionUpdateExec"
484
- }
485
-
486
- fn as_any(&self) -> &dyn Any {
487
- self
488
- }
489
-
490
- fn properties(&self) -> &Arc<PlanProperties> {
491
- &self.properties
492
- }
493
-
494
- fn children(&self) -> Vec<&Arc<dyn ExecutionPlan>> {
495
- Vec::new()
496
- }
497
-
498
- fn with_new_children(
499
- self: Arc<Self>,
500
- children: Vec<Arc<dyn ExecutionPlan>>,
501
- ) -> Result<Arc<dyn ExecutionPlan>> {
502
- if !children.is_empty() {
503
- return Err(DataFusionError::Execution(
504
- "LixVersionUpdateExec does not accept children".to_string(),
505
- ));
506
- }
507
- Ok(self)
508
- }
509
-
510
- fn execute(
511
- &self,
512
- partition: usize,
513
- _context: Arc<TaskContext>,
514
- ) -> Result<SendableRecordBatchStream> {
515
- if partition != 0 {
516
- return Err(DataFusionError::Execution(format!(
517
- "LixVersionUpdateExec only exposes one partition, got {partition}"
518
- )));
519
- }
520
- let write_ctx = self.write_ctx.clone();
521
- let live_state = Arc::clone(&self.live_state);
522
- let version_ref = Arc::clone(&self.version_ref);
523
- let table_schema = Arc::clone(&self.table_schema);
524
- let assignments = self.assignments.clone();
525
- let filters = self.filters.clone();
526
- let result_schema = Arc::clone(&self.result_schema);
527
- let stream_schema = Arc::clone(&result_schema);
528
-
529
- let stream = stream::once(async move {
530
- let rows = load_version_rows(live_state, version_ref)
531
- .await
532
- .map_err(lix_error_to_datafusion_error)?;
533
- let source_batch = version_record_batch(&version_projection_for_scan(None), &rows)?;
534
- let matched_batch = filter_version_batch(source_batch, &filters)?;
535
- let version_rows =
536
- version_update_rows_from_batch(&matched_batch, &assignments, &table_schema)?;
537
- let count = u64::try_from(version_rows.len())
538
- .map_err(|_| DataFusionError::Execution("UPDATE row count overflow".to_string()))?;
539
- let rows = version_rows
540
- .into_iter()
541
- .flat_map(version_update_stage_rows)
542
- .collect::<Vec<_>>();
543
-
544
- if !rows.is_empty() {
545
- write_ctx
546
- .stage_write(TransactionWrite::Rows {
547
- mode: TransactionWriteMode::Replace,
548
- rows,
549
- })
550
- .await
551
- .map_err(lix_error_to_datafusion_error)?;
552
- }
553
-
554
- Ok::<_, DataFusionError>(stream::iter(vec![Ok::<RecordBatch, DataFusionError>(
555
- dml_count_batch(Arc::clone(&stream_schema), count)?,
556
- )]))
557
- })
558
- .try_flatten();
559
- Ok(Box::pin(RecordBatchStreamAdapter::new(
560
- result_schema,
561
- stream,
562
- )))
563
- }
564
- }
565
-
566
- struct LixVersionScanExec {
567
- live_state: Arc<dyn LiveStateReader>,
568
- version_ref: Arc<dyn VersionRefReader>,
569
- schema: SchemaRef,
570
- projection: Option<Vec<usize>>,
571
- properties: Arc<PlanProperties>,
572
- }
573
-
574
- impl std::fmt::Debug for LixVersionScanExec {
575
- fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
576
- f.debug_struct("LixVersionScanExec").finish()
577
- }
578
- }
579
-
580
- impl LixVersionScanExec {
581
- fn new(
582
- live_state: Arc<dyn LiveStateReader>,
583
- version_ref: Arc<dyn VersionRefReader>,
584
- schema: SchemaRef,
585
- projection: Option<Vec<usize>>,
586
- ) -> Self {
587
- let properties = PlanProperties::new(
588
- EquivalenceProperties::new(schema.clone()),
589
- Partitioning::UnknownPartitioning(1),
590
- EmissionType::Incremental,
591
- Boundedness::Bounded,
592
- );
593
- Self {
594
- live_state,
595
- version_ref,
596
- schema,
597
- projection,
598
- properties: Arc::new(properties),
599
- }
600
- }
601
- }
602
-
603
- impl DisplayAs for LixVersionScanExec {
604
- fn fmt_as(&self, t: DisplayFormatType, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
605
- match t {
606
- DisplayFormatType::Default | DisplayFormatType::Verbose => {
607
- write!(f, "LixVersionScanExec")
608
- }
609
- DisplayFormatType::TreeRender => write!(f, "LixVersionScanExec"),
610
- }
611
- }
612
- }
613
-
614
- impl ExecutionPlan for LixVersionScanExec {
615
- fn name(&self) -> &str {
616
- "LixVersionScanExec"
617
- }
618
-
619
- fn as_any(&self) -> &dyn Any {
620
- self
621
- }
622
-
623
- fn properties(&self) -> &Arc<PlanProperties> {
624
- &self.properties
625
- }
626
-
627
- fn children(&self) -> Vec<&Arc<dyn ExecutionPlan>> {
628
- Vec::new()
629
- }
630
-
631
- fn with_new_children(
632
- self: Arc<Self>,
633
- children: Vec<Arc<dyn ExecutionPlan>>,
634
- ) -> Result<Arc<dyn ExecutionPlan>> {
635
- if !children.is_empty() {
636
- return Err(DataFusionError::Execution(
637
- "LixVersionScanExec does not accept children".to_string(),
638
- ));
639
- }
640
- Ok(self)
641
- }
642
-
643
- fn execute(
644
- &self,
645
- partition: usize,
646
- _context: Arc<TaskContext>,
647
- ) -> Result<SendableRecordBatchStream> {
648
- if partition != 0 {
649
- return Err(DataFusionError::Execution(format!(
650
- "LixVersionScanExec only exposes one partition, got {partition}"
651
- )));
652
- }
653
-
654
- let live_state = Arc::clone(&self.live_state);
655
- let version_ref = Arc::clone(&self.version_ref);
656
- let projection = version_projection_for_scan(self.projection.as_ref());
657
- let schema = Arc::clone(&self.schema);
658
- let stream = stream::once(async move {
659
- let rows = load_version_rows(live_state, version_ref)
660
- .await
661
- .map_err(lix_error_to_datafusion_error)?;
662
- version_record_batch(&projection, &rows)
663
- });
664
- Ok(Box::pin(RecordBatchStreamAdapter::new(schema, stream)))
665
- }
666
- }
667
-
668
- #[derive(Debug, Clone, PartialEq, Eq)]
669
- struct VersionRow {
670
- id: String,
671
- name: String,
672
- hidden: bool,
673
- commit_id: String,
674
- }
675
-
676
- #[derive(Debug, Clone, Copy)]
677
- enum VersionColumn {
678
- Id,
679
- Name,
680
- Hidden,
681
- CommitId,
682
- }
683
-
684
- async fn load_version_rows(
685
- live_state: Arc<dyn LiveStateReader>,
686
- version_ref: Arc<dyn VersionRefReader>,
687
- ) -> Result<Vec<VersionRow>, LixError> {
688
- let descriptor_rows = live_state
689
- .scan_rows(&LiveStateScanRequest {
690
- filter: LiveStateFilter {
691
- schema_keys: vec!["lix_version_descriptor".to_string()],
692
- version_ids: vec![GLOBAL_VERSION_ID.to_string()],
693
- ..LiveStateFilter::default()
694
- },
695
- projection: Default::default(),
696
- limit: None,
697
- })
698
- .await?;
699
-
700
- let mut out = Vec::new();
701
- for descriptor_row in descriptor_rows {
702
- let descriptor = parse_descriptor(&descriptor_row)?;
703
- let Some(commit_id) = version_ref.load_head_commit_id(&descriptor.id).await? else {
704
- continue;
705
- };
706
- out.push(VersionRow {
707
- commit_id,
708
- id: descriptor.id,
709
- name: descriptor.name,
710
- hidden: descriptor.hidden,
711
- });
712
- }
713
- Ok(out)
714
- }
715
-
716
- #[derive(Debug, Clone, PartialEq, Eq)]
717
- struct VersionDescriptor {
718
- id: String,
719
- name: String,
720
- hidden: bool,
721
- }
722
-
723
- fn parse_descriptor(row: &MaterializedLiveStateRow) -> Result<VersionDescriptor, LixError> {
724
- let snapshot = parse_snapshot(row, "lix_version_descriptor")?;
725
- let id = snapshot
726
- .get("id")
727
- .and_then(JsonValue::as_str)
728
- .ok_or_else(|| LixError::new("LIX_ERROR_UNKNOWN", "lix_version_descriptor is missing id"))?
729
- .to_string();
730
- let name = snapshot
731
- .get("name")
732
- .and_then(JsonValue::as_str)
733
- .ok_or_else(|| {
734
- LixError::new(
735
- "LIX_ERROR_UNKNOWN",
736
- "lix_version_descriptor is missing name",
737
- )
738
- })?
739
- .to_string();
740
- let hidden = snapshot
741
- .get("hidden")
742
- .and_then(JsonValue::as_bool)
743
- .unwrap_or(false);
744
- Ok(VersionDescriptor { id, name, hidden })
745
- }
746
-
747
- fn parse_snapshot(row: &MaterializedLiveStateRow, schema_key: &str) -> Result<JsonValue, LixError> {
748
- let snapshot_content = row.snapshot_content.as_deref().ok_or_else(|| {
749
- LixError::new(
750
- "LIX_ERROR_UNKNOWN",
751
- format!("{schema_key} row is missing snapshot_content"),
752
- )
753
- })?;
754
- serde_json::from_str(snapshot_content).map_err(|error| {
755
- LixError::new(
756
- "LIX_ERROR_UNKNOWN",
757
- format!("{schema_key} snapshot_content is invalid JSON: {error}"),
758
- )
759
- })
760
- }
761
-
762
- fn validate_lix_version_update_assignments(assignments: &[(String, Expr)]) -> Result<()> {
763
- for (column_name, _) in assignments {
764
- match column_name.as_str() {
765
- "name" | "hidden" | "commit_id" => {}
766
- "id" => {
767
- return Err(DataFusionError::Execution(
768
- "UPDATE lix_version cannot change immutable column 'id'".to_string(),
769
- ));
770
- }
771
- other => {
772
- return Err(DataFusionError::Plan(format!(
773
- "UPDATE lix_version failed: column '{other}' does not exist"
774
- )));
775
- }
776
- }
777
- }
778
- Ok(())
779
- }
780
-
781
- fn filter_version_batch(
782
- batch: RecordBatch,
783
- filters: &[Arc<dyn PhysicalExpr>],
784
- ) -> Result<RecordBatch> {
785
- let Some(mask) = evaluate_version_filters(&batch, filters)? else {
786
- return Ok(batch);
787
- };
788
- Ok(filter_record_batch(&batch, &mask)?)
789
- }
790
-
791
- fn evaluate_version_filters(
792
- batch: &RecordBatch,
793
- filters: &[Arc<dyn PhysicalExpr>],
794
- ) -> Result<Option<BooleanArray>> {
795
- if filters.is_empty() {
796
- return Ok(None);
797
- }
798
-
799
- let mut combined_mask: Option<BooleanArray> = None;
800
- for filter in filters {
801
- let result = filter.evaluate(batch)?;
802
- let array = result.into_array(batch.num_rows())?;
803
- let bool_array = array
804
- .as_any()
805
- .downcast_ref::<BooleanArray>()
806
- .ok_or_else(|| {
807
- DataFusionError::Execution("lix_version filter was not boolean".to_string())
808
- })?;
809
- let normalized = bool_array
810
- .iter()
811
- .map(|value| Some(value == Some(true)))
812
- .collect::<BooleanArray>();
813
- combined_mask = Some(match combined_mask {
814
- Some(existing) => and(&existing, &normalized)?,
815
- None => normalized,
816
- });
817
- }
818
- Ok(combined_mask)
819
- }
820
-
821
- fn version_insert_rows_from_batch(
822
- batch: &RecordBatch,
823
- default_commit_id: &str,
824
- ) -> Result<Vec<VersionRow>> {
825
- (0..batch.num_rows())
826
- .map(|row_index| {
827
- let id = required_string_value(batch, row_index, "id", "INSERT")?;
828
- let name = required_string_value(batch, row_index, "name", "INSERT")?;
829
- let hidden =
830
- optional_bool_value(batch, row_index, "hidden", "INSERT")?.unwrap_or(false);
831
- let commit_id = optional_string_value(batch, row_index, "commit_id", "INSERT")?
832
- .unwrap_or_else(|| default_commit_id.to_string());
833
- Ok(VersionRow {
834
- id,
835
- name,
836
- hidden,
837
- commit_id,
838
- })
839
- })
840
- .collect()
841
- }
842
-
843
- fn version_rows_from_batch(batch: &RecordBatch) -> Result<Vec<VersionRow>> {
844
- (0..batch.num_rows())
845
- .map(|row_index| {
846
- Ok(VersionRow {
847
- id: required_string_value(batch, row_index, "id", "DELETE")?,
848
- name: required_string_value(batch, row_index, "name", "DELETE")?,
849
- hidden: required_bool_value(batch, row_index, "hidden", "DELETE")?,
850
- commit_id: required_string_value(batch, row_index, "commit_id", "DELETE")?,
851
- })
852
- })
853
- .collect()
854
- }
855
-
856
- fn reject_protected_version_deletes(rows: &[VersionRow], active_version_id: &str) -> Result<()> {
857
- for row in rows {
858
- if row.id == GLOBAL_VERSION_ID {
859
- return Err(DataFusionError::Execution(
860
- "DELETE FROM lix_version cannot delete the global version".to_string(),
861
- ));
862
- }
863
- if row.id == active_version_id {
864
- return Err(DataFusionError::Execution(format!(
865
- "DELETE FROM lix_version cannot delete active version '{}'",
866
- row.id
867
- )));
868
- }
869
- }
870
- Ok(())
871
- }
872
-
873
- fn version_update_rows_from_batch(
874
- batch: &RecordBatch,
875
- assignments: &[(String, Arc<dyn PhysicalExpr>)],
876
- table_schema: &SchemaRef,
877
- ) -> Result<Vec<VersionRow>> {
878
- let assignment_values = UpdateAssignmentValues::evaluate(batch, assignments)?;
879
- (0..batch.num_rows())
880
- .map(|row_index| {
881
- Ok(VersionRow {
882
- id: required_string_value(batch, row_index, "id", "UPDATE")?,
883
- name: update_string_value(
884
- batch,
885
- &assignment_values,
886
- table_schema,
887
- row_index,
888
- "name",
889
- )?,
890
- hidden: update_bool_value(
891
- batch,
892
- &assignment_values,
893
- table_schema,
894
- row_index,
895
- "hidden",
896
- )?,
897
- commit_id: update_string_value(
898
- batch,
899
- &assignment_values,
900
- table_schema,
901
- row_index,
902
- "commit_id",
903
- )?,
904
- })
905
- })
906
- .collect()
907
- }
908
-
909
- fn version_stage_rows(
910
- row: VersionRow,
911
- origin: Option<TransactionWriteOrigin>,
912
- ) -> Vec<TransactionWriteRow> {
913
- vec![
914
- with_origin(
915
- version_descriptor_stage_row(&row.id, &row.name, row.hidden),
916
- origin.clone(),
917
- ),
918
- with_origin(version_ref_stage_row(&row.id, &row.commit_id), origin),
919
- ]
920
- }
921
-
922
- fn version_tombstone_rows(row: VersionRow) -> Vec<TransactionWriteRow> {
923
- let origin = Some(lix_version_origin(
924
- TransactionWriteOperation::Delete,
925
- &row.id,
926
- ));
927
- vec![
928
- with_origin(version_descriptor_tombstone_row(&row.id), origin.clone()),
929
- with_origin(version_ref_tombstone_row(&row.id), origin),
930
- ]
931
- }
932
-
933
- fn version_insert_stage_rows(row: VersionRow) -> Vec<TransactionWriteRow> {
934
- let origin = lix_version_origin(TransactionWriteOperation::Insert, &row.id);
935
- version_stage_rows(row, Some(origin))
936
- }
937
-
938
- fn version_update_stage_rows(row: VersionRow) -> Vec<TransactionWriteRow> {
939
- let origin = lix_version_origin(TransactionWriteOperation::Update, &row.id);
940
- version_stage_rows(row, Some(origin))
941
- }
942
-
943
- fn with_origin(
944
- mut row: TransactionWriteRow,
945
- origin: Option<TransactionWriteOrigin>,
946
- ) -> TransactionWriteRow {
947
- row.origin = origin;
948
- row
949
- }
950
-
951
- fn lix_version_origin(
952
- action: TransactionWriteOperation,
953
- version_id: &str,
954
- ) -> TransactionWriteOrigin {
955
- TransactionWriteOrigin {
956
- surface: "lix_version".to_string(),
957
- operation: action,
958
- primary_key: Some(LogicalPrimaryKey {
959
- columns: vec!["id".to_string()],
960
- values: vec![version_id.to_string()],
961
- }),
962
- }
963
- }
964
-
965
- fn update_string_value(
966
- batch: &RecordBatch,
967
- assignment_values: &UpdateAssignmentValues,
968
- table_schema: &SchemaRef,
969
- row_index: usize,
970
- column_name: &str,
971
- ) -> Result<String> {
972
- let column_index = table_schema.index_of(column_name)?;
973
- match assignment_values.assigned_or_existing_cell(batch, row_index, column_name)? {
974
- InsertCell::Omitted => required_string_value(batch, row_index, column_name, "UPDATE"),
975
- InsertCell::Provided(SqlCell::Value(
976
- ScalarValue::Utf8(Some(value))
977
- | ScalarValue::Utf8View(Some(value))
978
- | ScalarValue::LargeUtf8(Some(value)),
979
- )) => Ok(value),
980
- InsertCell::Provided(SqlCell::Null) => Err(DataFusionError::Execution(format!(
981
- "UPDATE lix_version requires non-null text column '{column_name}'"
982
- ))),
983
- InsertCell::Provided(SqlCell::Value(other)) => Err(DataFusionError::Execution(format!(
984
- "UPDATE lix_version expected text-compatible column '{column_name}', got {other:?}"
985
- ))),
986
- }
987
- .or_else(|error| {
988
- if batch.column(column_index).is_null(row_index) {
989
- Err(DataFusionError::Execution(format!(
990
- "UPDATE lix_version requires non-null text column '{column_name}'"
991
- )))
992
- } else {
993
- Err(error)
994
- }
995
- })
996
- }
997
-
998
- fn update_bool_value(
999
- batch: &RecordBatch,
1000
- assignment_values: &UpdateAssignmentValues,
1001
- table_schema: &SchemaRef,
1002
- row_index: usize,
1003
- column_name: &str,
1004
- ) -> Result<bool> {
1005
- let column_index = table_schema.index_of(column_name)?;
1006
- match assignment_values.assigned_or_existing_cell(batch, row_index, column_name)? {
1007
- InsertCell::Omitted => required_bool_value(batch, row_index, column_name, "UPDATE"),
1008
- InsertCell::Provided(SqlCell::Value(ScalarValue::Boolean(Some(value)))) => Ok(value),
1009
- InsertCell::Provided(SqlCell::Null) => Err(DataFusionError::Execution(format!(
1010
- "UPDATE lix_version requires non-null boolean column '{column_name}'"
1011
- ))),
1012
- InsertCell::Provided(SqlCell::Value(other)) => Err(DataFusionError::Execution(format!(
1013
- "UPDATE lix_version expected boolean column '{column_name}', got {other:?}"
1014
- ))),
1015
- }
1016
- .or_else(|error| {
1017
- if batch.column(column_index).is_null(row_index) {
1018
- Err(DataFusionError::Execution(format!(
1019
- "UPDATE lix_version requires non-null boolean column '{column_name}'"
1020
- )))
1021
- } else {
1022
- Err(error)
1023
- }
1024
- })
1025
- }
1026
-
1027
- fn required_string_value(
1028
- batch: &RecordBatch,
1029
- row_index: usize,
1030
- column_name: &str,
1031
- action: &str,
1032
- ) -> Result<String> {
1033
- optional_string_value(batch, row_index, column_name, action)?.ok_or_else(|| {
1034
- DataFusionError::Execution(format!(
1035
- "{action} lix_version requires non-null text column '{column_name}'"
1036
- ))
1037
- })
1038
- }
1039
-
1040
- fn optional_string_value(
1041
- batch: &RecordBatch,
1042
- row_index: usize,
1043
- column_name: &str,
1044
- action: &str,
1045
- ) -> Result<Option<String>> {
1046
- match optional_scalar_value(batch, row_index, column_name)? {
1047
- None
1048
- | Some(ScalarValue::Null)
1049
- | Some(ScalarValue::Utf8(None))
1050
- | Some(ScalarValue::Utf8View(None))
1051
- | Some(ScalarValue::LargeUtf8(None)) => Ok(None),
1052
- Some(ScalarValue::Utf8(Some(value)))
1053
- | Some(ScalarValue::Utf8View(Some(value)))
1054
- | Some(ScalarValue::LargeUtf8(Some(value))) => Ok(Some(value)),
1055
- Some(other) => Err(DataFusionError::Execution(format!(
1056
- "{action} lix_version expected text-compatible column '{column_name}', got {other:?}"
1057
- ))),
1058
- }
1059
- }
1060
-
1061
- fn required_bool_value(
1062
- batch: &RecordBatch,
1063
- row_index: usize,
1064
- column_name: &str,
1065
- action: &str,
1066
- ) -> Result<bool> {
1067
- optional_bool_value(batch, row_index, column_name, action)?.ok_or_else(|| {
1068
- DataFusionError::Execution(format!(
1069
- "{action} lix_version requires non-null boolean column '{column_name}'"
1070
- ))
1071
- })
1072
- }
1073
-
1074
- fn optional_bool_value(
1075
- batch: &RecordBatch,
1076
- row_index: usize,
1077
- column_name: &str,
1078
- action: &str,
1079
- ) -> Result<Option<bool>> {
1080
- match optional_scalar_value(batch, row_index, column_name)? {
1081
- None | Some(ScalarValue::Null) | Some(ScalarValue::Boolean(None)) => Ok(None),
1082
- Some(ScalarValue::Boolean(Some(value))) => Ok(Some(value)),
1083
- Some(other) => Err(DataFusionError::Execution(format!(
1084
- "{action} lix_version expected boolean column '{column_name}', got {other:?}"
1085
- ))),
1086
- }
1087
- }
1088
-
1089
- fn optional_scalar_value(
1090
- batch: &RecordBatch,
1091
- row_index: usize,
1092
- column_name: &str,
1093
- ) -> Result<Option<ScalarValue>> {
1094
- let Ok(column_index) = batch.schema().index_of(column_name) else {
1095
- return Ok(None);
1096
- };
1097
- Ok(Some(ScalarValue::try_from_array(
1098
- batch.column(column_index).as_ref(),
1099
- row_index,
1100
- )?))
1101
- }
1102
-
1103
- fn dml_count_schema() -> SchemaRef {
1104
- Arc::new(Schema::new(vec![Field::new(
1105
- "count",
1106
- DataType::UInt64,
1107
- false,
1108
- )]))
1109
- }
1110
-
1111
- fn dml_plan_properties(schema: SchemaRef) -> PlanProperties {
1112
- PlanProperties::new(
1113
- EquivalenceProperties::new(schema),
1114
- Partitioning::UnknownPartitioning(1),
1115
- EmissionType::Final,
1116
- Boundedness::Bounded,
1117
- )
1118
- }
1119
-
1120
- fn dml_count_batch(schema: SchemaRef, count: u64) -> Result<RecordBatch> {
1121
- RecordBatch::try_new(
1122
- schema,
1123
- vec![Arc::new(UInt64Array::from(vec![count])) as ArrayRef],
1124
- )
1125
- .map_err(DataFusionError::from)
1126
- }
1127
-
1128
- fn lix_version_schema() -> SchemaRef {
1129
- Arc::new(Schema::new(vec![
1130
- Field::new("id", DataType::Utf8, false),
1131
- Field::new("name", DataType::Utf8, false),
1132
- Field::new("hidden", DataType::Boolean, false),
1133
- Field::new("commit_id", DataType::Utf8, false),
1134
- ]))
1135
- }
1136
-
1137
- fn version_projection_for_scan(projection: Option<&Vec<usize>>) -> Vec<VersionColumn> {
1138
- let all_columns = vec![
1139
- VersionColumn::Id,
1140
- VersionColumn::Name,
1141
- VersionColumn::Hidden,
1142
- VersionColumn::CommitId,
1143
- ];
1144
- projection.map_or(all_columns.clone(), |indices| {
1145
- indices
1146
- .iter()
1147
- .filter_map(|index| all_columns.get(*index).copied())
1148
- .collect()
1149
- })
1150
- }
1151
-
1152
- fn projected_schema(schema: &SchemaRef, projection: Option<&Vec<usize>>) -> SchemaRef {
1153
- match projection {
1154
- Some(projection) => Arc::new(schema.project(projection).expect("projection is valid")),
1155
- None => Arc::clone(schema),
1156
- }
1157
- }
1158
-
1159
- fn version_record_batch(projection: &[VersionColumn], rows: &[VersionRow]) -> Result<RecordBatch> {
1160
- let arrays = projection
1161
- .iter()
1162
- .map(|column| match column {
1163
- VersionColumn::Id => string_array(rows.iter().map(|row| Some(row.id.as_str()))),
1164
- VersionColumn::Name => string_array(rows.iter().map(|row| Some(row.name.as_str()))),
1165
- VersionColumn::Hidden => Arc::new(BooleanArray::from(
1166
- rows.iter().map(|row| row.hidden).collect::<Vec<_>>(),
1167
- )) as ArrayRef,
1168
- VersionColumn::CommitId => {
1169
- string_array(rows.iter().map(|row| Some(row.commit_id.as_str())))
1170
- }
1171
- })
1172
- .collect::<Vec<_>>();
1173
- record_batch_with_row_count(version_schema(projection), arrays, rows.len()).map_err(|error| {
1174
- DataFusionError::Execution(format!("failed to build lix_version batch: {error}"))
1175
- })
1176
- }
1177
-
1178
- fn version_schema(projection: &[VersionColumn]) -> SchemaRef {
1179
- Arc::new(Schema::new(
1180
- projection
1181
- .iter()
1182
- .map(|column| match column {
1183
- VersionColumn::Id => Field::new("id", DataType::Utf8, false),
1184
- VersionColumn::Name => Field::new("name", DataType::Utf8, false),
1185
- VersionColumn::Hidden => Field::new("hidden", DataType::Boolean, false),
1186
- VersionColumn::CommitId => Field::new("commit_id", DataType::Utf8, false),
1187
- })
1188
- .collect::<Vec<_>>(),
1189
- ))
1190
- }
1191
-
1192
- fn string_array<'a>(values: impl Iterator<Item = Option<&'a str>>) -> ArrayRef {
1193
- Arc::new(StringArray::from(values.collect::<Vec<_>>())) as ArrayRef
1194
- }
1195
-
1196
- fn datafusion_error_to_lix_error(error: DataFusionError) -> LixError {
1197
- super::error::datafusion_error_to_lix_error(error)
1198
- }
1199
-
1200
- fn lix_error_to_datafusion_error(error: LixError) -> DataFusionError {
1201
- super::error::lix_error_to_datafusion_error(error)
1202
- }