@lix-js/sdk 0.6.0-preview.2 → 0.6.0-preview.3

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 (165) hide show
  1. package/SKILL.md +4 -5
  2. package/dist/engine-wasm/wasm/lix_engine.js +1 -1
  3. package/dist/engine-wasm/wasm/lix_engine.wasm +0 -0
  4. package/dist/generated/builtin-schemas.d.ts +87 -162
  5. package/dist/generated/builtin-schemas.js +139 -236
  6. package/dist/open-lix.d.ts +1 -1
  7. package/dist-engine-src/src/binary_cas/types.rs +0 -6
  8. package/dist-engine-src/src/catalog/context.rs +412 -0
  9. package/dist-engine-src/src/catalog/mod.rs +10 -0
  10. package/dist-engine-src/src/catalog/schema.rs +4 -0
  11. package/dist-engine-src/src/catalog/snapshot.rs +1114 -0
  12. package/dist-engine-src/src/cel/mod.rs +1 -1
  13. package/dist-engine-src/src/cel/provider.rs +1 -1
  14. package/dist-engine-src/src/commit_graph/context.rs +328 -1015
  15. package/dist-engine-src/src/commit_graph/mod.rs +2 -3
  16. package/dist-engine-src/src/commit_graph/types.rs +7 -43
  17. package/dist-engine-src/src/commit_graph/walker.rs +57 -81
  18. package/dist-engine-src/src/commit_store/codec.rs +887 -0
  19. package/dist-engine-src/src/commit_store/context.rs +944 -0
  20. package/dist-engine-src/src/commit_store/materialization.rs +84 -0
  21. package/dist-engine-src/src/commit_store/mod.rs +16 -0
  22. package/dist-engine-src/src/commit_store/storage.rs +600 -0
  23. package/dist-engine-src/src/commit_store/types.rs +215 -0
  24. package/dist-engine-src/src/common/identity.rs +15 -5
  25. package/dist-engine-src/src/common/json_pointer.rs +67 -0
  26. package/dist-engine-src/src/common/metadata.rs +17 -12
  27. package/dist-engine-src/src/common/mod.rs +5 -5
  28. package/dist-engine-src/src/domain.rs +324 -0
  29. package/dist-engine-src/src/engine.rs +29 -43
  30. package/dist-engine-src/src/entity_identity.rs +238 -118
  31. package/dist-engine-src/src/functions/context.rs +17 -52
  32. package/dist-engine-src/src/functions/deterministic.rs +1 -1
  33. package/dist-engine-src/src/functions/mod.rs +1 -1
  34. package/dist-engine-src/src/functions/provider.rs +4 -4
  35. package/dist-engine-src/src/functions/state.rs +39 -66
  36. package/dist-engine-src/src/functions/types.rs +1 -1
  37. package/dist-engine-src/src/init.rs +204 -151
  38. package/dist-engine-src/src/json_store/context.rs +354 -60
  39. package/dist-engine-src/src/json_store/encoded.rs +6 -6
  40. package/dist-engine-src/src/json_store/mod.rs +4 -1
  41. package/dist-engine-src/src/json_store/store.rs +884 -11
  42. package/dist-engine-src/src/json_store/types.rs +166 -1
  43. package/dist-engine-src/src/lib.rs +10 -9
  44. package/dist-engine-src/src/live_state/context.rs +608 -830
  45. package/dist-engine-src/src/live_state/mod.rs +3 -3
  46. package/dist-engine-src/src/live_state/overlay.rs +7 -7
  47. package/dist-engine-src/src/live_state/reader.rs +5 -5
  48. package/dist-engine-src/src/live_state/types.rs +19 -36
  49. package/dist-engine-src/src/live_state/visibility.rs +19 -14
  50. package/dist-engine-src/src/plugin/archive.rs +3 -6
  51. package/dist-engine-src/src/plugin/install.rs +0 -18
  52. package/dist-engine-src/src/plugin/plugin_manifest.json +0 -1
  53. package/dist-engine-src/src/schema/annotations/defaults.rs +2 -7
  54. package/dist-engine-src/src/schema/builtin/lix_account.json +0 -1
  55. package/dist-engine-src/src/schema/builtin/lix_active_account.json +0 -1
  56. package/dist-engine-src/src/schema/builtin/lix_binary_blob_ref.json +0 -1
  57. package/dist-engine-src/src/schema/builtin/lix_change.json +11 -10
  58. package/dist-engine-src/src/schema/builtin/lix_change_author.json +0 -1
  59. package/dist-engine-src/src/schema/builtin/lix_commit.json +8 -46
  60. package/dist-engine-src/src/schema/builtin/lix_commit_edge.json +29 -22
  61. package/dist-engine-src/src/schema/builtin/lix_directory_descriptor.json +0 -1
  62. package/dist-engine-src/src/schema/builtin/lix_file_descriptor.json +0 -1
  63. package/dist-engine-src/src/schema/builtin/lix_key_value.json +0 -1
  64. package/dist-engine-src/src/schema/builtin/lix_label.json +10 -3
  65. package/dist-engine-src/src/schema/builtin/lix_label_assignment.json +74 -0
  66. package/dist-engine-src/src/schema/builtin/lix_registered_schema.json +2 -8
  67. package/dist-engine-src/src/schema/builtin/lix_version_descriptor.json +0 -1
  68. package/dist-engine-src/src/schema/builtin/lix_version_ref.json +0 -1
  69. package/dist-engine-src/src/schema/builtin/mod.rs +10 -59
  70. package/dist-engine-src/src/schema/compatibility.rs +787 -0
  71. package/dist-engine-src/src/schema/definition.json +47 -17
  72. package/dist-engine-src/src/schema/definition.rs +202 -96
  73. package/dist-engine-src/src/schema/key.rs +9 -77
  74. package/dist-engine-src/src/schema/mod.rs +4 -4
  75. package/dist-engine-src/src/schema/tests.rs +133 -92
  76. package/dist-engine-src/src/session/context.rs +40 -42
  77. package/dist-engine-src/src/session/create_version.rs +22 -14
  78. package/dist-engine-src/src/session/execute.rs +45 -14
  79. package/dist-engine-src/src/session/merge/apply.rs +4 -4
  80. package/dist-engine-src/src/session/merge/conflicts.rs +3 -2
  81. package/dist-engine-src/src/session/merge/stats.rs +1 -1
  82. package/dist-engine-src/src/session/merge/version.rs +35 -45
  83. package/dist-engine-src/src/session/mod.rs +4 -2
  84. package/dist-engine-src/src/session/optimization9_sql2_bench.rs +100 -0
  85. package/dist-engine-src/src/session/switch_version.rs +16 -28
  86. package/dist-engine-src/src/sql2/change_provider.rs +14 -20
  87. package/dist-engine-src/src/sql2/classify.rs +61 -26
  88. package/dist-engine-src/src/sql2/context.rs +22 -18
  89. package/dist-engine-src/src/sql2/directory_history_provider.rs +28 -20
  90. package/dist-engine-src/src/sql2/directory_provider.rs +131 -83
  91. package/dist-engine-src/src/sql2/entity_history_provider.rs +10 -14
  92. package/dist-engine-src/src/sql2/entity_provider.rs +680 -169
  93. package/dist-engine-src/src/sql2/error.rs +21 -1
  94. package/dist-engine-src/src/sql2/execute.rs +325 -264
  95. package/dist-engine-src/src/sql2/file_history_provider.rs +29 -21
  96. package/dist-engine-src/src/sql2/file_provider.rs +533 -108
  97. package/dist-engine-src/src/sql2/filesystem_planner.rs +58 -94
  98. package/dist-engine-src/src/sql2/filesystem_visibility.rs +37 -23
  99. package/dist-engine-src/src/sql2/history_projection.rs +3 -27
  100. package/dist-engine-src/src/sql2/history_provider.rs +11 -17
  101. package/dist-engine-src/src/sql2/history_route.rs +22 -8
  102. package/dist-engine-src/src/sql2/lix_state_provider.rs +178 -96
  103. package/dist-engine-src/src/sql2/mod.rs +6 -3
  104. package/dist-engine-src/src/sql2/predicate_typecheck.rs +246 -0
  105. package/dist-engine-src/src/sql2/public_bind/assignment.rs +46 -0
  106. package/dist-engine-src/src/sql2/public_bind/capability.rs +41 -0
  107. package/dist-engine-src/src/sql2/public_bind/dml.rs +166 -0
  108. package/dist-engine-src/src/sql2/public_bind/mod.rs +25 -0
  109. package/dist-engine-src/src/sql2/public_bind/table.rs +168 -0
  110. package/dist-engine-src/src/sql2/read_only.rs +10 -12
  111. package/dist-engine-src/src/sql2/session.rs +7 -10
  112. package/dist-engine-src/src/sql2/udfs/lix_timestamp.rs +76 -0
  113. package/dist-engine-src/src/sql2/udfs/mod.rs +8 -1
  114. package/dist-engine-src/src/sql2/udfs/public_call.rs +211 -0
  115. package/dist-engine-src/src/sql2/version_provider.rs +46 -31
  116. package/dist-engine-src/src/sql2/version_scope.rs +4 -4
  117. package/dist-engine-src/src/storage_bench.rs +1782 -325
  118. package/dist-engine-src/src/test_support.rs +183 -36
  119. package/dist-engine-src/src/tracked_state/by_file_index.rs +20 -24
  120. package/dist-engine-src/src/tracked_state/codec.rs +1519 -181
  121. package/dist-engine-src/src/tracked_state/context.rs +1155 -271
  122. package/dist-engine-src/src/tracked_state/diff.rs +249 -57
  123. package/dist-engine-src/src/tracked_state/materialization.rs +365 -103
  124. package/dist-engine-src/src/tracked_state/materializer.rs +488 -0
  125. package/dist-engine-src/src/tracked_state/merge.rs +37 -19
  126. package/dist-engine-src/src/tracked_state/mod.rs +8 -7
  127. package/dist-engine-src/src/tracked_state/storage.rs +138 -6
  128. package/dist-engine-src/src/tracked_state/tree.rs +695 -252
  129. package/dist-engine-src/src/tracked_state/types.rs +176 -6
  130. package/dist-engine-src/src/transaction/commit.rs +695 -435
  131. package/dist-engine-src/src/transaction/context.rs +551 -310
  132. package/dist-engine-src/src/transaction/live_state_overlay.rs +9 -8
  133. package/dist-engine-src/src/transaction/mod.rs +2 -0
  134. package/dist-engine-src/src/transaction/normalization.rs +311 -447
  135. package/dist-engine-src/src/transaction/prep.rs +37 -0
  136. package/dist-engine-src/src/transaction/schema_resolver.rs +93 -71
  137. package/dist-engine-src/src/transaction/staging.rs +701 -406
  138. package/dist-engine-src/src/transaction/types.rs +231 -122
  139. package/dist-engine-src/src/transaction/validation.rs +2717 -1698
  140. package/dist-engine-src/src/untracked_state/codec.rs +40 -96
  141. package/dist-engine-src/src/untracked_state/context.rs +21 -5
  142. package/dist-engine-src/src/untracked_state/materialization.rs +10 -104
  143. package/dist-engine-src/src/untracked_state/mod.rs +3 -5
  144. package/dist-engine-src/src/untracked_state/storage.rs +105 -57
  145. package/dist-engine-src/src/untracked_state/types.rs +63 -13
  146. package/dist-engine-src/src/version/context.rs +1 -13
  147. package/dist-engine-src/src/version/lifecycle.rs +221 -0
  148. package/dist-engine-src/src/version/mod.rs +3 -2
  149. package/dist-engine-src/src/version/refs.rs +12 -103
  150. package/dist-engine-src/src/version/stage_rows.rs +15 -19
  151. package/package.json +1 -1
  152. package/dist-engine-src/src/changelog/codec.rs +0 -321
  153. package/dist-engine-src/src/changelog/context.rs +0 -92
  154. package/dist-engine-src/src/changelog/materialization.rs +0 -121
  155. package/dist-engine-src/src/changelog/mod.rs +0 -13
  156. package/dist-engine-src/src/changelog/reader.rs +0 -20
  157. package/dist-engine-src/src/changelog/storage.rs +0 -220
  158. package/dist-engine-src/src/changelog/types.rs +0 -38
  159. package/dist-engine-src/src/schema/builtin/lix_change_set.json +0 -18
  160. package/dist-engine-src/src/schema/builtin/lix_change_set_element.json +0 -75
  161. package/dist-engine-src/src/schema/builtin/lix_entity_label.json +0 -63
  162. package/dist-engine-src/src/schema_registry.rs +0 -294
  163. package/dist-engine-src/src/sql2/commit_derived_provider.rs +0 -591
  164. package/dist-engine-src/src/tracked_state/rebuild.rs +0 -771
  165. package/dist-engine-src/src/tracked_state/tree_types.rs +0 -176
@@ -26,7 +26,7 @@ use futures_util::{stream, TryStreamExt};
26
26
  use serde::Deserialize;
27
27
 
28
28
  use crate::functions::FunctionProviderHandle;
29
- use crate::live_state::LiveStateRow;
29
+ use crate::live_state::MaterializedLiveStateRow;
30
30
  use crate::live_state::{
31
31
  LiveStateFilter, LiveStateProjection, LiveStateReader, LiveStateScanRequest,
32
32
  };
@@ -34,14 +34,18 @@ use crate::sql2::dml::{InsertExec, InsertSink};
34
34
  use crate::sql2::filesystem_predicates::{
35
35
  canonicalize_filesystem_path_filters, FilesystemPathKind,
36
36
  };
37
+ use crate::sql2::predicate_typecheck::validate_json_predicate_filters;
37
38
  use crate::sql2::version_scope::{
38
39
  explicit_version_ids_from_dml_filters, resolve_provider_version_ids,
39
40
  resolve_write_version_scope, VersionBinding,
40
41
  };
41
42
  use crate::sql2::write_normalization::{InsertCell, SqlCell, UpdateAssignmentValues};
42
- use crate::transaction::types::{LogicalPrimaryKey, StageRow, StageRowOrigin, StageWriteOperation};
43
+ use crate::transaction::types::{
44
+ LogicalPrimaryKey, TransactionJson, TransactionWriteOperation, TransactionWriteOrigin,
45
+ TransactionWriteRow,
46
+ };
43
47
  use crate::version::VersionRefReader;
44
- use crate::{parse_row_metadata, serialize_row_metadata, LixError, RowMetadata};
48
+ use crate::{parse_row_metadata_value, serialize_row_metadata, LixError};
45
49
 
46
50
  use super::filesystem_planner::{
47
51
  directory_descriptor_write_row, directory_path_resolvers_from_state_rows,
@@ -53,7 +57,7 @@ use super::result_metadata::json_field;
53
57
  use crate::sql2::{
54
58
  SqlWriteContext, WriteAccess, WriteContextLiveStateReader, WriteContextVersionRefReader,
55
59
  };
56
- use crate::transaction::types::{StageWrite, StageWriteMode};
60
+ use crate::transaction::types::{TransactionWrite, TransactionWriteMode};
57
61
 
58
62
  const DIRECTORY_SCHEMA_KEY: &str = "lix_directory_descriptor";
59
63
  const FILE_DESCRIPTOR_SCHEMA_KEY: &str = "lix_file_descriptor";
@@ -220,8 +224,11 @@ impl TableProvider for LixDirectoryProvider {
220
224
  ) -> Result<Arc<dyn ExecutionPlan>> {
221
225
  let projected_schema = projected_schema(&self.schema, projection)?;
222
226
  let scan_limit = if filters.is_empty() { limit } else { None };
223
- let mut request =
224
- lix_directory_scan_request(self.version_binding.active_version_id(), scan_limit);
227
+ let mut request = lix_directory_scan_request(
228
+ self.version_binding.active_version_id(),
229
+ Some(projected_schema.as_ref()),
230
+ scan_limit,
231
+ );
225
232
  if self.write_access.is_write() && matches!(self.version_binding, VersionBinding::Explicit)
226
233
  {
227
234
  request.filter.version_ids = explicit_version_ids_from_dml_filters(filters);
@@ -241,6 +248,7 @@ impl TableProvider for LixDirectoryProvider {
241
248
  .map_err(lix_error_to_datafusion_error)?;
242
249
  let filters = canonicalize_filesystem_path_filters(filters, FilesystemPathKind::Directory)?;
243
250
  let df_schema = DFSchema::try_from(Arc::clone(&self.schema))?;
251
+ validate_json_predicate_filters(self.schema.as_ref(), &filters)?;
244
252
  let physical_filters = filters
245
253
  .iter()
246
254
  .map(|expr| create_physical_expr(expr, &df_schema, _state.execution_props()))
@@ -291,12 +299,13 @@ impl TableProvider for LixDirectoryProvider {
291
299
  let df_schema = DFSchema::try_from(Arc::clone(&self.schema))?;
292
300
  let filters =
293
301
  canonicalize_filesystem_path_filters(&filters, FilesystemPathKind::Directory)?;
302
+ validate_json_predicate_filters(self.schema.as_ref(), &filters)?;
294
303
  let physical_filters = filters
295
304
  .iter()
296
305
  .map(|expr| create_physical_expr(expr, &df_schema, state.execution_props()))
297
306
  .collect::<Result<Vec<_>>>()?;
298
307
  let mut request =
299
- lix_directory_scan_request(self.version_binding.active_version_id(), None);
308
+ lix_directory_scan_request(self.version_binding.active_version_id(), None, None);
300
309
  if matches!(self.version_binding, VersionBinding::Explicit) {
301
310
  request.filter.version_ids = explicit_version_ids_from_dml_filters(&filters);
302
311
  if request.filter.version_ids.is_empty() {
@@ -338,11 +347,13 @@ impl TableProvider for LixDirectoryProvider {
338
347
  .collect::<Result<Vec<_>>>()?;
339
348
  let filters =
340
349
  canonicalize_filesystem_path_filters(&filters, FilesystemPathKind::Directory)?;
350
+ validate_json_predicate_filters(self.schema.as_ref(), &filters)?;
341
351
  let physical_filters = filters
342
352
  .iter()
343
353
  .map(|expr| create_physical_expr(expr, &df_schema, state.execution_props()))
344
354
  .collect::<Result<Vec<_>>>()?;
345
- let request = lix_directory_scan_request(self.version_binding.active_version_id(), None);
355
+ let request =
356
+ lix_directory_scan_request(self.version_binding.active_version_id(), None, None);
346
357
 
347
358
  Ok(Arc::new(LixDirectoryUpdateExec::new(
348
359
  write_ctx.clone(),
@@ -449,8 +460,8 @@ impl InsertSink for LixDirectoryInsertSink {
449
460
  }
450
461
 
451
462
  self.write_ctx
452
- .stage_write(StageWrite::Rows {
453
- mode: StageWriteMode::Insert,
463
+ .stage_write(TransactionWrite::Rows {
464
+ mode: TransactionWriteMode::Insert,
454
465
  rows,
455
466
  })
456
467
  .await
@@ -601,8 +612,8 @@ impl ExecutionPlan for LixDirectoryDeleteExec {
601
612
 
602
613
  if count > 0 {
603
614
  write_ctx
604
- .stage_write(StageWrite::Rows {
605
- mode: StageWriteMode::Replace,
615
+ .stage_write(TransactionWrite::Rows {
616
+ mode: TransactionWriteMode::Replace,
606
617
  rows: write_rows,
607
618
  })
608
619
  .await
@@ -759,8 +770,8 @@ impl ExecutionPlan for LixDirectoryUpdateExec {
759
770
 
760
771
  if count > 0 {
761
772
  write_ctx
762
- .stage_write(StageWrite::Rows {
763
- mode: StageWriteMode::Replace,
773
+ .stage_write(TransactionWrite::Rows {
774
+ mode: TransactionWriteMode::Replace,
764
775
  rows: write_rows,
765
776
  })
766
777
  .await
@@ -917,7 +928,7 @@ struct DirectoryDescriptorRecord {
917
928
  parent_id: Option<String>,
918
929
  name: String,
919
930
  hidden: bool,
920
- live: LiveStateRow,
931
+ live: MaterializedLiveStateRow,
921
932
  }
922
933
 
923
934
  #[derive(Debug, Deserialize)]
@@ -932,7 +943,7 @@ struct DirectoryDescriptorSnapshot {
932
943
  fn lix_directory_write_rows_from_batch(
933
944
  batch: &RecordBatch,
934
945
  version_binding: Option<&str>,
935
- ) -> Result<Vec<StageRow>> {
946
+ ) -> Result<Vec<TransactionWriteRow>> {
936
947
  lix_directory_write_rows_from_batch_with_options(batch, version_binding, "lix_directory", true)
937
948
  }
938
949
 
@@ -942,7 +953,7 @@ fn lix_directory_write_rows_from_batch_with_path_resolvers(
942
953
  surface_name: &str,
943
954
  path_resolvers: &mut BTreeMap<String, DirectoryPathResolver>,
944
955
  generate_directory_id: &mut dyn FnMut() -> String,
945
- ) -> Result<Vec<StageRow>> {
956
+ ) -> Result<Vec<TransactionWriteRow>> {
946
957
  lix_directory_write_rows_from_batch_with_options_and_path_resolvers(
947
958
  batch,
948
959
  version_binding,
@@ -958,7 +969,7 @@ fn lix_directory_update_write_rows_from_batch(
958
969
  assignments: &[(String, Arc<dyn PhysicalExpr>)],
959
970
  version_binding: Option<&str>,
960
971
  path_resolvers: &mut BTreeMap<String, DirectoryPathResolver>,
961
- ) -> Result<Vec<StageRow>> {
972
+ ) -> Result<Vec<TransactionWriteRow>> {
962
973
  let assignment_values = UpdateAssignmentValues::evaluate(batch, assignments)?;
963
974
  let mut rows = Vec::new();
964
975
  for row_index in 0..batch.num_rows() {
@@ -1010,7 +1021,7 @@ fn lix_directory_recursive_delete_rows_from_batch(
1010
1021
  batch: &RecordBatch,
1011
1022
  version_binding: Option<&str>,
1012
1023
  visible_filesystems: &BTreeMap<String, VisibleFilesystem>,
1013
- ) -> Result<(Vec<StageRow>, u64)> {
1024
+ ) -> Result<(Vec<TransactionWriteRow>, u64)> {
1014
1025
  let mut rows = Vec::new();
1015
1026
  let mut seen = BTreeSet::new();
1016
1027
  let mut count = 0u64;
@@ -1036,7 +1047,7 @@ fn lix_directory_recursive_delete_rows_from_batch(
1036
1047
  }
1037
1048
 
1038
1049
  fn append_deduped_delete_plan(
1039
- rows: &mut Vec<StageRow>,
1050
+ rows: &mut Vec<TransactionWriteRow>,
1040
1051
  seen: &mut BTreeSet<StateRowDedupeKey>,
1041
1052
  plan: FilesystemDeletePlan,
1042
1053
  count: &mut u64,
@@ -1051,7 +1062,7 @@ fn append_deduped_delete_plan(
1051
1062
  }
1052
1063
  }
1053
1064
 
1054
- fn is_user_visible_filesystem_delete_row(row: &StageRow) -> bool {
1065
+ fn is_user_visible_filesystem_delete_row(row: &TransactionWriteRow) -> bool {
1055
1066
  matches!(
1056
1067
  row.schema_key.as_str(),
1057
1068
  "lix_directory_descriptor" | "lix_file_descriptor"
@@ -1068,14 +1079,14 @@ struct StateRowDedupeKey {
1068
1079
  untracked: bool,
1069
1080
  }
1070
1081
 
1071
- impl From<&StageRow> for StateRowDedupeKey {
1072
- fn from(row: &StageRow) -> Self {
1082
+ impl From<&TransactionWriteRow> for StateRowDedupeKey {
1083
+ fn from(row: &TransactionWriteRow) -> Self {
1073
1084
  Self {
1074
1085
  entity_id: row
1075
1086
  .entity_id
1076
1087
  .as_ref()
1077
1088
  .expect("directory provider staged row should carry entity_id")
1078
- .as_string()
1089
+ .as_single_string_owned()
1079
1090
  .expect("directory provider staged row entity identity should project"),
1080
1091
  schema_key: row.schema_key.clone(),
1081
1092
  file_id: row.file_id.clone(),
@@ -1092,7 +1103,7 @@ fn lix_directory_write_rows_from_batch_with_options(
1092
1103
  version_binding: Option<&str>,
1093
1104
  surface_name: &str,
1094
1105
  reject_read_only_fields: bool,
1095
- ) -> Result<Vec<StageRow>> {
1106
+ ) -> Result<Vec<TransactionWriteRow>> {
1096
1107
  lix_directory_write_rows_from_batch_with_options_and_path_resolvers(
1097
1108
  batch,
1098
1109
  version_binding,
@@ -1110,7 +1121,7 @@ fn lix_directory_write_rows_from_batch_with_options_and_path_resolvers(
1110
1121
  reject_read_only_fields: bool,
1111
1122
  mut path_resolvers: Option<&mut BTreeMap<String, DirectoryPathResolver>>,
1112
1123
  mut generate_directory_id: Option<&mut dyn FnMut() -> String>,
1113
- ) -> Result<Vec<StageRow>> {
1124
+ ) -> Result<Vec<TransactionWriteRow>> {
1114
1125
  let mut rows = Vec::new();
1115
1126
  for row_index in 0..batch.num_rows() {
1116
1127
  if reject_read_only_fields {
@@ -1189,7 +1200,7 @@ fn lix_directory_write_rows_from_batch_with_options_and_path_resolvers(
1189
1200
  }
1190
1201
 
1191
1202
  fn attach_lix_directory_insert_origin(
1192
- rows: &mut [StageRow],
1203
+ rows: &mut [TransactionWriteRow],
1193
1204
  surface_name: &str,
1194
1205
  directory_id: &str,
1195
1206
  ) {
@@ -1201,7 +1212,7 @@ fn attach_lix_directory_insert_origin(
1201
1212
  let Some(entity_id) = row
1202
1213
  .entity_id
1203
1214
  .as_ref()
1204
- .and_then(|entity_id| entity_id.as_string().ok())
1215
+ .and_then(|entity_id| entity_id.as_single_string_owned().ok())
1205
1216
  else {
1206
1217
  continue;
1207
1218
  };
@@ -1211,10 +1222,10 @@ fn attach_lix_directory_insert_origin(
1211
1222
  }
1212
1223
  }
1213
1224
 
1214
- fn lix_directory_insert_origin(surface_name: &str, directory_id: &str) -> StageRowOrigin {
1215
- StageRowOrigin {
1225
+ fn lix_directory_insert_origin(surface_name: &str, directory_id: &str) -> TransactionWriteOrigin {
1226
+ TransactionWriteOrigin {
1216
1227
  surface: surface_name.to_string(),
1217
- operation: StageWriteOperation::Insert,
1228
+ operation: TransactionWriteOperation::Insert,
1218
1229
  primary_key: Some(LogicalPrimaryKey {
1219
1230
  columns: vec!["id".to_string()],
1220
1231
  values: vec![directory_id.to_string()],
@@ -1313,7 +1324,7 @@ async fn directory_path_resolvers_from_live_state(
1313
1324
 
1314
1325
  fn lix_directory_record_batch(
1315
1326
  schema: &SchemaRef,
1316
- rows: Vec<LiveStateRow>,
1327
+ rows: Vec<MaterializedLiveStateRow>,
1317
1328
  ) -> Result<RecordBatch, LixError> {
1318
1329
  let mut directory_rows = Vec::<DirectoryDescriptorRecord>::new();
1319
1330
 
@@ -1349,7 +1360,6 @@ fn lix_directory_record_batch(
1349
1360
  let mut entity_ids = Vec::new();
1350
1361
  let mut schema_keys = Vec::new();
1351
1362
  let mut file_ids = Vec::new();
1352
- let mut schema_versions = Vec::new();
1353
1363
  let mut globals = Vec::new();
1354
1364
  let mut change_ids = Vec::new();
1355
1365
  let mut created_ats = Vec::new();
@@ -1369,10 +1379,9 @@ fn lix_directory_record_batch(
1369
1379
  parent_ids.push(directory.parent_id);
1370
1380
  names.push(Some(directory.name));
1371
1381
  hiddens.push(Some(directory.hidden));
1372
- entity_ids.push(Some(directory.live.entity_id.as_string()?));
1382
+ entity_ids.push(Some(directory.live.entity_id.as_json_array_text()?));
1373
1383
  schema_keys.push(Some(directory.live.schema_key));
1374
1384
  file_ids.push(directory.live.file_id);
1375
- schema_versions.push(directory.live.schema_version);
1376
1385
  globals.push(Some(directory.live.global));
1377
1386
  change_ids.push(directory.live.change_id);
1378
1387
  created_ats.push(directory.live.created_at);
@@ -1394,7 +1403,6 @@ fn lix_directory_record_batch(
1394
1403
  "lixcol_entity_id" => Arc::new(StringArray::from(entity_ids.clone())),
1395
1404
  "lixcol_schema_key" => Arc::new(StringArray::from(schema_keys.clone())),
1396
1405
  "lixcol_file_id" => Arc::new(StringArray::from(file_ids.clone())),
1397
- "lixcol_schema_version" => Arc::new(StringArray::from(schema_versions.clone())),
1398
1406
  "lixcol_global" => Arc::new(BooleanArray::from(globals.clone())),
1399
1407
  "lixcol_change_id" => Arc::new(StringArray::from(change_ids.clone())),
1400
1408
  "lixcol_created_at" => Arc::new(StringArray::from(created_ats.clone())),
@@ -1513,6 +1521,7 @@ fn projected_schema(base_schema: &SchemaRef, projection: Option<&Vec<usize>>) ->
1513
1521
 
1514
1522
  fn lix_directory_scan_request(
1515
1523
  version_binding: Option<&str>,
1524
+ projected_schema: Option<&Schema>,
1516
1525
  limit: Option<usize>,
1517
1526
  ) -> LiveStateScanRequest {
1518
1527
  LiveStateScanRequest {
@@ -1523,11 +1532,33 @@ fn lix_directory_scan_request(
1523
1532
  .unwrap_or_default(),
1524
1533
  ..LiveStateFilter::default()
1525
1534
  },
1526
- projection: LiveStateProjection::default(),
1535
+ projection: lix_directory_live_state_projection(projected_schema),
1527
1536
  limit,
1528
1537
  }
1529
1538
  }
1530
1539
 
1540
+ fn lix_directory_live_state_projection(projected_schema: Option<&Schema>) -> LiveStateProjection {
1541
+ let Some(schema) = projected_schema else {
1542
+ return LiveStateProjection::default();
1543
+ };
1544
+ let mut columns = Vec::new();
1545
+ let needs_snapshot = schema
1546
+ .fields()
1547
+ .iter()
1548
+ .any(|field| matches!(field.name().as_str(), "parent_id" | "name" | "hidden"));
1549
+ if needs_snapshot {
1550
+ columns.push("snapshot_content".to_string());
1551
+ }
1552
+ if schema
1553
+ .fields()
1554
+ .iter()
1555
+ .any(|field| field.name() == "lixcol_metadata")
1556
+ {
1557
+ columns.push("metadata".to_string());
1558
+ }
1559
+ LiveStateProjection { columns }
1560
+ }
1561
+
1531
1562
  fn validate_lix_directory_update_assignments(
1532
1563
  schema: &SchemaRef,
1533
1564
  assignments: &[(String, Expr)],
@@ -1682,10 +1713,13 @@ fn update_optional_metadata_value(
1682
1713
  row_index: usize,
1683
1714
  column_name: &str,
1684
1715
  context: &str,
1685
- ) -> Result<Option<RowMetadata>> {
1716
+ ) -> Result<Option<TransactionJson>> {
1686
1717
  update_optional_string_value(batch, assignment_values, row_index, column_name)?
1687
1718
  .map(|value| {
1688
- parse_row_metadata(&value, context).map_err(super::error::lix_error_to_datafusion_error)
1719
+ let metadata = parse_row_metadata_value(&value, context)
1720
+ .map_err(super::error::lix_error_to_datafusion_error)?;
1721
+ TransactionJson::from_value(metadata, &format!("{context} metadata"))
1722
+ .map_err(super::error::lix_error_to_datafusion_error)
1689
1723
  })
1690
1724
  .transpose()
1691
1725
  }
@@ -1730,10 +1764,13 @@ fn optional_metadata_value(
1730
1764
  row_index: usize,
1731
1765
  column_name: &str,
1732
1766
  context: &str,
1733
- ) -> Result<Option<RowMetadata>> {
1767
+ ) -> Result<Option<TransactionJson>> {
1734
1768
  optional_string_value(batch, row_index, column_name)?
1735
1769
  .map(|value| {
1736
- parse_row_metadata(&value, context).map_err(super::error::lix_error_to_datafusion_error)
1770
+ let metadata = parse_row_metadata_value(&value, context)
1771
+ .map_err(super::error::lix_error_to_datafusion_error)?;
1772
+ TransactionJson::from_value(metadata, &format!("{context} metadata"))
1773
+ .map_err(super::error::lix_error_to_datafusion_error)
1737
1774
  })
1738
1775
  .transpose()
1739
1776
  }
@@ -1784,10 +1821,9 @@ fn lix_directory_schema() -> SchemaRef {
1784
1821
  Field::new("parent_id", DataType::Utf8, true),
1785
1822
  Field::new("name", DataType::Utf8, false),
1786
1823
  Field::new("hidden", DataType::Boolean, true),
1787
- Field::new("lixcol_entity_id", DataType::Utf8, false),
1824
+ json_field("lixcol_entity_id", false),
1788
1825
  Field::new("lixcol_schema_key", DataType::Utf8, false),
1789
1826
  Field::new("lixcol_file_id", DataType::Utf8, true),
1790
- Field::new("lixcol_schema_version", DataType::Utf8, false),
1791
1827
  Field::new("lixcol_global", DataType::Boolean, true),
1792
1828
  Field::new("lixcol_change_id", DataType::Utf8, true),
1793
1829
  Field::new("lixcol_created_at", DataType::Utf8, true),
@@ -1833,11 +1869,14 @@ mod tests {
1833
1869
  FunctionProvider, FunctionProviderHandle, SharedFunctionProvider, SystemFunctionProvider,
1834
1870
  };
1835
1871
  use crate::live_state::{
1836
- LiveStateReader, LiveStateRow, LiveStateRowRequest, LiveStateScanRequest,
1872
+ LiveStateReader, LiveStateRowRequest, LiveStateScanRequest, MaterializedLiveStateRow,
1837
1873
  };
1838
1874
  use crate::sql2::dml::InsertSink;
1839
1875
  use crate::sql2::{SqlWriteContext, SqlWriteExecutionContext};
1840
- use crate::transaction::types::{StageRow, StageWrite, StageWriteMode, StageWriteOutcome};
1876
+ use crate::transaction::types::{
1877
+ TransactionJson, TransactionWrite, TransactionWriteMode, TransactionWriteOutcome,
1878
+ TransactionWriteRow,
1879
+ };
1841
1880
  use crate::LixError;
1842
1881
 
1843
1882
  use super::{
@@ -1862,8 +1901,8 @@ mod tests {
1862
1901
 
1863
1902
  #[derive(Default)]
1864
1903
  struct CapturingWriteContext {
1865
- rows: Vec<LiveStateRow>,
1866
- writes: Vec<StageWrite>,
1904
+ rows: Vec<MaterializedLiveStateRow>,
1905
+ writes: Vec<TransactionWrite>,
1867
1906
  }
1868
1907
 
1869
1908
  #[async_trait]
@@ -1872,7 +1911,10 @@ mod tests {
1872
1911
  &self,
1873
1912
  hashes: &[crate::binary_cas::BlobHash],
1874
1913
  ) -> Result<crate::binary_cas::BlobBytesBatch, LixError> {
1875
- Ok(crate::binary_cas::BlobBytesBatch::missing(hashes.len()))
1914
+ Ok(crate::binary_cas::BlobBytesBatch::new(vec![
1915
+ None;
1916
+ hashes.len()
1917
+ ]))
1876
1918
  }
1877
1919
  }
1878
1920
 
@@ -1900,7 +1942,7 @@ mod tests {
1900
1942
  async fn scan_live_state(
1901
1943
  &mut self,
1902
1944
  _request: &LiveStateScanRequest,
1903
- ) -> Result<Vec<LiveStateRow>, LixError> {
1945
+ ) -> Result<Vec<MaterializedLiveStateRow>, LixError> {
1904
1946
  Ok(self.rows.clone())
1905
1947
  }
1906
1948
 
@@ -1914,16 +1956,19 @@ mod tests {
1914
1956
  Ok(Some(format!("commit-{version_id}")))
1915
1957
  }
1916
1958
 
1917
- async fn stage_write(&mut self, write: StageWrite) -> Result<StageWriteOutcome, LixError> {
1959
+ async fn stage_write(
1960
+ &mut self,
1961
+ write: TransactionWrite,
1962
+ ) -> Result<TransactionWriteOutcome, LixError> {
1918
1963
  self.writes.push(write);
1919
- Ok(StageWriteOutcome { count: 0 })
1964
+ Ok(TransactionWriteOutcome { count: 0 })
1920
1965
  }
1921
1966
  }
1922
1967
 
1923
1968
  #[derive(Default)]
1924
1969
  #[allow(dead_code)]
1925
1970
  struct RowsLiveStateReader {
1926
- rows: Vec<LiveStateRow>,
1971
+ rows: Vec<MaterializedLiveStateRow>,
1927
1972
  }
1928
1973
 
1929
1974
  #[async_trait]
@@ -1931,19 +1976,23 @@ mod tests {
1931
1976
  async fn scan_rows(
1932
1977
  &self,
1933
1978
  _request: &LiveStateScanRequest,
1934
- ) -> Result<Vec<LiveStateRow>, LixError> {
1979
+ ) -> Result<Vec<MaterializedLiveStateRow>, LixError> {
1935
1980
  Ok(self.rows.clone())
1936
1981
  }
1937
1982
 
1938
1983
  async fn load_row(
1939
1984
  &self,
1940
1985
  _request: &LiveStateRowRequest,
1941
- ) -> Result<Option<LiveStateRow>, LixError> {
1986
+ ) -> Result<Option<MaterializedLiveStateRow>, LixError> {
1942
1987
  Ok(None)
1943
1988
  }
1944
1989
  }
1945
1990
 
1946
- fn live_row(entity_id: &str, version_id: &str, snapshot_content: &str) -> LiveStateRow {
1991
+ fn live_row(
1992
+ entity_id: &str,
1993
+ version_id: &str,
1994
+ snapshot_content: &str,
1995
+ ) -> MaterializedLiveStateRow {
1947
1996
  live_filesystem_row(
1948
1997
  entity_id,
1949
1998
  super::DIRECTORY_SCHEMA_KEY,
@@ -1959,15 +2008,14 @@ mod tests {
1959
2008
  file_id: Option<&str>,
1960
2009
  version_id: &str,
1961
2010
  snapshot_content: &str,
1962
- ) -> LiveStateRow {
1963
- LiveStateRow {
1964
- entity_id: crate::entity_identity::EntityIdentity::from_string(entity_id)
1965
- .expect("entity id should decode"),
2011
+ ) -> MaterializedLiveStateRow {
2012
+ MaterializedLiveStateRow {
2013
+ entity_id: crate::entity_identity::EntityIdentity::single(entity_id),
1966
2014
  schema_key: schema_key.to_string(),
1967
2015
  file_id: file_id.map(ToOwned::to_owned),
1968
2016
  snapshot_content: Some(snapshot_content.to_string()),
1969
- metadata: Some(json!({"source": "test"})),
1970
- schema_version: "1".to_string(),
2017
+ metadata: Some(json!({"source": "test"}).to_string()),
2018
+ deleted: false,
1971
2019
  version_id: version_id.to_string(),
1972
2020
  change_id: Some(format!("change-{entity_id}")),
1973
2021
  commit_id: Some(format!("commit-{entity_id}")),
@@ -1978,7 +2026,7 @@ mod tests {
1978
2026
  }
1979
2027
  }
1980
2028
 
1981
- fn filesystem_rows() -> Vec<LiveStateRow> {
2029
+ fn filesystem_rows() -> Vec<MaterializedLiveStateRow> {
1982
2030
  vec![
1983
2031
  live_filesystem_row(
1984
2032
  "dir-docs",
@@ -2169,17 +2217,17 @@ mod tests {
2169
2217
 
2170
2218
  assert_eq!(
2171
2219
  rows,
2172
- vec![StageRow {
2220
+ vec![TransactionWriteRow {
2173
2221
  entity_id: Some(crate::entity_identity::EntityIdentity::single("dir-docs")),
2174
2222
  schema_key: super::DIRECTORY_SCHEMA_KEY.to_string(),
2175
2223
  file_id: None,
2176
- snapshot_content: Some(
2177
- "{\"hidden\":false,\"id\":\"dir-docs\",\"name\":\"docs\",\"parent_id\":null}"
2178
- .to_string()
2179
- ),
2180
- metadata: Some(json!({"source": "directory"})),
2224
+ snapshot: Some(TransactionJson::from_value_for_test(
2225
+ json!({"hidden":false,"id":"dir-docs","name":"docs","parent_id":null})
2226
+ )),
2227
+ metadata: Some(TransactionJson::from_value_for_test(
2228
+ json!({"source": "directory"})
2229
+ )),
2181
2230
  origin: Some(lix_directory_insert_origin("lix_directory", "dir-docs")),
2182
- schema_version: "1".to_string(),
2183
2231
  created_at: None,
2184
2232
  updated_at: None,
2185
2233
  global: false,
@@ -2247,8 +2295,7 @@ mod tests {
2247
2295
  .expect("directory path batch should decode");
2248
2296
 
2249
2297
  assert_eq!(rows.len(), 1);
2250
- let snapshot: serde_json::Value =
2251
- serde_json::from_str(rows[0].snapshot_content.as_deref().unwrap()).unwrap();
2298
+ let snapshot = rows[0].snapshot.as_ref().unwrap();
2252
2299
  assert_eq!(snapshot["id"], "dir-nested");
2253
2300
  assert_eq!(snapshot["parent_id"], "dir-docs");
2254
2301
  assert_eq!(snapshot["name"], "nested");
@@ -2277,7 +2324,7 @@ mod tests {
2277
2324
  row.entity_id
2278
2325
  .as_ref()
2279
2326
  .expect("planned delete row should carry entity_id")
2280
- .as_string()
2327
+ .as_single_string_owned()
2281
2328
  .expect("planned delete row should project entity_id"),
2282
2329
  )
2283
2330
  })
@@ -2290,7 +2337,7 @@ mod tests {
2290
2337
  ("lix_directory_descriptor", "dir-docs".to_string()),
2291
2338
  ]
2292
2339
  );
2293
- assert!(rows.iter().all(|row| row.snapshot_content.is_none()));
2340
+ assert!(rows.iter().all(|row| row.snapshot.is_none()));
2294
2341
  }
2295
2342
 
2296
2343
  #[test]
@@ -2342,20 +2389,22 @@ mod tests {
2342
2389
  assert_eq!(count, 1);
2343
2390
  assert_eq!(
2344
2391
  write_context.writes.as_slice(),
2345
- &[StageWrite::Rows { mode: StageWriteMode::Insert, rows: vec![StageRow {
2392
+ &[TransactionWrite::Rows {
2393
+ mode: TransactionWriteMode::Insert,
2394
+ rows: vec![TransactionWriteRow {
2346
2395
  entity_id: Some(crate::entity_identity::EntityIdentity::single("dir-docs")),
2347
2396
  schema_key: super::DIRECTORY_SCHEMA_KEY.to_string(),
2348
2397
  file_id: None,
2349
- snapshot_content: Some(
2350
- "{\"hidden\":false,\"id\":\"dir-docs\",\"name\":\"docs\",\"parent_id\":null}"
2351
- .to_string()
2352
- ),
2353
- metadata: Some(json!({"source": "directory"})),
2398
+ snapshot: Some(TransactionJson::from_value_for_test(
2399
+ json!({"hidden":false,"id":"dir-docs","name":"docs","parent_id":null})
2400
+ )),
2401
+ metadata: Some(TransactionJson::from_value_for_test(
2402
+ json!({"source": "directory"})
2403
+ )),
2354
2404
  origin: Some(lix_directory_insert_origin(
2355
2405
  "lix_directory_by_version",
2356
2406
  "dir-docs"
2357
2407
  )),
2358
- schema_version: "1".to_string(),
2359
2408
  created_at: None,
2360
2409
  updated_at: None,
2361
2410
  global: false,
@@ -2392,12 +2441,11 @@ mod tests {
2392
2441
  .expect("directory sink should stage path write");
2393
2442
 
2394
2443
  assert_eq!(count, 1);
2395
- let [StageWrite::Rows { rows, .. }] = write_context.writes.as_slice() else {
2444
+ let [TransactionWrite::Rows { rows, .. }] = write_context.writes.as_slice() else {
2396
2445
  panic!("expected one directory staged write");
2397
2446
  };
2398
2447
  assert_eq!(rows.len(), 1);
2399
- let snapshot: serde_json::Value =
2400
- serde_json::from_str(rows[0].snapshot_content.as_deref().unwrap()).unwrap();
2448
+ let snapshot = rows[0].snapshot.as_ref().unwrap();
2401
2449
  assert_eq!(snapshot["id"], "dir-nested");
2402
2450
  assert_eq!(snapshot["parent_id"], "dir-docs");
2403
2451
  assert_eq!(snapshot["name"], "nested");