@lix-js/sdk 0.6.0-preview.3 → 0.6.0-preview.5
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/README.md +1 -1
- package/SKILL.md +105 -65
- package/dist/engine-wasm/index.js +4 -4
- package/dist/engine-wasm/wasm/lix_engine.d.ts +30 -6
- package/dist/engine-wasm/wasm/lix_engine.js +187 -117
- package/dist/engine-wasm/wasm/lix_engine.wasm +0 -0
- package/dist/engine-wasm/wasm/lix_engine.wasm.d.ts +14 -8
- package/dist/generated/builtin-schemas.d.ts +69 -69
- package/dist/generated/builtin-schemas.js +94 -94
- package/dist/open-lix.d.ts +42 -28
- package/dist/open-lix.js +49 -10
- package/dist/sqlite/index.js +86 -30
- package/dist-engine-src/README.md +3 -3
- package/dist-engine-src/src/backend/capabilities.rs +67 -0
- package/dist-engine-src/src/backend/conformance/baseline.rs +1127 -0
- package/dist-engine-src/src/backend/conformance/factory.rs +93 -0
- package/dist-engine-src/src/backend/conformance/failure_tests.rs +608 -0
- package/dist-engine-src/src/backend/conformance/fixtures.rs +26 -0
- package/dist-engine-src/src/backend/conformance/mod.rs +75 -0
- package/dist-engine-src/src/backend/conformance/model.rs +28 -0
- package/dist-engine-src/src/backend/conformance/model_based.rs +257 -0
- package/dist-engine-src/src/backend/conformance/persistence.rs +204 -0
- package/dist-engine-src/src/backend/conformance/projection.rs +21 -0
- package/dist-engine-src/src/backend/conformance/pushdown.rs +24 -0
- package/dist-engine-src/src/backend/conformance/runner.rs +90 -0
- package/dist-engine-src/src/backend/conformance/scan.rs +24 -0
- package/dist-engine-src/src/backend/conformance/write.rs +16 -0
- package/dist-engine-src/src/backend/error.rs +94 -0
- package/dist-engine-src/src/backend/in_memory.rs +670 -0
- package/dist-engine-src/src/backend/mod.rs +36 -9
- package/dist-engine-src/src/backend/predicate.rs +80 -0
- package/dist-engine-src/src/backend/traits.rs +260 -0
- package/dist-engine-src/src/backend/types.rs +224 -81
- package/dist-engine-src/src/binary_cas/context.rs +8 -8
- package/dist-engine-src/src/binary_cas/kv.rs +234 -259
- package/dist-engine-src/src/{version → branch}/context.rs +12 -12
- package/dist-engine-src/src/branch/lifecycle.rs +221 -0
- package/dist-engine-src/src/branch/mod.rs +13 -0
- package/dist-engine-src/src/branch/refs.rs +321 -0
- package/dist-engine-src/src/branch/stage_rows.rs +67 -0
- package/dist-engine-src/src/branch/types.rs +21 -0
- package/dist-engine-src/src/catalog/context.rs +18 -18
- package/dist-engine-src/src/catalog/snapshot.rs +8 -8
- package/dist-engine-src/src/changelog/bench_support.rs +785 -0
- package/dist-engine-src/src/changelog/change.rs +1 -0
- package/dist-engine-src/src/changelog/codec.rs +497 -0
- package/dist-engine-src/src/changelog/commit.rs +1 -0
- package/dist-engine-src/src/changelog/context.rs +1614 -0
- package/dist-engine-src/src/changelog/mod.rs +29 -0
- package/dist-engine-src/src/changelog/store.rs +163 -0
- package/dist-engine-src/src/changelog/test_support.rs +54 -0
- package/dist-engine-src/src/changelog/types.rs +213 -0
- package/dist-engine-src/src/commit_graph/context.rs +317 -274
- package/dist-engine-src/src/commit_graph/mod.rs +2 -4
- package/dist-engine-src/src/commit_graph/types.rs +22 -42
- package/dist-engine-src/src/commit_graph/walker.rs +133 -103
- package/dist-engine-src/src/common/error.rs +52 -18
- package/dist-engine-src/src/common/identity.rs +2 -2
- package/dist-engine-src/src/common/mod.rs +1 -1
- package/dist-engine-src/src/domain.rs +42 -46
- package/dist-engine-src/src/engine.rs +74 -96
- package/dist-engine-src/src/{entity_identity.rs → entity_pk.rs} +89 -92
- package/dist-engine-src/src/functions/context.rs +56 -52
- package/dist-engine-src/src/functions/state.rs +51 -52
- package/dist-engine-src/src/init.rs +288 -154
- package/dist-engine-src/src/json_store/context.rs +15 -266
- package/dist-engine-src/src/json_store/mod.rs +26 -0
- package/dist-engine-src/src/json_store/store.rs +103 -718
- package/dist-engine-src/src/json_store/types.rs +4 -9
- package/dist-engine-src/src/lib.rs +49 -19
- package/dist-engine-src/src/live_state/context.rs +654 -790
- package/dist-engine-src/src/live_state/mod.rs +9 -3
- package/dist-engine-src/src/live_state/overlay.rs +4 -4
- package/dist-engine-src/src/live_state/types.rs +30 -21
- package/dist-engine-src/src/live_state/visibility.rs +514 -71
- package/dist-engine-src/src/plugin/install.rs +48 -48
- package/dist-engine-src/src/plugin/manifest.rs +7 -7
- package/dist-engine-src/src/plugin/materializer.rs +0 -275
- package/dist-engine-src/src/plugin/plugin_manifest.json +4 -3
- package/dist-engine-src/src/schema/builtin/lix_binary_blob_ref.json +2 -2
- package/dist-engine-src/src/schema/builtin/lix_branch_descriptor.json +34 -0
- package/dist-engine-src/src/schema/builtin/lix_branch_ref.json +48 -0
- package/dist-engine-src/src/schema/builtin/lix_change.json +3 -3
- package/dist-engine-src/src/schema/builtin/lix_commit.json +1 -1
- package/dist-engine-src/src/schema/builtin/lix_label_assignment.json +6 -6
- package/dist-engine-src/src/schema/builtin/mod.rs +18 -20
- package/dist-engine-src/src/schema/compatibility.rs +11 -11
- package/dist-engine-src/src/schema/definition.json +2 -2
- package/dist-engine-src/src/schema/definition.rs +5 -5
- package/dist-engine-src/src/schema/key.rs +3 -3
- package/dist-engine-src/src/schema/mod.rs +1 -1
- package/dist-engine-src/src/schema/tests.rs +18 -18
- package/dist-engine-src/src/session/context.rs +819 -124
- package/dist-engine-src/src/session/create_branch.rs +94 -0
- package/dist-engine-src/src/session/execute.rs +260 -57
- package/dist-engine-src/src/session/merge/analysis.rs +9 -3
- package/dist-engine-src/src/session/merge/{version.rs → branch.rs} +119 -129
- package/dist-engine-src/src/session/merge/conflicts.rs +2 -2
- package/dist-engine-src/src/session/merge/mod.rs +5 -6
- package/dist-engine-src/src/session/merge/stats.rs +7 -11
- package/dist-engine-src/src/session/mod.rs +19 -16
- package/dist-engine-src/src/session/switch_branch.rs +113 -0
- package/dist-engine-src/src/session/transaction.rs +557 -0
- package/dist-engine-src/src/sql2/bind/classify.rs +102 -0
- package/dist-engine-src/src/sql2/bind/error.rs +5 -0
- package/dist-engine-src/src/sql2/bind/expr.rs +29 -0
- package/dist-engine-src/src/sql2/bind/mod.rs +12 -0
- package/dist-engine-src/src/sql2/{udfs/public_call.rs → bind/public_udf.rs} +98 -3
- package/dist-engine-src/src/sql2/bind/read.rs +65 -0
- package/dist-engine-src/src/sql2/bind/statement.rs +2236 -0
- package/dist-engine-src/src/sql2/bind/table.rs +273 -0
- package/dist-engine-src/src/sql2/bind/write.rs +86 -0
- package/dist-engine-src/src/sql2/branch_scope.rs +436 -0
- package/dist-engine-src/src/sql2/catalog/capability.rs +20 -0
- package/dist-engine-src/src/sql2/catalog/entity_surface.rs +296 -0
- package/dist-engine-src/src/sql2/catalog/mod.rs +15 -0
- package/dist-engine-src/src/sql2/catalog/registry.rs +556 -0
- package/dist-engine-src/src/sql2/catalog/schema.rs +88 -0
- package/dist-engine-src/src/sql2/catalog/surface.rs +41 -0
- package/dist-engine-src/src/sql2/change_materialization.rs +122 -0
- package/dist-engine-src/src/sql2/context.rs +36 -30
- package/dist-engine-src/src/sql2/error.rs +4 -5
- package/dist-engine-src/src/sql2/exec/bound_public_write.rs +1593 -0
- package/dist-engine-src/src/sql2/exec/datafusion.rs +5266 -0
- package/dist-engine-src/src/sql2/exec/fast_write.rs +82 -0
- package/dist-engine-src/src/sql2/exec/mod.rs +24 -0
- package/dist-engine-src/src/sql2/exec/write.rs +661 -0
- package/dist-engine-src/src/sql2/filesystem_planner.rs +72 -77
- package/dist-engine-src/src/sql2/filesystem_visibility.rs +21 -21
- package/dist-engine-src/src/sql2/history_projection.rs +8 -8
- package/dist-engine-src/src/sql2/history_route.rs +35 -31
- package/dist-engine-src/src/sql2/mod.rs +30 -24
- package/dist-engine-src/src/sql2/optimize/datafusion.rs +1 -0
- package/dist-engine-src/src/sql2/optimize/mod.rs +2 -0
- package/dist-engine-src/src/sql2/optimize/simple_write.rs +116 -0
- package/dist-engine-src/src/sql2/parse/mod.rs +69 -0
- package/dist-engine-src/src/sql2/parse/normalize.rs +1 -0
- package/dist-engine-src/src/sql2/plan/branch_scope.rs +24 -0
- package/dist-engine-src/src/sql2/plan/mod.rs +5 -0
- package/dist-engine-src/src/sql2/plan/predicate.rs +22 -0
- package/dist-engine-src/src/sql2/plan/write.rs +147 -0
- package/dist-engine-src/src/sql2/predicate_typecheck.rs +258 -0
- package/dist-engine-src/src/sql2/{version_provider.rs → providers/branch.rs} +218 -214
- package/dist-engine-src/src/sql2/{change_provider.rs → providers/change.rs} +156 -42
- package/dist-engine-src/src/sql2/{directory_provider.rs → providers/directory.rs} +291 -322
- package/dist-engine-src/src/sql2/{directory_history_provider.rs → providers/directory_history.rs} +56 -42
- package/dist-engine-src/src/sql2/providers/entity.rs +1484 -0
- package/dist-engine-src/src/sql2/{entity_history_provider.rs → providers/entity_history.rs} +43 -31
- package/dist-engine-src/src/sql2/{file_provider.rs → providers/file.rs} +323 -316
- package/dist-engine-src/src/sql2/{file_history_provider.rs → providers/file_history.rs} +60 -46
- package/dist-engine-src/src/sql2/{history_provider.rs → providers/history.rs} +46 -32
- package/dist-engine-src/src/sql2/{lix_state_provider.rs → providers/lix_state.rs} +359 -329
- package/dist-engine-src/src/sql2/providers/mod.rs +508 -0
- package/dist-engine-src/src/sql2/read_only.rs +2 -2
- package/dist-engine-src/src/sql2/session.rs +47 -96
- package/dist-engine-src/src/sql2/storage/constraints.rs +1 -0
- package/dist-engine-src/src/sql2/storage/mod.rs +1 -0
- package/dist-engine-src/src/sql2/test_support/differential.rs +712 -0
- package/dist-engine-src/src/sql2/test_support/generators.rs +354 -0
- package/dist-engine-src/src/sql2/test_support/mod.rs +2 -0
- package/dist-engine-src/src/sql2/udfs/{lix_active_version_commit_id.rs → lix_active_branch_commit_id.rs} +7 -7
- package/dist-engine-src/src/sql2/udfs/mod.rs +3 -6
- package/dist-engine-src/src/sql2/write_normalization.rs +45 -22
- package/dist-engine-src/src/storage/conformance.rs +399 -0
- package/dist-engine-src/src/storage/context.rs +552 -288
- package/dist-engine-src/src/storage/mod.rs +48 -10
- package/dist-engine-src/src/storage/point.rs +440 -0
- package/dist-engine-src/src/storage/read_scope.rs +43 -64
- package/dist-engine-src/src/storage/reader.rs +867 -0
- package/dist-engine-src/src/storage/scan.rs +784 -0
- package/dist-engine-src/src/storage/spaces.rs +236 -0
- package/dist-engine-src/src/storage/stats.rs +80 -0
- package/dist-engine-src/src/storage/write_set.rs +962 -0
- package/dist-engine-src/src/storage_bench.rs +136 -4828
- package/dist-engine-src/src/test_support.rs +360 -138
- package/dist-engine-src/src/tracked_state/bench_support.rs +394 -0
- package/dist-engine-src/src/tracked_state/codec.rs +155 -1057
- package/dist-engine-src/src/tracked_state/commit_root_rebuild.rs +358 -0
- package/dist-engine-src/src/tracked_state/context.rs +1927 -993
- package/dist-engine-src/src/tracked_state/diff.rs +1715 -261
- package/dist-engine-src/src/tracked_state/merge.rs +74 -88
- package/dist-engine-src/src/tracked_state/mod.rs +19 -16
- package/dist-engine-src/src/tracked_state/{materialization.rs → row_materialization.rs} +50 -178
- package/dist-engine-src/src/tracked_state/storage.rs +243 -191
- package/dist-engine-src/src/tracked_state/tree.rs +247 -371
- package/dist-engine-src/src/tracked_state/types.rs +49 -42
- package/dist-engine-src/src/transaction/bench_support.rs +407 -0
- package/dist-engine-src/src/transaction/commit.rs +821 -713
- package/dist-engine-src/src/transaction/context.rs +705 -600
- package/dist-engine-src/src/transaction/mod.rs +13 -2
- package/dist-engine-src/src/transaction/normalization.rs +63 -76
- package/dist-engine-src/src/transaction/prep.rs +13 -13
- package/dist-engine-src/src/transaction/schema_resolver.rs +19 -5
- package/dist-engine-src/src/transaction/staging.rs +228 -434
- package/dist-engine-src/src/transaction/types.rs +41 -98
- package/dist-engine-src/src/transaction/validation.rs +382 -446
- package/dist-engine-src/src/untracked_state/codec.rs +337 -29
- package/dist-engine-src/src/untracked_state/context.rs +7 -7
- package/dist-engine-src/src/untracked_state/materialization.rs +2 -2
- package/dist-engine-src/src/untracked_state/mod.rs +1 -1
- package/dist-engine-src/src/untracked_state/storage.rs +659 -157
- package/dist-engine-src/src/untracked_state/types.rs +21 -21
- package/package.json +71 -68
- package/dist-engine-src/src/backend/kv.rs +0 -358
- package/dist-engine-src/src/backend/testing.rs +0 -658
- package/dist-engine-src/src/commit_store/codec.rs +0 -887
- package/dist-engine-src/src/commit_store/context.rs +0 -944
- package/dist-engine-src/src/commit_store/materialization.rs +0 -84
- package/dist-engine-src/src/commit_store/mod.rs +0 -16
- package/dist-engine-src/src/commit_store/storage.rs +0 -600
- package/dist-engine-src/src/commit_store/types.rs +0 -215
- package/dist-engine-src/src/schema/builtin/lix_version_descriptor.json +0 -34
- package/dist-engine-src/src/schema/builtin/lix_version_ref.json +0 -48
- package/dist-engine-src/src/session/create_version.rs +0 -88
- package/dist-engine-src/src/session/merge/apply.rs +0 -23
- package/dist-engine-src/src/session/optimization9_sql2_bench.rs +0 -100
- package/dist-engine-src/src/session/switch_version.rs +0 -109
- package/dist-engine-src/src/sql2/classify.rs +0 -182
- package/dist-engine-src/src/sql2/entity_provider.rs +0 -3211
- package/dist-engine-src/src/sql2/execute.rs +0 -3440
- package/dist-engine-src/src/sql2/public_bind/assignment.rs +0 -46
- package/dist-engine-src/src/sql2/public_bind/capability.rs +0 -41
- package/dist-engine-src/src/sql2/public_bind/dml.rs +0 -166
- package/dist-engine-src/src/sql2/public_bind/mod.rs +0 -25
- package/dist-engine-src/src/sql2/public_bind/table.rs +0 -168
- package/dist-engine-src/src/sql2/version_scope.rs +0 -394
- package/dist-engine-src/src/storage/types.rs +0 -501
- package/dist-engine-src/src/tracked_state/by_file_index.rs +0 -98
- package/dist-engine-src/src/tracked_state/materializer.rs +0 -488
- package/dist-engine-src/src/transaction/live_state_overlay.rs +0 -35
- package/dist-engine-src/src/version/lifecycle.rs +0 -221
- package/dist-engine-src/src/version/mod.rs +0 -13
- package/dist-engine-src/src/version/refs.rs +0 -330
- package/dist-engine-src/src/version/stage_rows.rs +0 -67
- package/dist-engine-src/src/version/types.rs +0 -21
|
@@ -10,7 +10,7 @@ use datafusion::arrow::compute::{and, filter_record_batch};
|
|
|
10
10
|
use datafusion::arrow::datatypes::{DataType, Field, Schema, SchemaRef};
|
|
11
11
|
use datafusion::arrow::record_batch::RecordBatch;
|
|
12
12
|
use datafusion::catalog::{Session, TableProvider};
|
|
13
|
-
use datafusion::common::{not_impl_err, DFSchema, DataFusionError, Result, ScalarValue};
|
|
13
|
+
use datafusion::common::{not_impl_err, DFSchema, DataFusionError, Result, ScalarValue, SchemaExt};
|
|
14
14
|
use datafusion::datasource::TableType;
|
|
15
15
|
use datafusion::execution::TaskContext;
|
|
16
16
|
use datafusion::logical_expr::dml::InsertOp;
|
|
@@ -27,20 +27,23 @@ use futures_util::{stream, TryStreamExt};
|
|
|
27
27
|
use serde::Deserialize;
|
|
28
28
|
|
|
29
29
|
use crate::binary_cas::{BlobDataReader, BlobHash};
|
|
30
|
-
use crate::
|
|
30
|
+
use crate::branch::BranchRefReader;
|
|
31
|
+
use crate::entity_pk::EntityPk;
|
|
31
32
|
use crate::functions::FunctionProviderHandle;
|
|
32
33
|
use crate::live_state::MaterializedLiveStateRow;
|
|
33
34
|
use crate::live_state::{
|
|
34
35
|
LiveStateFilter, LiveStateProjection, LiveStateReader, LiveStateScanRequest,
|
|
35
36
|
};
|
|
37
|
+
use crate::sql2::branch_scope::{
|
|
38
|
+
explicit_branch_ids_from_dml_filters, resolve_provider_branch_ids, resolve_write_branch_scope,
|
|
39
|
+
BranchBinding,
|
|
40
|
+
};
|
|
36
41
|
use crate::sql2::dml::{InsertExec, InsertSink};
|
|
37
42
|
use crate::sql2::filesystem_predicates::{
|
|
38
43
|
canonicalize_filesystem_path_filters, FilesystemPathKind,
|
|
39
44
|
};
|
|
40
|
-
use crate::sql2::predicate_typecheck::
|
|
41
|
-
|
|
42
|
-
explicit_version_ids_from_dml_filters, resolve_provider_version_ids,
|
|
43
|
-
resolve_write_version_scope, VersionBinding,
|
|
45
|
+
use crate::sql2::predicate_typecheck::{
|
|
46
|
+
canonicalize_json_identity_text_filters, validate_json_predicate_filters,
|
|
44
47
|
};
|
|
45
48
|
use crate::sql2::write_normalization::{
|
|
46
49
|
is_binary_type, lix_file_data_type_error, lix_file_data_type_error_with_value,
|
|
@@ -49,56 +52,68 @@ use crate::sql2::write_normalization::{
|
|
|
49
52
|
UpdateCell,
|
|
50
53
|
};
|
|
51
54
|
use crate::transaction::types::{TransactionJson, TransactionWriteRow};
|
|
52
|
-
use crate::version::VersionRefReader;
|
|
53
55
|
use crate::{parse_row_metadata_value, serialize_row_metadata, LixError};
|
|
54
56
|
|
|
55
57
|
const FILE_DESCRIPTOR_SCHEMA_KEY: &str = "lix_file_descriptor";
|
|
56
58
|
const BLOB_REF_SCHEMA_KEY: &str = "lix_binary_blob_ref";
|
|
57
59
|
const DIRECTORY_DESCRIPTOR_SCHEMA_KEY: &str = "lix_directory_descriptor";
|
|
58
60
|
|
|
59
|
-
use
|
|
61
|
+
use crate::sql2::filesystem_planner::{
|
|
60
62
|
blob_ref_row, directory_path_resolvers_from_state_rows, file_descriptor_row,
|
|
61
63
|
file_descriptor_write_row, filesystem_storage_scope_key, plan_file_delete,
|
|
62
64
|
plan_file_path_update, BlobRefRowInput, DirectoryPathResolver, FileDeleteInput,
|
|
63
65
|
FileDescriptorRowInput, FileDescriptorWriteIntent, FilePathWriteInput, FilesystemDeletePlan,
|
|
64
66
|
FilesystemRowContext,
|
|
65
67
|
};
|
|
66
|
-
use
|
|
68
|
+
use crate::sql2::result_metadata::json_field;
|
|
69
|
+
use crate::sql2::session::SqlWriteSessionOptions;
|
|
67
70
|
use crate::sql2::{
|
|
68
|
-
SqlWriteContext, WriteAccess,
|
|
71
|
+
SqlWriteContext, WriteAccess, WriteContextBranchRefReader, WriteContextLiveStateReader,
|
|
69
72
|
};
|
|
70
73
|
use crate::transaction::types::{
|
|
71
74
|
LogicalPrimaryKey, TransactionFileData, TransactionWrite, TransactionWriteMode,
|
|
72
75
|
TransactionWriteOperation, TransactionWriteOrigin,
|
|
73
76
|
};
|
|
74
77
|
|
|
75
|
-
pub(
|
|
78
|
+
pub(super) async fn register_lix_file_active_provider(
|
|
76
79
|
session: &SessionContext,
|
|
77
|
-
|
|
80
|
+
surface_name: &str,
|
|
81
|
+
active_branch_id: &str,
|
|
78
82
|
live_state: Arc<dyn LiveStateReader>,
|
|
79
|
-
|
|
83
|
+
branch_ref: Arc<dyn BranchRefReader>,
|
|
80
84
|
blob_reader: Arc<dyn BlobDataReader>,
|
|
81
85
|
functions: FunctionProviderHandle,
|
|
82
86
|
) -> Result<(), LixError> {
|
|
83
87
|
session
|
|
84
88
|
.register_table(
|
|
85
|
-
|
|
86
|
-
Arc::new(LixFileProvider::
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
89
|
+
surface_name,
|
|
90
|
+
Arc::new(LixFileProvider::active_branch(
|
|
91
|
+
active_branch_id,
|
|
92
|
+
live_state,
|
|
93
|
+
branch_ref,
|
|
94
|
+
blob_reader,
|
|
95
|
+
functions,
|
|
91
96
|
)),
|
|
92
97
|
)
|
|
93
98
|
.map_err(datafusion_error_to_lix_error)?;
|
|
99
|
+
Ok(())
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
pub(super) async fn register_lix_file_by_branch_provider(
|
|
103
|
+
session: &SessionContext,
|
|
104
|
+
surface_name: &str,
|
|
105
|
+
live_state: Arc<dyn LiveStateReader>,
|
|
106
|
+
branch_ref: Arc<dyn BranchRefReader>,
|
|
107
|
+
blob_reader: Arc<dyn BlobDataReader>,
|
|
108
|
+
functions: FunctionProviderHandle,
|
|
109
|
+
) -> Result<(), LixError> {
|
|
94
110
|
session
|
|
95
111
|
.register_table(
|
|
96
|
-
|
|
97
|
-
Arc::new(LixFileProvider::
|
|
98
|
-
active_version_id,
|
|
112
|
+
surface_name,
|
|
113
|
+
Arc::new(LixFileProvider::by_branch(
|
|
99
114
|
live_state,
|
|
100
|
-
|
|
101
|
-
|
|
115
|
+
branch_ref,
|
|
116
|
+
blob_reader,
|
|
102
117
|
functions,
|
|
103
118
|
)),
|
|
104
119
|
)
|
|
@@ -106,20 +121,33 @@ pub(crate) async fn register_lix_file_providers(
|
|
|
106
121
|
Ok(())
|
|
107
122
|
}
|
|
108
123
|
|
|
109
|
-
pub(
|
|
124
|
+
pub(super) async fn register_by_branch_write_provider(
|
|
110
125
|
session: &SessionContext,
|
|
126
|
+
surface_name: &str,
|
|
111
127
|
write_ctx: SqlWriteContext,
|
|
128
|
+
options: SqlWriteSessionOptions,
|
|
112
129
|
) -> Result<(), LixError> {
|
|
113
130
|
session
|
|
114
131
|
.register_table(
|
|
115
|
-
|
|
116
|
-
Arc::new(LixFileProvider::
|
|
132
|
+
surface_name,
|
|
133
|
+
Arc::new(LixFileProvider::by_branch_with_write(write_ctx, options)),
|
|
117
134
|
)
|
|
118
135
|
.map_err(datafusion_error_to_lix_error)?;
|
|
136
|
+
Ok(())
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
pub(super) async fn register_active_write_provider(
|
|
140
|
+
session: &SessionContext,
|
|
141
|
+
surface_name: &str,
|
|
142
|
+
write_ctx: SqlWriteContext,
|
|
143
|
+
options: SqlWriteSessionOptions,
|
|
144
|
+
) -> Result<(), LixError> {
|
|
119
145
|
session
|
|
120
146
|
.register_table(
|
|
121
|
-
|
|
122
|
-
Arc::new(LixFileProvider::
|
|
147
|
+
surface_name,
|
|
148
|
+
Arc::new(LixFileProvider::active_branch_with_write(
|
|
149
|
+
write_ctx, options,
|
|
150
|
+
)),
|
|
123
151
|
)
|
|
124
152
|
.map_err(datafusion_error_to_lix_error)?;
|
|
125
153
|
Ok(())
|
|
@@ -128,11 +156,12 @@ pub(crate) async fn register_lix_file_write_providers(
|
|
|
128
156
|
pub(crate) struct LixFileProvider {
|
|
129
157
|
schema: SchemaRef,
|
|
130
158
|
live_state: Arc<dyn LiveStateReader>,
|
|
131
|
-
|
|
159
|
+
branch_ref: Arc<dyn BranchRefReader>,
|
|
132
160
|
blob_reader: Arc<dyn BlobDataReader>,
|
|
133
161
|
write_access: WriteAccess,
|
|
134
162
|
functions: FunctionProviderHandle,
|
|
135
|
-
|
|
163
|
+
branch_binding: BranchBinding,
|
|
164
|
+
options: SqlWriteSessionOptions,
|
|
136
165
|
}
|
|
137
166
|
|
|
138
167
|
impl std::fmt::Debug for LixFileProvider {
|
|
@@ -142,71 +171,81 @@ impl std::fmt::Debug for LixFileProvider {
|
|
|
142
171
|
}
|
|
143
172
|
|
|
144
173
|
impl LixFileProvider {
|
|
145
|
-
pub(crate) fn
|
|
146
|
-
|
|
174
|
+
pub(crate) fn active_branch(
|
|
175
|
+
active_branch_id: impl Into<String>,
|
|
147
176
|
live_state: Arc<dyn LiveStateReader>,
|
|
148
|
-
|
|
177
|
+
branch_ref: Arc<dyn BranchRefReader>,
|
|
149
178
|
blob_reader: Arc<dyn BlobDataReader>,
|
|
150
179
|
functions: FunctionProviderHandle,
|
|
151
180
|
) -> Self {
|
|
152
181
|
Self {
|
|
153
182
|
schema: lix_file_schema(),
|
|
154
183
|
live_state,
|
|
155
|
-
|
|
184
|
+
branch_ref,
|
|
156
185
|
blob_reader,
|
|
157
186
|
write_access: WriteAccess::read_only(),
|
|
158
187
|
functions,
|
|
159
|
-
|
|
188
|
+
branch_binding: BranchBinding::active(active_branch_id),
|
|
189
|
+
options: SqlWriteSessionOptions::default(),
|
|
160
190
|
}
|
|
161
191
|
}
|
|
162
192
|
|
|
163
|
-
pub(crate) fn
|
|
164
|
-
|
|
193
|
+
pub(crate) fn active_branch_with_write(
|
|
194
|
+
write_ctx: SqlWriteContext,
|
|
195
|
+
options: SqlWriteSessionOptions,
|
|
196
|
+
) -> Self {
|
|
197
|
+
let active_branch_id = write_ctx.active_branch_id();
|
|
165
198
|
let functions = write_ctx.functions();
|
|
166
199
|
let live_state = Arc::new(WriteContextLiveStateReader::new(write_ctx.clone()));
|
|
167
|
-
let
|
|
200
|
+
let branch_ref = Arc::new(WriteContextBranchRefReader::new(write_ctx.clone()));
|
|
168
201
|
let blob_reader = write_ctx.blob_reader();
|
|
169
202
|
Self {
|
|
170
203
|
schema: lix_file_schema(),
|
|
171
204
|
live_state,
|
|
172
|
-
|
|
205
|
+
branch_ref,
|
|
173
206
|
blob_reader,
|
|
174
207
|
write_access: WriteAccess::write(write_ctx),
|
|
175
208
|
functions,
|
|
176
|
-
|
|
209
|
+
branch_binding: BranchBinding::active(active_branch_id),
|
|
210
|
+
options,
|
|
177
211
|
}
|
|
178
212
|
}
|
|
179
213
|
|
|
180
|
-
pub(crate) fn
|
|
214
|
+
pub(crate) fn by_branch(
|
|
181
215
|
live_state: Arc<dyn LiveStateReader>,
|
|
182
|
-
|
|
216
|
+
branch_ref: Arc<dyn BranchRefReader>,
|
|
183
217
|
blob_reader: Arc<dyn BlobDataReader>,
|
|
184
218
|
functions: FunctionProviderHandle,
|
|
185
219
|
) -> Self {
|
|
186
220
|
Self {
|
|
187
|
-
schema:
|
|
221
|
+
schema: lix_file_by_branch_schema(),
|
|
188
222
|
live_state,
|
|
189
|
-
|
|
223
|
+
branch_ref,
|
|
190
224
|
blob_reader,
|
|
191
225
|
write_access: WriteAccess::read_only(),
|
|
192
226
|
functions,
|
|
193
|
-
|
|
227
|
+
branch_binding: BranchBinding::explicit(),
|
|
228
|
+
options: SqlWriteSessionOptions::default(),
|
|
194
229
|
}
|
|
195
230
|
}
|
|
196
231
|
|
|
197
|
-
pub(crate) fn
|
|
232
|
+
pub(crate) fn by_branch_with_write(
|
|
233
|
+
write_ctx: SqlWriteContext,
|
|
234
|
+
options: SqlWriteSessionOptions,
|
|
235
|
+
) -> Self {
|
|
198
236
|
let functions = write_ctx.functions();
|
|
199
237
|
let live_state = Arc::new(WriteContextLiveStateReader::new(write_ctx.clone()));
|
|
200
|
-
let
|
|
238
|
+
let branch_ref = Arc::new(WriteContextBranchRefReader::new(write_ctx.clone()));
|
|
201
239
|
let blob_reader = write_ctx.blob_reader();
|
|
202
240
|
Self {
|
|
203
|
-
schema:
|
|
241
|
+
schema: lix_file_by_branch_schema(),
|
|
204
242
|
live_state,
|
|
205
|
-
|
|
243
|
+
branch_ref,
|
|
206
244
|
blob_reader,
|
|
207
245
|
write_access: WriteAccess::write(write_ctx),
|
|
208
246
|
functions,
|
|
209
|
-
|
|
247
|
+
branch_binding: BranchBinding::explicit(),
|
|
248
|
+
options,
|
|
210
249
|
}
|
|
211
250
|
}
|
|
212
251
|
}
|
|
@@ -233,7 +272,7 @@ impl TableProvider for LixFileProvider {
|
|
|
233
272
|
Ok(filters
|
|
234
273
|
.iter()
|
|
235
274
|
.map(|filter| {
|
|
236
|
-
if ExactStringColumnFilterAnalyzer::new("
|
|
275
|
+
if ExactStringColumnFilterAnalyzer::new("lixcol_branch_id").supports(filter)
|
|
237
276
|
|| analyzer.supports(filter)
|
|
238
277
|
|| contains_column(filter, "path")
|
|
239
278
|
{
|
|
@@ -255,24 +294,14 @@ impl TableProvider for LixFileProvider {
|
|
|
255
294
|
let projected_schema = projected_schema(&self.schema, projection)?;
|
|
256
295
|
let scan_limit = if filters.is_empty() { limit } else { None };
|
|
257
296
|
let mut request = lix_file_scan_request(
|
|
258
|
-
self.
|
|
297
|
+
self.branch_binding.active_branch_id(),
|
|
259
298
|
Some(projected_schema.as_ref()),
|
|
260
299
|
scan_limit,
|
|
261
300
|
);
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
return Err(DataFusionError::Plan(
|
|
267
|
-
"DELETE FROM lix_file_by_version requires an explicit lixcol_version_id predicate"
|
|
268
|
-
.to_string(),
|
|
269
|
-
));
|
|
270
|
-
}
|
|
271
|
-
}
|
|
272
|
-
request.filter.version_ids = resolve_provider_version_ids(
|
|
273
|
-
self.version_ref.as_ref(),
|
|
274
|
-
&self.version_binding,
|
|
275
|
-
request.filter.version_ids,
|
|
301
|
+
request.filter.branch_ids = resolve_provider_branch_ids(
|
|
302
|
+
self.branch_ref.as_ref(),
|
|
303
|
+
&self.branch_binding,
|
|
304
|
+
request.filter.branch_ids,
|
|
276
305
|
)
|
|
277
306
|
.await
|
|
278
307
|
.map_err(lix_error_to_datafusion_error)?;
|
|
@@ -306,19 +335,20 @@ impl TableProvider for LixFileProvider {
|
|
|
306
335
|
if insert_op != InsertOp::Append {
|
|
307
336
|
return not_impl_err!("{insert_op} not implemented for lix_file yet");
|
|
308
337
|
}
|
|
309
|
-
|
|
310
338
|
let write_ctx = self.write_access.require_write("INSERT into lix_file")?;
|
|
311
|
-
|
|
312
|
-
|
|
339
|
+
self.schema
|
|
340
|
+
.logically_equivalent_names_and_types(&input.schema())?;
|
|
341
|
+
let insert_intents = InsertColumnIntents::from_input(&input);
|
|
342
|
+
let include_data_writes = self.schema.field_with_name("data").is_ok()
|
|
343
|
+
&& insert_intents.includes_column("data")
|
|
344
|
+
&& !self.options.omitted_insert_columns.contains("data");
|
|
313
345
|
if include_data_writes {
|
|
314
346
|
reject_non_binary_casts_for_insert_column(&input, "data", "INSERT into lix_file")?;
|
|
315
347
|
}
|
|
316
|
-
|
|
317
348
|
let sink = LixFileInsertSink::new(
|
|
318
|
-
|
|
319
|
-
write_ctx.clone(),
|
|
349
|
+
write_ctx,
|
|
320
350
|
self.functions.clone(),
|
|
321
|
-
self.
|
|
351
|
+
self.branch_binding.clone(),
|
|
322
352
|
include_data_writes,
|
|
323
353
|
);
|
|
324
354
|
Ok(Arc::new(InsertExec::new(input, Arc::new(sink))))
|
|
@@ -330,32 +360,29 @@ impl TableProvider for LixFileProvider {
|
|
|
330
360
|
filters: Vec<Expr>,
|
|
331
361
|
) -> Result<Arc<dyn ExecutionPlan>> {
|
|
332
362
|
let write_ctx = self.write_access.require_write("DELETE FROM lix_file")?;
|
|
333
|
-
|
|
334
|
-
let df_schema = DFSchema::try_from(Arc::clone(&self.schema))?;
|
|
335
363
|
let filters = canonicalize_filesystem_path_filters(&filters, FilesystemPathKind::File)?;
|
|
364
|
+
let filters = canonicalize_json_identity_text_filters(self.schema.as_ref(), &filters)?;
|
|
365
|
+
let target_file_ids = file_id_constraint_from_filters(&filters)?;
|
|
366
|
+
let df_schema = DFSchema::try_from(Arc::clone(&self.schema))?;
|
|
336
367
|
validate_json_predicate_filters(self.schema.as_ref(), &filters)?;
|
|
337
368
|
let physical_filters = filters
|
|
338
369
|
.iter()
|
|
339
370
|
.map(|expr| create_physical_expr(expr, &df_schema, state.execution_props()))
|
|
340
371
|
.collect::<Result<Vec<_>>>()?;
|
|
341
|
-
let
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
));
|
|
351
|
-
}
|
|
352
|
-
}
|
|
353
|
-
|
|
372
|
+
let mut request = lix_file_scan_request(self.branch_binding.active_branch_id(), None, None);
|
|
373
|
+
request.filter.branch_ids = explicit_branch_ids_from_dml_filters(&filters);
|
|
374
|
+
request.filter.branch_ids = resolve_provider_branch_ids(
|
|
375
|
+
self.branch_ref.as_ref(),
|
|
376
|
+
&self.branch_binding,
|
|
377
|
+
request.filter.branch_ids,
|
|
378
|
+
)
|
|
379
|
+
.await
|
|
380
|
+
.map_err(lix_error_to_datafusion_error)?;
|
|
354
381
|
Ok(Arc::new(LixFileDeleteExec::new(
|
|
355
382
|
Arc::clone(&self.blob_reader),
|
|
356
|
-
write_ctx
|
|
383
|
+
write_ctx,
|
|
357
384
|
Arc::clone(&self.schema),
|
|
358
|
-
self.
|
|
385
|
+
self.branch_binding.clone(),
|
|
359
386
|
request,
|
|
360
387
|
target_file_ids,
|
|
361
388
|
physical_filters,
|
|
@@ -369,10 +396,12 @@ impl TableProvider for LixFileProvider {
|
|
|
369
396
|
filters: Vec<Expr>,
|
|
370
397
|
) -> Result<Arc<dyn ExecutionPlan>> {
|
|
371
398
|
let write_ctx = self.write_access.require_write("UPDATE lix_file")?;
|
|
372
|
-
|
|
373
399
|
validate_lix_file_update_assignments(&self.schema, &assignments)?;
|
|
374
|
-
|
|
400
|
+
let filters = canonicalize_filesystem_path_filters(&filters, FilesystemPathKind::File)?;
|
|
401
|
+
let filters = canonicalize_json_identity_text_filters(self.schema.as_ref(), &filters)?;
|
|
402
|
+
let target_file_ids = file_id_constraint_from_filters(&filters)?;
|
|
375
403
|
let df_schema = DFSchema::try_from(Arc::clone(&self.schema))?;
|
|
404
|
+
validate_json_predicate_filters(self.schema.as_ref(), &filters)?;
|
|
376
405
|
let physical_assignments = assignments
|
|
377
406
|
.iter()
|
|
378
407
|
.map(|(column_name, expr)| {
|
|
@@ -382,20 +411,24 @@ impl TableProvider for LixFileProvider {
|
|
|
382
411
|
))
|
|
383
412
|
})
|
|
384
413
|
.collect::<Result<Vec<_>>>()?;
|
|
385
|
-
let filters = canonicalize_filesystem_path_filters(&filters, FilesystemPathKind::File)?;
|
|
386
|
-
let target_file_ids = file_id_constraint_from_filters(&filters)?;
|
|
387
|
-
validate_json_predicate_filters(self.schema.as_ref(), &filters)?;
|
|
388
414
|
let physical_filters = filters
|
|
389
415
|
.iter()
|
|
390
416
|
.map(|expr| create_physical_expr(expr, &df_schema, state.execution_props()))
|
|
391
417
|
.collect::<Result<Vec<_>>>()?;
|
|
392
|
-
let request = lix_file_scan_request(self.
|
|
393
|
-
|
|
418
|
+
let mut request = lix_file_scan_request(self.branch_binding.active_branch_id(), None, None);
|
|
419
|
+
request.filter.branch_ids = explicit_branch_ids_from_dml_filters(&filters);
|
|
420
|
+
request.filter.branch_ids = resolve_provider_branch_ids(
|
|
421
|
+
self.branch_ref.as_ref(),
|
|
422
|
+
&self.branch_binding,
|
|
423
|
+
request.filter.branch_ids,
|
|
424
|
+
)
|
|
425
|
+
.await
|
|
426
|
+
.map_err(lix_error_to_datafusion_error)?;
|
|
394
427
|
Ok(Arc::new(LixFileUpdateExec::new(
|
|
395
428
|
Arc::clone(&self.blob_reader),
|
|
396
|
-
write_ctx
|
|
429
|
+
write_ctx,
|
|
397
430
|
Arc::clone(&self.schema),
|
|
398
|
-
self.
|
|
431
|
+
self.branch_binding.clone(),
|
|
399
432
|
self.functions.clone(),
|
|
400
433
|
request,
|
|
401
434
|
target_file_ids,
|
|
@@ -409,7 +442,7 @@ impl TableProvider for LixFileProvider {
|
|
|
409
442
|
struct LixFileInsertSink {
|
|
410
443
|
write_ctx: SqlWriteContext,
|
|
411
444
|
functions: FunctionProviderHandle,
|
|
412
|
-
|
|
445
|
+
branch_binding: BranchBinding,
|
|
413
446
|
surface_name: &'static str,
|
|
414
447
|
include_data_writes: bool,
|
|
415
448
|
}
|
|
@@ -422,17 +455,16 @@ impl std::fmt::Debug for LixFileInsertSink {
|
|
|
422
455
|
|
|
423
456
|
impl LixFileInsertSink {
|
|
424
457
|
fn new(
|
|
425
|
-
_schema: SchemaRef,
|
|
426
458
|
write_ctx: SqlWriteContext,
|
|
427
459
|
functions: FunctionProviderHandle,
|
|
428
|
-
|
|
460
|
+
branch_binding: BranchBinding,
|
|
429
461
|
include_data_writes: bool,
|
|
430
462
|
) -> Self {
|
|
431
|
-
let surface_name = lix_file_surface_name(&
|
|
463
|
+
let surface_name = lix_file_surface_name(&branch_binding);
|
|
432
464
|
Self {
|
|
433
465
|
write_ctx,
|
|
434
466
|
functions,
|
|
435
|
-
|
|
467
|
+
branch_binding,
|
|
436
468
|
surface_name,
|
|
437
469
|
include_data_writes,
|
|
438
470
|
}
|
|
@@ -464,7 +496,7 @@ impl InsertSink for LixFileInsertSink {
|
|
|
464
496
|
path_resolvers = Some(
|
|
465
497
|
file_path_resolvers_from_live_state(
|
|
466
498
|
Arc::new(WriteContextLiveStateReader::new(self.write_ctx.clone())),
|
|
467
|
-
self.
|
|
499
|
+
self.branch_binding.active_branch_id(),
|
|
468
500
|
)
|
|
469
501
|
.await
|
|
470
502
|
.map_err(lix_error_to_datafusion_error)?,
|
|
@@ -473,7 +505,7 @@ impl InsertSink for LixFileInsertSink {
|
|
|
473
505
|
if record_batch_has_non_null_column(&batch, "path")? {
|
|
474
506
|
staged.extend(lix_file_insert_stage_from_batch_with_path_resolvers(
|
|
475
507
|
&batch,
|
|
476
|
-
self.
|
|
508
|
+
self.branch_binding.active_branch_id(),
|
|
477
509
|
self.surface_name,
|
|
478
510
|
path_resolvers
|
|
479
511
|
.as_mut()
|
|
@@ -485,7 +517,7 @@ impl InsertSink for LixFileInsertSink {
|
|
|
485
517
|
staged.extend(
|
|
486
518
|
lix_file_insert_stage_from_batch_with_id_generator_and_path_resolvers(
|
|
487
519
|
&batch,
|
|
488
|
-
self.
|
|
520
|
+
self.branch_binding.active_branch_id(),
|
|
489
521
|
self.surface_name,
|
|
490
522
|
path_resolvers
|
|
491
523
|
.as_mut()
|
|
@@ -521,10 +553,10 @@ impl InsertSink for LixFileInsertSink {
|
|
|
521
553
|
}
|
|
522
554
|
}
|
|
523
555
|
|
|
524
|
-
fn lix_file_surface_name(
|
|
525
|
-
match
|
|
526
|
-
|
|
527
|
-
|
|
556
|
+
fn lix_file_surface_name(branch_binding: &BranchBinding) -> &'static str {
|
|
557
|
+
match branch_binding {
|
|
558
|
+
BranchBinding::Active { .. } => "lix_file",
|
|
559
|
+
BranchBinding::Explicit => "lix_file_by_branch",
|
|
528
560
|
}
|
|
529
561
|
}
|
|
530
562
|
|
|
@@ -533,7 +565,7 @@ struct LixFileDeleteExec {
|
|
|
533
565
|
blob_reader: Arc<dyn BlobDataReader>,
|
|
534
566
|
write_ctx: SqlWriteContext,
|
|
535
567
|
table_schema: SchemaRef,
|
|
536
|
-
|
|
568
|
+
branch_binding: BranchBinding,
|
|
537
569
|
request: LiveStateScanRequest,
|
|
538
570
|
target_file_ids: FileIdConstraint,
|
|
539
571
|
filters: Vec<Arc<dyn PhysicalExpr>>,
|
|
@@ -552,7 +584,7 @@ impl LixFileDeleteExec {
|
|
|
552
584
|
blob_reader: Arc<dyn BlobDataReader>,
|
|
553
585
|
write_ctx: SqlWriteContext,
|
|
554
586
|
table_schema: SchemaRef,
|
|
555
|
-
|
|
587
|
+
branch_binding: BranchBinding,
|
|
556
588
|
request: LiveStateScanRequest,
|
|
557
589
|
target_file_ids: FileIdConstraint,
|
|
558
590
|
filters: Vec<Arc<dyn PhysicalExpr>>,
|
|
@@ -568,7 +600,7 @@ impl LixFileDeleteExec {
|
|
|
568
600
|
blob_reader,
|
|
569
601
|
write_ctx,
|
|
570
602
|
table_schema,
|
|
571
|
-
|
|
603
|
+
branch_binding,
|
|
572
604
|
request,
|
|
573
605
|
target_file_ids,
|
|
574
606
|
filters,
|
|
@@ -632,7 +664,7 @@ impl ExecutionPlan for LixFileDeleteExec {
|
|
|
632
664
|
let blob_reader = Arc::clone(&self.blob_reader);
|
|
633
665
|
let write_ctx = self.write_ctx.clone();
|
|
634
666
|
let table_schema = Arc::clone(&self.table_schema);
|
|
635
|
-
let
|
|
667
|
+
let branch_binding = self.branch_binding.clone();
|
|
636
668
|
let request = self.request.clone();
|
|
637
669
|
let target_file_ids = self.target_file_ids.clone();
|
|
638
670
|
let filters = self.filters.clone();
|
|
@@ -655,7 +687,7 @@ impl ExecutionPlan for LixFileDeleteExec {
|
|
|
655
687
|
let matched_batch = filter_lix_file_batch(source_batch, &filters)?;
|
|
656
688
|
let staged = lix_file_delete_stage_from_batch(
|
|
657
689
|
&matched_batch,
|
|
658
|
-
|
|
690
|
+
branch_binding.active_branch_id(),
|
|
659
691
|
&blob_ref_file_ids,
|
|
660
692
|
)?;
|
|
661
693
|
let count = staged.count;
|
|
@@ -688,7 +720,7 @@ struct LixFileUpdateExec {
|
|
|
688
720
|
blob_reader: Arc<dyn BlobDataReader>,
|
|
689
721
|
write_ctx: SqlWriteContext,
|
|
690
722
|
table_schema: SchemaRef,
|
|
691
|
-
|
|
723
|
+
branch_binding: BranchBinding,
|
|
692
724
|
functions: FunctionProviderHandle,
|
|
693
725
|
request: LiveStateScanRequest,
|
|
694
726
|
target_file_ids: FileIdConstraint,
|
|
@@ -709,7 +741,7 @@ impl LixFileUpdateExec {
|
|
|
709
741
|
blob_reader: Arc<dyn BlobDataReader>,
|
|
710
742
|
write_ctx: SqlWriteContext,
|
|
711
743
|
table_schema: SchemaRef,
|
|
712
|
-
|
|
744
|
+
branch_binding: BranchBinding,
|
|
713
745
|
functions: FunctionProviderHandle,
|
|
714
746
|
request: LiveStateScanRequest,
|
|
715
747
|
target_file_ids: FileIdConstraint,
|
|
@@ -727,7 +759,7 @@ impl LixFileUpdateExec {
|
|
|
727
759
|
blob_reader,
|
|
728
760
|
write_ctx,
|
|
729
761
|
table_schema,
|
|
730
|
-
|
|
762
|
+
branch_binding,
|
|
731
763
|
functions,
|
|
732
764
|
request,
|
|
733
765
|
target_file_ids,
|
|
@@ -798,7 +830,7 @@ impl ExecutionPlan for LixFileUpdateExec {
|
|
|
798
830
|
let blob_reader = Arc::clone(&self.blob_reader);
|
|
799
831
|
let write_ctx = self.write_ctx.clone();
|
|
800
832
|
let table_schema = Arc::clone(&self.table_schema);
|
|
801
|
-
let
|
|
833
|
+
let branch_binding = self.branch_binding.clone();
|
|
802
834
|
let functions = self.functions.clone();
|
|
803
835
|
let request = self.request.clone();
|
|
804
836
|
let target_file_ids = self.target_file_ids.clone();
|
|
@@ -826,7 +858,7 @@ impl ExecutionPlan for LixFileUpdateExec {
|
|
|
826
858
|
path_resolvers = Some(
|
|
827
859
|
file_path_resolvers_from_live_state(
|
|
828
860
|
Arc::new(WriteContextLiveStateReader::new(write_ctx.clone())),
|
|
829
|
-
|
|
861
|
+
branch_binding.active_branch_id(),
|
|
830
862
|
)
|
|
831
863
|
.await
|
|
832
864
|
.map_err(lix_error_to_datafusion_error)?,
|
|
@@ -835,7 +867,7 @@ impl ExecutionPlan for LixFileUpdateExec {
|
|
|
835
867
|
let staged = lix_file_update_stage_from_batch(
|
|
836
868
|
&matched_batch,
|
|
837
869
|
&assignment_values,
|
|
838
|
-
|
|
870
|
+
branch_binding.active_branch_id(),
|
|
839
871
|
update_columns,
|
|
840
872
|
path_resolvers.as_mut(),
|
|
841
873
|
&mut || functions.call_uuid_v7(),
|
|
@@ -1035,7 +1067,7 @@ struct DirectoryDescriptorRecord {
|
|
|
1035
1067
|
id: String,
|
|
1036
1068
|
parent_id: Option<String>,
|
|
1037
1069
|
name: String,
|
|
1038
|
-
|
|
1070
|
+
branch_id: String,
|
|
1039
1071
|
}
|
|
1040
1072
|
|
|
1041
1073
|
#[derive(Debug, Deserialize)]
|
|
@@ -1073,7 +1105,10 @@ impl LixFileStagedBatch {
|
|
|
1073
1105
|
self.count += other.count;
|
|
1074
1106
|
}
|
|
1075
1107
|
|
|
1076
|
-
fn extend_filesystem_plan(
|
|
1108
|
+
fn extend_filesystem_plan(
|
|
1109
|
+
&mut self,
|
|
1110
|
+
plan: crate::sql2::filesystem_planner::FilesystemWritePlan,
|
|
1111
|
+
) {
|
|
1077
1112
|
self.state_rows.extend(plan.rows);
|
|
1078
1113
|
self.file_data_writes.extend(plan.file_data);
|
|
1079
1114
|
self.count += plan.count;
|
|
@@ -1088,20 +1123,20 @@ impl LixFileStagedBatch {
|
|
|
1088
1123
|
#[cfg(test)]
|
|
1089
1124
|
fn lix_file_write_rows_from_batch(
|
|
1090
1125
|
batch: &RecordBatch,
|
|
1091
|
-
|
|
1126
|
+
branch_binding: Option<&str>,
|
|
1092
1127
|
) -> Result<Vec<TransactionWriteRow>> {
|
|
1093
|
-
Ok(lix_file_insert_stage_from_batch(batch,
|
|
1128
|
+
Ok(lix_file_insert_stage_from_batch(batch, branch_binding)?.state_rows)
|
|
1094
1129
|
}
|
|
1095
1130
|
|
|
1096
1131
|
fn lix_file_delete_stage_from_batch(
|
|
1097
1132
|
batch: &RecordBatch,
|
|
1098
|
-
|
|
1133
|
+
branch_binding: Option<&str>,
|
|
1099
1134
|
blob_ref_file_ids: &BTreeSet<String>,
|
|
1100
1135
|
) -> Result<LixFileStagedBatch> {
|
|
1101
1136
|
let mut staged = LixFileStagedBatch::default();
|
|
1102
1137
|
for row_index in 0..batch.num_rows() {
|
|
1103
1138
|
let file_id = required_string_value(batch, row_index, "id")?;
|
|
1104
|
-
let context = file_row_context_from_batch(batch, row_index,
|
|
1139
|
+
let context = file_row_context_from_batch(batch, row_index, branch_binding)?;
|
|
1105
1140
|
staged.extend_filesystem_delete_plan(plan_file_delete(FileDeleteInput {
|
|
1106
1141
|
file_id: file_id.clone(),
|
|
1107
1142
|
has_blob_ref: blob_ref_file_ids.contains(&file_id),
|
|
@@ -1137,14 +1172,14 @@ fn blob_ref_file_ids_from_live_rows(
|
|
|
1137
1172
|
#[cfg(test)]
|
|
1138
1173
|
fn lix_file_insert_stage_from_batch(
|
|
1139
1174
|
batch: &RecordBatch,
|
|
1140
|
-
|
|
1175
|
+
branch_binding: Option<&str>,
|
|
1141
1176
|
) -> Result<LixFileStagedBatch> {
|
|
1142
|
-
lix_file_stage_from_batch_with_options(batch,
|
|
1177
|
+
lix_file_stage_from_batch_with_options(batch, branch_binding, "lix_file", true, true, true)
|
|
1143
1178
|
}
|
|
1144
1179
|
|
|
1145
1180
|
fn lix_file_insert_stage_from_batch_with_id_generator_and_path_resolvers(
|
|
1146
1181
|
batch: &RecordBatch,
|
|
1147
|
-
|
|
1182
|
+
branch_binding: Option<&str>,
|
|
1148
1183
|
surface_name: &str,
|
|
1149
1184
|
path_resolvers: &mut BTreeMap<String, DirectoryPathResolver>,
|
|
1150
1185
|
generate_id: &mut dyn FnMut() -> String,
|
|
@@ -1152,7 +1187,7 @@ fn lix_file_insert_stage_from_batch_with_id_generator_and_path_resolvers(
|
|
|
1152
1187
|
) -> Result<LixFileStagedBatch> {
|
|
1153
1188
|
lix_file_stage_from_batch_with_options_and_path_resolvers(
|
|
1154
1189
|
batch,
|
|
1155
|
-
|
|
1190
|
+
branch_binding,
|
|
1156
1191
|
surface_name,
|
|
1157
1192
|
true,
|
|
1158
1193
|
true,
|
|
@@ -1164,7 +1199,7 @@ fn lix_file_insert_stage_from_batch_with_id_generator_and_path_resolvers(
|
|
|
1164
1199
|
|
|
1165
1200
|
fn lix_file_insert_stage_from_batch_with_path_resolvers(
|
|
1166
1201
|
batch: &RecordBatch,
|
|
1167
|
-
|
|
1202
|
+
branch_binding: Option<&str>,
|
|
1168
1203
|
surface_name: &str,
|
|
1169
1204
|
path_resolvers: &mut BTreeMap<String, DirectoryPathResolver>,
|
|
1170
1205
|
generate_directory_id: &mut dyn FnMut() -> String,
|
|
@@ -1172,7 +1207,7 @@ fn lix_file_insert_stage_from_batch_with_path_resolvers(
|
|
|
1172
1207
|
) -> Result<LixFileStagedBatch> {
|
|
1173
1208
|
lix_file_stage_from_batch_with_options_and_path_resolvers(
|
|
1174
1209
|
batch,
|
|
1175
|
-
|
|
1210
|
+
branch_binding,
|
|
1176
1211
|
surface_name,
|
|
1177
1212
|
true,
|
|
1178
1213
|
true,
|
|
@@ -1185,7 +1220,7 @@ fn lix_file_insert_stage_from_batch_with_path_resolvers(
|
|
|
1185
1220
|
fn lix_file_existing_update_stage_from_batch(
|
|
1186
1221
|
batch: &RecordBatch,
|
|
1187
1222
|
assignment_values: &UpdateAssignmentValues,
|
|
1188
|
-
|
|
1223
|
+
branch_binding: Option<&str>,
|
|
1189
1224
|
include_descriptor_writes: bool,
|
|
1190
1225
|
include_data_writes: bool,
|
|
1191
1226
|
path_resolvers: Option<&mut BTreeMap<String, DirectoryPathResolver>>,
|
|
@@ -1198,7 +1233,7 @@ fn lix_file_existing_update_stage_from_batch(
|
|
|
1198
1233
|
let hidden = update_optional_bool_value(batch, assignment_values, row_index, "hidden")?
|
|
1199
1234
|
.unwrap_or(false);
|
|
1200
1235
|
let context =
|
|
1201
|
-
file_row_context_from_update(batch, assignment_values, row_index,
|
|
1236
|
+
file_row_context_from_update(batch, assignment_values, row_index, branch_binding)?;
|
|
1202
1237
|
|
|
1203
1238
|
if include_descriptor_writes {
|
|
1204
1239
|
let directory_id =
|
|
@@ -1266,7 +1301,7 @@ impl LixFileUpdateColumns {
|
|
|
1266
1301
|
fn lix_file_update_stage_from_batch(
|
|
1267
1302
|
batch: &RecordBatch,
|
|
1268
1303
|
assignment_values: &UpdateAssignmentValues,
|
|
1269
|
-
|
|
1304
|
+
branch_binding: Option<&str>,
|
|
1270
1305
|
update_columns: LixFileUpdateColumns,
|
|
1271
1306
|
path_resolvers: Option<&mut BTreeMap<String, DirectoryPathResolver>>,
|
|
1272
1307
|
generate_directory_id: &mut dyn FnMut() -> String,
|
|
@@ -1281,7 +1316,7 @@ fn lix_file_update_stage_from_batch(
|
|
|
1281
1316
|
lix_file_path_update_stage_from_batch(
|
|
1282
1317
|
batch,
|
|
1283
1318
|
assignment_values,
|
|
1284
|
-
|
|
1319
|
+
branch_binding,
|
|
1285
1320
|
update_columns,
|
|
1286
1321
|
path_resolvers,
|
|
1287
1322
|
generate_directory_id,
|
|
@@ -1290,7 +1325,7 @@ fn lix_file_update_stage_from_batch(
|
|
|
1290
1325
|
lix_file_existing_update_stage_from_batch(
|
|
1291
1326
|
batch,
|
|
1292
1327
|
assignment_values,
|
|
1293
|
-
|
|
1328
|
+
branch_binding,
|
|
1294
1329
|
update_columns.descriptor,
|
|
1295
1330
|
update_columns.data,
|
|
1296
1331
|
Some(path_resolvers),
|
|
@@ -1301,7 +1336,7 @@ fn lix_file_update_stage_from_batch(
|
|
|
1301
1336
|
lix_file_existing_update_stage_from_batch(
|
|
1302
1337
|
batch,
|
|
1303
1338
|
assignment_values,
|
|
1304
|
-
|
|
1339
|
+
branch_binding,
|
|
1305
1340
|
update_columns.descriptor,
|
|
1306
1341
|
update_columns.data,
|
|
1307
1342
|
None,
|
|
@@ -1311,7 +1346,7 @@ fn lix_file_update_stage_from_batch(
|
|
|
1311
1346
|
fn lix_file_path_update_stage_from_batch(
|
|
1312
1347
|
batch: &RecordBatch,
|
|
1313
1348
|
assignment_values: &UpdateAssignmentValues,
|
|
1314
|
-
|
|
1349
|
+
branch_binding: Option<&str>,
|
|
1315
1350
|
update_columns: LixFileUpdateColumns,
|
|
1316
1351
|
path_resolvers: &mut BTreeMap<String, DirectoryPathResolver>,
|
|
1317
1352
|
generate_directory_id: &mut dyn FnMut() -> String,
|
|
@@ -1324,7 +1359,7 @@ fn lix_file_path_update_stage_from_batch(
|
|
|
1324
1359
|
let hidden = update_optional_bool_value(batch, assignment_values, row_index, "hidden")?
|
|
1325
1360
|
.unwrap_or(false);
|
|
1326
1361
|
let context =
|
|
1327
|
-
file_row_context_from_update(batch, assignment_values, row_index,
|
|
1362
|
+
file_row_context_from_update(batch, assignment_values, row_index, branch_binding)?;
|
|
1328
1363
|
let assigned_data = if update_columns.data {
|
|
1329
1364
|
Some(update_required_binary_value(
|
|
1330
1365
|
batch,
|
|
@@ -1362,7 +1397,7 @@ fn lix_file_path_update_stage_from_batch(
|
|
|
1362
1397
|
#[cfg(test)]
|
|
1363
1398
|
fn lix_file_stage_from_batch_with_options(
|
|
1364
1399
|
batch: &RecordBatch,
|
|
1365
|
-
|
|
1400
|
+
branch_binding: Option<&str>,
|
|
1366
1401
|
surface_name: &str,
|
|
1367
1402
|
reject_read_only_fields: bool,
|
|
1368
1403
|
include_descriptor_writes: bool,
|
|
@@ -1370,7 +1405,7 @@ fn lix_file_stage_from_batch_with_options(
|
|
|
1370
1405
|
) -> Result<LixFileStagedBatch> {
|
|
1371
1406
|
lix_file_stage_from_batch_with_options_and_path_resolvers(
|
|
1372
1407
|
batch,
|
|
1373
|
-
|
|
1408
|
+
branch_binding,
|
|
1374
1409
|
surface_name,
|
|
1375
1410
|
reject_read_only_fields,
|
|
1376
1411
|
include_descriptor_writes,
|
|
@@ -1382,7 +1417,7 @@ fn lix_file_stage_from_batch_with_options(
|
|
|
1382
1417
|
|
|
1383
1418
|
fn lix_file_stage_from_batch_with_options_and_path_resolvers(
|
|
1384
1419
|
batch: &RecordBatch,
|
|
1385
|
-
|
|
1420
|
+
branch_binding: Option<&str>,
|
|
1386
1421
|
surface_name: &str,
|
|
1387
1422
|
reject_read_only_fields: bool,
|
|
1388
1423
|
include_descriptor_writes: bool,
|
|
@@ -1394,7 +1429,7 @@ fn lix_file_stage_from_batch_with_options_and_path_resolvers(
|
|
|
1394
1429
|
|
|
1395
1430
|
for row_index in 0..batch.num_rows() {
|
|
1396
1431
|
if reject_read_only_fields {
|
|
1397
|
-
reject_read_only_lix_file_insert_field(batch, row_index, "
|
|
1432
|
+
reject_read_only_lix_file_insert_field(batch, row_index, "lixcol_entity_pk")?;
|
|
1398
1433
|
reject_read_only_lix_file_insert_field(batch, row_index, "lixcol_schema_key")?;
|
|
1399
1434
|
reject_read_only_lix_file_insert_field(batch, row_index, "lixcol_change_id")?;
|
|
1400
1435
|
reject_read_only_lix_file_insert_field(batch, row_index, "lixcol_created_at")?;
|
|
@@ -1405,7 +1440,7 @@ fn lix_file_stage_from_batch_with_options_and_path_resolvers(
|
|
|
1405
1440
|
let path = optional_string_value(batch, row_index, "path")?;
|
|
1406
1441
|
let id = optional_string_value(batch, row_index, "id")?;
|
|
1407
1442
|
let hidden = optional_bool_value(batch, row_index, "hidden")?;
|
|
1408
|
-
let context = file_row_context_from_batch(batch, row_index,
|
|
1443
|
+
let context = file_row_context_from_batch(batch, row_index, branch_binding)?;
|
|
1409
1444
|
let data = if include_data_writes {
|
|
1410
1445
|
insert_optional_binary_value(batch, row_index, "data")?
|
|
1411
1446
|
} else {
|
|
@@ -1430,7 +1465,7 @@ fn lix_file_stage_from_batch_with_options_and_path_resolvers(
|
|
|
1430
1465
|
));
|
|
1431
1466
|
};
|
|
1432
1467
|
let file_id = id.unwrap_or_else(|| generate_directory_id());
|
|
1433
|
-
let mut plan =
|
|
1468
|
+
let mut plan = crate::sql2::filesystem_planner::plan_file_path_write(
|
|
1434
1469
|
resolver,
|
|
1435
1470
|
FilePathWriteInput {
|
|
1436
1471
|
id: Some(file_id.clone()),
|
|
@@ -1524,7 +1559,7 @@ fn stage_lix_file_data_write(
|
|
|
1524
1559
|
staged.state_rows.push(row);
|
|
1525
1560
|
staged.file_data_writes.push(TransactionFileData {
|
|
1526
1561
|
file_id,
|
|
1527
|
-
|
|
1562
|
+
branch_id: context.branch_id,
|
|
1528
1563
|
untracked: context.untracked,
|
|
1529
1564
|
data,
|
|
1530
1565
|
});
|
|
@@ -1558,19 +1593,19 @@ fn lix_file_insert_origin(surface_name: &str, file_id: &str) -> TransactionWrite
|
|
|
1558
1593
|
fn file_row_context_from_batch(
|
|
1559
1594
|
batch: &RecordBatch,
|
|
1560
1595
|
row_index: usize,
|
|
1561
|
-
|
|
1596
|
+
branch_binding: Option<&str>,
|
|
1562
1597
|
) -> Result<FilesystemRowContext> {
|
|
1563
|
-
let
|
|
1564
|
-
let scope =
|
|
1598
|
+
let explicit_branch_id = optional_string_value(batch, row_index, "lixcol_branch_id")?;
|
|
1599
|
+
let scope = resolve_write_branch_scope(
|
|
1565
1600
|
optional_bool_value(batch, row_index, "lixcol_global")?,
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
"INSERT into
|
|
1601
|
+
explicit_branch_id,
|
|
1602
|
+
branch_binding,
|
|
1603
|
+
"INSERT into lix_file_by_branch",
|
|
1569
1604
|
"lix_file",
|
|
1570
1605
|
)?;
|
|
1571
1606
|
|
|
1572
1607
|
Ok(FilesystemRowContext {
|
|
1573
|
-
|
|
1608
|
+
branch_id: scope.branch_id,
|
|
1574
1609
|
global: scope.global,
|
|
1575
1610
|
untracked: optional_bool_value(batch, row_index, "lixcol_untracked")?.unwrap_or(false),
|
|
1576
1611
|
file_id: optional_string_value(batch, row_index, "lixcol_file_id")?,
|
|
@@ -1582,19 +1617,19 @@ fn file_row_context_from_update(
|
|
|
1582
1617
|
batch: &RecordBatch,
|
|
1583
1618
|
assignment_values: &UpdateAssignmentValues,
|
|
1584
1619
|
row_index: usize,
|
|
1585
|
-
|
|
1620
|
+
branch_binding: Option<&str>,
|
|
1586
1621
|
) -> Result<FilesystemRowContext> {
|
|
1587
|
-
let
|
|
1588
|
-
let scope =
|
|
1622
|
+
let explicit_branch_id = optional_string_value(batch, row_index, "lixcol_branch_id")?;
|
|
1623
|
+
let scope = resolve_write_branch_scope(
|
|
1589
1624
|
optional_bool_value(batch, row_index, "lixcol_global")?,
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
"UPDATE into
|
|
1625
|
+
explicit_branch_id,
|
|
1626
|
+
branch_binding,
|
|
1627
|
+
"UPDATE into lix_file_by_branch",
|
|
1593
1628
|
"lix_file",
|
|
1594
1629
|
)?;
|
|
1595
1630
|
|
|
1596
1631
|
Ok(FilesystemRowContext {
|
|
1597
|
-
|
|
1632
|
+
branch_id: scope.branch_id,
|
|
1598
1633
|
global: scope.global,
|
|
1599
1634
|
untracked: optional_bool_value(batch, row_index, "lixcol_untracked")?.unwrap_or(false),
|
|
1600
1635
|
file_id: optional_string_value(batch, row_index, "lixcol_file_id")?,
|
|
@@ -1610,7 +1645,7 @@ fn file_row_context_from_update(
|
|
|
1610
1645
|
|
|
1611
1646
|
fn file_path_resolver_key(context: &FilesystemRowContext) -> String {
|
|
1612
1647
|
filesystem_storage_scope_key(
|
|
1613
|
-
&context.
|
|
1648
|
+
&context.branch_id,
|
|
1614
1649
|
context.global,
|
|
1615
1650
|
context.untracked,
|
|
1616
1651
|
context.file_id.as_deref(),
|
|
@@ -1619,7 +1654,7 @@ fn file_path_resolver_key(context: &FilesystemRowContext) -> String {
|
|
|
1619
1654
|
|
|
1620
1655
|
async fn file_path_resolvers_from_live_state(
|
|
1621
1656
|
live_state: Arc<dyn LiveStateReader>,
|
|
1622
|
-
|
|
1657
|
+
branch_binding: Option<&str>,
|
|
1623
1658
|
) -> std::result::Result<BTreeMap<String, DirectoryPathResolver>, LixError> {
|
|
1624
1659
|
let rows = live_state
|
|
1625
1660
|
.scan_rows(&LiveStateScanRequest {
|
|
@@ -1628,8 +1663,8 @@ async fn file_path_resolvers_from_live_state(
|
|
|
1628
1663
|
DIRECTORY_DESCRIPTOR_SCHEMA_KEY.to_string(),
|
|
1629
1664
|
FILE_DESCRIPTOR_SCHEMA_KEY.to_string(),
|
|
1630
1665
|
],
|
|
1631
|
-
|
|
1632
|
-
.map(|
|
|
1666
|
+
branch_ids: branch_binding
|
|
1667
|
+
.map(|branch_id| vec![branch_id.to_string()])
|
|
1633
1668
|
.unwrap_or_default(),
|
|
1634
1669
|
..Default::default()
|
|
1635
1670
|
},
|
|
@@ -1637,8 +1672,8 @@ async fn file_path_resolvers_from_live_state(
|
|
|
1637
1672
|
})
|
|
1638
1673
|
.await?;
|
|
1639
1674
|
let mut resolvers = directory_path_resolvers_from_state_rows(rows)?;
|
|
1640
|
-
if let Some(
|
|
1641
|
-
let key = filesystem_storage_scope_key(
|
|
1675
|
+
if let Some(branch_id) = branch_binding {
|
|
1676
|
+
let key = filesystem_storage_scope_key(branch_id, false, false, None);
|
|
1642
1677
|
resolvers
|
|
1643
1678
|
.entry(key)
|
|
1644
1679
|
.or_insert_with(DirectoryPathResolver::default);
|
|
@@ -1678,7 +1713,7 @@ async fn lix_file_record_batch(
|
|
|
1678
1713
|
)
|
|
1679
1714
|
})?;
|
|
1680
1715
|
file_rows.insert(
|
|
1681
|
-
(row.
|
|
1716
|
+
(row.branch_id.clone(), snapshot.id.clone()),
|
|
1682
1717
|
FileDescriptorRecord {
|
|
1683
1718
|
id: snapshot.id,
|
|
1684
1719
|
directory_id: snapshot.directory_id,
|
|
@@ -1700,7 +1735,7 @@ async fn lix_file_record_batch(
|
|
|
1700
1735
|
)
|
|
1701
1736
|
})?;
|
|
1702
1737
|
blob_rows.insert(
|
|
1703
|
-
(row.
|
|
1738
|
+
(row.branch_id.clone(), snapshot.id.clone()),
|
|
1704
1739
|
BlobRefRecord {
|
|
1705
1740
|
blob_hash: snapshot.blob_hash,
|
|
1706
1741
|
},
|
|
@@ -1721,7 +1756,7 @@ async fn lix_file_record_batch(
|
|
|
1721
1756
|
id: snapshot.id,
|
|
1722
1757
|
parent_id: snapshot.parent_id,
|
|
1723
1758
|
name: snapshot.name,
|
|
1724
|
-
|
|
1759
|
+
branch_id: row.branch_id,
|
|
1725
1760
|
});
|
|
1726
1761
|
}
|
|
1727
1762
|
_ => {}
|
|
@@ -1735,7 +1770,7 @@ async fn lix_file_record_batch(
|
|
|
1735
1770
|
let mut names = Vec::new();
|
|
1736
1771
|
let mut hiddens = Vec::new();
|
|
1737
1772
|
let mut data_values = Vec::new();
|
|
1738
|
-
let mut
|
|
1773
|
+
let mut entity_pks = Vec::new();
|
|
1739
1774
|
let mut schema_keys = Vec::new();
|
|
1740
1775
|
let mut file_ids = Vec::new();
|
|
1741
1776
|
let mut globals = Vec::new();
|
|
@@ -1745,18 +1780,18 @@ async fn lix_file_record_batch(
|
|
|
1745
1780
|
let mut commit_ids = Vec::new();
|
|
1746
1781
|
let mut untracked_values = Vec::new();
|
|
1747
1782
|
let mut metadata_values = Vec::new();
|
|
1748
|
-
let mut
|
|
1783
|
+
let mut branch_ids = Vec::new();
|
|
1749
1784
|
|
|
1750
|
-
for ((
|
|
1785
|
+
for ((branch_id, _), file) in file_rows {
|
|
1751
1786
|
let directory_path = match file.directory_id.as_ref() {
|
|
1752
1787
|
Some(directory_id) => {
|
|
1753
|
-
let key = (
|
|
1788
|
+
let key = (branch_id.clone(), directory_id.clone());
|
|
1754
1789
|
let Some(path) = directory_paths.get(&key).cloned() else {
|
|
1755
1790
|
return Err(LixError::new(
|
|
1756
1791
|
LixError::CODE_FOREIGN_KEY,
|
|
1757
1792
|
format!(
|
|
1758
|
-
"lix_file_descriptor '{}' references missing directory_id '{}' in
|
|
1759
|
-
file.id, directory_id,
|
|
1793
|
+
"lix_file_descriptor '{}' references missing directory_id '{}' in branch '{}'",
|
|
1794
|
+
file.id, directory_id, branch_id
|
|
1760
1795
|
),
|
|
1761
1796
|
));
|
|
1762
1797
|
};
|
|
@@ -1769,7 +1804,7 @@ async fn lix_file_record_batch(
|
|
|
1769
1804
|
None => format!("/{}", file.name),
|
|
1770
1805
|
};
|
|
1771
1806
|
let data = if needs_data {
|
|
1772
|
-
match blob_rows.get(&(
|
|
1807
|
+
match blob_rows.get(&(branch_id.clone(), file.id.clone())) {
|
|
1773
1808
|
Some(blob_ref) => load_single_blob_bytes(blob_reader, &blob_ref.blob_hash).await?,
|
|
1774
1809
|
None => None,
|
|
1775
1810
|
}
|
|
@@ -1783,7 +1818,7 @@ async fn lix_file_record_batch(
|
|
|
1783
1818
|
names.push(Some(file.name));
|
|
1784
1819
|
hiddens.push(Some(file.hidden));
|
|
1785
1820
|
data_values.push(data);
|
|
1786
|
-
|
|
1821
|
+
entity_pks.push(Some(file.live.entity_pk.as_json_array_text()?));
|
|
1787
1822
|
schema_keys.push(Some(file.live.schema_key));
|
|
1788
1823
|
file_ids.push(file.live.file_id);
|
|
1789
1824
|
globals.push(Some(file.live.global));
|
|
@@ -1793,7 +1828,7 @@ async fn lix_file_record_batch(
|
|
|
1793
1828
|
commit_ids.push(file.live.commit_id);
|
|
1794
1829
|
untracked_values.push(Some(file.live.untracked));
|
|
1795
1830
|
metadata_values.push(file.live.metadata.as_ref().map(serialize_row_metadata));
|
|
1796
|
-
|
|
1831
|
+
branch_ids.push(Some(branch_id));
|
|
1797
1832
|
}
|
|
1798
1833
|
|
|
1799
1834
|
let mut columns = Vec::<ArrayRef>::with_capacity(schema.fields().len());
|
|
@@ -1810,7 +1845,7 @@ async fn lix_file_record_batch(
|
|
|
1810
1845
|
.map(|value| value.as_deref())
|
|
1811
1846
|
.collect::<Vec<_>>(),
|
|
1812
1847
|
)),
|
|
1813
|
-
"
|
|
1848
|
+
"lixcol_entity_pk" => Arc::new(StringArray::from(entity_pks.clone())),
|
|
1814
1849
|
"lixcol_schema_key" => Arc::new(StringArray::from(schema_keys.clone())),
|
|
1815
1850
|
"lixcol_file_id" => Arc::new(StringArray::from(file_ids.clone())),
|
|
1816
1851
|
"lixcol_global" => Arc::new(BooleanArray::from(globals.clone())),
|
|
@@ -1820,7 +1855,7 @@ async fn lix_file_record_batch(
|
|
|
1820
1855
|
"lixcol_commit_id" => Arc::new(StringArray::from(commit_ids.clone())),
|
|
1821
1856
|
"lixcol_untracked" => Arc::new(BooleanArray::from(untracked_values.clone())),
|
|
1822
1857
|
"lixcol_metadata" => Arc::new(StringArray::from(metadata_values.clone())),
|
|
1823
|
-
"
|
|
1858
|
+
"lixcol_branch_id" => Arc::new(StringArray::from(branch_ids.clone())),
|
|
1824
1859
|
other => {
|
|
1825
1860
|
return Err(LixError::new(
|
|
1826
1861
|
"LIX_ERROR_UNKNOWN",
|
|
@@ -1857,19 +1892,19 @@ async fn load_single_blob_bytes(
|
|
|
1857
1892
|
fn derive_directory_paths(
|
|
1858
1893
|
rows: &[DirectoryDescriptorRecord],
|
|
1859
1894
|
) -> Result<BTreeMap<(String, String), String>, LixError> {
|
|
1860
|
-
let mut
|
|
1895
|
+
let mut by_branch = BTreeMap::<String, BTreeMap<String, &DirectoryDescriptorRecord>>::new();
|
|
1861
1896
|
for row in rows {
|
|
1862
|
-
|
|
1863
|
-
.entry(row.
|
|
1897
|
+
by_branch
|
|
1898
|
+
.entry(row.branch_id.clone())
|
|
1864
1899
|
.or_default()
|
|
1865
1900
|
.insert(row.id.clone(), row);
|
|
1866
1901
|
}
|
|
1867
1902
|
|
|
1868
1903
|
let mut paths = BTreeMap::<(String, String), String>::new();
|
|
1869
|
-
for (
|
|
1904
|
+
for (branch_id, records) in by_branch {
|
|
1870
1905
|
for directory_id in records.keys() {
|
|
1871
1906
|
derive_directory_path_for(
|
|
1872
|
-
&
|
|
1907
|
+
&branch_id,
|
|
1873
1908
|
directory_id,
|
|
1874
1909
|
&records,
|
|
1875
1910
|
&mut paths,
|
|
@@ -1881,17 +1916,17 @@ fn derive_directory_paths(
|
|
|
1881
1916
|
}
|
|
1882
1917
|
|
|
1883
1918
|
fn derive_directory_path_for(
|
|
1884
|
-
|
|
1919
|
+
branch_id: &str,
|
|
1885
1920
|
directory_id: &str,
|
|
1886
1921
|
records: &BTreeMap<String, &DirectoryDescriptorRecord>,
|
|
1887
1922
|
paths: &mut BTreeMap<(String, String), String>,
|
|
1888
1923
|
visiting: &mut BTreeSet<String>,
|
|
1889
1924
|
) -> Result<Option<String>, LixError> {
|
|
1890
|
-
if let Some(path) = paths.get(&(
|
|
1925
|
+
if let Some(path) = paths.get(&(branch_id.to_string(), directory_id.to_string())) {
|
|
1891
1926
|
return Ok(Some(path.clone()));
|
|
1892
1927
|
}
|
|
1893
1928
|
if !visiting.insert(directory_id.to_string()) {
|
|
1894
|
-
return Err(directory_parent_cycle_error(
|
|
1929
|
+
return Err(directory_parent_cycle_error(branch_id, directory_id));
|
|
1895
1930
|
}
|
|
1896
1931
|
let Some(row) = records.get(directory_id) else {
|
|
1897
1932
|
visiting.remove(directory_id);
|
|
@@ -1900,7 +1935,7 @@ fn derive_directory_path_for(
|
|
|
1900
1935
|
let path = match row.parent_id.as_deref() {
|
|
1901
1936
|
Some(parent_id) => {
|
|
1902
1937
|
let Some(parent_path) =
|
|
1903
|
-
derive_directory_path_for(
|
|
1938
|
+
derive_directory_path_for(branch_id, parent_id, records, paths, visiting)?
|
|
1904
1939
|
else {
|
|
1905
1940
|
visiting.remove(directory_id);
|
|
1906
1941
|
return Ok(None);
|
|
@@ -1911,17 +1946,17 @@ fn derive_directory_path_for(
|
|
|
1911
1946
|
};
|
|
1912
1947
|
visiting.remove(directory_id);
|
|
1913
1948
|
paths.insert(
|
|
1914
|
-
(
|
|
1949
|
+
(branch_id.to_string(), directory_id.to_string()),
|
|
1915
1950
|
path.clone(),
|
|
1916
1951
|
);
|
|
1917
1952
|
Ok(Some(path))
|
|
1918
1953
|
}
|
|
1919
1954
|
|
|
1920
|
-
fn directory_parent_cycle_error(
|
|
1955
|
+
fn directory_parent_cycle_error(branch_id: &str, directory_id: &str) -> LixError {
|
|
1921
1956
|
LixError::new(
|
|
1922
1957
|
LixError::CODE_CONSTRAINT_VIOLATION,
|
|
1923
1958
|
format!(
|
|
1924
|
-
"lix_directory_descriptor parent_id cycle in
|
|
1959
|
+
"lix_directory_descriptor parent_id cycle in branch '{branch_id}' while resolving directory '{directory_id}'"
|
|
1925
1960
|
),
|
|
1926
1961
|
)
|
|
1927
1962
|
}
|
|
@@ -1942,7 +1977,7 @@ fn projected_schema(base_schema: &SchemaRef, projection: Option<&Vec<usize>>) ->
|
|
|
1942
1977
|
}
|
|
1943
1978
|
|
|
1944
1979
|
fn lix_file_scan_request(
|
|
1945
|
-
|
|
1980
|
+
branch_binding: Option<&str>,
|
|
1946
1981
|
projected_schema: Option<&Schema>,
|
|
1947
1982
|
limit: Option<usize>,
|
|
1948
1983
|
) -> LiveStateScanRequest {
|
|
@@ -1953,8 +1988,8 @@ fn lix_file_scan_request(
|
|
|
1953
1988
|
BLOB_REF_SCHEMA_KEY.to_string(),
|
|
1954
1989
|
DIRECTORY_DESCRIPTOR_SCHEMA_KEY.to_string(),
|
|
1955
1990
|
],
|
|
1956
|
-
|
|
1957
|
-
.map(|
|
|
1991
|
+
branch_ids: branch_binding
|
|
1992
|
+
.map(|branch_id| vec![branch_id.to_string()])
|
|
1958
1993
|
.unwrap_or_default(),
|
|
1959
1994
|
..LiveStateFilter::default()
|
|
1960
1995
|
},
|
|
@@ -2003,16 +2038,16 @@ async fn scan_lix_file_live_rows(
|
|
|
2003
2038
|
FILE_DESCRIPTOR_SCHEMA_KEY.to_string(),
|
|
2004
2039
|
BLOB_REF_SCHEMA_KEY.to_string(),
|
|
2005
2040
|
];
|
|
2006
|
-
file_request.filter.
|
|
2041
|
+
file_request.filter.entity_pks = target_file_ids
|
|
2007
2042
|
.iter()
|
|
2008
|
-
.map(|file_id|
|
|
2043
|
+
.map(|file_id| EntityPk::single(file_id.clone()))
|
|
2009
2044
|
.collect();
|
|
2010
2045
|
|
|
2011
2046
|
let mut rows = live_state.scan_rows(&file_request).await?;
|
|
2012
2047
|
|
|
2013
2048
|
let mut directory_request = request.clone();
|
|
2014
2049
|
directory_request.filter.schema_keys = vec![DIRECTORY_DESCRIPTOR_SCHEMA_KEY.to_string()];
|
|
2015
|
-
directory_request.filter.
|
|
2050
|
+
directory_request.filter.entity_pks.clear();
|
|
2016
2051
|
directory_request.limit = None;
|
|
2017
2052
|
rows.extend(live_state.scan_rows(&directory_request).await?);
|
|
2018
2053
|
|
|
@@ -2400,9 +2435,9 @@ fn update_optional_metadata_value(
|
|
|
2400
2435
|
update_optional_string_value(batch, assignment_values, row_index, column_name)?
|
|
2401
2436
|
.map(|value| {
|
|
2402
2437
|
let metadata = parse_row_metadata_value(&value, context)
|
|
2403
|
-
.map_err(
|
|
2438
|
+
.map_err(crate::sql2::error::lix_error_to_datafusion_error)?;
|
|
2404
2439
|
TransactionJson::from_value(metadata, &format!("{context} metadata"))
|
|
2405
|
-
.map_err(
|
|
2440
|
+
.map_err(crate::sql2::error::lix_error_to_datafusion_error)
|
|
2406
2441
|
})
|
|
2407
2442
|
.transpose()
|
|
2408
2443
|
}
|
|
@@ -2479,9 +2514,9 @@ fn optional_metadata_value(
|
|
|
2479
2514
|
optional_string_value(batch, row_index, column_name)?
|
|
2480
2515
|
.map(|value| {
|
|
2481
2516
|
let metadata = parse_row_metadata_value(&value, context)
|
|
2482
|
-
.map_err(
|
|
2517
|
+
.map_err(crate::sql2::error::lix_error_to_datafusion_error)?;
|
|
2483
2518
|
TransactionJson::from_value(metadata, &format!("{context} metadata"))
|
|
2484
|
-
.map_err(
|
|
2519
|
+
.map_err(crate::sql2::error::lix_error_to_datafusion_error)
|
|
2485
2520
|
})
|
|
2486
2521
|
.transpose()
|
|
2487
2522
|
}
|
|
@@ -2553,7 +2588,7 @@ fn optional_scalar_value(
|
|
|
2553
2588
|
})
|
|
2554
2589
|
}
|
|
2555
2590
|
|
|
2556
|
-
fn lix_file_schema() -> SchemaRef {
|
|
2591
|
+
pub(super) fn lix_file_schema() -> SchemaRef {
|
|
2557
2592
|
Arc::new(Schema::new(vec![
|
|
2558
2593
|
Field::new("id", DataType::Utf8, true),
|
|
2559
2594
|
Field::new("path", DataType::Utf8, false),
|
|
@@ -2561,7 +2596,7 @@ fn lix_file_schema() -> SchemaRef {
|
|
|
2561
2596
|
Field::new("name", DataType::Utf8, false),
|
|
2562
2597
|
Field::new("hidden", DataType::Boolean, true),
|
|
2563
2598
|
Field::new("data", DataType::Binary, true),
|
|
2564
|
-
json_field("
|
|
2599
|
+
json_field("lixcol_entity_pk", false),
|
|
2565
2600
|
Field::new("lixcol_schema_key", DataType::Utf8, false),
|
|
2566
2601
|
Field::new("lixcol_file_id", DataType::Utf8, true),
|
|
2567
2602
|
Field::new("lixcol_global", DataType::Boolean, true),
|
|
@@ -2574,22 +2609,22 @@ fn lix_file_schema() -> SchemaRef {
|
|
|
2574
2609
|
]))
|
|
2575
2610
|
}
|
|
2576
2611
|
|
|
2577
|
-
fn
|
|
2612
|
+
pub(super) fn lix_file_by_branch_schema() -> SchemaRef {
|
|
2578
2613
|
let mut fields = lix_file_schema()
|
|
2579
2614
|
.fields()
|
|
2580
2615
|
.iter()
|
|
2581
2616
|
.map(|field| field.as_ref().clone())
|
|
2582
2617
|
.collect::<Vec<_>>();
|
|
2583
|
-
fields.push(Field::new("
|
|
2618
|
+
fields.push(Field::new("lixcol_branch_id", DataType::Utf8, false));
|
|
2584
2619
|
Arc::new(Schema::new(fields))
|
|
2585
2620
|
}
|
|
2586
2621
|
|
|
2587
2622
|
fn datafusion_error_to_lix_error(error: DataFusionError) -> LixError {
|
|
2588
|
-
|
|
2623
|
+
crate::sql2::error::datafusion_error_to_lix_error(error)
|
|
2589
2624
|
}
|
|
2590
2625
|
|
|
2591
2626
|
fn lix_error_to_datafusion_error(error: LixError) -> DataFusionError {
|
|
2592
|
-
|
|
2627
|
+
crate::sql2::error::lix_error_to_datafusion_error(error)
|
|
2593
2628
|
}
|
|
2594
2629
|
|
|
2595
2630
|
#[cfg(test)]
|
|
@@ -2624,8 +2659,8 @@ mod tests {
|
|
|
2624
2659
|
use super::{
|
|
2625
2660
|
derive_directory_path_for, lix_file_delete_stage_from_batch,
|
|
2626
2661
|
lix_file_insert_stage_from_batch, lix_file_insert_stage_from_batch_with_path_resolvers,
|
|
2627
|
-
lix_file_write_rows_from_batch,
|
|
2628
|
-
|
|
2662
|
+
lix_file_write_rows_from_batch, BranchBinding, DirectoryDescriptorRecord,
|
|
2663
|
+
LixFileInsertSink,
|
|
2629
2664
|
};
|
|
2630
2665
|
|
|
2631
2666
|
fn test_id_generator(ids: &'static [&'static str]) -> impl FnMut() -> String {
|
|
@@ -2755,7 +2790,7 @@ mod tests {
|
|
|
2755
2790
|
|
|
2756
2791
|
fn lix_file_update_stage_from_batch_for_test(
|
|
2757
2792
|
batch: &RecordBatch,
|
|
2758
|
-
|
|
2793
|
+
branch_binding: Option<&str>,
|
|
2759
2794
|
update_columns: super::LixFileUpdateColumns,
|
|
2760
2795
|
path_resolvers: Option<&mut BTreeMap<String, super::DirectoryPathResolver>>,
|
|
2761
2796
|
generate_directory_id: &mut dyn FnMut() -> String,
|
|
@@ -2774,7 +2809,7 @@ mod tests {
|
|
|
2774
2809
|
super::lix_file_update_stage_from_batch(
|
|
2775
2810
|
batch,
|
|
2776
2811
|
&assignment_values,
|
|
2777
|
-
|
|
2812
|
+
branch_binding,
|
|
2778
2813
|
update_columns,
|
|
2779
2814
|
path_resolvers,
|
|
2780
2815
|
generate_directory_id,
|
|
@@ -2802,8 +2837,8 @@ mod tests {
|
|
|
2802
2837
|
|
|
2803
2838
|
#[async_trait]
|
|
2804
2839
|
impl SqlWriteExecutionContext for CapturingWriteContext {
|
|
2805
|
-
fn
|
|
2806
|
-
"
|
|
2840
|
+
fn active_branch_id(&self) -> &str {
|
|
2841
|
+
"branch-b"
|
|
2807
2842
|
}
|
|
2808
2843
|
|
|
2809
2844
|
fn functions(&self) -> FunctionProviderHandle {
|
|
@@ -2828,14 +2863,11 @@ mod tests {
|
|
|
2828
2863
|
Ok(self.rows.clone())
|
|
2829
2864
|
}
|
|
2830
2865
|
|
|
2831
|
-
async fn
|
|
2832
|
-
|
|
2833
|
-
version_id: &str,
|
|
2834
|
-
) -> Result<Option<String>, LixError> {
|
|
2835
|
-
if version_id == "ghost-version" {
|
|
2866
|
+
async fn load_branch_head(&mut self, branch_id: &str) -> Result<Option<String>, LixError> {
|
|
2867
|
+
if branch_id == "ghost-branch" {
|
|
2836
2868
|
return Ok(None);
|
|
2837
2869
|
}
|
|
2838
|
-
Ok(Some(format!("commit-{
|
|
2870
|
+
Ok(Some(format!("commit-{branch_id}")))
|
|
2839
2871
|
}
|
|
2840
2872
|
|
|
2841
2873
|
async fn stage_write(
|
|
@@ -2870,20 +2902,20 @@ mod tests {
|
|
|
2870
2902
|
}
|
|
2871
2903
|
|
|
2872
2904
|
fn live_directory_row(
|
|
2873
|
-
|
|
2874
|
-
|
|
2905
|
+
entity_pk: &str,
|
|
2906
|
+
branch_id: &str,
|
|
2875
2907
|
snapshot_content: &str,
|
|
2876
2908
|
) -> MaterializedLiveStateRow {
|
|
2877
2909
|
MaterializedLiveStateRow {
|
|
2878
|
-
|
|
2910
|
+
entity_pk: crate::entity_pk::EntityPk::single(entity_pk),
|
|
2879
2911
|
schema_key: super::DIRECTORY_DESCRIPTOR_SCHEMA_KEY.to_string(),
|
|
2880
2912
|
file_id: None,
|
|
2881
2913
|
snapshot_content: Some(snapshot_content.to_string()),
|
|
2882
2914
|
metadata: None,
|
|
2883
2915
|
deleted: false,
|
|
2884
|
-
|
|
2885
|
-
change_id: Some(format!("change-{
|
|
2886
|
-
commit_id: Some(format!("commit-{
|
|
2916
|
+
branch_id: branch_id.to_string(),
|
|
2917
|
+
change_id: Some(format!("change-{entity_pk}")),
|
|
2918
|
+
commit_id: Some(format!("commit-{entity_pk}")),
|
|
2887
2919
|
global: false,
|
|
2888
2920
|
untracked: false,
|
|
2889
2921
|
created_at: "2026-04-23T00:00:00Z".to_string(),
|
|
@@ -2892,20 +2924,20 @@ mod tests {
|
|
|
2892
2924
|
}
|
|
2893
2925
|
|
|
2894
2926
|
fn live_file_row(
|
|
2895
|
-
|
|
2896
|
-
|
|
2927
|
+
entity_pk: &str,
|
|
2928
|
+
branch_id: &str,
|
|
2897
2929
|
snapshot_content: &str,
|
|
2898
2930
|
) -> MaterializedLiveStateRow {
|
|
2899
2931
|
MaterializedLiveStateRow {
|
|
2900
|
-
|
|
2932
|
+
entity_pk: crate::entity_pk::EntityPk::single(entity_pk),
|
|
2901
2933
|
schema_key: super::FILE_DESCRIPTOR_SCHEMA_KEY.to_string(),
|
|
2902
2934
|
file_id: None,
|
|
2903
2935
|
snapshot_content: Some(snapshot_content.to_string()),
|
|
2904
2936
|
metadata: None,
|
|
2905
2937
|
deleted: false,
|
|
2906
|
-
|
|
2907
|
-
change_id: Some(format!("change-{
|
|
2908
|
-
commit_id: Some(format!("commit-{
|
|
2938
|
+
branch_id: branch_id.to_string(),
|
|
2939
|
+
change_id: Some(format!("change-{entity_pk}")),
|
|
2940
|
+
commit_id: Some(format!("commit-{entity_pk}")),
|
|
2909
2941
|
global: false,
|
|
2910
2942
|
untracked: false,
|
|
2911
2943
|
created_at: "2026-04-23T00:00:00Z".to_string(),
|
|
@@ -2917,7 +2949,7 @@ mod tests {
|
|
|
2917
2949
|
Arc::new(StringArray::from(values)) as ArrayRef
|
|
2918
2950
|
}
|
|
2919
2951
|
|
|
2920
|
-
fn file_insert_batch(
|
|
2952
|
+
fn file_insert_batch(include_branch: bool, global: bool) -> RecordBatch {
|
|
2921
2953
|
let mut fields = vec![
|
|
2922
2954
|
Field::new("id", DataType::Utf8, false),
|
|
2923
2955
|
Field::new("directory_id", DataType::Utf8, true),
|
|
@@ -2934,9 +2966,9 @@ mod tests {
|
|
|
2934
2966
|
Arc::new(BooleanArray::from(vec![global])) as ArrayRef,
|
|
2935
2967
|
string_column(vec![Some("{\"source\":\"file\"}")]),
|
|
2936
2968
|
];
|
|
2937
|
-
if
|
|
2938
|
-
fields.push(Field::new("
|
|
2939
|
-
columns.push(string_column(vec![Some("
|
|
2969
|
+
if include_branch {
|
|
2970
|
+
fields.push(Field::new("lixcol_branch_id", DataType::Utf8, false));
|
|
2971
|
+
columns.push(string_column(vec![Some("branch-b")]));
|
|
2940
2972
|
}
|
|
2941
2973
|
RecordBatch::try_new(Arc::new(Schema::new(fields)), columns).expect("file insert batch")
|
|
2942
2974
|
}
|
|
@@ -2949,7 +2981,7 @@ mod tests {
|
|
|
2949
2981
|
Field::new("name", DataType::Utf8, false),
|
|
2950
2982
|
Field::new("hidden", DataType::Boolean, false),
|
|
2951
2983
|
Field::new("data", DataType::Binary, true),
|
|
2952
|
-
Field::new("
|
|
2984
|
+
Field::new("lixcol_branch_id", DataType::Utf8, false),
|
|
2953
2985
|
])),
|
|
2954
2986
|
vec![
|
|
2955
2987
|
string_column(vec![Some("file-readme")]),
|
|
@@ -2957,7 +2989,7 @@ mod tests {
|
|
|
2957
2989
|
string_column(vec![Some("readme.md")]),
|
|
2958
2990
|
Arc::new(BooleanArray::from(vec![false])) as ArrayRef,
|
|
2959
2991
|
Arc::new(BinaryArray::from_vec(vec![b"hello"])) as ArrayRef,
|
|
2960
|
-
string_column(vec![Some("
|
|
2992
|
+
string_column(vec![Some("branch-b")]),
|
|
2961
2993
|
],
|
|
2962
2994
|
)
|
|
2963
2995
|
.expect("file data batch")
|
|
@@ -2970,14 +3002,14 @@ mod tests {
|
|
|
2970
3002
|
Field::new("path", DataType::Utf8, false),
|
|
2971
3003
|
Field::new("hidden", DataType::Boolean, false),
|
|
2972
3004
|
Field::new("data", DataType::Binary, true),
|
|
2973
|
-
Field::new("
|
|
3005
|
+
Field::new("lixcol_branch_id", DataType::Utf8, false),
|
|
2974
3006
|
])),
|
|
2975
3007
|
vec![
|
|
2976
3008
|
string_column(vec![Some("file-readme")]),
|
|
2977
3009
|
string_column(vec![Some("/docs/guides/readme.md")]),
|
|
2978
3010
|
Arc::new(BooleanArray::from(vec![false])) as ArrayRef,
|
|
2979
3011
|
Arc::new(BinaryArray::from_vec(vec![b"hello"])) as ArrayRef,
|
|
2980
|
-
string_column(vec![Some("
|
|
3012
|
+
string_column(vec![Some("branch-b")]),
|
|
2981
3013
|
],
|
|
2982
3014
|
)
|
|
2983
3015
|
.expect("file path data batch")
|
|
@@ -2990,14 +3022,14 @@ mod tests {
|
|
|
2990
3022
|
Field::new("path", DataType::Utf8, false),
|
|
2991
3023
|
Field::new("hidden", DataType::Boolean, false),
|
|
2992
3024
|
Field::new("data", DataType::Binary, true),
|
|
2993
|
-
Field::new("
|
|
3025
|
+
Field::new("lixcol_branch_id", DataType::Utf8, false),
|
|
2994
3026
|
])),
|
|
2995
3027
|
vec![
|
|
2996
3028
|
string_column(vec![Some("file-readme")]),
|
|
2997
3029
|
string_column(vec![Some("/docs/renamed.md")]),
|
|
2998
3030
|
Arc::new(BooleanArray::from(vec![false])) as ArrayRef,
|
|
2999
3031
|
Arc::new(BinaryArray::from_vec(vec![b"hello"])) as ArrayRef,
|
|
3000
|
-
string_column(vec![Some("
|
|
3032
|
+
string_column(vec![Some("branch-b")]),
|
|
3001
3033
|
],
|
|
3002
3034
|
)
|
|
3003
3035
|
.expect("file path update batch")
|
|
@@ -3007,11 +3039,11 @@ mod tests {
|
|
|
3007
3039
|
RecordBatch::try_new(
|
|
3008
3040
|
Arc::new(Schema::new(vec![
|
|
3009
3041
|
Field::new("id", DataType::Utf8, false),
|
|
3010
|
-
Field::new("
|
|
3042
|
+
Field::new("lixcol_branch_id", DataType::Utf8, false),
|
|
3011
3043
|
])),
|
|
3012
3044
|
vec![
|
|
3013
3045
|
string_column(vec![Some("file-readme")]),
|
|
3014
|
-
string_column(vec![Some("
|
|
3046
|
+
string_column(vec![Some("branch-b")]),
|
|
3015
3047
|
],
|
|
3016
3048
|
)
|
|
3017
3049
|
.expect("file delete batch")
|
|
@@ -3023,13 +3055,13 @@ mod tests {
|
|
|
3023
3055
|
id: "dir-docs".to_string(),
|
|
3024
3056
|
parent_id: None,
|
|
3025
3057
|
name: "docs".to_string(),
|
|
3026
|
-
|
|
3058
|
+
branch_id: "branch-a".to_string(),
|
|
3027
3059
|
};
|
|
3028
3060
|
let child = DirectoryDescriptorRecord {
|
|
3029
3061
|
id: "dir-guides".to_string(),
|
|
3030
3062
|
parent_id: Some("dir-docs".to_string()),
|
|
3031
3063
|
name: "guides".to_string(),
|
|
3032
|
-
|
|
3064
|
+
branch_id: "branch-a".to_string(),
|
|
3033
3065
|
};
|
|
3034
3066
|
let mut records = BTreeMap::new();
|
|
3035
3067
|
records.insert(root.id.clone(), &root);
|
|
@@ -3038,7 +3070,7 @@ mod tests {
|
|
|
3038
3070
|
|
|
3039
3071
|
assert_eq!(
|
|
3040
3072
|
derive_directory_path_for(
|
|
3041
|
-
"
|
|
3073
|
+
"branch-a",
|
|
3042
3074
|
"dir-guides",
|
|
3043
3075
|
&records,
|
|
3044
3076
|
&mut paths,
|
|
@@ -3057,7 +3089,7 @@ mod tests {
|
|
|
3057
3089
|
&blob_reader,
|
|
3058
3090
|
vec![live_file_row(
|
|
3059
3091
|
"file-readme",
|
|
3060
|
-
"
|
|
3092
|
+
"branch-b",
|
|
3061
3093
|
"{\"id\":\"file-readme\",\"directory_id\":\"missing-dir\",\"name\":\"readme.md\",\"hidden\":false}",
|
|
3062
3094
|
)],
|
|
3063
3095
|
)
|
|
@@ -3076,13 +3108,11 @@ mod tests {
|
|
|
3076
3108
|
|
|
3077
3109
|
assert_eq!(rows.len(), 1);
|
|
3078
3110
|
assert_eq!(
|
|
3079
|
-
rows[0].
|
|
3080
|
-
Some(&crate::
|
|
3081
|
-
"file-readme"
|
|
3082
|
-
))
|
|
3111
|
+
rows[0].entity_pk.as_ref(),
|
|
3112
|
+
Some(&crate::entity_pk::EntityPk::single("file-readme"))
|
|
3083
3113
|
);
|
|
3084
3114
|
assert_eq!(rows[0].schema_key, "lix_file_descriptor");
|
|
3085
|
-
assert_eq!(rows[0].
|
|
3115
|
+
assert_eq!(rows[0].branch_id, "branch-b");
|
|
3086
3116
|
assert_eq!(
|
|
3087
3117
|
rows[0].metadata.as_ref(),
|
|
3088
3118
|
Some(&TransactionJson::from_value_for_test(
|
|
@@ -3097,38 +3127,38 @@ mod tests {
|
|
|
3097
3127
|
}
|
|
3098
3128
|
|
|
3099
3129
|
#[test]
|
|
3100
|
-
fn
|
|
3130
|
+
fn active_file_insert_defaults_branch_id() {
|
|
3101
3131
|
let batch = file_insert_batch(false, false);
|
|
3102
3132
|
|
|
3103
3133
|
let rows =
|
|
3104
|
-
lix_file_write_rows_from_batch(&batch, Some("
|
|
3134
|
+
lix_file_write_rows_from_batch(&batch, Some("branch-a")).expect("decode file insert");
|
|
3105
3135
|
|
|
3106
3136
|
assert_eq!(rows.len(), 1);
|
|
3107
|
-
assert_eq!(rows[0].
|
|
3137
|
+
assert_eq!(rows[0].branch_id, "branch-a");
|
|
3108
3138
|
}
|
|
3109
3139
|
|
|
3110
3140
|
#[test]
|
|
3111
|
-
fn
|
|
3141
|
+
fn by_branch_file_insert_requires_branch_id_for_non_global_rows() {
|
|
3112
3142
|
let batch = file_insert_batch(false, false);
|
|
3113
3143
|
|
|
3114
3144
|
let error =
|
|
3115
|
-
lix_file_write_rows_from_batch(&batch, None).expect_err("
|
|
3145
|
+
lix_file_write_rows_from_batch(&batch, None).expect_err("branch id is required");
|
|
3116
3146
|
|
|
3117
3147
|
assert!(
|
|
3118
|
-
error.to_string().contains("requires
|
|
3148
|
+
error.to_string().contains("requires lixcol_branch_id"),
|
|
3119
3149
|
"unexpected error: {error}"
|
|
3120
3150
|
);
|
|
3121
3151
|
}
|
|
3122
3152
|
|
|
3123
3153
|
#[test]
|
|
3124
|
-
fn
|
|
3154
|
+
fn file_insert_rejects_global_with_non_global_branch_id() {
|
|
3125
3155
|
let error = lix_file_write_rows_from_batch(&file_insert_batch(true, true), None)
|
|
3126
|
-
.expect_err("global file write should reject conflicting
|
|
3156
|
+
.expect_err("global file write should reject conflicting branch id");
|
|
3127
3157
|
|
|
3128
3158
|
assert!(
|
|
3129
3159
|
error
|
|
3130
3160
|
.to_string()
|
|
3131
|
-
.contains("cannot set lixcol_global=true with non-global
|
|
3161
|
+
.contains("cannot set lixcol_global=true with non-global lixcol_branch_id"),
|
|
3132
3162
|
"unexpected error: {error}"
|
|
3133
3163
|
);
|
|
3134
3164
|
}
|
|
@@ -3146,7 +3176,7 @@ mod tests {
|
|
|
3146
3176
|
fn file_path_update_stages_descriptor_from_new_path() {
|
|
3147
3177
|
let mut resolvers = BTreeMap::new();
|
|
3148
3178
|
resolvers.insert(
|
|
3149
|
-
super::filesystem_storage_scope_key("
|
|
3179
|
+
super::filesystem_storage_scope_key("branch-b", false, false, None),
|
|
3150
3180
|
super::DirectoryPathResolver::from_existing([(
|
|
3151
3181
|
"/docs/".to_string(),
|
|
3152
3182
|
"dir-docs".to_string(),
|
|
@@ -3186,7 +3216,7 @@ mod tests {
|
|
|
3186
3216
|
fn file_path_update_preserves_existing_data_unless_data_is_assigned() {
|
|
3187
3217
|
let mut resolvers = BTreeMap::new();
|
|
3188
3218
|
resolvers.insert(
|
|
3189
|
-
super::filesystem_storage_scope_key("
|
|
3219
|
+
super::filesystem_storage_scope_key("branch-b", false, false, None),
|
|
3190
3220
|
super::DirectoryPathResolver::from_existing([(
|
|
3191
3221
|
"/docs/".to_string(),
|
|
3192
3222
|
"dir-docs".to_string(),
|
|
@@ -3226,11 +3256,11 @@ mod tests {
|
|
|
3226
3256
|
Arc::new(RowsLiveStateReader {
|
|
3227
3257
|
rows: vec![live_directory_row(
|
|
3228
3258
|
"dir-docs",
|
|
3229
|
-
"
|
|
3259
|
+
"branch-b",
|
|
3230
3260
|
"{\"id\":\"dir-docs\",\"parent_id\":null,\"name\":\"docs\"}",
|
|
3231
3261
|
)],
|
|
3232
3262
|
}) as Arc<dyn LiveStateReader>,
|
|
3233
|
-
Some("
|
|
3263
|
+
Some("branch-b"),
|
|
3234
3264
|
)
|
|
3235
3265
|
.await
|
|
3236
3266
|
.expect("directory state should seed path resolver");
|
|
@@ -3269,7 +3299,7 @@ mod tests {
|
|
|
3269
3299
|
async fn file_path_update_stages_only_missing_parent_directories() {
|
|
3270
3300
|
let mut resolvers = super::file_path_resolvers_from_live_state(
|
|
3271
3301
|
Arc::new(RowsLiveStateReader::default()) as Arc<dyn LiveStateReader>,
|
|
3272
|
-
Some("
|
|
3302
|
+
Some("branch-b"),
|
|
3273
3303
|
)
|
|
3274
3304
|
.await
|
|
3275
3305
|
.expect("empty directory state should seed path resolver");
|
|
@@ -3304,10 +3334,8 @@ mod tests {
|
|
|
3304
3334
|
.find(|row| row.schema_key == "lix_directory_descriptor")
|
|
3305
3335
|
.expect("missing /docs/ directory should be staged");
|
|
3306
3336
|
assert_eq!(
|
|
3307
|
-
directory.
|
|
3308
|
-
Some(&crate::
|
|
3309
|
-
"dir-generated-docs"
|
|
3310
|
-
))
|
|
3337
|
+
directory.entity_pk.as_ref(),
|
|
3338
|
+
Some(&crate::entity_pk::EntityPk::single("dir-generated-docs"))
|
|
3311
3339
|
);
|
|
3312
3340
|
|
|
3313
3341
|
let descriptor = staged
|
|
@@ -3323,7 +3351,7 @@ mod tests {
|
|
|
3323
3351
|
fn file_path_update_with_data_assignment_stages_blob_ref_and_payload() {
|
|
3324
3352
|
let mut resolvers = BTreeMap::new();
|
|
3325
3353
|
resolvers.insert(
|
|
3326
|
-
super::filesystem_storage_scope_key("
|
|
3354
|
+
super::filesystem_storage_scope_key("branch-b", false, false, None),
|
|
3327
3355
|
super::DirectoryPathResolver::from_existing([(
|
|
3328
3356
|
"/docs/".to_string(),
|
|
3329
3357
|
"dir-docs".to_string(),
|
|
@@ -3398,15 +3426,13 @@ mod tests {
|
|
|
3398
3426
|
.find(|row| row.schema_key == "lix_binary_blob_ref")
|
|
3399
3427
|
.expect("data insert should stage blob ref row");
|
|
3400
3428
|
assert_eq!(
|
|
3401
|
-
blob_ref_row.
|
|
3402
|
-
Some(&crate::
|
|
3403
|
-
"file-readme"
|
|
3404
|
-
))
|
|
3429
|
+
blob_ref_row.entity_pk.as_ref(),
|
|
3430
|
+
Some(&crate::entity_pk::EntityPk::single("file-readme"))
|
|
3405
3431
|
);
|
|
3406
3432
|
assert_eq!(blob_ref_row.file_id.as_deref(), Some("file-readme"));
|
|
3407
3433
|
assert_eq!(staged.file_data_writes.len(), 1);
|
|
3408
3434
|
assert_eq!(staged.file_data_writes[0].file_id, "file-readme");
|
|
3409
|
-
assert_eq!(staged.file_data_writes[0].
|
|
3435
|
+
assert_eq!(staged.file_data_writes[0].branch_id, "branch-b");
|
|
3410
3436
|
assert_eq!(staged.file_data_writes[0].data, b"hello");
|
|
3411
3437
|
}
|
|
3412
3438
|
|
|
@@ -3428,10 +3454,8 @@ mod tests {
|
|
|
3428
3454
|
.find(|row| row.schema_key == "lix_file_descriptor")
|
|
3429
3455
|
.expect("file descriptor tombstone should be staged");
|
|
3430
3456
|
assert_eq!(
|
|
3431
|
-
descriptor.
|
|
3432
|
-
Some(&crate::
|
|
3433
|
-
"file-readme"
|
|
3434
|
-
))
|
|
3457
|
+
descriptor.entity_pk.as_ref(),
|
|
3458
|
+
Some(&crate::entity_pk::EntityPk::single("file-readme"))
|
|
3435
3459
|
);
|
|
3436
3460
|
assert_eq!(descriptor.file_id, None);
|
|
3437
3461
|
assert_eq!(descriptor.snapshot, None);
|
|
@@ -3442,10 +3466,8 @@ mod tests {
|
|
|
3442
3466
|
.find(|row| row.schema_key == "lix_binary_blob_ref")
|
|
3443
3467
|
.expect("blob ref tombstone should be staged");
|
|
3444
3468
|
assert_eq!(
|
|
3445
|
-
blob_ref.
|
|
3446
|
-
Some(&crate::
|
|
3447
|
-
"file-readme"
|
|
3448
|
-
))
|
|
3469
|
+
blob_ref.entity_pk.as_ref(),
|
|
3470
|
+
Some(&crate::entity_pk::EntityPk::single("file-readme"))
|
|
3449
3471
|
);
|
|
3450
3472
|
assert_eq!(blob_ref.file_id.as_deref(), Some("file-readme"));
|
|
3451
3473
|
assert_eq!(blob_ref.snapshot, None);
|
|
@@ -3461,10 +3483,8 @@ mod tests {
|
|
|
3461
3483
|
assert_eq!(staged.state_rows.len(), 1);
|
|
3462
3484
|
assert_eq!(staged.state_rows[0].schema_key, "lix_file_descriptor");
|
|
3463
3485
|
assert_eq!(
|
|
3464
|
-
staged.state_rows[0].
|
|
3465
|
-
Some(&crate::
|
|
3466
|
-
"file-readme"
|
|
3467
|
-
))
|
|
3486
|
+
staged.state_rows[0].entity_pk.as_ref(),
|
|
3487
|
+
Some(&crate::entity_pk::EntityPk::single("file-readme"))
|
|
3468
3488
|
);
|
|
3469
3489
|
assert_eq!(staged.state_rows[0].snapshot, None);
|
|
3470
3490
|
}
|
|
@@ -3473,7 +3493,7 @@ mod tests {
|
|
|
3473
3493
|
fn file_path_insert_reuses_existing_parent_directory() {
|
|
3474
3494
|
let mut resolvers = BTreeMap::new();
|
|
3475
3495
|
resolvers.insert(
|
|
3476
|
-
super::filesystem_storage_scope_key("
|
|
3496
|
+
super::filesystem_storage_scope_key("branch-b", false, false, None),
|
|
3477
3497
|
super::DirectoryPathResolver::from_existing([
|
|
3478
3498
|
("/docs/".to_string(), "dir-docs".to_string()),
|
|
3479
3499
|
("/docs/guides/".to_string(), "dir-guides".to_string()),
|
|
@@ -3544,10 +3564,9 @@ mod tests {
|
|
|
3544
3564
|
let mut write_context = CapturingWriteContext::default();
|
|
3545
3565
|
let write_ctx = SqlWriteContext::new(&mut write_context);
|
|
3546
3566
|
let sink = LixFileInsertSink::new(
|
|
3547
|
-
batch.schema(),
|
|
3548
3567
|
write_ctx,
|
|
3549
3568
|
test_functions(),
|
|
3550
|
-
|
|
3569
|
+
BranchBinding::explicit(),
|
|
3551
3570
|
false,
|
|
3552
3571
|
);
|
|
3553
3572
|
|
|
@@ -3564,10 +3583,8 @@ mod tests {
|
|
|
3564
3583
|
assert_eq!(*mode, TransactionWriteMode::Insert);
|
|
3565
3584
|
assert_eq!(rows.len(), 1);
|
|
3566
3585
|
assert_eq!(
|
|
3567
|
-
rows[0].
|
|
3568
|
-
Some(&crate::
|
|
3569
|
-
"file-readme"
|
|
3570
|
-
))
|
|
3586
|
+
rows[0].entity_pk.as_ref(),
|
|
3587
|
+
Some(&crate::entity_pk::EntityPk::single("file-readme"))
|
|
3571
3588
|
);
|
|
3572
3589
|
assert_eq!(rows[0].schema_key, "lix_file_descriptor");
|
|
3573
3590
|
}
|
|
@@ -3580,13 +3597,8 @@ mod tests {
|
|
|
3580
3597
|
let batch = data_insert_batch();
|
|
3581
3598
|
let mut write_context = CapturingWriteContext::default();
|
|
3582
3599
|
let write_ctx = SqlWriteContext::new(&mut write_context);
|
|
3583
|
-
let sink =
|
|
3584
|
-
|
|
3585
|
-
write_ctx,
|
|
3586
|
-
test_functions(),
|
|
3587
|
-
VersionBinding::explicit(),
|
|
3588
|
-
true,
|
|
3589
|
-
);
|
|
3600
|
+
let sink =
|
|
3601
|
+
LixFileInsertSink::new(write_ctx, test_functions(), BranchBinding::explicit(), true);
|
|
3590
3602
|
|
|
3591
3603
|
let count = sink
|
|
3592
3604
|
.write_batches(vec![batch], &Arc::new(TaskContext::default()))
|
|
@@ -3628,25 +3640,20 @@ mod tests {
|
|
|
3628
3640
|
rows: vec![
|
|
3629
3641
|
live_directory_row(
|
|
3630
3642
|
"dir-docs",
|
|
3631
|
-
"
|
|
3643
|
+
"branch-b",
|
|
3632
3644
|
"{\"id\":\"dir-docs\",\"parent_id\":null,\"name\":\"docs\"}",
|
|
3633
3645
|
),
|
|
3634
3646
|
live_directory_row(
|
|
3635
3647
|
"dir-guides",
|
|
3636
|
-
"
|
|
3648
|
+
"branch-b",
|
|
3637
3649
|
"{\"id\":\"dir-guides\",\"parent_id\":\"dir-docs\",\"name\":\"guides\"}",
|
|
3638
3650
|
),
|
|
3639
3651
|
],
|
|
3640
3652
|
writes: Vec::new(),
|
|
3641
3653
|
};
|
|
3642
3654
|
let write_ctx = SqlWriteContext::new(&mut write_context);
|
|
3643
|
-
let sink =
|
|
3644
|
-
|
|
3645
|
-
write_ctx,
|
|
3646
|
-
test_functions(),
|
|
3647
|
-
VersionBinding::explicit(),
|
|
3648
|
-
true,
|
|
3649
|
-
);
|
|
3655
|
+
let sink =
|
|
3656
|
+
LixFileInsertSink::new(write_ctx, test_functions(), BranchBinding::explicit(), true);
|
|
3650
3657
|
|
|
3651
3658
|
let count = sink
|
|
3652
3659
|
.write_batches(vec![batch], &Arc::new(TaskContext::default()))
|