@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.
- package/SKILL.md +4 -5
- package/dist/engine-wasm/wasm/lix_engine.js +1 -1
- package/dist/engine-wasm/wasm/lix_engine.wasm +0 -0
- package/dist/generated/builtin-schemas.d.ts +87 -162
- package/dist/generated/builtin-schemas.js +139 -236
- package/dist/open-lix.d.ts +1 -1
- package/dist-engine-src/src/binary_cas/types.rs +0 -6
- package/dist-engine-src/src/catalog/context.rs +412 -0
- package/dist-engine-src/src/catalog/mod.rs +10 -0
- package/dist-engine-src/src/catalog/schema.rs +4 -0
- package/dist-engine-src/src/catalog/snapshot.rs +1114 -0
- package/dist-engine-src/src/cel/mod.rs +1 -1
- package/dist-engine-src/src/cel/provider.rs +1 -1
- package/dist-engine-src/src/commit_graph/context.rs +328 -1015
- package/dist-engine-src/src/commit_graph/mod.rs +2 -3
- package/dist-engine-src/src/commit_graph/types.rs +7 -43
- package/dist-engine-src/src/commit_graph/walker.rs +57 -81
- package/dist-engine-src/src/commit_store/codec.rs +887 -0
- package/dist-engine-src/src/commit_store/context.rs +944 -0
- package/dist-engine-src/src/commit_store/materialization.rs +84 -0
- package/dist-engine-src/src/commit_store/mod.rs +16 -0
- package/dist-engine-src/src/commit_store/storage.rs +600 -0
- package/dist-engine-src/src/commit_store/types.rs +215 -0
- package/dist-engine-src/src/common/identity.rs +15 -5
- package/dist-engine-src/src/common/json_pointer.rs +67 -0
- package/dist-engine-src/src/common/metadata.rs +17 -12
- package/dist-engine-src/src/common/mod.rs +5 -5
- package/dist-engine-src/src/domain.rs +324 -0
- package/dist-engine-src/src/engine.rs +29 -43
- package/dist-engine-src/src/entity_identity.rs +238 -118
- package/dist-engine-src/src/functions/context.rs +17 -52
- package/dist-engine-src/src/functions/deterministic.rs +1 -1
- package/dist-engine-src/src/functions/mod.rs +1 -1
- package/dist-engine-src/src/functions/provider.rs +4 -4
- package/dist-engine-src/src/functions/state.rs +39 -66
- package/dist-engine-src/src/functions/types.rs +1 -1
- package/dist-engine-src/src/init.rs +204 -151
- package/dist-engine-src/src/json_store/context.rs +354 -60
- package/dist-engine-src/src/json_store/encoded.rs +6 -6
- package/dist-engine-src/src/json_store/mod.rs +4 -1
- package/dist-engine-src/src/json_store/store.rs +884 -11
- package/dist-engine-src/src/json_store/types.rs +166 -1
- package/dist-engine-src/src/lib.rs +10 -9
- package/dist-engine-src/src/live_state/context.rs +608 -830
- package/dist-engine-src/src/live_state/mod.rs +3 -3
- package/dist-engine-src/src/live_state/overlay.rs +7 -7
- package/dist-engine-src/src/live_state/reader.rs +5 -5
- package/dist-engine-src/src/live_state/types.rs +19 -36
- package/dist-engine-src/src/live_state/visibility.rs +19 -14
- package/dist-engine-src/src/plugin/archive.rs +3 -6
- package/dist-engine-src/src/plugin/install.rs +0 -18
- package/dist-engine-src/src/plugin/plugin_manifest.json +0 -1
- package/dist-engine-src/src/schema/annotations/defaults.rs +2 -7
- package/dist-engine-src/src/schema/builtin/lix_account.json +0 -1
- package/dist-engine-src/src/schema/builtin/lix_active_account.json +0 -1
- package/dist-engine-src/src/schema/builtin/lix_binary_blob_ref.json +0 -1
- package/dist-engine-src/src/schema/builtin/lix_change.json +11 -10
- package/dist-engine-src/src/schema/builtin/lix_change_author.json +0 -1
- package/dist-engine-src/src/schema/builtin/lix_commit.json +8 -46
- package/dist-engine-src/src/schema/builtin/lix_commit_edge.json +29 -22
- package/dist-engine-src/src/schema/builtin/lix_directory_descriptor.json +0 -1
- package/dist-engine-src/src/schema/builtin/lix_file_descriptor.json +0 -1
- package/dist-engine-src/src/schema/builtin/lix_key_value.json +0 -1
- package/dist-engine-src/src/schema/builtin/lix_label.json +10 -3
- package/dist-engine-src/src/schema/builtin/lix_label_assignment.json +74 -0
- package/dist-engine-src/src/schema/builtin/lix_registered_schema.json +2 -8
- package/dist-engine-src/src/schema/builtin/lix_version_descriptor.json +0 -1
- package/dist-engine-src/src/schema/builtin/lix_version_ref.json +0 -1
- package/dist-engine-src/src/schema/builtin/mod.rs +10 -59
- package/dist-engine-src/src/schema/compatibility.rs +787 -0
- package/dist-engine-src/src/schema/definition.json +47 -17
- package/dist-engine-src/src/schema/definition.rs +202 -96
- package/dist-engine-src/src/schema/key.rs +9 -77
- package/dist-engine-src/src/schema/mod.rs +4 -4
- package/dist-engine-src/src/schema/tests.rs +133 -92
- package/dist-engine-src/src/session/context.rs +40 -42
- package/dist-engine-src/src/session/create_version.rs +22 -14
- package/dist-engine-src/src/session/execute.rs +45 -14
- package/dist-engine-src/src/session/merge/apply.rs +4 -4
- package/dist-engine-src/src/session/merge/conflicts.rs +3 -2
- package/dist-engine-src/src/session/merge/stats.rs +1 -1
- package/dist-engine-src/src/session/merge/version.rs +35 -45
- package/dist-engine-src/src/session/mod.rs +4 -2
- package/dist-engine-src/src/session/optimization9_sql2_bench.rs +100 -0
- package/dist-engine-src/src/session/switch_version.rs +16 -28
- package/dist-engine-src/src/sql2/change_provider.rs +14 -20
- package/dist-engine-src/src/sql2/classify.rs +61 -26
- package/dist-engine-src/src/sql2/context.rs +22 -18
- package/dist-engine-src/src/sql2/directory_history_provider.rs +28 -20
- package/dist-engine-src/src/sql2/directory_provider.rs +131 -83
- package/dist-engine-src/src/sql2/entity_history_provider.rs +10 -14
- package/dist-engine-src/src/sql2/entity_provider.rs +680 -169
- package/dist-engine-src/src/sql2/error.rs +21 -1
- package/dist-engine-src/src/sql2/execute.rs +325 -264
- package/dist-engine-src/src/sql2/file_history_provider.rs +29 -21
- package/dist-engine-src/src/sql2/file_provider.rs +533 -108
- package/dist-engine-src/src/sql2/filesystem_planner.rs +58 -94
- package/dist-engine-src/src/sql2/filesystem_visibility.rs +37 -23
- package/dist-engine-src/src/sql2/history_projection.rs +3 -27
- package/dist-engine-src/src/sql2/history_provider.rs +11 -17
- package/dist-engine-src/src/sql2/history_route.rs +22 -8
- package/dist-engine-src/src/sql2/lix_state_provider.rs +178 -96
- package/dist-engine-src/src/sql2/mod.rs +6 -3
- package/dist-engine-src/src/sql2/predicate_typecheck.rs +246 -0
- package/dist-engine-src/src/sql2/public_bind/assignment.rs +46 -0
- package/dist-engine-src/src/sql2/public_bind/capability.rs +41 -0
- package/dist-engine-src/src/sql2/public_bind/dml.rs +166 -0
- package/dist-engine-src/src/sql2/public_bind/mod.rs +25 -0
- package/dist-engine-src/src/sql2/public_bind/table.rs +168 -0
- package/dist-engine-src/src/sql2/read_only.rs +10 -12
- package/dist-engine-src/src/sql2/session.rs +7 -10
- package/dist-engine-src/src/sql2/udfs/lix_timestamp.rs +76 -0
- package/dist-engine-src/src/sql2/udfs/mod.rs +8 -1
- package/dist-engine-src/src/sql2/udfs/public_call.rs +211 -0
- package/dist-engine-src/src/sql2/version_provider.rs +46 -31
- package/dist-engine-src/src/sql2/version_scope.rs +4 -4
- package/dist-engine-src/src/storage_bench.rs +1782 -325
- package/dist-engine-src/src/test_support.rs +183 -36
- package/dist-engine-src/src/tracked_state/by_file_index.rs +20 -24
- package/dist-engine-src/src/tracked_state/codec.rs +1519 -181
- package/dist-engine-src/src/tracked_state/context.rs +1155 -271
- package/dist-engine-src/src/tracked_state/diff.rs +249 -57
- package/dist-engine-src/src/tracked_state/materialization.rs +365 -103
- package/dist-engine-src/src/tracked_state/materializer.rs +488 -0
- package/dist-engine-src/src/tracked_state/merge.rs +37 -19
- package/dist-engine-src/src/tracked_state/mod.rs +8 -7
- package/dist-engine-src/src/tracked_state/storage.rs +138 -6
- package/dist-engine-src/src/tracked_state/tree.rs +695 -252
- package/dist-engine-src/src/tracked_state/types.rs +176 -6
- package/dist-engine-src/src/transaction/commit.rs +695 -435
- package/dist-engine-src/src/transaction/context.rs +551 -310
- package/dist-engine-src/src/transaction/live_state_overlay.rs +9 -8
- package/dist-engine-src/src/transaction/mod.rs +2 -0
- package/dist-engine-src/src/transaction/normalization.rs +311 -447
- package/dist-engine-src/src/transaction/prep.rs +37 -0
- package/dist-engine-src/src/transaction/schema_resolver.rs +93 -71
- package/dist-engine-src/src/transaction/staging.rs +701 -406
- package/dist-engine-src/src/transaction/types.rs +231 -122
- package/dist-engine-src/src/transaction/validation.rs +2717 -1698
- package/dist-engine-src/src/untracked_state/codec.rs +40 -96
- package/dist-engine-src/src/untracked_state/context.rs +21 -5
- package/dist-engine-src/src/untracked_state/materialization.rs +10 -104
- package/dist-engine-src/src/untracked_state/mod.rs +3 -5
- package/dist-engine-src/src/untracked_state/storage.rs +105 -57
- package/dist-engine-src/src/untracked_state/types.rs +63 -13
- package/dist-engine-src/src/version/context.rs +1 -13
- package/dist-engine-src/src/version/lifecycle.rs +221 -0
- package/dist-engine-src/src/version/mod.rs +3 -2
- package/dist-engine-src/src/version/refs.rs +12 -103
- package/dist-engine-src/src/version/stage_rows.rs +15 -19
- package/package.json +1 -1
- package/dist-engine-src/src/changelog/codec.rs +0 -321
- package/dist-engine-src/src/changelog/context.rs +0 -92
- package/dist-engine-src/src/changelog/materialization.rs +0 -121
- package/dist-engine-src/src/changelog/mod.rs +0 -13
- package/dist-engine-src/src/changelog/reader.rs +0 -20
- package/dist-engine-src/src/changelog/storage.rs +0 -220
- package/dist-engine-src/src/changelog/types.rs +0 -38
- package/dist-engine-src/src/schema/builtin/lix_change_set.json +0 -18
- package/dist-engine-src/src/schema/builtin/lix_change_set_element.json +0 -75
- package/dist-engine-src/src/schema/builtin/lix_entity_label.json +0 -63
- package/dist-engine-src/src/schema_registry.rs +0 -294
- package/dist-engine-src/src/sql2/commit_derived_provider.rs +0 -591
- package/dist-engine-src/src/tracked_state/rebuild.rs +0 -771
- package/dist-engine-src/src/tracked_state/tree_types.rs +0 -176
|
@@ -1,16 +1,19 @@
|
|
|
1
1
|
use datafusion::arrow::datatypes::Field;
|
|
2
2
|
use datafusion::arrow::record_batch::RecordBatch;
|
|
3
|
-
use datafusion::common::
|
|
3
|
+
use datafusion::common::metadata::{FieldMetadata, ScalarAndMetadata};
|
|
4
|
+
use datafusion::common::{ParamValues, ScalarValue};
|
|
4
5
|
use datafusion::logical_expr::{Expr, LogicalPlan, WriteOp};
|
|
5
6
|
use datafusion::prelude::SessionContext;
|
|
7
|
+
use datafusion::sql::parser::Statement as DataFusionStatement;
|
|
6
8
|
use serde_json::{json, Value as JsonValue};
|
|
7
|
-
use std::collections::{BTreeSet, HashSet};
|
|
9
|
+
use std::collections::{BTreeMap, BTreeSet, HashSet};
|
|
8
10
|
|
|
9
11
|
use crate::schema::schema_key_from_definition;
|
|
10
12
|
use crate::{LixError, LixNotice, SqlQueryResult, Value};
|
|
11
13
|
|
|
12
|
-
use super::
|
|
13
|
-
use super::
|
|
14
|
+
use super::predicate_typecheck::validate_json_predicate_expr_with_dfschema;
|
|
15
|
+
use super::result_metadata::{field_is_json, LIX_VALUE_TYPE_JSON, LIX_VALUE_TYPE_METADATA_KEY};
|
|
16
|
+
use super::session::{build_read_session, build_write_session, new_sql_session_context};
|
|
14
17
|
use super::write_normalization::{
|
|
15
18
|
is_binary_type, lix_file_data_type_lix_error, logical_expr_is_binary_or_null,
|
|
16
19
|
};
|
|
@@ -58,6 +61,8 @@ pub(crate) async fn create_logical_plan(
|
|
|
58
61
|
sql: &str,
|
|
59
62
|
) -> Result<SqlLogicalPlan, LixError> {
|
|
60
63
|
super::validate_supported_statement_ast(sql)?;
|
|
64
|
+
super::udfs::validate_public_udf_calls(sql)?;
|
|
65
|
+
validate_public_read_sql_surface(sql)?;
|
|
61
66
|
let session = build_read_session(ctx).await?;
|
|
62
67
|
let plan = session
|
|
63
68
|
.state()
|
|
@@ -65,6 +70,7 @@ pub(crate) async fn create_logical_plan(
|
|
|
65
70
|
.await
|
|
66
71
|
.map_err(datafusion_error_to_lix_error)?;
|
|
67
72
|
validate_supported_logical_plan(&plan)?;
|
|
73
|
+
validate_json_predicates_in_logical_plan(&plan)?;
|
|
68
74
|
let kind = classify_logical_plan(&plan);
|
|
69
75
|
let notices = history_filter_notices(&plan);
|
|
70
76
|
|
|
@@ -82,15 +88,17 @@ pub(crate) async fn create_write_logical_plan(
|
|
|
82
88
|
ctx: &mut dyn SqlWriteExecutionContext,
|
|
83
89
|
sql: &str,
|
|
84
90
|
) -> Result<SqlLogicalPlan, LixError> {
|
|
85
|
-
super::
|
|
86
|
-
|
|
91
|
+
super::udfs::validate_public_udf_calls(sql)?;
|
|
92
|
+
let visible_schemas = ctx.list_visible_schemas()?;
|
|
93
|
+
super::public_bind::validate_public_dml_sql(sql, &visible_schemas)?;
|
|
94
|
+
let statement = parse_datafusion_statement(sql)?;
|
|
95
|
+
super::validate_supported_datafusion_statement_ast(&statement)?;
|
|
96
|
+
reject_read_only_history_view_dml_from_statement(&statement, &visible_schemas)?;
|
|
87
97
|
let session = build_write_session(ctx).await?;
|
|
88
|
-
let plan = session
|
|
89
|
-
.state()
|
|
90
|
-
.create_logical_plan(sql)
|
|
91
|
-
.await
|
|
92
|
-
.map_err(datafusion_error_to_lix_error)?;
|
|
98
|
+
let plan = create_logical_plan_from_statement(&session, statement).await?;
|
|
93
99
|
validate_supported_logical_plan(&plan)?;
|
|
100
|
+
super::public_bind::validate_public_dml_plan(&plan, &visible_schemas)?;
|
|
101
|
+
validate_json_predicates_in_logical_plan(&plan)?;
|
|
94
102
|
let strict_binary_params = validate_strict_lix_file_data_writes(&plan)?;
|
|
95
103
|
let kind = classify_logical_plan(&plan);
|
|
96
104
|
|
|
@@ -103,6 +111,65 @@ pub(crate) async fn create_write_logical_plan(
|
|
|
103
111
|
})
|
|
104
112
|
}
|
|
105
113
|
|
|
114
|
+
fn validate_public_read_sql_surface(sql: &str) -> Result<(), LixError> {
|
|
115
|
+
let normalized = sql.to_ascii_lowercase();
|
|
116
|
+
if normalized.contains("lower(path)") {
|
|
117
|
+
return Err(LixError::new(
|
|
118
|
+
LixError::CODE_UNSUPPORTED_SQL,
|
|
119
|
+
"public column 'path' must be compared directly to a literal or parameter",
|
|
120
|
+
));
|
|
121
|
+
}
|
|
122
|
+
if normalized.contains("lixcol_version_id")
|
|
123
|
+
&& (normalized.contains("= lower(") || normalized.contains(" in (lower("))
|
|
124
|
+
{
|
|
125
|
+
return Err(LixError::new(
|
|
126
|
+
LixError::CODE_UNSUPPORTED_SQL,
|
|
127
|
+
"public column 'lixcol_version_id' must be compared directly to a literal or parameter",
|
|
128
|
+
));
|
|
129
|
+
}
|
|
130
|
+
Ok(())
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
fn parse_datafusion_statement(sql: &str) -> Result<DataFusionStatement, LixError> {
|
|
134
|
+
let session = new_sql_session_context();
|
|
135
|
+
let dialect = session.state().config_options().sql_parser.dialect;
|
|
136
|
+
session
|
|
137
|
+
.state()
|
|
138
|
+
.sql_to_statement(sql, &dialect)
|
|
139
|
+
.map_err(datafusion_error_to_lix_error)
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
async fn create_logical_plan_from_statement(
|
|
143
|
+
session: &SessionContext,
|
|
144
|
+
statement: DataFusionStatement,
|
|
145
|
+
) -> Result<LogicalPlan, LixError> {
|
|
146
|
+
session
|
|
147
|
+
.state()
|
|
148
|
+
.statement_to_plan(statement)
|
|
149
|
+
.await
|
|
150
|
+
.map_err(datafusion_error_to_lix_error)
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
fn validate_json_predicates_in_logical_plan(plan: &LogicalPlan) -> Result<(), LixError> {
|
|
154
|
+
match plan {
|
|
155
|
+
LogicalPlan::Filter(filter) => {
|
|
156
|
+
validate_json_predicate_expr_with_dfschema(filter.input.schema(), &filter.predicate)?;
|
|
157
|
+
}
|
|
158
|
+
LogicalPlan::TableScan(scan) => {
|
|
159
|
+
for filter in &scan.filters {
|
|
160
|
+
validate_json_predicate_expr_with_dfschema(scan.projected_schema.as_ref(), filter)?;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
_ => {}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
for input in plan.inputs() {
|
|
167
|
+
validate_json_predicates_in_logical_plan(input)?;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
Ok(())
|
|
171
|
+
}
|
|
172
|
+
|
|
106
173
|
fn validate_strict_lix_file_data_writes(plan: &LogicalPlan) -> Result<BTreeSet<usize>, LixError> {
|
|
107
174
|
let mut strict_binary_params = BTreeSet::new();
|
|
108
175
|
let LogicalPlan::Dml(dml) = plan else {
|
|
@@ -224,12 +291,9 @@ pub(crate) async fn execute_logical_plan(
|
|
|
224
291
|
.map_err(datafusion_error_to_lix_error)?;
|
|
225
292
|
if !params.is_empty() {
|
|
226
293
|
dataframe = dataframe
|
|
227
|
-
.with_param_values(
|
|
228
|
-
params
|
|
229
|
-
|
|
230
|
-
.map(scalar_value_from_lix_value)
|
|
231
|
-
.collect::<Vec<_>>(),
|
|
232
|
-
)
|
|
294
|
+
.with_param_values(ParamValues::List(
|
|
295
|
+
params.iter().map(scalar_value_from_lix_value).collect(),
|
|
296
|
+
))
|
|
233
297
|
.map_err(datafusion_error_to_lix_error)?;
|
|
234
298
|
}
|
|
235
299
|
|
|
@@ -326,11 +390,11 @@ fn sorted_parameter_names(parameter_names: &HashSet<String>) -> Vec<String> {
|
|
|
326
390
|
names
|
|
327
391
|
}
|
|
328
392
|
|
|
329
|
-
fn
|
|
330
|
-
|
|
393
|
+
fn reject_read_only_history_view_dml_from_statement(
|
|
394
|
+
statement: &DataFusionStatement,
|
|
331
395
|
visible_schemas: &[JsonValue],
|
|
332
396
|
) -> Result<(), LixError> {
|
|
333
|
-
let target_names = super::
|
|
397
|
+
let target_names = super::datafusion_statement_dml_target_table_names(statement);
|
|
334
398
|
for target_name in target_names {
|
|
335
399
|
if is_history_view_name(&target_name, visible_schemas)? {
|
|
336
400
|
return Err(read_only_history_view_error(&target_name));
|
|
@@ -419,18 +483,28 @@ fn validate_supported_logical_plan(plan: &LogicalPlan) -> Result<(), LixError> {
|
|
|
419
483
|
Ok(())
|
|
420
484
|
}
|
|
421
485
|
|
|
422
|
-
fn scalar_value_from_lix_value(value: &Value) ->
|
|
486
|
+
fn scalar_value_from_lix_value(value: &Value) -> ScalarAndMetadata {
|
|
423
487
|
match value {
|
|
424
|
-
Value::Null => ScalarValue::Null,
|
|
425
|
-
Value::Boolean(value) => ScalarValue::Boolean(Some(*value)),
|
|
426
|
-
Value::Integer(value) => ScalarValue::Int64(Some(*value)),
|
|
427
|
-
Value::Real(value) => ScalarValue::Float64(Some(*value)),
|
|
428
|
-
Value::Text(value) => ScalarValue::Utf8(Some(value.clone())),
|
|
429
|
-
Value::Json(value) =>
|
|
430
|
-
|
|
488
|
+
Value::Null => ScalarValue::Null.into(),
|
|
489
|
+
Value::Boolean(value) => ScalarValue::Boolean(Some(*value)).into(),
|
|
490
|
+
Value::Integer(value) => ScalarValue::Int64(Some(*value)).into(),
|
|
491
|
+
Value::Real(value) => ScalarValue::Float64(Some(*value)).into(),
|
|
492
|
+
Value::Text(value) => ScalarValue::Utf8(Some(value.clone())).into(),
|
|
493
|
+
Value::Json(value) => ScalarAndMetadata::new(
|
|
494
|
+
ScalarValue::Utf8(Some(value.to_string())),
|
|
495
|
+
Some(json_field_metadata()),
|
|
496
|
+
),
|
|
497
|
+
Value::Blob(value) => ScalarValue::Binary(Some(value.clone())).into(),
|
|
431
498
|
}
|
|
432
499
|
}
|
|
433
500
|
|
|
501
|
+
fn json_field_metadata() -> FieldMetadata {
|
|
502
|
+
FieldMetadata::new(BTreeMap::from([(
|
|
503
|
+
LIX_VALUE_TYPE_METADATA_KEY.to_string(),
|
|
504
|
+
LIX_VALUE_TYPE_JSON.to_string(),
|
|
505
|
+
)]))
|
|
506
|
+
}
|
|
507
|
+
|
|
434
508
|
fn datafusion_error_to_lix_error(error: datafusion::error::DataFusionError) -> LixError {
|
|
435
509
|
super::error::datafusion_error_to_lix_error(error)
|
|
436
510
|
}
|
|
@@ -660,27 +734,30 @@ mod tests {
|
|
|
660
734
|
SqlWriteExecutionContext,
|
|
661
735
|
};
|
|
662
736
|
use crate::binary_cas::BlobDataReader;
|
|
663
|
-
use crate::changelog::{CanonicalChange, ChangelogReader, ChangelogScanRequest};
|
|
664
737
|
use crate::commit_graph::{
|
|
665
|
-
CommitGraphChangeHistoryEntry, CommitGraphChangeHistoryRequest,
|
|
666
|
-
|
|
667
|
-
ReachableCommitGraphCommit,
|
|
738
|
+
CommitGraphChangeHistoryEntry, CommitGraphChangeHistoryRequest, CommitGraphCommit,
|
|
739
|
+
CommitGraphEdge, CommitGraphReader, ReachableCommitGraphCommit,
|
|
668
740
|
};
|
|
741
|
+
use crate::commit_store::CommitStoreContext;
|
|
669
742
|
use crate::functions::{
|
|
670
743
|
FunctionProvider, FunctionProviderHandle, SharedFunctionProvider, SystemFunctionProvider,
|
|
671
744
|
};
|
|
672
745
|
use crate::json_store::JsonStoreContext;
|
|
673
746
|
use crate::live_state::{
|
|
674
|
-
LiveStateContext, LiveStateReader,
|
|
747
|
+
LiveStateContext, LiveStateReader, LiveStateRowRequest, LiveStateScanRequest,
|
|
748
|
+
MaterializedLiveStateRow,
|
|
675
749
|
};
|
|
676
|
-
use crate::sql2::{
|
|
750
|
+
use crate::sql2::{CommitStoreQuerySource, SqlCommitStoreQuerySource};
|
|
677
751
|
use crate::storage::{
|
|
678
752
|
KvEntryPage, KvExistsBatch, KvGetRequest, KvKeyPage, KvScanRequest, KvValueBatch,
|
|
679
753
|
KvValuePage, StorageContext, StorageReadScope, StorageReadTransaction, StorageReader,
|
|
680
754
|
StorageWriteSet,
|
|
681
755
|
};
|
|
682
756
|
use crate::tracked_state::TrackedStateContext;
|
|
683
|
-
use crate::transaction::
|
|
757
|
+
use crate::transaction::prepare_version_ref_row;
|
|
758
|
+
use crate::transaction::types::{
|
|
759
|
+
TransactionWrite, TransactionWriteOutcome, TransactionWriteRow,
|
|
760
|
+
};
|
|
684
761
|
use crate::untracked_state::UntrackedStateContext;
|
|
685
762
|
use crate::version::VersionRefReader;
|
|
686
763
|
use crate::{Engine, ExecuteResult, SessionContext};
|
|
@@ -689,10 +766,9 @@ mod tests {
|
|
|
689
766
|
struct DummyBlobReader;
|
|
690
767
|
struct DummyLiveStateReader;
|
|
691
768
|
struct RowsLiveStateReader {
|
|
692
|
-
rows: Vec<
|
|
769
|
+
rows: Vec<MaterializedLiveStateRow>,
|
|
693
770
|
}
|
|
694
771
|
struct BackendBlobReader(StorageContext);
|
|
695
|
-
struct DummyChangelogReader;
|
|
696
772
|
struct DummyCommitGraphReader;
|
|
697
773
|
struct DummyVersionRefReader;
|
|
698
774
|
struct TestReadTransaction(StorageContext);
|
|
@@ -747,7 +823,7 @@ mod tests {
|
|
|
747
823
|
|
|
748
824
|
#[derive(Clone)]
|
|
749
825
|
struct CapturedStageWrite {
|
|
750
|
-
rows: Vec<
|
|
826
|
+
rows: Vec<TransactionWriteRow>,
|
|
751
827
|
}
|
|
752
828
|
|
|
753
829
|
impl CapturedStageWrite {
|
|
@@ -759,7 +835,7 @@ mod tests {
|
|
|
759
835
|
}
|
|
760
836
|
|
|
761
837
|
struct CapturedStageOverlay {
|
|
762
|
-
rows: Vec<
|
|
838
|
+
rows: Vec<TransactionWriteRow>,
|
|
763
839
|
}
|
|
764
840
|
|
|
765
841
|
impl CapturedStageOverlay {
|
|
@@ -787,33 +863,31 @@ mod tests {
|
|
|
787
863
|
struct CapturedStageRow {
|
|
788
864
|
entity_id: String,
|
|
789
865
|
schema_key: String,
|
|
790
|
-
schema_version: String,
|
|
791
866
|
version_id: String,
|
|
792
867
|
file_id: Option<String>,
|
|
793
868
|
snapshot_content: Option<String>,
|
|
794
|
-
metadata: Option<
|
|
869
|
+
metadata: Option<String>,
|
|
795
870
|
global: bool,
|
|
796
871
|
untracked: bool,
|
|
797
872
|
tombstone: bool,
|
|
798
873
|
}
|
|
799
874
|
|
|
800
|
-
impl From<
|
|
801
|
-
fn from(row:
|
|
875
|
+
impl From<TransactionWriteRow> for CapturedStageRow {
|
|
876
|
+
fn from(row: TransactionWriteRow) -> Self {
|
|
802
877
|
Self {
|
|
803
878
|
entity_id: row
|
|
804
879
|
.entity_id
|
|
805
880
|
.expect("captured staged row should carry entity_id")
|
|
806
|
-
.
|
|
881
|
+
.as_json_array_text()
|
|
807
882
|
.expect("captured staged row should project entity_id"),
|
|
808
883
|
schema_key: row.schema_key,
|
|
809
|
-
schema_version: row.schema_version,
|
|
810
884
|
version_id: row.version_id,
|
|
811
885
|
file_id: row.file_id,
|
|
812
886
|
global: row.global,
|
|
813
887
|
untracked: row.untracked,
|
|
814
|
-
tombstone: row.
|
|
815
|
-
snapshot_content: row.
|
|
816
|
-
metadata: row.metadata,
|
|
888
|
+
tombstone: row.snapshot.is_none(),
|
|
889
|
+
snapshot_content: row.snapshot.map(|snapshot| snapshot.to_string()),
|
|
890
|
+
metadata: row.metadata.map(|metadata| metadata.to_string()),
|
|
817
891
|
}
|
|
818
892
|
}
|
|
819
893
|
}
|
|
@@ -842,13 +916,13 @@ mod tests {
|
|
|
842
916
|
Arc::clone(&self.blob_reader)
|
|
843
917
|
}
|
|
844
918
|
|
|
845
|
-
fn
|
|
919
|
+
fn commit_store_query_source(&self) -> SqlCommitStoreQuerySource {
|
|
846
920
|
let base_scope = test_read_scope(StorageContext::new(Arc::new(
|
|
847
921
|
crate::backend::testing::UnitTestBackend::new(),
|
|
848
922
|
)));
|
|
849
923
|
let read_scope = StorageReadScope::new(base_scope.store());
|
|
850
|
-
|
|
851
|
-
|
|
924
|
+
CommitStoreQuerySource {
|
|
925
|
+
commit_store_reader: Arc::new(CommitStoreContext::new().reader(read_scope.store())),
|
|
852
926
|
json_reader: JsonStoreContext::new().reader(read_scope.store()),
|
|
853
927
|
}
|
|
854
928
|
}
|
|
@@ -898,7 +972,7 @@ mod tests {
|
|
|
898
972
|
async fn scan_live_state(
|
|
899
973
|
&mut self,
|
|
900
974
|
request: &LiveStateScanRequest,
|
|
901
|
-
) -> Result<Vec<
|
|
975
|
+
) -> Result<Vec<MaterializedLiveStateRow>, LixError> {
|
|
902
976
|
self.live_state.scan_rows(request).await
|
|
903
977
|
}
|
|
904
978
|
|
|
@@ -909,23 +983,26 @@ mod tests {
|
|
|
909
983
|
Ok(Some(format!("commit-{version_id}")))
|
|
910
984
|
}
|
|
911
985
|
|
|
912
|
-
async fn stage_write(
|
|
986
|
+
async fn stage_write(
|
|
987
|
+
&mut self,
|
|
988
|
+
write: TransactionWrite,
|
|
989
|
+
) -> Result<TransactionWriteOutcome, LixError> {
|
|
913
990
|
let count = match &write {
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
991
|
+
TransactionWrite::Rows { rows, .. } => rows.len() as u64,
|
|
992
|
+
TransactionWrite::RowsWithFileData { count, .. } => *count,
|
|
993
|
+
TransactionWrite::AdoptedChanges { changes } => changes.len() as u64,
|
|
917
994
|
};
|
|
918
995
|
let rows = match write {
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
996
|
+
TransactionWrite::Rows { rows, .. } => rows,
|
|
997
|
+
TransactionWrite::RowsWithFileData { rows, .. } => rows,
|
|
998
|
+
TransactionWrite::AdoptedChanges { .. } => Vec::new(),
|
|
922
999
|
};
|
|
923
1000
|
self.staged_writes
|
|
924
1001
|
.lock()
|
|
925
1002
|
.expect("staged writes lock")
|
|
926
1003
|
.deltas
|
|
927
1004
|
.push(CapturedStageWrite { rows });
|
|
928
|
-
Ok(
|
|
1005
|
+
Ok(TransactionWriteOutcome { count })
|
|
929
1006
|
}
|
|
930
1007
|
}
|
|
931
1008
|
|
|
@@ -938,20 +1015,6 @@ mod tests {
|
|
|
938
1015
|
execute_logical_plan(plan, params).await
|
|
939
1016
|
}
|
|
940
1017
|
|
|
941
|
-
#[async_trait]
|
|
942
|
-
impl ChangelogReader for DummyChangelogReader {
|
|
943
|
-
async fn load_change(&self, _change_id: &str) -> Result<Option<CanonicalChange>, LixError> {
|
|
944
|
-
Ok(None)
|
|
945
|
-
}
|
|
946
|
-
|
|
947
|
-
async fn scan_changes(
|
|
948
|
-
&self,
|
|
949
|
-
_request: &ChangelogScanRequest,
|
|
950
|
-
) -> Result<Vec<CanonicalChange>, LixError> {
|
|
951
|
-
Ok(Vec::new())
|
|
952
|
-
}
|
|
953
|
-
}
|
|
954
|
-
|
|
955
1018
|
#[async_trait]
|
|
956
1019
|
impl VersionRefReader for DummyVersionRefReader {
|
|
957
1020
|
async fn load_head(
|
|
@@ -1009,17 +1072,6 @@ mod tests {
|
|
|
1009
1072
|
Vec::new()
|
|
1010
1073
|
}
|
|
1011
1074
|
|
|
1012
|
-
fn change_sets(&self, _commits: &[CommitGraphCommit]) -> Vec<CommitGraphChangeSet> {
|
|
1013
|
-
Vec::new()
|
|
1014
|
-
}
|
|
1015
|
-
|
|
1016
|
-
async fn change_set_elements(
|
|
1017
|
-
&mut self,
|
|
1018
|
-
_commits: &[CommitGraphCommit],
|
|
1019
|
-
) -> Result<Vec<CommitGraphChangeSetElement>, LixError> {
|
|
1020
|
-
Ok(Vec::new())
|
|
1021
|
-
}
|
|
1022
|
-
|
|
1023
1075
|
async fn change_history_from_commit(
|
|
1024
1076
|
&mut self,
|
|
1025
1077
|
_start_commit_id: &str,
|
|
@@ -1034,14 +1086,14 @@ mod tests {
|
|
|
1034
1086
|
async fn scan_rows(
|
|
1035
1087
|
&self,
|
|
1036
1088
|
_request: &LiveStateScanRequest,
|
|
1037
|
-
) -> Result<Vec<
|
|
1089
|
+
) -> Result<Vec<MaterializedLiveStateRow>, LixError> {
|
|
1038
1090
|
Ok(vec![])
|
|
1039
1091
|
}
|
|
1040
1092
|
|
|
1041
1093
|
async fn load_row(
|
|
1042
1094
|
&self,
|
|
1043
1095
|
_request: &LiveStateRowRequest,
|
|
1044
|
-
) -> Result<Option<
|
|
1096
|
+
) -> Result<Option<MaterializedLiveStateRow>, LixError> {
|
|
1045
1097
|
Ok(None)
|
|
1046
1098
|
}
|
|
1047
1099
|
}
|
|
@@ -1051,14 +1103,14 @@ mod tests {
|
|
|
1051
1103
|
async fn scan_rows(
|
|
1052
1104
|
&self,
|
|
1053
1105
|
_request: &LiveStateScanRequest,
|
|
1054
|
-
) -> Result<Vec<
|
|
1106
|
+
) -> Result<Vec<MaterializedLiveStateRow>, LixError> {
|
|
1055
1107
|
Ok(self.rows.clone())
|
|
1056
1108
|
}
|
|
1057
1109
|
|
|
1058
1110
|
async fn load_row(
|
|
1059
1111
|
&self,
|
|
1060
1112
|
_request: &LiveStateRowRequest,
|
|
1061
|
-
) -> Result<Option<
|
|
1113
|
+
) -> Result<Option<MaterializedLiveStateRow>, LixError> {
|
|
1062
1114
|
Ok(None)
|
|
1063
1115
|
}
|
|
1064
1116
|
}
|
|
@@ -1069,7 +1121,10 @@ mod tests {
|
|
|
1069
1121
|
&self,
|
|
1070
1122
|
hashes: &[crate::binary_cas::BlobHash],
|
|
1071
1123
|
) -> Result<crate::binary_cas::BlobBytesBatch, LixError> {
|
|
1072
|
-
Ok(crate::binary_cas::BlobBytesBatch::
|
|
1124
|
+
Ok(crate::binary_cas::BlobBytesBatch::new(vec![
|
|
1125
|
+
None;
|
|
1126
|
+
hashes.len()
|
|
1127
|
+
]))
|
|
1073
1128
|
}
|
|
1074
1129
|
}
|
|
1075
1130
|
|
|
@@ -1085,17 +1140,14 @@ mod tests {
|
|
|
1085
1140
|
}
|
|
1086
1141
|
}
|
|
1087
1142
|
|
|
1088
|
-
fn live_lix_state_row(entity_id: &str, metadata: Option<&str>) ->
|
|
1089
|
-
|
|
1090
|
-
entity_id: crate::entity_identity::EntityIdentity::
|
|
1091
|
-
.expect("entity id should decode"),
|
|
1143
|
+
fn live_lix_state_row(entity_id: &str, metadata: Option<&str>) -> MaterializedLiveStateRow {
|
|
1144
|
+
MaterializedLiveStateRow {
|
|
1145
|
+
entity_id: crate::entity_identity::EntityIdentity::single(entity_id),
|
|
1092
1146
|
schema_key: "lix_key_value".to_string(),
|
|
1093
1147
|
file_id: None,
|
|
1094
1148
|
snapshot_content: Some("{\"key\":\"hello\",\"value\":\"world\"}".to_string()),
|
|
1095
|
-
metadata: metadata.map(
|
|
1096
|
-
|
|
1097
|
-
}),
|
|
1098
|
-
schema_version: "1".to_string(),
|
|
1149
|
+
metadata: metadata.map(str::to_string),
|
|
1150
|
+
deleted: false,
|
|
1099
1151
|
version_id: "version-a".to_string(),
|
|
1100
1152
|
change_id: Some(format!("change-{entity_id}")),
|
|
1101
1153
|
commit_id: Some(format!("commit-{entity_id}")),
|
|
@@ -1106,15 +1158,14 @@ mod tests {
|
|
|
1106
1158
|
}
|
|
1107
1159
|
}
|
|
1108
1160
|
|
|
1109
|
-
fn live_entity_row(entity_id: &str, version_id: &str, value: &str) ->
|
|
1110
|
-
|
|
1111
|
-
entity_id: crate::entity_identity::EntityIdentity::
|
|
1112
|
-
.expect("entity id should decode"),
|
|
1161
|
+
fn live_entity_row(entity_id: &str, version_id: &str, value: &str) -> MaterializedLiveStateRow {
|
|
1162
|
+
MaterializedLiveStateRow {
|
|
1163
|
+
entity_id: crate::entity_identity::EntityIdentity::single(entity_id),
|
|
1113
1164
|
schema_key: "test_state_schema".to_string(),
|
|
1114
1165
|
file_id: None,
|
|
1115
1166
|
snapshot_content: Some(format!("{{\"value\":\"{value}\"}}")),
|
|
1116
|
-
metadata: Some(json!({ "source": entity_id })),
|
|
1117
|
-
|
|
1167
|
+
metadata: Some(json!({ "source": entity_id }).to_string()),
|
|
1168
|
+
deleted: false,
|
|
1118
1169
|
version_id: version_id.to_string(),
|
|
1119
1170
|
change_id: Some(format!("change-{entity_id}")),
|
|
1120
1171
|
commit_id: Some(format!("commit-{entity_id}")),
|
|
@@ -1131,10 +1182,9 @@ mod tests {
|
|
|
1131
1182
|
parent_id: Option<&str>,
|
|
1132
1183
|
name: &str,
|
|
1133
1184
|
hidden: bool,
|
|
1134
|
-
) ->
|
|
1135
|
-
|
|
1136
|
-
entity_id: crate::entity_identity::EntityIdentity::
|
|
1137
|
-
.expect("entity id should decode"),
|
|
1185
|
+
) -> MaterializedLiveStateRow {
|
|
1186
|
+
MaterializedLiveStateRow {
|
|
1187
|
+
entity_id: crate::entity_identity::EntityIdentity::single(entity_id),
|
|
1138
1188
|
schema_key: "lix_directory_descriptor".to_string(),
|
|
1139
1189
|
file_id: None,
|
|
1140
1190
|
snapshot_content: Some(
|
|
@@ -1146,8 +1196,8 @@ mod tests {
|
|
|
1146
1196
|
})
|
|
1147
1197
|
.to_string(),
|
|
1148
1198
|
),
|
|
1149
|
-
metadata: Some(json!({ "source": entity_id })),
|
|
1150
|
-
|
|
1199
|
+
metadata: Some(json!({ "source": entity_id }).to_string()),
|
|
1200
|
+
deleted: false,
|
|
1151
1201
|
version_id: version_id.to_string(),
|
|
1152
1202
|
change_id: Some(format!("change-{entity_id}")),
|
|
1153
1203
|
commit_id: Some(format!("commit-{entity_id}")),
|
|
@@ -1164,10 +1214,9 @@ mod tests {
|
|
|
1164
1214
|
directory_id: Option<&str>,
|
|
1165
1215
|
name: &str,
|
|
1166
1216
|
hidden: bool,
|
|
1167
|
-
) ->
|
|
1168
|
-
|
|
1169
|
-
entity_id: crate::entity_identity::EntityIdentity::
|
|
1170
|
-
.expect("entity id should decode"),
|
|
1217
|
+
) -> MaterializedLiveStateRow {
|
|
1218
|
+
MaterializedLiveStateRow {
|
|
1219
|
+
entity_id: crate::entity_identity::EntityIdentity::single(entity_id),
|
|
1171
1220
|
schema_key: "lix_file_descriptor".to_string(),
|
|
1172
1221
|
file_id: None,
|
|
1173
1222
|
snapshot_content: Some(
|
|
@@ -1179,8 +1228,8 @@ mod tests {
|
|
|
1179
1228
|
})
|
|
1180
1229
|
.to_string(),
|
|
1181
1230
|
),
|
|
1182
|
-
metadata: Some(json!({ "source": entity_id })),
|
|
1183
|
-
|
|
1231
|
+
metadata: Some(json!({ "source": entity_id }).to_string()),
|
|
1232
|
+
deleted: false,
|
|
1184
1233
|
version_id: version_id.to_string(),
|
|
1185
1234
|
change_id: Some(format!("change-{entity_id}")),
|
|
1186
1235
|
commit_id: Some(format!("commit-{entity_id}")),
|
|
@@ -1317,7 +1366,7 @@ mod tests {
|
|
|
1317
1366
|
}));
|
|
1318
1367
|
}
|
|
1319
1368
|
|
|
1320
|
-
async fn
|
|
1369
|
+
async fn setup_engine_history_fixture() -> Result<(SessionContext, String), LixError> {
|
|
1321
1370
|
let backend = crate::backend::testing::UnitTestBackend::new();
|
|
1322
1371
|
let init_receipt = Engine::initialize(Box::new(backend.clone())).await?;
|
|
1323
1372
|
let engine = Engine::new(Box::new(backend)).await?;
|
|
@@ -1327,9 +1376,9 @@ mod tests {
|
|
|
1327
1376
|
.execute(
|
|
1328
1377
|
"INSERT INTO lix_registered_schema (value, lixcol_global, lixcol_untracked) \
|
|
1329
1378
|
VALUES (\
|
|
1330
|
-
lix_json('{\"x-lix-key\":\"test_state_schema\",\"
|
|
1379
|
+
lix_json('{\"x-lix-key\":\"test_state_schema\",\"type\":\"object\",\"properties\":{\"value\":{\"type\":\"string\"},\"count\":{\"type\":\"integer\"}},\"required\":[\"value\",\"count\"],\"additionalProperties\":false}'),\
|
|
1331
1380
|
false,\
|
|
1332
|
-
|
|
1381
|
+
false\
|
|
1333
1382
|
)",
|
|
1334
1383
|
&[],
|
|
1335
1384
|
)
|
|
@@ -1337,8 +1386,8 @@ mod tests {
|
|
|
1337
1386
|
session
|
|
1338
1387
|
.execute(
|
|
1339
1388
|
"INSERT INTO test_state_schema \
|
|
1340
|
-
|
|
1341
|
-
|
|
1389
|
+
(lixcol_entity_id, value, count, lixcol_metadata, lixcol_untracked) \
|
|
1390
|
+
VALUES (lix_json('[\"entity-history\"]'), 'A', 7, '{\"source\":\"history\"}', false)",
|
|
1342
1391
|
&[],
|
|
1343
1392
|
)
|
|
1344
1393
|
.await?;
|
|
@@ -1616,23 +1665,23 @@ mod tests {
|
|
|
1616
1665
|
|
|
1617
1666
|
#[tokio::test]
|
|
1618
1667
|
async fn execute_sql_reads_lix_state_history_from_history_context() {
|
|
1619
|
-
let (session, head_commit_id) =
|
|
1668
|
+
let (session, head_commit_id) = setup_engine_history_fixture()
|
|
1620
1669
|
.await
|
|
1621
1670
|
.expect("history fixture should initialize");
|
|
1622
1671
|
let result = session
|
|
1623
1672
|
.execute(
|
|
1624
1673
|
&format!(
|
|
1625
1674
|
"SELECT entity_id, snapshot_content, metadata, depth, start_commit_id \
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
|
|
1675
|
+
FROM lix_state_history \
|
|
1676
|
+
WHERE schema_key = 'test_state_schema' \
|
|
1677
|
+
AND entity_id = lix_json('[\"entity-history\"]') \
|
|
1678
|
+
AND start_commit_id = '{head_commit_id}' \
|
|
1679
|
+
AND depth >= 0"
|
|
1631
1680
|
),
|
|
1632
1681
|
&[],
|
|
1633
1682
|
)
|
|
1634
1683
|
.await
|
|
1635
|
-
.expect("sql2 execute should read lix_state_history through real
|
|
1684
|
+
.expect("sql2 execute should read lix_state_history through real engine context");
|
|
1636
1685
|
let (columns, rows) = rows_from_execute_result(result);
|
|
1637
1686
|
|
|
1638
1687
|
assert_eq!(
|
|
@@ -1646,7 +1695,7 @@ mod tests {
|
|
|
1646
1695
|
]
|
|
1647
1696
|
);
|
|
1648
1697
|
assert_eq!(rows.len(), 1);
|
|
1649
|
-
assert_eq!(rows[0][0], Value::
|
|
1698
|
+
assert_eq!(rows[0][0], Value::Json(json!(["entity-history"])));
|
|
1650
1699
|
assert_eq!(rows[0][1], Value::Json(json!({"count": 7, "value": "A"})));
|
|
1651
1700
|
assert_eq!(rows[0][2], Value::Json(json!({"source": "history"})));
|
|
1652
1701
|
assert!(matches!(rows[0][3], Value::Integer(_)));
|
|
@@ -1655,21 +1704,21 @@ mod tests {
|
|
|
1655
1704
|
|
|
1656
1705
|
#[tokio::test]
|
|
1657
1706
|
async fn execute_sql_reads_entity_history_view_from_history_context() {
|
|
1658
|
-
let (session, head_commit_id) =
|
|
1707
|
+
let (session, head_commit_id) = setup_engine_history_fixture()
|
|
1659
1708
|
.await
|
|
1660
1709
|
.expect("history fixture should initialize");
|
|
1661
1710
|
let result = session
|
|
1662
1711
|
.execute(
|
|
1663
1712
|
&format!(
|
|
1664
1713
|
"SELECT value, count, lixcol_entity_id, lixcol_start_commit_id, lixcol_depth \
|
|
1665
|
-
|
|
1666
|
-
|
|
1667
|
-
|
|
1714
|
+
FROM test_state_schema_history \
|
|
1715
|
+
WHERE lixcol_start_commit_id = '{head_commit_id}' \
|
|
1716
|
+
AND lixcol_entity_id = lix_json('[\"entity-history\"]')"
|
|
1668
1717
|
),
|
|
1669
1718
|
&[],
|
|
1670
1719
|
)
|
|
1671
1720
|
.await
|
|
1672
|
-
.expect("sql2 execute should read entity history through real
|
|
1721
|
+
.expect("sql2 execute should read entity history through real engine context");
|
|
1673
1722
|
let (columns, rows) = rows_from_execute_result(result);
|
|
1674
1723
|
|
|
1675
1724
|
assert_eq!(
|
|
@@ -1685,14 +1734,14 @@ mod tests {
|
|
|
1685
1734
|
assert_eq!(rows.len(), 1);
|
|
1686
1735
|
assert_eq!(rows[0][0], Value::Text("A".to_string()));
|
|
1687
1736
|
assert_eq!(rows[0][1], Value::Integer(7));
|
|
1688
|
-
assert_eq!(rows[0][2], Value::
|
|
1737
|
+
assert_eq!(rows[0][2], Value::Json(json!(["entity-history"])));
|
|
1689
1738
|
assert_eq!(rows[0][3], Value::Text(head_commit_id));
|
|
1690
1739
|
assert!(matches!(rows[0][4], Value::Integer(_)));
|
|
1691
1740
|
}
|
|
1692
1741
|
|
|
1693
1742
|
#[tokio::test]
|
|
1694
1743
|
async fn execute_sql_reads_directory_history_view_from_history_context() {
|
|
1695
|
-
let (session, head_commit_id) =
|
|
1744
|
+
let (session, head_commit_id) = setup_engine_history_fixture()
|
|
1696
1745
|
.await
|
|
1697
1746
|
.expect("history fixture should initialize");
|
|
1698
1747
|
let result = session
|
|
@@ -1705,7 +1754,7 @@ mod tests {
|
|
|
1705
1754
|
&[],
|
|
1706
1755
|
)
|
|
1707
1756
|
.await
|
|
1708
|
-
.expect("sql2 execute should read directory history through real
|
|
1757
|
+
.expect("sql2 execute should read directory history through real engine context");
|
|
1709
1758
|
assert!(
|
|
1710
1759
|
result.notices().is_empty(),
|
|
1711
1760
|
"identity-filtered directory history should not emit soft notices"
|
|
@@ -1754,7 +1803,7 @@ mod tests {
|
|
|
1754
1803
|
|
|
1755
1804
|
#[tokio::test]
|
|
1756
1805
|
async fn execute_sql_reads_file_history_view_from_history_context() {
|
|
1757
|
-
let (session, head_commit_id) =
|
|
1806
|
+
let (session, head_commit_id) = setup_engine_history_fixture()
|
|
1758
1807
|
.await
|
|
1759
1808
|
.expect("history fixture should initialize");
|
|
1760
1809
|
let result = session
|
|
@@ -1770,7 +1819,7 @@ mod tests {
|
|
|
1770
1819
|
&[],
|
|
1771
1820
|
)
|
|
1772
1821
|
.await
|
|
1773
|
-
.expect("sql2 execute should read file history through real
|
|
1822
|
+
.expect("sql2 execute should read file history through real engine context");
|
|
1774
1823
|
assert!(
|
|
1775
1824
|
result.notices().is_empty(),
|
|
1776
1825
|
"identity-filtered file history should not emit soft notices"
|
|
@@ -1815,6 +1864,37 @@ mod tests {
|
|
|
1815
1864
|
);
|
|
1816
1865
|
}
|
|
1817
1866
|
|
|
1867
|
+
#[tokio::test]
|
|
1868
|
+
async fn execute_sql_rejects_writes_to_history_views_before_planning() {
|
|
1869
|
+
for sql in [
|
|
1870
|
+
"DELETE FROM lix_state_history",
|
|
1871
|
+
"DELETE FROM LIX_STATE_HISTORY",
|
|
1872
|
+
"DELETE FROM main.LIX_STATE_HISTORY",
|
|
1873
|
+
"EXPLAIN DELETE FROM lix_state_history",
|
|
1874
|
+
] {
|
|
1875
|
+
let blob_reader: Arc<dyn BlobDataReader> = Arc::new(DummyBlobReader);
|
|
1876
|
+
let live_state = Arc::new(DummyLiveStateReader);
|
|
1877
|
+
let staged_writes = Arc::new(Mutex::new(CapturingStagedWrites::default()));
|
|
1878
|
+
let mut ctx = DummySqlWriteExecutionContext {
|
|
1879
|
+
active_version_id: "version-a",
|
|
1880
|
+
blob_reader,
|
|
1881
|
+
live_state,
|
|
1882
|
+
staged_writes,
|
|
1883
|
+
schema_definitions: vec![],
|
|
1884
|
+
};
|
|
1885
|
+
|
|
1886
|
+
let error = execute_write_sql(&mut ctx, sql, &[])
|
|
1887
|
+
.await
|
|
1888
|
+
.expect_err("history views are read-only");
|
|
1889
|
+
|
|
1890
|
+
assert_eq!(error.code, LixError::CODE_READ_ONLY, "{sql}");
|
|
1891
|
+
assert_eq!(
|
|
1892
|
+
error.message, "DML cannot write read-only history view 'lix_state_history'",
|
|
1893
|
+
"{sql}"
|
|
1894
|
+
);
|
|
1895
|
+
}
|
|
1896
|
+
}
|
|
1897
|
+
|
|
1818
1898
|
#[tokio::test]
|
|
1819
1899
|
async fn execute_sql_insert_into_lix_state_values_stages_write() {
|
|
1820
1900
|
let blob_reader: Arc<dyn BlobDataReader> = Arc::new(DummyBlobReader);
|
|
@@ -1829,14 +1909,14 @@ mod tests {
|
|
|
1829
1909
|
};
|
|
1830
1910
|
|
|
1831
1911
|
let result = execute_write_sql(
|
|
1832
|
-
|
|
1833
|
-
|
|
1834
|
-
|
|
1835
|
-
|
|
1836
|
-
|
|
1837
|
-
|
|
1838
|
-
|
|
1839
|
-
|
|
1912
|
+
&mut ctx,
|
|
1913
|
+
"INSERT INTO lix_state (\
|
|
1914
|
+
entity_id, schema_key, file_id, snapshot_content, metadata, global, untracked\
|
|
1915
|
+
) VALUES (\
|
|
1916
|
+
lix_json('[\"entity-1\"]'), 'lix_key_value', NULL, '{\"key\":\"hello\",\"value\":\"world\"}', '{\"source\":\"sql\"}', false, false\
|
|
1917
|
+
)",
|
|
1918
|
+
&[],
|
|
1919
|
+
)
|
|
1840
1920
|
.await
|
|
1841
1921
|
.expect("INSERT INTO lix_state VALUES should stage write");
|
|
1842
1922
|
|
|
@@ -1850,8 +1930,7 @@ mod tests {
|
|
|
1850
1930
|
.expect("staged delta should expose pending overlay");
|
|
1851
1931
|
let rows = overlay.visible_semantic_rows(false, "lix_key_value");
|
|
1852
1932
|
assert_eq!(rows.len(), 1);
|
|
1853
|
-
assert_eq!(rows[0].entity_id, "entity-1");
|
|
1854
|
-
assert_eq!(rows[0].schema_version, "1");
|
|
1933
|
+
assert_eq!(rows[0].entity_id, "[\"entity-1\"]");
|
|
1855
1934
|
assert_eq!(rows[0].version_id, "version-a");
|
|
1856
1935
|
assert!(!rows[0].global);
|
|
1857
1936
|
assert!(!rows[0].untracked);
|
|
@@ -1859,7 +1938,7 @@ mod tests {
|
|
|
1859
1938
|
rows[0].snapshot_content.as_deref(),
|
|
1860
1939
|
Some("{\"key\":\"hello\",\"value\":\"world\"}")
|
|
1861
1940
|
);
|
|
1862
|
-
assert_eq!(rows[0].metadata.
|
|
1941
|
+
assert_eq!(rows[0].metadata.as_deref(), Some("{\"source\":\"sql\"}"));
|
|
1863
1942
|
}
|
|
1864
1943
|
|
|
1865
1944
|
#[tokio::test]
|
|
@@ -1876,14 +1955,14 @@ mod tests {
|
|
|
1876
1955
|
};
|
|
1877
1956
|
|
|
1878
1957
|
let result = execute_write_sql(
|
|
1879
|
-
|
|
1880
|
-
|
|
1881
|
-
|
|
1882
|
-
|
|
1883
|
-
|
|
1884
|
-
|
|
1885
|
-
|
|
1886
|
-
|
|
1958
|
+
&mut ctx,
|
|
1959
|
+
"INSERT INTO lix_state (\
|
|
1960
|
+
entity_id, schema_key, file_id, snapshot_content, metadata\
|
|
1961
|
+
) VALUES (\
|
|
1962
|
+
lix_json('[\"entity-defaults\"]'), 'lix_key_value', NULL, '{\"key\":\"hello\",\"value\":\"defaults\"}', NULL\
|
|
1963
|
+
)",
|
|
1964
|
+
&[],
|
|
1965
|
+
)
|
|
1887
1966
|
.await
|
|
1888
1967
|
.expect("INSERT INTO lix_state should default bookkeeping flags");
|
|
1889
1968
|
|
|
@@ -1897,7 +1976,7 @@ mod tests {
|
|
|
1897
1976
|
.expect("staged delta should expose pending overlay");
|
|
1898
1977
|
let rows = overlay.visible_semantic_rows(false, "lix_key_value");
|
|
1899
1978
|
assert_eq!(rows.len(), 1);
|
|
1900
|
-
assert_eq!(rows[0].entity_id, "entity-defaults");
|
|
1979
|
+
assert_eq!(rows[0].entity_id, "[\"entity-defaults\"]");
|
|
1901
1980
|
assert_eq!(rows[0].version_id, "version-a");
|
|
1902
1981
|
assert!(!rows[0].global);
|
|
1903
1982
|
assert!(!rows[0].untracked);
|
|
@@ -1919,15 +1998,14 @@ mod tests {
|
|
|
1919
1998
|
let result = execute_write_sql(
|
|
1920
1999
|
&mut ctx,
|
|
1921
2000
|
"INSERT INTO lix_state (\
|
|
1922
|
-
|
|
1923
|
-
|
|
1924
|
-
|
|
1925
|
-
|
|
1926
|
-
|
|
1927
|
-
|
|
2001
|
+
entity_id, schema_key, file_id, snapshot_content, metadata, global, untracked\
|
|
2002
|
+
) \
|
|
2003
|
+
SELECT \
|
|
2004
|
+
lix_json('[\"entity-from-select\"]') AS entity_id, \
|
|
2005
|
+
'lix_key_value' AS schema_key, \
|
|
2006
|
+
NULL AS file_id, \
|
|
1928
2007
|
'{\"key\":\"hello\",\"value\":\"from-select\"}' AS snapshot_content, \
|
|
1929
2008
|
'{\"source\":\"select\"}' AS metadata, \
|
|
1930
|
-
'1' AS schema_version, \
|
|
1931
2009
|
false AS global, \
|
|
1932
2010
|
false AS untracked",
|
|
1933
2011
|
&[],
|
|
@@ -1945,17 +2023,13 @@ mod tests {
|
|
|
1945
2023
|
.expect("staged delta should expose pending overlay");
|
|
1946
2024
|
let rows = overlay.visible_semantic_rows(false, "lix_key_value");
|
|
1947
2025
|
assert_eq!(rows.len(), 1);
|
|
1948
|
-
assert_eq!(rows[0].entity_id, "entity-from-select");
|
|
1949
|
-
assert_eq!(rows[0].schema_version, "1");
|
|
2026
|
+
assert_eq!(rows[0].entity_id, "[\"entity-from-select\"]");
|
|
1950
2027
|
assert_eq!(rows[0].version_id, "version-a");
|
|
1951
2028
|
assert_eq!(
|
|
1952
2029
|
rows[0].snapshot_content.as_deref(),
|
|
1953
2030
|
Some("{\"key\":\"hello\",\"value\":\"from-select\"}")
|
|
1954
2031
|
);
|
|
1955
|
-
assert_eq!(
|
|
1956
|
-
rows[0].metadata.as_ref(),
|
|
1957
|
-
Some(&json!({"source": "select"}))
|
|
1958
|
-
);
|
|
2032
|
+
assert_eq!(rows[0].metadata.as_deref(), Some("{\"source\":\"select\"}"));
|
|
1959
2033
|
}
|
|
1960
2034
|
|
|
1961
2035
|
#[tokio::test]
|
|
@@ -1970,7 +2044,6 @@ mod tests {
|
|
|
1970
2044
|
staged_writes: Arc::clone(&staged_writes),
|
|
1971
2045
|
schema_definitions: vec![json!({
|
|
1972
2046
|
"x-lix-key": "test_state_schema",
|
|
1973
|
-
"x-lix-version": "1",
|
|
1974
2047
|
"type": "object",
|
|
1975
2048
|
"properties": {
|
|
1976
2049
|
"value": { "type": "string" }
|
|
@@ -1981,8 +2054,8 @@ mod tests {
|
|
|
1981
2054
|
let result = execute_write_sql(
|
|
1982
2055
|
&mut ctx,
|
|
1983
2056
|
"INSERT INTO test_state_schema_by_version (\
|
|
1984
|
-
|
|
1985
|
-
|
|
2057
|
+
lixcol_entity_id, lixcol_version_id, value\
|
|
2058
|
+
) VALUES (lix_json('[\"entity-c\"]'), 'version-b', 'C')",
|
|
1986
2059
|
&[],
|
|
1987
2060
|
)
|
|
1988
2061
|
.await
|
|
@@ -1998,8 +2071,7 @@ mod tests {
|
|
|
1998
2071
|
.expect("staged delta should expose pending overlay");
|
|
1999
2072
|
let rows = overlay.visible_semantic_rows(false, "test_state_schema");
|
|
2000
2073
|
assert_eq!(rows.len(), 1);
|
|
2001
|
-
assert_eq!(rows[0].entity_id, "entity-c");
|
|
2002
|
-
assert_eq!(rows[0].schema_version, "1");
|
|
2074
|
+
assert_eq!(rows[0].entity_id, "[\"entity-c\"]");
|
|
2003
2075
|
assert_eq!(rows[0].version_id, "version-b");
|
|
2004
2076
|
assert!(!rows[0].global);
|
|
2005
2077
|
assert!(!rows[0].untracked);
|
|
@@ -2021,7 +2093,6 @@ mod tests {
|
|
|
2021
2093
|
staged_writes: Arc::clone(&staged_writes),
|
|
2022
2094
|
schema_definitions: vec![json!({
|
|
2023
2095
|
"x-lix-key": "test_state_schema",
|
|
2024
|
-
"x-lix-version": "1",
|
|
2025
2096
|
"type": "object",
|
|
2026
2097
|
"properties": {
|
|
2027
2098
|
"value": { "type": "string" }
|
|
@@ -2032,7 +2103,7 @@ mod tests {
|
|
|
2032
2103
|
let result = execute_write_sql(
|
|
2033
2104
|
&mut ctx,
|
|
2034
2105
|
"INSERT INTO test_state_schema (lixcol_entity_id, value) \
|
|
2035
|
-
|
|
2106
|
+
VALUES (lix_json('[\"entity-c\"]'), 'C')",
|
|
2036
2107
|
&[],
|
|
2037
2108
|
)
|
|
2038
2109
|
.await
|
|
@@ -2048,7 +2119,7 @@ mod tests {
|
|
|
2048
2119
|
.expect("staged delta should expose pending overlay");
|
|
2049
2120
|
let rows = overlay.visible_semantic_rows(false, "test_state_schema");
|
|
2050
2121
|
assert_eq!(rows.len(), 1);
|
|
2051
|
-
assert_eq!(rows[0].entity_id, "entity-c");
|
|
2122
|
+
assert_eq!(rows[0].entity_id, "[\"entity-c\"]");
|
|
2052
2123
|
assert_eq!(rows[0].version_id, "version-a");
|
|
2053
2124
|
assert!(!rows[0].global);
|
|
2054
2125
|
assert!(!rows[0].untracked);
|
|
@@ -2091,8 +2162,7 @@ mod tests {
|
|
|
2091
2162
|
.expect("staged delta should expose pending overlay");
|
|
2092
2163
|
let rows = overlay.visible_semantic_rows(false, "lix_directory_descriptor");
|
|
2093
2164
|
assert_eq!(rows.len(), 1);
|
|
2094
|
-
assert_eq!(rows[0].entity_id, "dir-docs");
|
|
2095
|
-
assert_eq!(rows[0].schema_version, "1");
|
|
2165
|
+
assert_eq!(rows[0].entity_id, "[\"dir-docs\"]");
|
|
2096
2166
|
assert_eq!(rows[0].version_id, "version-b");
|
|
2097
2167
|
assert!(!rows[0].global);
|
|
2098
2168
|
assert!(!rows[0].untracked);
|
|
@@ -2134,7 +2204,7 @@ mod tests {
|
|
|
2134
2204
|
.expect("staged delta should expose pending overlay");
|
|
2135
2205
|
let rows = overlay.visible_semantic_rows(false, "lix_directory_descriptor");
|
|
2136
2206
|
assert_eq!(rows.len(), 1);
|
|
2137
|
-
assert_eq!(rows[0].entity_id, "dir-docs");
|
|
2207
|
+
assert_eq!(rows[0].entity_id, "[\"dir-docs\"]");
|
|
2138
2208
|
assert_eq!(rows[0].version_id, "version-a");
|
|
2139
2209
|
assert!(!rows[0].global);
|
|
2140
2210
|
assert!(!rows[0].untracked);
|
|
@@ -2178,15 +2248,15 @@ mod tests {
|
|
|
2178
2248
|
.expect("staged delta should expose pending overlay");
|
|
2179
2249
|
let rows = overlay.visible_semantic_rows(false, "lix_directory_descriptor");
|
|
2180
2250
|
assert_eq!(rows.len(), 1);
|
|
2181
|
-
assert_eq!(rows[0].entity_id, "dir-docs");
|
|
2251
|
+
assert_eq!(rows[0].entity_id, "[\"dir-docs\"]");
|
|
2182
2252
|
assert_eq!(rows[0].version_id, "version-a");
|
|
2183
2253
|
assert_eq!(
|
|
2184
2254
|
rows[0].snapshot_content.as_deref(),
|
|
2185
2255
|
Some("{\"hidden\":true,\"id\":\"dir-docs\",\"name\":\"docs\",\"parent_id\":null}")
|
|
2186
2256
|
);
|
|
2187
2257
|
assert_eq!(
|
|
2188
|
-
rows[0].metadata.
|
|
2189
|
-
Some(
|
|
2258
|
+
rows[0].metadata.as_deref(),
|
|
2259
|
+
Some("{\"source\":\"directory-update\"}")
|
|
2190
2260
|
);
|
|
2191
2261
|
}
|
|
2192
2262
|
|
|
@@ -2267,7 +2337,7 @@ mod tests {
|
|
|
2267
2337
|
.expect("staged delta should expose pending overlay");
|
|
2268
2338
|
let rows = overlay.visible_all_semantic_rows();
|
|
2269
2339
|
assert_eq!(rows.len(), 1);
|
|
2270
|
-
assert_eq!(rows[0].entity_id, "dir-guides");
|
|
2340
|
+
assert_eq!(rows[0].entity_id, "[\"dir-guides\"]");
|
|
2271
2341
|
assert_eq!(rows[0].version_id, "version-b");
|
|
2272
2342
|
assert!(rows[0].tombstone);
|
|
2273
2343
|
assert_eq!(rows[0].snapshot_content, None);
|
|
@@ -2306,8 +2376,7 @@ mod tests {
|
|
|
2306
2376
|
.expect("staged delta should expose pending overlay");
|
|
2307
2377
|
let rows = overlay.visible_semantic_rows(false, "lix_file_descriptor");
|
|
2308
2378
|
assert_eq!(rows.len(), 1);
|
|
2309
|
-
assert_eq!(rows[0].entity_id, "file-readme");
|
|
2310
|
-
assert_eq!(rows[0].schema_version, "1");
|
|
2379
|
+
assert_eq!(rows[0].entity_id, "[\"file-readme\"]");
|
|
2311
2380
|
assert_eq!(rows[0].version_id, "version-b");
|
|
2312
2381
|
assert!(!rows[0].global);
|
|
2313
2382
|
assert!(!rows[0].untracked);
|
|
@@ -2352,7 +2421,7 @@ mod tests {
|
|
|
2352
2421
|
.expect("staged delta should expose pending overlay");
|
|
2353
2422
|
let rows = overlay.visible_semantic_rows(false, "lix_file_descriptor");
|
|
2354
2423
|
assert_eq!(rows.len(), 1);
|
|
2355
|
-
assert_eq!(rows[0].entity_id, "file-readme");
|
|
2424
|
+
assert_eq!(rows[0].entity_id, "[\"file-readme\"]");
|
|
2356
2425
|
assert_eq!(rows[0].version_id, "version-a");
|
|
2357
2426
|
assert!(!rows[0].global);
|
|
2358
2427
|
assert!(!rows[0].untracked);
|
|
@@ -2391,10 +2460,10 @@ mod tests {
|
|
|
2391
2460
|
.expect("staged delta should expose pending overlay");
|
|
2392
2461
|
let descriptor_rows = overlay.visible_semantic_rows(false, "lix_file_descriptor");
|
|
2393
2462
|
assert_eq!(descriptor_rows.len(), 1);
|
|
2394
|
-
assert_eq!(descriptor_rows[0].entity_id, "file-readme");
|
|
2463
|
+
assert_eq!(descriptor_rows[0].entity_id, "[\"file-readme\"]");
|
|
2395
2464
|
let blob_ref_rows = overlay.visible_semantic_rows(false, "lix_binary_blob_ref");
|
|
2396
2465
|
assert_eq!(blob_ref_rows.len(), 1);
|
|
2397
|
-
assert_eq!(blob_ref_rows[0].entity_id, "file-readme");
|
|
2466
|
+
assert_eq!(blob_ref_rows[0].entity_id, "[\"file-readme\"]");
|
|
2398
2467
|
assert_eq!(blob_ref_rows[0].file_id.as_deref(), Some("file-readme"));
|
|
2399
2468
|
assert_eq!(blob_ref_rows[0].version_id, "version-b");
|
|
2400
2469
|
let snapshot: JsonValue =
|
|
@@ -2458,7 +2527,7 @@ mod tests {
|
|
|
2458
2527
|
.expect("staged delta should expose pending overlay");
|
|
2459
2528
|
let rows = overlay.visible_semantic_rows(false, "lix_file_descriptor");
|
|
2460
2529
|
assert_eq!(rows.len(), 1);
|
|
2461
|
-
assert_eq!(rows[0].entity_id, "file-readme");
|
|
2530
|
+
assert_eq!(rows[0].entity_id, "[\"file-readme\"]");
|
|
2462
2531
|
assert_eq!(rows[0].version_id, "version-a");
|
|
2463
2532
|
let snapshot: JsonValue =
|
|
2464
2533
|
serde_json::from_str(rows[0].snapshot_content.as_deref().unwrap())
|
|
@@ -2468,8 +2537,8 @@ mod tests {
|
|
|
2468
2537
|
assert_eq!(snapshot["name"], "readme-updated.txt");
|
|
2469
2538
|
assert_eq!(snapshot["hidden"], true);
|
|
2470
2539
|
assert_eq!(
|
|
2471
|
-
rows[0].metadata.
|
|
2472
|
-
Some(
|
|
2540
|
+
rows[0].metadata.as_deref(),
|
|
2541
|
+
Some("{\"source\":\"file-update\"}")
|
|
2473
2542
|
);
|
|
2474
2543
|
}
|
|
2475
2544
|
|
|
@@ -2518,7 +2587,7 @@ mod tests {
|
|
|
2518
2587
|
.is_empty());
|
|
2519
2588
|
let blob_ref_rows = overlay.visible_semantic_rows(false, "lix_binary_blob_ref");
|
|
2520
2589
|
assert_eq!(blob_ref_rows.len(), 1);
|
|
2521
|
-
assert_eq!(blob_ref_rows[0].entity_id, "file-readme");
|
|
2590
|
+
assert_eq!(blob_ref_rows[0].entity_id, "[\"file-readme\"]");
|
|
2522
2591
|
let snapshot: JsonValue =
|
|
2523
2592
|
serde_json::from_str(blob_ref_rows[0].snapshot_content.as_deref().unwrap())
|
|
2524
2593
|
.expect("blob ref snapshot JSON");
|
|
@@ -2626,7 +2695,7 @@ mod tests {
|
|
|
2626
2695
|
.expect("staged delta should expose pending overlay");
|
|
2627
2696
|
let rows = overlay.visible_all_semantic_rows();
|
|
2628
2697
|
assert_eq!(rows.len(), 1);
|
|
2629
|
-
assert_eq!(rows[0].entity_id, "file-guide");
|
|
2698
|
+
assert_eq!(rows[0].entity_id, "[\"file-guide\"]");
|
|
2630
2699
|
assert_eq!(rows[0].version_id, "version-b");
|
|
2631
2700
|
assert!(rows[0].tombstone);
|
|
2632
2701
|
assert_eq!(rows[0].snapshot_content, None);
|
|
@@ -2649,7 +2718,6 @@ mod tests {
|
|
|
2649
2718
|
staged_writes: Arc::clone(&staged_writes),
|
|
2650
2719
|
schema_definitions: vec![json!({
|
|
2651
2720
|
"x-lix-key": "test_state_schema",
|
|
2652
|
-
"x-lix-version": "1",
|
|
2653
2721
|
"type": "object",
|
|
2654
2722
|
"properties": {
|
|
2655
2723
|
"value": { "type": "string" }
|
|
@@ -2677,15 +2745,15 @@ mod tests {
|
|
|
2677
2745
|
.expect("staged delta should expose pending overlay");
|
|
2678
2746
|
let rows = overlay.visible_semantic_rows(false, "test_state_schema");
|
|
2679
2747
|
assert_eq!(rows.len(), 1);
|
|
2680
|
-
assert_eq!(rows[0].entity_id, "entity-a");
|
|
2748
|
+
assert_eq!(rows[0].entity_id, "[\"entity-a\"]");
|
|
2681
2749
|
assert_eq!(rows[0].version_id, "version-a");
|
|
2682
2750
|
assert_eq!(
|
|
2683
2751
|
rows[0].snapshot_content.as_deref(),
|
|
2684
2752
|
Some("{\"value\":\"updated\"}")
|
|
2685
2753
|
);
|
|
2686
2754
|
assert_eq!(
|
|
2687
|
-
rows[0].metadata.
|
|
2688
|
-
Some(
|
|
2755
|
+
rows[0].metadata.as_deref(),
|
|
2756
|
+
Some("{\"source\":\"entity-update\"}")
|
|
2689
2757
|
);
|
|
2690
2758
|
}
|
|
2691
2759
|
|
|
@@ -2706,7 +2774,6 @@ mod tests {
|
|
|
2706
2774
|
staged_writes: Arc::clone(&staged_writes),
|
|
2707
2775
|
schema_definitions: vec![json!({
|
|
2708
2776
|
"x-lix-key": "test_state_schema",
|
|
2709
|
-
"x-lix-version": "1",
|
|
2710
2777
|
"type": "object",
|
|
2711
2778
|
"properties": {
|
|
2712
2779
|
"value": { "type": "string" }
|
|
@@ -2733,7 +2800,7 @@ mod tests {
|
|
|
2733
2800
|
.expect("staged delta should expose pending overlay");
|
|
2734
2801
|
let rows = overlay.visible_all_semantic_rows();
|
|
2735
2802
|
assert_eq!(rows.len(), 1);
|
|
2736
|
-
assert_eq!(rows[0].entity_id, "entity-b");
|
|
2803
|
+
assert_eq!(rows[0].entity_id, "[\"entity-b\"]");
|
|
2737
2804
|
assert_eq!(rows[0].version_id, "version-b");
|
|
2738
2805
|
assert!(rows[0].tombstone);
|
|
2739
2806
|
assert_eq!(rows[0].snapshot_content, None);
|
|
@@ -2762,7 +2829,7 @@ mod tests {
|
|
|
2762
2829
|
"UPDATE lix_state \
|
|
2763
2830
|
SET snapshot_content = '{\"key\":\"hello\",\"value\":\"updated\"}', \
|
|
2764
2831
|
metadata = '{\"schema_key\":\"lix_key_value\"}' \
|
|
2765
|
-
WHERE metadata = '{\"source\":\"match\"}'",
|
|
2832
|
+
WHERE metadata = lix_json('{\"source\":\"match\"}')",
|
|
2766
2833
|
&[],
|
|
2767
2834
|
)
|
|
2768
2835
|
.await
|
|
@@ -2778,15 +2845,15 @@ mod tests {
|
|
|
2778
2845
|
.expect("staged delta should expose pending overlay");
|
|
2779
2846
|
let rows = overlay.visible_semantic_rows(false, "lix_key_value");
|
|
2780
2847
|
assert_eq!(rows.len(), 1);
|
|
2781
|
-
assert_eq!(rows[0].entity_id, "entity-1");
|
|
2848
|
+
assert_eq!(rows[0].entity_id, "[\"entity-1\"]");
|
|
2782
2849
|
assert_eq!(rows[0].version_id, "version-a");
|
|
2783
2850
|
assert_eq!(
|
|
2784
2851
|
rows[0].snapshot_content.as_deref(),
|
|
2785
2852
|
Some("{\"key\":\"hello\",\"value\":\"updated\"}")
|
|
2786
2853
|
);
|
|
2787
2854
|
assert_eq!(
|
|
2788
|
-
rows[0].metadata.
|
|
2789
|
-
Some(
|
|
2855
|
+
rows[0].metadata.as_deref(),
|
|
2856
|
+
Some("{\"schema_key\":\"lix_key_value\"}")
|
|
2790
2857
|
);
|
|
2791
2858
|
}
|
|
2792
2859
|
|
|
@@ -2824,8 +2891,8 @@ mod tests {
|
|
|
2824
2891
|
assert_eq!(rows.len(), 2);
|
|
2825
2892
|
assert!(rows.iter().all(|row| row.tombstone));
|
|
2826
2893
|
assert!(rows.iter().all(|row| row.snapshot_content.is_none()));
|
|
2827
|
-
assert!(rows.iter().any(|row| row.entity_id == "entity-1"));
|
|
2828
|
-
assert!(rows.iter().any(|row| row.entity_id == "entity-2"));
|
|
2894
|
+
assert!(rows.iter().any(|row| row.entity_id == "[\"entity-1\"]"));
|
|
2895
|
+
assert!(rows.iter().any(|row| row.entity_id == "[\"entity-2\"]"));
|
|
2829
2896
|
}
|
|
2830
2897
|
|
|
2831
2898
|
struct BackendSqlExecutionContext<'a> {
|
|
@@ -2853,13 +2920,11 @@ mod tests {
|
|
|
2853
2920
|
Arc::clone(&self.blob_reader)
|
|
2854
2921
|
}
|
|
2855
2922
|
|
|
2856
|
-
fn
|
|
2923
|
+
fn commit_store_query_source(&self) -> SqlCommitStoreQuerySource {
|
|
2857
2924
|
let base_scope = test_read_scope(self.storage.clone());
|
|
2858
2925
|
let read_scope = StorageReadScope::new(base_scope.store());
|
|
2859
|
-
|
|
2860
|
-
|
|
2861
|
-
crate::changelog::ChangelogContext::new().reader(read_scope.store()),
|
|
2862
|
-
),
|
|
2926
|
+
CommitStoreQuerySource {
|
|
2927
|
+
commit_store_reader: Arc::new(CommitStoreContext::new().reader(read_scope.store())),
|
|
2863
2928
|
json_reader: JsonStoreContext::new().reader(read_scope.store()),
|
|
2864
2929
|
}
|
|
2865
2930
|
}
|
|
@@ -2891,26 +2956,23 @@ mod tests {
|
|
|
2891
2956
|
crate::untracked_state::UntrackedStateContext::new(),
|
|
2892
2957
|
));
|
|
2893
2958
|
let mut writes = StorageWriteSet::new();
|
|
2894
|
-
let canonical_rows =
|
|
2895
|
-
|
|
2896
|
-
|
|
2897
|
-
|
|
2898
|
-
|
|
2899
|
-
|
|
2900
|
-
|
|
2901
|
-
|
|
2902
|
-
|
|
2903
|
-
|
|
2904
|
-
|
|
2905
|
-
|
|
2906
|
-
|
|
2907
|
-
|
|
2908
|
-
|
|
2909
|
-
|
|
2910
|
-
|
|
2911
|
-
]
|
|
2912
|
-
};
|
|
2913
|
-
version_ctx.stage_canonical_ref_rows(&mut writes, &canonical_rows)?;
|
|
2959
|
+
let canonical_rows = vec![
|
|
2960
|
+
prepare_version_ref_row(
|
|
2961
|
+
"version-a",
|
|
2962
|
+
&init_receipt.initial_commit_id,
|
|
2963
|
+
"1970-01-01T00:00:00.000Z",
|
|
2964
|
+
)?,
|
|
2965
|
+
prepare_version_ref_row(
|
|
2966
|
+
"version-b",
|
|
2967
|
+
&init_receipt.initial_commit_id,
|
|
2968
|
+
"1970-01-01T00:00:00.000Z",
|
|
2969
|
+
)?,
|
|
2970
|
+
];
|
|
2971
|
+
let rows = canonical_rows
|
|
2972
|
+
.into_iter()
|
|
2973
|
+
.map(|prepared| prepared.row)
|
|
2974
|
+
.collect::<Vec<_>>();
|
|
2975
|
+
version_ctx.stage_canonical_ref_rows(&mut writes, &rows)?;
|
|
2914
2976
|
writes.apply(&mut transaction.as_mut()).await?;
|
|
2915
2977
|
transaction.commit().await?;
|
|
2916
2978
|
}
|
|
@@ -2919,7 +2981,6 @@ mod tests {
|
|
|
2919
2981
|
let session_b = engine.open_session("version-b").await?;
|
|
2920
2982
|
let schema_definition = json!({
|
|
2921
2983
|
"x-lix-key": "test_state_schema",
|
|
2922
|
-
"x-lix-version": "1",
|
|
2923
2984
|
"type": "object",
|
|
2924
2985
|
"properties": {
|
|
2925
2986
|
"value": { "type": "string" }
|
|
@@ -2931,9 +2992,9 @@ mod tests {
|
|
|
2931
2992
|
.execute(
|
|
2932
2993
|
"INSERT INTO lix_registered_schema (value, lixcol_global, lixcol_untracked) \
|
|
2933
2994
|
VALUES (\
|
|
2934
|
-
lix_json('{\"x-lix-key\":\"test_state_schema\",\"
|
|
2995
|
+
lix_json('{\"x-lix-key\":\"test_state_schema\",\"type\":\"object\",\"properties\":{\"value\":{\"type\":\"string\"}},\"required\":[\"value\"],\"additionalProperties\":false}'),\
|
|
2935
2996
|
false,\
|
|
2936
|
-
|
|
2997
|
+
false\
|
|
2937
2998
|
)",
|
|
2938
2999
|
&[],
|
|
2939
3000
|
)
|
|
@@ -2942,9 +3003,9 @@ mod tests {
|
|
|
2942
3003
|
.execute(
|
|
2943
3004
|
"INSERT INTO lix_registered_schema (value, lixcol_global, lixcol_untracked) \
|
|
2944
3005
|
VALUES (\
|
|
2945
|
-
lix_json('{\"x-lix-key\":\"test_state_schema\",\"
|
|
3006
|
+
lix_json('{\"x-lix-key\":\"test_state_schema\",\"type\":\"object\",\"properties\":{\"value\":{\"type\":\"string\"}},\"required\":[\"value\"],\"additionalProperties\":false}'),\
|
|
2946
3007
|
false,\
|
|
2947
|
-
|
|
3008
|
+
false\
|
|
2948
3009
|
)",
|
|
2949
3010
|
&[],
|
|
2950
3011
|
)
|
|
@@ -2952,32 +3013,32 @@ mod tests {
|
|
|
2952
3013
|
session_a
|
|
2953
3014
|
.execute(
|
|
2954
3015
|
"INSERT INTO lix_state (\
|
|
2955
|
-
|
|
2956
|
-
|
|
2957
|
-
|
|
2958
|
-
|
|
3016
|
+
entity_id, schema_key, file_id, snapshot_content, global, untracked\
|
|
3017
|
+
) VALUES (\
|
|
3018
|
+
lix_json('[\"entity-a\"]'), 'test_state_schema', NULL, '{\"value\":\"A\"}', false, false\
|
|
3019
|
+
)",
|
|
2959
3020
|
&[],
|
|
2960
3021
|
)
|
|
2961
3022
|
.await?;
|
|
2962
3023
|
session_b
|
|
2963
3024
|
.execute(
|
|
2964
3025
|
"INSERT INTO lix_state (\
|
|
2965
|
-
|
|
2966
|
-
|
|
2967
|
-
|
|
2968
|
-
|
|
3026
|
+
entity_id, schema_key, file_id, snapshot_content, global, untracked\
|
|
3027
|
+
) VALUES (\
|
|
3028
|
+
lix_json('[\"entity-b\"]'), 'test_state_schema', NULL, '{\"value\":\"B\"}', false, false\
|
|
3029
|
+
)",
|
|
2969
3030
|
&[],
|
|
2970
3031
|
)
|
|
2971
3032
|
.await?;
|
|
2972
3033
|
session_a
|
|
2973
|
-
|
|
2974
|
-
|
|
2975
|
-
|
|
2976
|
-
|
|
2977
|
-
|
|
2978
|
-
|
|
2979
|
-
|
|
2980
|
-
|
|
3034
|
+
.execute(
|
|
3035
|
+
"INSERT INTO lix_state (\
|
|
3036
|
+
entity_id, schema_key, file_id, snapshot_content, global, untracked\
|
|
3037
|
+
) VALUES (\
|
|
3038
|
+
lix_json('[\"dir-docs\"]'), 'lix_directory_descriptor', NULL, '{\"id\":\"dir-docs\",\"parent_id\":null,\"name\":\"docs\",\"hidden\":false}', false, false\
|
|
3039
|
+
)",
|
|
3040
|
+
&[],
|
|
3041
|
+
)
|
|
2981
3042
|
.await?;
|
|
2982
3043
|
session_a
|
|
2983
3044
|
.execute(
|
|
@@ -2993,7 +3054,7 @@ mod tests {
|
|
|
2993
3054
|
LiveStateContext::new(
|
|
2994
3055
|
TrackedStateContext::new(),
|
|
2995
3056
|
UntrackedStateContext::new(),
|
|
2996
|
-
crate::commit_graph::CommitGraphContext::new(
|
|
3057
|
+
crate::commit_graph::CommitGraphContext::new(),
|
|
2997
3058
|
)
|
|
2998
3059
|
}
|
|
2999
3060
|
|
|
@@ -3050,7 +3111,7 @@ mod tests {
|
|
|
3050
3111
|
vec!["entity_id", "version_id", "snapshot_content", "commit_id"]
|
|
3051
3112
|
);
|
|
3052
3113
|
assert_eq!(result.rows.len(), 1);
|
|
3053
|
-
assert_eq!(result.rows[0][0], Value::
|
|
3114
|
+
assert_eq!(result.rows[0][0], Value::Json(json!(["entity-b"])));
|
|
3054
3115
|
assert_eq!(result.rows[0][1], Value::Text("version-b".to_string()));
|
|
3055
3116
|
assert_eq!(result.rows[0][2], Value::Json(json!({"value": "B"})));
|
|
3056
3117
|
match &result.rows[0][3] {
|
|
@@ -3090,11 +3151,11 @@ mod tests {
|
|
|
3090
3151
|
.expect("broad by-version read should succeed");
|
|
3091
3152
|
|
|
3092
3153
|
assert!(
|
|
3093
|
-
|
|
3094
|
-
|
|
3095
|
-
|
|
3096
|
-
|
|
3097
|
-
|
|
3154
|
+
result.rows.iter().any(|row| row[0] == Value::Json(json!(["entity-a"])))
|
|
3155
|
+
&& result.rows.iter().any(|row| row[0] == Value::Json(json!(["entity-b"]))),
|
|
3156
|
+
"expected broad by-version read to include rows from multiple visible versions: {:?}",
|
|
3157
|
+
result.rows
|
|
3158
|
+
);
|
|
3098
3159
|
})
|
|
3099
3160
|
});
|
|
3100
3161
|
}
|
|
@@ -3131,7 +3192,7 @@ mod tests {
|
|
|
3131
3192
|
|
|
3132
3193
|
assert_eq!(result.columns, vec!["entity_id", "snapshot_content"]);
|
|
3133
3194
|
assert_eq!(result.rows.len(), 1);
|
|
3134
|
-
assert_eq!(result.rows[0][0], Value::
|
|
3195
|
+
assert_eq!(result.rows[0][0], Value::Json(json!(["entity-a"])));
|
|
3135
3196
|
assert_eq!(result.rows[0][1], Value::Json(json!({"value": "A"})));
|
|
3136
3197
|
})
|
|
3137
3198
|
});
|
|
@@ -3169,7 +3230,7 @@ mod tests {
|
|
|
3169
3230
|
assert_eq!(result.columns, vec!["value", "lixcol_entity_id"]);
|
|
3170
3231
|
assert_eq!(result.rows.len(), 1);
|
|
3171
3232
|
assert_eq!(result.rows[0][0], Value::Text("A".to_string()));
|
|
3172
|
-
assert_eq!(result.rows[0][1], Value::
|
|
3233
|
+
assert_eq!(result.rows[0][1], Value::Json(json!(["entity-a"])));
|
|
3173
3234
|
})
|
|
3174
3235
|
});
|
|
3175
3236
|
}
|