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

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 (169) hide show
  1. package/SKILL.md +46 -8
  2. package/dist/engine-wasm/wasm/lix_engine.d.ts +25 -1
  3. package/dist/engine-wasm/wasm/lix_engine.js +60 -2
  4. package/dist/engine-wasm/wasm/lix_engine.wasm +0 -0
  5. package/dist/engine-wasm/wasm/lix_engine.wasm.d.ts +5 -0
  6. package/dist/generated/builtin-schemas.d.ts +87 -162
  7. package/dist/generated/builtin-schemas.js +139 -236
  8. package/dist/open-lix.d.ts +10 -3
  9. package/dist/open-lix.js +39 -0
  10. package/dist-engine-src/src/binary_cas/types.rs +0 -6
  11. package/dist-engine-src/src/catalog/context.rs +412 -0
  12. package/dist-engine-src/src/catalog/mod.rs +10 -0
  13. package/dist-engine-src/src/catalog/schema.rs +4 -0
  14. package/dist-engine-src/src/catalog/snapshot.rs +1114 -0
  15. package/dist-engine-src/src/cel/mod.rs +1 -1
  16. package/dist-engine-src/src/cel/provider.rs +1 -1
  17. package/dist-engine-src/src/commit_graph/context.rs +328 -1015
  18. package/dist-engine-src/src/commit_graph/mod.rs +2 -3
  19. package/dist-engine-src/src/commit_graph/types.rs +7 -43
  20. package/dist-engine-src/src/commit_graph/walker.rs +57 -81
  21. package/dist-engine-src/src/commit_store/codec.rs +887 -0
  22. package/dist-engine-src/src/commit_store/context.rs +944 -0
  23. package/dist-engine-src/src/commit_store/materialization.rs +84 -0
  24. package/dist-engine-src/src/commit_store/mod.rs +16 -0
  25. package/dist-engine-src/src/commit_store/storage.rs +600 -0
  26. package/dist-engine-src/src/commit_store/types.rs +215 -0
  27. package/dist-engine-src/src/common/identity.rs +15 -5
  28. package/dist-engine-src/src/common/json_pointer.rs +67 -0
  29. package/dist-engine-src/src/common/metadata.rs +17 -12
  30. package/dist-engine-src/src/common/mod.rs +5 -5
  31. package/dist-engine-src/src/domain.rs +324 -0
  32. package/dist-engine-src/src/engine.rs +29 -43
  33. package/dist-engine-src/src/entity_identity.rs +238 -118
  34. package/dist-engine-src/src/functions/context.rs +17 -52
  35. package/dist-engine-src/src/functions/deterministic.rs +1 -1
  36. package/dist-engine-src/src/functions/mod.rs +1 -1
  37. package/dist-engine-src/src/functions/provider.rs +4 -4
  38. package/dist-engine-src/src/functions/state.rs +39 -66
  39. package/dist-engine-src/src/functions/types.rs +1 -1
  40. package/dist-engine-src/src/init.rs +204 -151
  41. package/dist-engine-src/src/json_store/context.rs +354 -60
  42. package/dist-engine-src/src/json_store/encoded.rs +6 -6
  43. package/dist-engine-src/src/json_store/mod.rs +4 -1
  44. package/dist-engine-src/src/json_store/store.rs +884 -11
  45. package/dist-engine-src/src/json_store/types.rs +166 -1
  46. package/dist-engine-src/src/lib.rs +11 -10
  47. package/dist-engine-src/src/live_state/context.rs +608 -830
  48. package/dist-engine-src/src/live_state/mod.rs +3 -3
  49. package/dist-engine-src/src/live_state/overlay.rs +7 -7
  50. package/dist-engine-src/src/live_state/reader.rs +5 -5
  51. package/dist-engine-src/src/live_state/types.rs +19 -36
  52. package/dist-engine-src/src/live_state/visibility.rs +19 -14
  53. package/dist-engine-src/src/plugin/archive.rs +3 -6
  54. package/dist-engine-src/src/plugin/install.rs +0 -18
  55. package/dist-engine-src/src/plugin/plugin_manifest.json +0 -1
  56. package/dist-engine-src/src/schema/annotations/defaults.rs +2 -7
  57. package/dist-engine-src/src/schema/builtin/lix_account.json +0 -1
  58. package/dist-engine-src/src/schema/builtin/lix_active_account.json +0 -1
  59. package/dist-engine-src/src/schema/builtin/lix_binary_blob_ref.json +0 -1
  60. package/dist-engine-src/src/schema/builtin/lix_change.json +11 -10
  61. package/dist-engine-src/src/schema/builtin/lix_change_author.json +0 -1
  62. package/dist-engine-src/src/schema/builtin/lix_commit.json +8 -46
  63. package/dist-engine-src/src/schema/builtin/lix_commit_edge.json +29 -22
  64. package/dist-engine-src/src/schema/builtin/lix_directory_descriptor.json +0 -1
  65. package/dist-engine-src/src/schema/builtin/lix_file_descriptor.json +0 -1
  66. package/dist-engine-src/src/schema/builtin/lix_key_value.json +0 -1
  67. package/dist-engine-src/src/schema/builtin/lix_label.json +10 -3
  68. package/dist-engine-src/src/schema/builtin/lix_label_assignment.json +74 -0
  69. package/dist-engine-src/src/schema/builtin/lix_registered_schema.json +2 -8
  70. package/dist-engine-src/src/schema/builtin/lix_version_descriptor.json +0 -1
  71. package/dist-engine-src/src/schema/builtin/lix_version_ref.json +0 -1
  72. package/dist-engine-src/src/schema/builtin/mod.rs +10 -59
  73. package/dist-engine-src/src/schema/compatibility.rs +787 -0
  74. package/dist-engine-src/src/schema/definition.json +47 -17
  75. package/dist-engine-src/src/schema/definition.rs +202 -96
  76. package/dist-engine-src/src/schema/key.rs +9 -77
  77. package/dist-engine-src/src/schema/mod.rs +4 -4
  78. package/dist-engine-src/src/schema/tests.rs +133 -92
  79. package/dist-engine-src/src/session/context.rs +86 -48
  80. package/dist-engine-src/src/session/create_version.rs +22 -14
  81. package/dist-engine-src/src/session/execute.rs +117 -23
  82. package/dist-engine-src/src/session/merge/apply.rs +4 -4
  83. package/dist-engine-src/src/session/merge/conflicts.rs +3 -2
  84. package/dist-engine-src/src/session/merge/stats.rs +1 -1
  85. package/dist-engine-src/src/session/merge/version.rs +35 -45
  86. package/dist-engine-src/src/session/mod.rs +9 -7
  87. package/dist-engine-src/src/session/optimization9_sql2_bench.rs +100 -0
  88. package/dist-engine-src/src/session/switch_version.rs +17 -28
  89. package/dist-engine-src/src/session/transaction.rs +76 -0
  90. package/dist-engine-src/src/sql2/change_provider.rs +14 -20
  91. package/dist-engine-src/src/sql2/classify.rs +75 -48
  92. package/dist-engine-src/src/sql2/context.rs +22 -18
  93. package/dist-engine-src/src/sql2/directory_history_provider.rs +28 -20
  94. package/dist-engine-src/src/sql2/directory_provider.rs +131 -83
  95. package/dist-engine-src/src/sql2/entity_history_provider.rs +10 -14
  96. package/dist-engine-src/src/sql2/entity_provider.rs +680 -169
  97. package/dist-engine-src/src/sql2/error.rs +24 -5
  98. package/dist-engine-src/src/sql2/execute.rs +426 -272
  99. package/dist-engine-src/src/sql2/file_history_provider.rs +29 -21
  100. package/dist-engine-src/src/sql2/file_provider.rs +533 -108
  101. package/dist-engine-src/src/sql2/filesystem_planner.rs +58 -94
  102. package/dist-engine-src/src/sql2/filesystem_visibility.rs +37 -23
  103. package/dist-engine-src/src/sql2/history_projection.rs +3 -27
  104. package/dist-engine-src/src/sql2/history_provider.rs +11 -17
  105. package/dist-engine-src/src/sql2/history_route.rs +22 -8
  106. package/dist-engine-src/src/sql2/lix_state_provider.rs +178 -96
  107. package/dist-engine-src/src/sql2/mod.rs +8 -4
  108. package/dist-engine-src/src/sql2/predicate_typecheck.rs +246 -0
  109. package/dist-engine-src/src/sql2/public_bind/assignment.rs +46 -0
  110. package/dist-engine-src/src/sql2/public_bind/capability.rs +41 -0
  111. package/dist-engine-src/src/sql2/public_bind/dml.rs +172 -0
  112. package/dist-engine-src/src/sql2/public_bind/mod.rs +26 -0
  113. package/dist-engine-src/src/sql2/public_bind/table.rs +168 -0
  114. package/dist-engine-src/src/sql2/read_only.rs +10 -12
  115. package/dist-engine-src/src/sql2/session.rs +7 -10
  116. package/dist-engine-src/src/sql2/udfs/lix_timestamp.rs +76 -0
  117. package/dist-engine-src/src/sql2/udfs/mod.rs +8 -1
  118. package/dist-engine-src/src/sql2/udfs/public_call.rs +238 -0
  119. package/dist-engine-src/src/sql2/version_provider.rs +46 -31
  120. package/dist-engine-src/src/sql2/version_scope.rs +4 -4
  121. package/dist-engine-src/src/storage_bench.rs +1782 -325
  122. package/dist-engine-src/src/test_support.rs +183 -36
  123. package/dist-engine-src/src/tracked_state/by_file_index.rs +20 -24
  124. package/dist-engine-src/src/tracked_state/codec.rs +1519 -181
  125. package/dist-engine-src/src/tracked_state/context.rs +1155 -271
  126. package/dist-engine-src/src/tracked_state/diff.rs +249 -57
  127. package/dist-engine-src/src/tracked_state/materialization.rs +365 -103
  128. package/dist-engine-src/src/tracked_state/materializer.rs +488 -0
  129. package/dist-engine-src/src/tracked_state/merge.rs +37 -19
  130. package/dist-engine-src/src/tracked_state/mod.rs +8 -7
  131. package/dist-engine-src/src/tracked_state/storage.rs +138 -6
  132. package/dist-engine-src/src/tracked_state/tree.rs +695 -252
  133. package/dist-engine-src/src/tracked_state/types.rs +176 -6
  134. package/dist-engine-src/src/transaction/commit.rs +695 -435
  135. package/dist-engine-src/src/transaction/context.rs +551 -310
  136. package/dist-engine-src/src/transaction/live_state_overlay.rs +9 -8
  137. package/dist-engine-src/src/transaction/mod.rs +2 -0
  138. package/dist-engine-src/src/transaction/normalization.rs +311 -447
  139. package/dist-engine-src/src/transaction/prep.rs +37 -0
  140. package/dist-engine-src/src/transaction/schema_resolver.rs +93 -71
  141. package/dist-engine-src/src/transaction/staging.rs +701 -406
  142. package/dist-engine-src/src/transaction/types.rs +231 -122
  143. package/dist-engine-src/src/transaction/validation.rs +2717 -1698
  144. package/dist-engine-src/src/untracked_state/codec.rs +40 -96
  145. package/dist-engine-src/src/untracked_state/context.rs +21 -5
  146. package/dist-engine-src/src/untracked_state/materialization.rs +10 -104
  147. package/dist-engine-src/src/untracked_state/mod.rs +3 -5
  148. package/dist-engine-src/src/untracked_state/storage.rs +105 -57
  149. package/dist-engine-src/src/untracked_state/types.rs +63 -13
  150. package/dist-engine-src/src/version/context.rs +1 -13
  151. package/dist-engine-src/src/version/lifecycle.rs +221 -0
  152. package/dist-engine-src/src/version/mod.rs +3 -2
  153. package/dist-engine-src/src/version/refs.rs +12 -103
  154. package/dist-engine-src/src/version/stage_rows.rs +15 -19
  155. package/package.json +1 -1
  156. package/dist-engine-src/src/changelog/codec.rs +0 -321
  157. package/dist-engine-src/src/changelog/context.rs +0 -92
  158. package/dist-engine-src/src/changelog/materialization.rs +0 -121
  159. package/dist-engine-src/src/changelog/mod.rs +0 -13
  160. package/dist-engine-src/src/changelog/reader.rs +0 -20
  161. package/dist-engine-src/src/changelog/storage.rs +0 -220
  162. package/dist-engine-src/src/changelog/types.rs +0 -38
  163. package/dist-engine-src/src/schema/builtin/lix_change_set.json +0 -18
  164. package/dist-engine-src/src/schema/builtin/lix_change_set_element.json +0 -75
  165. package/dist-engine-src/src/schema/builtin/lix_entity_label.json +0 -63
  166. package/dist-engine-src/src/schema_registry.rs +0 -294
  167. package/dist-engine-src/src/sql2/commit_derived_provider.rs +0 -591
  168. package/dist-engine-src/src/tracked_state/rebuild.rs +0 -771
  169. package/dist-engine-src/src/tracked_state/tree_types.rs +0 -176
@@ -1,8 +1,8 @@
1
+ use datafusion::sql::parser::Statement as DataFusionStatement;
1
2
  use datafusion::sql::sqlparser::ast::{
2
- FromTable, ObjectName, Query, SetExpr, Statement, TableFactor, TableObject, TableWithJoins,
3
+ FromTable, ObjectName, Query, SetExpr, Statement as SqlStatement, TableFactor, TableObject,
4
+ TableWithJoins,
3
5
  };
4
- use datafusion::sql::sqlparser::dialect::GenericDialect;
5
- use datafusion::sql::sqlparser::parser::Parser;
6
6
 
7
7
  use crate::LixError;
8
8
 
@@ -13,56 +13,73 @@ pub(crate) enum SqlStatementKind {
13
13
  Other,
14
14
  }
15
15
 
16
- pub(crate) fn classify_statement(sql: &str) -> Result<SqlStatementKind, LixError> {
17
- let statements = parse_sql_statements(sql)?;
18
- let [statement] = statements.as_slice() else {
19
- return Ok(SqlStatementKind::Other);
20
- };
21
- Ok(classify_ast_statement(statement))
16
+ pub(crate) fn validate_supported_datafusion_statement_ast(
17
+ statement: &DataFusionStatement,
18
+ ) -> Result<(), LixError> {
19
+ match statement {
20
+ DataFusionStatement::Statement(statement) => validate_supported_ast_statement(statement),
21
+ DataFusionStatement::Explain(explain) => {
22
+ if classify_datafusion_statement(explain.statement.as_ref()) == SqlStatementKind::Write
23
+ {
24
+ return Err(unsupported_sql_error(
25
+ "EXPLAIN of write statements is not supported by Lix SQL",
26
+ ));
27
+ }
28
+ validate_supported_datafusion_statement_ast(explain.statement.as_ref())
29
+ }
30
+ _ => Err(unsupported_sql_error(format!(
31
+ "SQL statement is not supported by Lix SQL: {statement}"
32
+ ))),
33
+ }
22
34
  }
23
35
 
24
- pub(crate) fn validate_supported_statement_ast(sql: &str) -> Result<(), LixError> {
25
- let statements = parse_sql_statements(sql)?;
26
- let [statement] = statements.as_slice() else {
27
- return Err(unsupported_sql_error(
28
- "Lix SQL only supports one statement per execute() call",
29
- ));
30
- };
31
- validate_supported_ast_statement(statement)
36
+ pub(crate) fn classify_datafusion_statement(statement: &DataFusionStatement) -> SqlStatementKind {
37
+ match statement {
38
+ DataFusionStatement::Statement(statement) => classify_ast_statement(statement),
39
+ DataFusionStatement::Explain(_) => SqlStatementKind::Read,
40
+ _ => SqlStatementKind::Other,
41
+ }
32
42
  }
33
43
 
34
- pub(crate) fn dml_target_table_names(sql: &str) -> Result<Vec<String>, LixError> {
35
- let statements = parse_sql_statements(sql)?;
36
- let [statement] = statements.as_slice() else {
37
- return Ok(Vec::new());
38
- };
44
+ pub(crate) fn datafusion_statement_dml_target_table_names(
45
+ statement: &DataFusionStatement,
46
+ ) -> Vec<String> {
39
47
  let mut targets = Vec::new();
40
- collect_dml_target_table_names(statement, &mut targets);
41
- Ok(targets)
48
+ collect_datafusion_statement_dml_target_table_names(statement, &mut targets);
49
+ targets
42
50
  }
43
51
 
44
- fn parse_sql_statements(sql: &str) -> Result<Vec<Statement>, LixError> {
45
- Parser::parse_sql(&GenericDialect {}, sql).map_err(|error| {
46
- LixError::new(
47
- LixError::CODE_PARSE_ERROR,
48
- format!("sql2 SQL parse error: {error}"),
49
- )
50
- })
52
+ fn collect_datafusion_statement_dml_target_table_names(
53
+ statement: &DataFusionStatement,
54
+ targets: &mut Vec<String>,
55
+ ) {
56
+ match statement {
57
+ DataFusionStatement::Statement(statement) => {
58
+ collect_dml_target_table_names(statement, targets);
59
+ }
60
+ DataFusionStatement::Explain(explain) => {
61
+ collect_datafusion_statement_dml_target_table_names(
62
+ explain.statement.as_ref(),
63
+ targets,
64
+ );
65
+ }
66
+ _ => {}
67
+ }
51
68
  }
52
69
 
53
- fn collect_dml_target_table_names(statement: &Statement, targets: &mut Vec<String>) {
70
+ fn collect_dml_target_table_names(statement: &SqlStatement, targets: &mut Vec<String>) {
54
71
  match statement {
55
- Statement::Insert(insert) => {
72
+ SqlStatement::Insert(insert) => {
56
73
  if let TableObject::TableName(name) = &insert.table {
57
74
  if let Some(table_name) = object_name_table_part(name) {
58
75
  targets.push(table_name);
59
76
  }
60
77
  }
61
78
  }
62
- Statement::Update(update) => {
79
+ SqlStatement::Update(update) => {
63
80
  collect_table_with_joins_target(&update.table, targets);
64
81
  }
65
- Statement::Delete(delete) => {
82
+ SqlStatement::Delete(delete) => {
66
83
  let tables = match &delete.from {
67
84
  FromTable::WithFromKeyword(tables) | FromTable::WithoutKeyword(tables) => tables,
68
85
  };
@@ -70,7 +87,7 @@ fn collect_dml_target_table_names(statement: &Statement, targets: &mut Vec<Strin
70
87
  collect_table_with_joins_target(table, targets);
71
88
  }
72
89
  }
73
- Statement::Explain { statement, .. } => {
90
+ SqlStatement::Explain { statement, .. } => {
74
91
  collect_dml_target_table_names(statement.as_ref(), targets);
75
92
  }
76
93
  _ => {}
@@ -86,28 +103,38 @@ fn collect_table_with_joins_target(table: &TableWithJoins, targets: &mut Vec<Str
86
103
  }
87
104
 
88
105
  fn object_name_table_part(name: &ObjectName) -> Option<String> {
89
- name.0
90
- .last()
91
- .and_then(|part| part.as_ident())
92
- .map(|ident| ident.value.clone())
106
+ name.0.last().and_then(|part| part.as_ident()).map(|ident| {
107
+ if ident.quote_style.is_some() {
108
+ ident.value.clone()
109
+ } else {
110
+ ident.value.to_ascii_lowercase()
111
+ }
112
+ })
93
113
  }
94
114
 
95
- fn classify_ast_statement(statement: &Statement) -> SqlStatementKind {
115
+ fn classify_ast_statement(statement: &SqlStatement) -> SqlStatementKind {
96
116
  match statement {
97
- Statement::Insert(_) | Statement::Update(_) | Statement::Delete(_) => {
117
+ SqlStatement::Insert(_) | SqlStatement::Update(_) | SqlStatement::Delete(_) => {
98
118
  SqlStatementKind::Write
99
119
  }
100
- Statement::Query(_) => SqlStatementKind::Read,
101
- Statement::Explain { statement, .. } => classify_ast_statement(statement.as_ref()),
120
+ SqlStatement::Query(_) => SqlStatementKind::Read,
121
+ SqlStatement::Explain { .. } => SqlStatementKind::Read,
102
122
  _ => SqlStatementKind::Other,
103
123
  }
104
124
  }
105
125
 
106
- fn validate_supported_ast_statement(statement: &Statement) -> Result<(), LixError> {
126
+ fn validate_supported_ast_statement(statement: &SqlStatement) -> Result<(), LixError> {
107
127
  match statement {
108
- Statement::Query(query) => validate_supported_query(query),
109
- Statement::Insert(_) | Statement::Update(_) | Statement::Delete(_) => Ok(()),
110
- Statement::Explain { statement, .. } => validate_supported_ast_statement(statement),
128
+ SqlStatement::Query(query) => validate_supported_query(query),
129
+ SqlStatement::Insert(_) | SqlStatement::Update(_) | SqlStatement::Delete(_) => Ok(()),
130
+ SqlStatement::Explain { statement, .. } => {
131
+ if classify_ast_statement(statement.as_ref()) == SqlStatementKind::Write {
132
+ return Err(unsupported_sql_error(
133
+ "EXPLAIN of write statements is not supported by Lix SQL",
134
+ ));
135
+ }
136
+ validate_supported_ast_statement(statement)
137
+ }
111
138
  _ => Err(unsupported_sql_error(format!(
112
139
  "SQL statement is not supported by Lix SQL: {statement}"
113
140
  ))),
@@ -6,26 +6,27 @@ use serde_json::Value as JsonValue;
6
6
  use tokio::sync::Mutex;
7
7
 
8
8
  use crate::binary_cas::{BlobBytesBatch, BlobDataReader, BlobHash};
9
- use crate::changelog::ChangelogReader;
10
9
  use crate::commit_graph::CommitGraphReader;
10
+ use crate::commit_store::CommitStoreReader;
11
11
  use crate::functions::FunctionProviderHandle;
12
12
  use crate::json_store::JsonStoreReader;
13
13
  use crate::live_state::{
14
- LiveStateFilter, LiveStateReader, LiveStateRow, LiveStateRowRequest, LiveStateScanRequest,
14
+ LiveStateFilter, LiveStateReader, LiveStateRowRequest, LiveStateScanRequest,
15
+ MaterializedLiveStateRow,
15
16
  };
16
17
  use crate::storage::{ScopedStorageReader, StorageReadTransaction};
17
- use crate::transaction::types::{StageWrite, StageWriteOutcome};
18
+ use crate::transaction::types::{TransactionWrite, TransactionWriteOutcome};
18
19
  use crate::version::{VersionHead, VersionRefReader};
19
20
  use crate::LixError;
20
21
 
21
22
  pub(crate) type SqlReadStore =
22
23
  ScopedStorageReader<Box<dyn StorageReadTransaction + Send + Sync + 'static>>;
23
- pub(crate) type SqlChangelogQuerySource = ChangelogQuerySource<SqlReadStore>;
24
+ pub(crate) type SqlCommitStoreQuerySource = CommitStoreQuerySource<SqlReadStore>;
24
25
  pub(crate) type SqlJsonReader = JsonStoreReader<ScopedStorageReader<SqlReadStore>>;
25
26
 
26
27
  #[derive(Clone)]
27
- pub(crate) struct ChangelogQuerySource<S> {
28
- pub(crate) changelog_reader: Arc<dyn ChangelogReader>,
28
+ pub(crate) struct CommitStoreQuerySource<S> {
29
+ pub(crate) commit_store_reader: Arc<CommitStoreReader<ScopedStorageReader<S>>>,
29
30
  pub(crate) json_reader: JsonStoreReader<ScopedStorageReader<S>>,
30
31
  }
31
32
 
@@ -43,7 +44,7 @@ pub(crate) trait SqlExecutionContext {
43
44
  fn active_version_id(&self) -> &str;
44
45
  fn live_state(&self) -> Arc<dyn LiveStateReader>;
45
46
  fn functions(&self) -> FunctionProviderHandle;
46
- fn changelog_query_source(&self) -> SqlChangelogQuerySource;
47
+ fn commit_store_query_source(&self) -> SqlCommitStoreQuerySource;
47
48
  fn commit_graph(&self) -> Box<dyn CommitGraphReader>;
48
49
  fn version_ref(&self) -> Arc<dyn VersionRefReader>;
49
50
  fn blob_reader(&self) -> Arc<dyn BlobDataReader>;
@@ -52,9 +53,9 @@ pub(crate) trait SqlExecutionContext {
52
53
 
53
54
  /// Write-capable SQL runtime boundary.
54
55
  ///
55
- /// Providers that mutate engine2 state should target this shape instead of
56
+ /// Providers that mutate engine state should target this shape instead of
56
57
  /// reaching through session/backend escape hatches. The request and write
57
- /// payloads stay in the existing engine2 forms so this boundary centralizes
58
+ /// payloads stay in the existing engine forms so this boundary centralizes
58
59
  /// authority without adding another translation layer.
59
60
  #[async_trait]
60
61
  #[allow(dead_code)]
@@ -68,11 +69,14 @@ pub(crate) trait SqlWriteExecutionContext {
68
69
  async fn scan_live_state(
69
70
  &mut self,
70
71
  request: &LiveStateScanRequest,
71
- ) -> Result<Vec<LiveStateRow>, LixError>;
72
+ ) -> Result<Vec<MaterializedLiveStateRow>, LixError>;
72
73
 
73
74
  async fn load_version_head(&mut self, version_id: &str) -> Result<Option<String>, LixError>;
74
75
 
75
- async fn stage_write(&mut self, write: StageWrite) -> Result<StageWriteOutcome, LixError>;
76
+ async fn stage_write(
77
+ &mut self,
78
+ write: TransactionWrite,
79
+ ) -> Result<TransactionWriteOutcome, LixError>;
76
80
  }
77
81
 
78
82
  #[derive(Clone)]
@@ -123,7 +127,7 @@ impl SqlWriteContext {
123
127
  pub(crate) async fn scan_live_state(
124
128
  &self,
125
129
  request: &LiveStateScanRequest,
126
- ) -> Result<Vec<LiveStateRow>, LixError> {
130
+ ) -> Result<Vec<MaterializedLiveStateRow>, LixError> {
127
131
  let _guard = self.gate.lock().await;
128
132
  unsafe {
129
133
  self.ptr
@@ -170,8 +174,8 @@ impl SqlWriteContext {
170
174
 
171
175
  pub(crate) async fn stage_write(
172
176
  &self,
173
- write: StageWrite,
174
- ) -> Result<StageWriteOutcome, LixError> {
177
+ write: TransactionWrite,
178
+ ) -> Result<TransactionWriteOutcome, LixError> {
175
179
  let _guard = self.gate.lock().await;
176
180
  unsafe {
177
181
  self.ptr
@@ -219,12 +223,12 @@ impl WriteAccess {
219
223
 
220
224
  pub(crate) fn require_write(
221
225
  &self,
222
- operation: &str,
226
+ action: &str,
223
227
  ) -> Result<SqlWriteContext, datafusion::error::DataFusionError> {
224
228
  match self {
225
229
  Self::Write { ctx } => Ok(ctx.clone()),
226
230
  Self::ReadOnly => Err(datafusion::error::DataFusionError::Execution(format!(
227
- "{operation} requires a write transaction"
231
+ "{action} requires a write transaction"
228
232
  ))),
229
233
  }
230
234
  }
@@ -249,14 +253,14 @@ impl LiveStateReader for WriteContextLiveStateReader {
249
253
  async fn scan_rows(
250
254
  &self,
251
255
  request: &LiveStateScanRequest,
252
- ) -> Result<Vec<LiveStateRow>, LixError> {
256
+ ) -> Result<Vec<MaterializedLiveStateRow>, LixError> {
253
257
  self.ctx.scan_live_state(request).await
254
258
  }
255
259
 
256
260
  async fn load_row(
257
261
  &self,
258
262
  request: &LiveStateRowRequest,
259
- ) -> Result<Option<LiveStateRow>, LixError> {
263
+ ) -> Result<Option<MaterializedLiveStateRow>, LixError> {
260
264
  let mut rows = self
261
265
  .ctx
262
266
  .scan_live_state(&LiveStateScanRequest {
@@ -21,7 +21,6 @@ use futures_util::stream;
21
21
  use serde::Deserialize;
22
22
  use tokio::sync::Mutex;
23
23
 
24
- use crate::changelog::MaterializedCanonicalChange;
25
24
  use crate::commit_graph::CommitGraphReader;
26
25
  use crate::serialize_row_metadata;
27
26
  use crate::LixError;
@@ -32,17 +31,18 @@ use super::history_route::{
32
31
  HistoryColumnStyle, HistoryEntry, HistoryRoute, HistoryViewDescriptor, HISTORY_COL_CHANGE_ID,
33
32
  HISTORY_COL_COMMIT_CREATED_AT, HISTORY_COL_DEPTH, HISTORY_COL_ENTITY_ID, HISTORY_COL_FILE_ID,
34
33
  HISTORY_COL_METADATA, HISTORY_COL_OBSERVED_COMMIT_ID, HISTORY_COL_SCHEMA_KEY,
35
- HISTORY_COL_SCHEMA_VERSION, HISTORY_COL_SNAPSHOT_CONTENT, HISTORY_COL_START_COMMIT_ID,
34
+ HISTORY_COL_SNAPSHOT_CONTENT, HISTORY_COL_START_COMMIT_ID,
36
35
  };
37
36
  use super::result_metadata::json_field;
38
- use super::SqlChangelogQuerySource;
37
+ use super::SqlCommitStoreQuerySource;
38
+ use crate::commit_store::MaterializedChange;
39
39
 
40
40
  const DIRECTORY_DESCRIPTOR_SCHEMA_KEY: &str = "lix_directory_descriptor";
41
41
 
42
42
  pub(crate) async fn register_lix_directory_history_provider(
43
43
  session: &datafusion::prelude::SessionContext,
44
44
  commit_graph: Box<dyn CommitGraphReader>,
45
- query_source: SqlChangelogQuerySource,
45
+ query_source: SqlCommitStoreQuerySource,
46
46
  ) -> Result<(), LixError> {
47
47
  session
48
48
  .register_table(
@@ -59,7 +59,7 @@ pub(crate) async fn register_lix_directory_history_provider(
59
59
  struct LixDirectoryHistoryProvider {
60
60
  schema: SchemaRef,
61
61
  commit_graph: Arc<Mutex<Box<dyn CommitGraphReader>>>,
62
- query_source: SqlChangelogQuerySource,
62
+ query_source: SqlCommitStoreQuerySource,
63
63
  }
64
64
 
65
65
  impl std::fmt::Debug for LixDirectoryHistoryProvider {
@@ -71,7 +71,7 @@ impl std::fmt::Debug for LixDirectoryHistoryProvider {
71
71
  impl LixDirectoryHistoryProvider {
72
72
  fn new(
73
73
  commit_graph: Arc<Mutex<Box<dyn CommitGraphReader>>>,
74
- query_source: SqlChangelogQuerySource,
74
+ query_source: SqlCommitStoreQuerySource,
75
75
  ) -> Self {
76
76
  Self {
77
77
  schema: lix_directory_history_schema(),
@@ -130,7 +130,7 @@ impl TableProvider for LixDirectoryHistoryProvider {
130
130
 
131
131
  struct LixDirectoryHistoryScanExec {
132
132
  commit_graph: Arc<Mutex<Box<dyn CommitGraphReader>>>,
133
- query_source: SqlChangelogQuerySource,
133
+ query_source: SqlCommitStoreQuerySource,
134
134
  schema: SchemaRef,
135
135
  route: HistoryRoute,
136
136
  limit: Option<usize>,
@@ -149,7 +149,7 @@ impl std::fmt::Debug for LixDirectoryHistoryScanExec {
149
149
  impl LixDirectoryHistoryScanExec {
150
150
  fn new(
151
151
  commit_graph: Arc<Mutex<Box<dyn CommitGraphReader>>>,
152
- query_source: SqlChangelogQuerySource,
152
+ query_source: SqlCommitStoreQuerySource,
153
153
  schema: SchemaRef,
154
154
  route: HistoryRoute,
155
155
  limit: Option<usize>,
@@ -265,7 +265,7 @@ struct DirectoryHistoryOutputRow {
265
265
  parent_id: Option<String>,
266
266
  name: Option<String>,
267
267
  hidden: Option<bool>,
268
- descriptor_change: MaterializedCanonicalChange,
268
+ descriptor_change: MaterializedChange,
269
269
  event: DirectoryHistoryEvent,
270
270
  }
271
271
 
@@ -274,7 +274,7 @@ struct DirectoryHistoryEvent {
274
274
  directory_id: String,
275
275
  start_commit_id: String,
276
276
  depth: u32,
277
- change: MaterializedCanonicalChange,
277
+ change: MaterializedChange,
278
278
  observed_commit_id: String,
279
279
  commit_created_at: String,
280
280
  }
@@ -289,7 +289,7 @@ struct DirectoryDescriptorSnapshot {
289
289
 
290
290
  async fn load_directory_history_rows(
291
291
  commit_graph: Arc<Mutex<Box<dyn CommitGraphReader>>>,
292
- query_source: SqlChangelogQuerySource,
292
+ query_source: SqlCommitStoreQuerySource,
293
293
  route: &HistoryRoute,
294
294
  ) -> Result<Vec<DirectoryHistoryOutputRow>, LixError> {
295
295
  let event_route = route.traversal_only();
@@ -356,9 +356,10 @@ async fn load_directory_history_rows(
356
356
  });
357
357
  }
358
358
  output.retain(|row| {
359
+ let entity_id = entity_id_json_array(&row.entity_id).ok();
359
360
  route.matches_surface_row(
360
361
  DIRECTORY_DESCRIPTOR_SCHEMA_KEY,
361
- &row.entity_id,
362
+ entity_id.as_deref().unwrap_or(&row.entity_id),
362
363
  None,
363
364
  row.event.depth,
364
365
  )
@@ -388,7 +389,7 @@ fn parse_directory_history_records(
388
389
  .map(|entry| {
389
390
  let Some(snapshot_content) = entry.change.snapshot_content.as_deref() else {
390
391
  return Ok(DirectoryHistoryRecord {
391
- id: entry.change.entity_id.as_string()?,
392
+ id: entry.change.entity_id.as_single_string_owned()?,
392
393
  parent_id: None,
393
394
  name: None,
394
395
  hidden: None,
@@ -528,15 +529,15 @@ fn directory_history_column_array(
528
529
  "hidden" => Arc::new(BooleanArray::from(
529
530
  rows.iter().map(|row| row.hidden).collect::<Vec<_>>(),
530
531
  )) as ArrayRef,
531
- HISTORY_COL_ENTITY_ID => string_array(rows.iter().map(|row| Some(row.entity_id.as_str()))),
532
+ HISTORY_COL_ENTITY_ID => Arc::new(StringArray::from(
533
+ rows.iter()
534
+ .map(|row| entity_id_json_array(&row.entity_id).map(Some))
535
+ .collect::<std::result::Result<Vec<_>, _>>()?,
536
+ )) as ArrayRef,
532
537
  HISTORY_COL_SCHEMA_KEY => {
533
538
  string_array(rows.iter().map(|_| Some(DIRECTORY_DESCRIPTOR_SCHEMA_KEY)))
534
539
  }
535
540
  HISTORY_COL_FILE_ID => string_array(rows.iter().map(|_| None)),
536
- HISTORY_COL_SCHEMA_VERSION => string_array(
537
- rows.iter()
538
- .map(|row| Some(row.descriptor_change.schema_version.as_str())),
539
- ),
540
541
  HISTORY_COL_CHANGE_ID => {
541
542
  string_array(rows.iter().map(|row| Some(row.event.change.id.as_str())))
542
543
  }
@@ -589,11 +590,10 @@ fn lix_directory_history_schema() -> SchemaRef {
589
590
  Field::new("parent_id", DataType::Utf8, true),
590
591
  Field::new("name", DataType::Utf8, true),
591
592
  Field::new("hidden", DataType::Boolean, true),
592
- Field::new(HISTORY_COL_ENTITY_ID, DataType::Utf8, false),
593
+ json_field(HISTORY_COL_ENTITY_ID, false),
593
594
  Field::new(HISTORY_COL_SCHEMA_KEY, DataType::Utf8, false),
594
595
  Field::new(HISTORY_COL_FILE_ID, DataType::Utf8, true),
595
596
  json_field(HISTORY_COL_SNAPSHOT_CONTENT, true),
596
- Field::new(HISTORY_COL_SCHEMA_VERSION, DataType::Utf8, false),
597
597
  Field::new(HISTORY_COL_CHANGE_ID, DataType::Utf8, false),
598
598
  json_field(HISTORY_COL_METADATA, true),
599
599
  Field::new(HISTORY_COL_OBSERVED_COMMIT_ID, DataType::Utf8, false),
@@ -618,6 +618,14 @@ fn datafusion_error_to_lix_error(error: DataFusionError) -> LixError {
618
618
  super::error::datafusion_error_to_lix_error(error)
619
619
  }
620
620
 
621
+ fn entity_id_json_array(entity_id: &str) -> Result<String, LixError> {
622
+ serde_json::to_string(&[entity_id]).map_err(|error| {
623
+ LixError::unknown(format!(
624
+ "failed to encode history entity id as JSON: {error}"
625
+ ))
626
+ })
627
+ }
628
+
621
629
  fn lix_error_to_datafusion_error(error: LixError) -> DataFusionError {
622
630
  super::error::lix_error_to_datafusion_error(error)
623
631
  }