@lix-js/sdk 0.6.0-preview.4 → 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 +65 -64
- package/dist/engine-wasm/index.js +4 -4
- package/dist/engine-wasm/wasm/lix_engine.d.ts +5 -5
- package/dist/engine-wasm/wasm/lix_engine.js +130 -118
- package/dist/engine-wasm/wasm/lix_engine.wasm +0 -0
- package/dist/engine-wasm/wasm/lix_engine.wasm.d.ts +9 -8
- package/dist/generated/builtin-schemas.d.ts +69 -69
- package/dist/generated/builtin-schemas.js +94 -94
- package/dist/open-lix.d.ts +33 -26
- package/dist/open-lix.js +10 -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 +803 -148
- package/dist-engine-src/src/session/create_branch.rs +94 -0
- package/dist-engine-src/src/session/execute.rs +223 -83
- 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 +15 -12
- package/dist-engine-src/src/session/switch_branch.rs +113 -0
- package/dist-engine-src/src/session/transaction.rs +495 -14
- package/dist-engine-src/src/sql2/{classify.rs → bind/classify.rs} +3 -75
- 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} +71 -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 +1 -1
- 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 +28 -23
- 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 -110
- package/dist-engine-src/src/sql2/entity_provider.rs +0 -3211
- package/dist-engine-src/src/sql2/execute.rs +0 -3533
- 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 -172
- package/dist-engine-src/src/sql2/public_bind/mod.rs +0 -26
- 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
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
use crate::branch::{
|
|
2
|
+
branch_descriptor_stage_row, branch_ref_stage_row, BranchLifecycle, BranchOperation,
|
|
3
|
+
BranchReferenceRole,
|
|
4
|
+
};
|
|
5
|
+
use crate::storage::StorageBackend;
|
|
6
|
+
use crate::transaction::types::{TransactionWrite, TransactionWriteMode};
|
|
7
|
+
use crate::LixError;
|
|
8
|
+
|
|
9
|
+
use super::context::SessionContext;
|
|
10
|
+
|
|
11
|
+
/// Options for creating a new branch from the session's active branch.
|
|
12
|
+
#[derive(Debug, Clone, PartialEq, Eq)]
|
|
13
|
+
pub struct CreateBranchOptions {
|
|
14
|
+
/// Optional caller-provided branch id. If omitted, engine generates one.
|
|
15
|
+
pub id: Option<String>,
|
|
16
|
+
/// User-facing branch name.
|
|
17
|
+
pub name: String,
|
|
18
|
+
/// Optional commit id for the new branch head. If omitted, the current
|
|
19
|
+
/// active branch head is used.
|
|
20
|
+
pub from_commit_id: Option<String>,
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/// Receipt returned after creating a branch.
|
|
24
|
+
#[derive(Debug, Clone, PartialEq, Eq)]
|
|
25
|
+
pub struct CreateBranchReceipt {
|
|
26
|
+
pub id: String,
|
|
27
|
+
pub name: String,
|
|
28
|
+
pub hidden: bool,
|
|
29
|
+
pub commit_id: String,
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
impl<B> SessionContext<B>
|
|
33
|
+
where
|
|
34
|
+
B: StorageBackend + Clone + Send + Sync + 'static,
|
|
35
|
+
for<'backend> B::Read<'backend>: Clone + Send + Sync + 'static,
|
|
36
|
+
for<'backend> B::Write<'backend>: Send,
|
|
37
|
+
{
|
|
38
|
+
/// Creates a new branch from this session's current branch head.
|
|
39
|
+
///
|
|
40
|
+
/// Branch descriptors are tracked global facts so every branch agrees on
|
|
41
|
+
/// which branches exist. Branch refs are untracked global moving pointers,
|
|
42
|
+
/// so creating a ref does not add another changelog fact.
|
|
43
|
+
pub async fn create_branch(
|
|
44
|
+
&self,
|
|
45
|
+
options: CreateBranchOptions,
|
|
46
|
+
) -> Result<CreateBranchReceipt, LixError> {
|
|
47
|
+
self.with_write_transaction(|transaction| {
|
|
48
|
+
Box::pin(async move {
|
|
49
|
+
let branch_id = options
|
|
50
|
+
.id
|
|
51
|
+
.unwrap_or_else(|| transaction.functions().call_uuid_v7());
|
|
52
|
+
let source_head = if let Some(from_commit_id) = options.from_commit_id {
|
|
53
|
+
let mut commit_graph = transaction.commit_graph_reader();
|
|
54
|
+
BranchLifecycle::require_existing_commit(
|
|
55
|
+
&mut commit_graph,
|
|
56
|
+
&from_commit_id,
|
|
57
|
+
BranchOperation::CreateBranch,
|
|
58
|
+
BranchReferenceRole::CommitSource,
|
|
59
|
+
)
|
|
60
|
+
.await?;
|
|
61
|
+
from_commit_id
|
|
62
|
+
} else {
|
|
63
|
+
let active_branch_id = transaction.active_branch_id().to_string();
|
|
64
|
+
let reader = transaction.branch_ref_reader();
|
|
65
|
+
BranchLifecycle::new(&reader)
|
|
66
|
+
.require_existing_commit_id(
|
|
67
|
+
&active_branch_id,
|
|
68
|
+
BranchOperation::CreateBranch,
|
|
69
|
+
BranchReferenceRole::Source,
|
|
70
|
+
)
|
|
71
|
+
.await?
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
transaction
|
|
75
|
+
.stage_write(TransactionWrite::Rows {
|
|
76
|
+
mode: TransactionWriteMode::Insert,
|
|
77
|
+
rows: vec![
|
|
78
|
+
branch_descriptor_stage_row(&branch_id, &options.name, false),
|
|
79
|
+
branch_ref_stage_row(&branch_id, &source_head),
|
|
80
|
+
],
|
|
81
|
+
})
|
|
82
|
+
.await?;
|
|
83
|
+
|
|
84
|
+
Ok(CreateBranchReceipt {
|
|
85
|
+
id: branch_id,
|
|
86
|
+
name: options.name,
|
|
87
|
+
hidden: false,
|
|
88
|
+
commit_id: source_head,
|
|
89
|
+
})
|
|
90
|
+
})
|
|
91
|
+
})
|
|
92
|
+
.await
|
|
93
|
+
}
|
|
94
|
+
}
|
|
@@ -2,10 +2,11 @@ use std::sync::Arc;
|
|
|
2
2
|
|
|
3
3
|
use crate::functions::FunctionContext;
|
|
4
4
|
use crate::sql2;
|
|
5
|
-
use crate::storage::{
|
|
5
|
+
use crate::storage::{StorageBackend, StorageReadOptions, StorageWriteOptions, StorageWriteSet};
|
|
6
|
+
use crate::transaction::{begin_commit_boundary, commit_at_boundary};
|
|
6
7
|
use crate::{LixError, LixNotice, SqlQueryResult, Value};
|
|
7
8
|
|
|
8
|
-
use super::context::{SessionContext, SessionSqlExecutionContext};
|
|
9
|
+
use super::context::{SessionContext, SessionSqlExecutionContext, SessionWriteAccess};
|
|
9
10
|
use super::transaction::SessionTransaction;
|
|
10
11
|
|
|
11
12
|
/// Result of executing one SQL statement through engine.
|
|
@@ -287,7 +288,12 @@ impl RowRef<'_> {
|
|
|
287
288
|
}
|
|
288
289
|
}
|
|
289
290
|
|
|
290
|
-
impl SessionContext
|
|
291
|
+
impl<B> SessionContext<B>
|
|
292
|
+
where
|
|
293
|
+
B: StorageBackend + Clone + Send + Sync + 'static,
|
|
294
|
+
for<'backend> B::Read<'backend>: Clone + Send + Sync + 'static,
|
|
295
|
+
for<'backend> B::Write<'backend>: Send,
|
|
296
|
+
{
|
|
291
297
|
/// Executes one DataFusion SQL statement against this Lix session.
|
|
292
298
|
///
|
|
293
299
|
/// The SQL dialect is DataFusion SQL, not SQLite SQL. Positional
|
|
@@ -297,55 +303,58 @@ impl SessionContext {
|
|
|
297
303
|
/// catalog inspection. Lix owns transaction boundaries for each statement.
|
|
298
304
|
pub async fn execute(&self, sql: &str, params: &[Value]) -> Result<ExecuteResult, LixError> {
|
|
299
305
|
self.ensure_open()?;
|
|
300
|
-
let _transaction_guard = self.reserve_session_transaction()?;
|
|
301
306
|
let statement = sql2::parse_statement(sql)?;
|
|
302
|
-
|
|
303
|
-
|
|
307
|
+
if sql2::bind_statement_route(&statement)? == sql2::BoundStatementRoute::Write {
|
|
308
|
+
let write_access = self.begin_session_write_access().await?;
|
|
304
309
|
let sql_for_error = sql.to_string();
|
|
305
310
|
let params = params.to_vec();
|
|
306
311
|
return self
|
|
307
|
-
.with_write_transaction_reserved(|transaction| {
|
|
312
|
+
.with_write_transaction_reserved(write_access, |transaction| {
|
|
308
313
|
Box::pin(async move {
|
|
309
314
|
// Re-plan against the transaction-backed write
|
|
310
315
|
// session so provider hooks read and stage through the
|
|
311
316
|
// transaction-owned SQL write context.
|
|
317
|
+
transaction.prepare_sql_visible_schemas().await?;
|
|
312
318
|
let tx_plan =
|
|
313
319
|
sql2::create_write_logical_plan_from_parsed(transaction, statement)
|
|
314
320
|
.await?;
|
|
315
|
-
let
|
|
316
|
-
|
|
321
|
+
let affected_rows =
|
|
322
|
+
sql2::execute_write_logical_plan(transaction, tx_plan, ¶ms).await?;
|
|
317
323
|
Ok(ExecuteResult::from_rows_affected(affected_rows))
|
|
318
324
|
})
|
|
319
325
|
})
|
|
320
326
|
.await
|
|
321
327
|
.map_err(|error| normalize_sql_surface_error(error, &sql_for_error));
|
|
322
328
|
}
|
|
323
|
-
if kind == sql2::SqlStatementKind::Other {
|
|
324
|
-
return Err(LixError::new(
|
|
325
|
-
LixError::CODE_UNSUPPORTED_SQL,
|
|
326
|
-
"SQL statement is not supported by Lix SQL",
|
|
327
|
-
));
|
|
328
|
-
}
|
|
329
329
|
|
|
330
|
-
let
|
|
330
|
+
let runtime_write_access = if sql2::statement_has_durable_runtime_function(&statement) {
|
|
331
|
+
Some(self.begin_session_write_access().await?)
|
|
332
|
+
} else {
|
|
333
|
+
None
|
|
334
|
+
};
|
|
335
|
+
let _operation_guard = if runtime_write_access.is_some() {
|
|
336
|
+
None
|
|
337
|
+
} else {
|
|
338
|
+
Some(self.begin_session_operation()?)
|
|
339
|
+
};
|
|
340
|
+
let read_scope = self.storage.begin_read(StorageReadOptions::default())?;
|
|
331
341
|
let read_result = async {
|
|
332
342
|
let mut read_store = read_scope.store();
|
|
333
343
|
let live_state: Arc<dyn crate::live_state::LiveStateReader> =
|
|
334
344
|
Arc::new(self.live_state.reader(read_store.clone()));
|
|
335
345
|
let runtime_functions = FunctionContext::prepare(live_state.as_ref()).await?;
|
|
336
346
|
let functions = runtime_functions.provider();
|
|
337
|
-
let
|
|
347
|
+
let active_branch_id = self.active_branch_id_from_reader(&mut read_store).await?;
|
|
338
348
|
let visible_schemas = self
|
|
339
349
|
.catalog_context
|
|
340
|
-
.schema_jsons_for_sql_read_planning(live_state.as_ref(), &
|
|
350
|
+
.schema_jsons_for_sql_read_planning(live_state.as_ref(), &active_branch_id)
|
|
341
351
|
.await?;
|
|
342
352
|
let ctx = SessionSqlExecutionContext {
|
|
343
|
-
|
|
353
|
+
active_branch_id: &active_branch_id,
|
|
344
354
|
read_store,
|
|
345
355
|
live_state: Arc::clone(&self.live_state),
|
|
346
356
|
binary_cas: Arc::clone(&self.binary_cas),
|
|
347
|
-
|
|
348
|
-
version_ctx: Arc::clone(&self.version_ctx),
|
|
357
|
+
branch_ctx: Arc::clone(&self.branch_ctx),
|
|
349
358
|
visible_schemas,
|
|
350
359
|
functions: functions.clone(),
|
|
351
360
|
};
|
|
@@ -358,19 +367,55 @@ impl SessionContext {
|
|
|
358
367
|
};
|
|
359
368
|
let (runtime_functions, result) = match read_result.await {
|
|
360
369
|
Ok(result) => {
|
|
361
|
-
read_scope.
|
|
370
|
+
read_scope.close()?;
|
|
362
371
|
result
|
|
363
372
|
}
|
|
364
373
|
Err(error) => {
|
|
365
|
-
let _ = read_scope.
|
|
374
|
+
let _ = read_scope.close();
|
|
366
375
|
return Err(normalize_sql_surface_error(error, sql));
|
|
367
376
|
}
|
|
368
377
|
};
|
|
369
|
-
self.persist_runtime_functions_if_needed(&runtime_functions)
|
|
378
|
+
self.persist_runtime_functions_if_needed(&runtime_functions, runtime_write_access.as_ref())
|
|
370
379
|
.await?;
|
|
371
380
|
Ok(ExecuteResult::from_sql_query_result(result))
|
|
372
381
|
}
|
|
373
382
|
|
|
383
|
+
#[cfg(test)]
|
|
384
|
+
pub(crate) async fn execute_with_write_executor_mode(
|
|
385
|
+
&self,
|
|
386
|
+
sql: &str,
|
|
387
|
+
params: &[Value],
|
|
388
|
+
mode: sql2::WriteExecutorMode,
|
|
389
|
+
) -> Result<ExecuteResult, LixError> {
|
|
390
|
+
self.ensure_open()?;
|
|
391
|
+
let statement = sql2::parse_statement(sql)?;
|
|
392
|
+
if sql2::bind_statement_route(&statement)? == sql2::BoundStatementRoute::Write {
|
|
393
|
+
let write_access = self.begin_session_write_access().await?;
|
|
394
|
+
let sql_for_error = sql.to_string();
|
|
395
|
+
let params = params.to_vec();
|
|
396
|
+
return self
|
|
397
|
+
.with_write_transaction_reserved(write_access, |transaction| {
|
|
398
|
+
Box::pin(async move {
|
|
399
|
+
transaction.prepare_sql_visible_schemas().await?;
|
|
400
|
+
let tx_plan =
|
|
401
|
+
sql2::create_write_logical_plan_from_parsed(transaction, statement)
|
|
402
|
+
.await?;
|
|
403
|
+
let affected_rows = sql2::execute_write_logical_plan_with_mode(
|
|
404
|
+
transaction,
|
|
405
|
+
tx_plan,
|
|
406
|
+
¶ms,
|
|
407
|
+
mode,
|
|
408
|
+
)
|
|
409
|
+
.await?;
|
|
410
|
+
Ok(ExecuteResult::from_rows_affected(affected_rows))
|
|
411
|
+
})
|
|
412
|
+
})
|
|
413
|
+
.await
|
|
414
|
+
.map_err(|error| normalize_sql_surface_error(error, &sql_for_error));
|
|
415
|
+
}
|
|
416
|
+
self.execute(sql, params).await
|
|
417
|
+
}
|
|
418
|
+
|
|
374
419
|
/// Persists execution-scoped runtime function state after a successful read.
|
|
375
420
|
///
|
|
376
421
|
/// Reads do not otherwise own a write transaction, but SQL functions such as
|
|
@@ -380,6 +425,7 @@ impl SessionContext {
|
|
|
380
425
|
async fn persist_runtime_functions_if_needed(
|
|
381
426
|
&self,
|
|
382
427
|
runtime_functions: &FunctionContext,
|
|
428
|
+
runtime_write_access: Option<&SessionWriteAccess>,
|
|
383
429
|
) -> Result<(), LixError> {
|
|
384
430
|
let mut writes = StorageWriteSet::new();
|
|
385
431
|
runtime_functions
|
|
@@ -388,13 +434,29 @@ impl SessionContext {
|
|
|
388
434
|
if writes.is_empty() {
|
|
389
435
|
return Ok(());
|
|
390
436
|
}
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
437
|
+
if runtime_write_access.is_none() {
|
|
438
|
+
return Err(LixError::new(
|
|
439
|
+
LixError::CODE_INTERNAL_ERROR,
|
|
440
|
+
"runtime function state changed without reserved write access",
|
|
441
|
+
));
|
|
442
|
+
}
|
|
443
|
+
let commit_boundary = self.transaction_commit_boundary();
|
|
444
|
+
let _commit_guard = begin_commit_boundary(Some(&commit_boundary));
|
|
445
|
+
commit_at_boundary(Some(&commit_boundary), || {
|
|
446
|
+
self.storage
|
|
447
|
+
.commit_write_set(writes, StorageWriteOptions::default())?;
|
|
448
|
+
Ok(())
|
|
449
|
+
})?;
|
|
450
|
+
Ok(())
|
|
394
451
|
}
|
|
395
452
|
}
|
|
396
453
|
|
|
397
|
-
impl SessionTransaction
|
|
454
|
+
impl<B> SessionTransaction<B>
|
|
455
|
+
where
|
|
456
|
+
B: StorageBackend + Clone + Send + Sync + 'static,
|
|
457
|
+
for<'backend> B::Read<'backend>: Clone + Send + Sync + 'static,
|
|
458
|
+
for<'backend> B::Write<'backend>: Send,
|
|
459
|
+
{
|
|
398
460
|
/// Executes one SQL statement inside this transaction.
|
|
399
461
|
///
|
|
400
462
|
/// Write statements are staged until `commit()`. Read statements use the
|
|
@@ -405,47 +467,156 @@ impl SessionTransaction {
|
|
|
405
467
|
sql: &str,
|
|
406
468
|
params: &[Value],
|
|
407
469
|
) -> Result<ExecuteResult, LixError> {
|
|
470
|
+
let _operation_guard = self.begin_session_operation()?;
|
|
408
471
|
let statement = sql2::parse_statement(sql)?;
|
|
409
|
-
let kind = sql2::classify_datafusion_statement(&statement);
|
|
410
472
|
let transaction = self.transaction_mut()?;
|
|
411
|
-
match
|
|
412
|
-
sql2::
|
|
413
|
-
|
|
473
|
+
match sql2::bind_statement_route(&statement)? {
|
|
474
|
+
sql2::BoundStatementRoute::Write => {
|
|
475
|
+
execute_transaction_write_auto(transaction, statement, params)
|
|
414
476
|
.await
|
|
415
477
|
.map_err(|error| normalize_sql_surface_error(error, sql))
|
|
416
478
|
}
|
|
417
|
-
sql2::
|
|
418
|
-
let
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
.await
|
|
427
|
-
|
|
479
|
+
sql2::BoundStatementRoute::Read => {
|
|
480
|
+
let read_ctx = transaction.sql_read_execution_context().await?;
|
|
481
|
+
let read_result = async {
|
|
482
|
+
let plan = sql2::create_transaction_read_logical_plan_from_parsed(
|
|
483
|
+
&read_ctx,
|
|
484
|
+
transaction,
|
|
485
|
+
sql,
|
|
486
|
+
statement,
|
|
487
|
+
)
|
|
488
|
+
.await?;
|
|
489
|
+
sql2::execute_logical_plan(plan, params).await
|
|
490
|
+
}
|
|
491
|
+
.await;
|
|
492
|
+
let close_result = read_ctx.close();
|
|
493
|
+
let result = match read_result {
|
|
494
|
+
Ok(result) => {
|
|
495
|
+
close_result?;
|
|
496
|
+
result
|
|
497
|
+
}
|
|
498
|
+
Err(error) => {
|
|
499
|
+
let _ = close_result;
|
|
500
|
+
return Err(normalize_sql_surface_error(error, sql));
|
|
501
|
+
}
|
|
502
|
+
};
|
|
428
503
|
Ok(ExecuteResult::from_sql_query_result(result))
|
|
429
504
|
}
|
|
430
|
-
sql2::SqlStatementKind::Other => Err(LixError::new(
|
|
431
|
-
LixError::CODE_UNSUPPORTED_SQL,
|
|
432
|
-
"SQL statement is not supported by Lix SQL",
|
|
433
|
-
)),
|
|
434
505
|
}
|
|
435
506
|
}
|
|
507
|
+
|
|
508
|
+
#[cfg(test)]
|
|
509
|
+
pub(crate) async fn execute_with_write_executor_mode(
|
|
510
|
+
&mut self,
|
|
511
|
+
sql: &str,
|
|
512
|
+
params: &[Value],
|
|
513
|
+
mode: sql2::WriteExecutorMode,
|
|
514
|
+
) -> Result<ExecuteResult, LixError> {
|
|
515
|
+
let _operation_guard = self.begin_session_operation()?;
|
|
516
|
+
let statement = sql2::parse_statement(sql)?;
|
|
517
|
+
let transaction = self.transaction_mut()?;
|
|
518
|
+
match sql2::bind_statement_route(&statement)? {
|
|
519
|
+
sql2::BoundStatementRoute::Write => {
|
|
520
|
+
execute_transaction_write_with_mode(transaction, statement, params, mode)
|
|
521
|
+
.await
|
|
522
|
+
.map_err(|error| normalize_sql_surface_error(error, sql))
|
|
523
|
+
}
|
|
524
|
+
sql2::BoundStatementRoute::Read => self.execute(sql, params).await,
|
|
525
|
+
}
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
#[cfg(test)]
|
|
529
|
+
pub(crate) async fn execute_with_write_executor_mode_and_trace(
|
|
530
|
+
&mut self,
|
|
531
|
+
sql: &str,
|
|
532
|
+
params: &[Value],
|
|
533
|
+
mode: sql2::WriteExecutorMode,
|
|
534
|
+
) -> Result<(ExecuteResult, Option<sql2::WriteExecutorPath>), LixError> {
|
|
535
|
+
let _operation_guard = self.begin_session_operation()?;
|
|
536
|
+
let statement = sql2::parse_statement(sql)?;
|
|
537
|
+
let transaction = self.transaction_mut()?;
|
|
538
|
+
match sql2::bind_statement_route(&statement)? {
|
|
539
|
+
sql2::BoundStatementRoute::Write => {
|
|
540
|
+
execute_transaction_write_with_mode_and_trace(transaction, statement, params, mode)
|
|
541
|
+
.await
|
|
542
|
+
.map_err(|error| normalize_sql_surface_error(error, sql))
|
|
543
|
+
}
|
|
544
|
+
sql2::BoundStatementRoute::Read => {
|
|
545
|
+
self.execute(sql, params).await.map(|result| (result, None))
|
|
546
|
+
}
|
|
547
|
+
}
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
#[cfg(test)]
|
|
551
|
+
pub(crate) async fn scan_live_state_for_test(
|
|
552
|
+
&mut self,
|
|
553
|
+
request: &crate::live_state::LiveStateScanRequest,
|
|
554
|
+
) -> Result<Vec<crate::live_state::MaterializedLiveStateRow>, LixError> {
|
|
555
|
+
let _operation_guard = self.begin_session_operation()?;
|
|
556
|
+
let transaction = self.transaction_mut()?;
|
|
557
|
+
<crate::transaction::Transaction<B> as sql2::SqlWriteExecutionContext>::scan_live_state(
|
|
558
|
+
transaction,
|
|
559
|
+
request,
|
|
560
|
+
)
|
|
561
|
+
.await
|
|
562
|
+
}
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
async fn execute_transaction_write_auto<B>(
|
|
566
|
+
transaction: &mut crate::transaction::Transaction<B>,
|
|
567
|
+
statement: datafusion::sql::parser::Statement,
|
|
568
|
+
params: &[Value],
|
|
569
|
+
) -> Result<ExecuteResult, LixError>
|
|
570
|
+
where
|
|
571
|
+
B: StorageBackend + Clone + Send + Sync + 'static,
|
|
572
|
+
for<'backend> B::Read<'backend>: Clone + Send + Sync + 'static,
|
|
573
|
+
for<'backend> B::Write<'backend>: Send,
|
|
574
|
+
{
|
|
575
|
+
transaction.prepare_sql_visible_schemas().await?;
|
|
576
|
+
let tx_plan = sql2::create_write_logical_plan_from_parsed(transaction, statement).await?;
|
|
577
|
+
let affected_rows = sql2::execute_write_logical_plan(transaction, tx_plan, params).await?;
|
|
578
|
+
Ok(ExecuteResult::from_rows_affected(affected_rows))
|
|
436
579
|
}
|
|
437
580
|
|
|
438
|
-
|
|
439
|
-
|
|
581
|
+
#[cfg(test)]
|
|
582
|
+
async fn execute_transaction_write_with_mode<B>(
|
|
583
|
+
transaction: &mut crate::transaction::Transaction<B>,
|
|
440
584
|
statement: datafusion::sql::parser::Statement,
|
|
441
585
|
params: &[Value],
|
|
442
|
-
|
|
586
|
+
mode: sql2::WriteExecutorMode,
|
|
587
|
+
) -> Result<ExecuteResult, LixError>
|
|
588
|
+
where
|
|
589
|
+
B: StorageBackend + Clone + Send + Sync + 'static,
|
|
590
|
+
for<'backend> B::Read<'backend>: Clone + Send + Sync + 'static,
|
|
591
|
+
for<'backend> B::Write<'backend>: Send,
|
|
592
|
+
{
|
|
593
|
+
transaction.prepare_sql_visible_schemas().await?;
|
|
443
594
|
let tx_plan = sql2::create_write_logical_plan_from_parsed(transaction, statement).await?;
|
|
444
|
-
let
|
|
445
|
-
|
|
595
|
+
let affected_rows =
|
|
596
|
+
sql2::execute_write_logical_plan_with_mode(transaction, tx_plan, params, mode).await?;
|
|
446
597
|
Ok(ExecuteResult::from_rows_affected(affected_rows))
|
|
447
598
|
}
|
|
448
599
|
|
|
600
|
+
#[cfg(test)]
|
|
601
|
+
async fn execute_transaction_write_with_mode_and_trace<B>(
|
|
602
|
+
transaction: &mut crate::transaction::Transaction<B>,
|
|
603
|
+
statement: datafusion::sql::parser::Statement,
|
|
604
|
+
params: &[Value],
|
|
605
|
+
mode: sql2::WriteExecutorMode,
|
|
606
|
+
) -> Result<(ExecuteResult, Option<sql2::WriteExecutorPath>), LixError>
|
|
607
|
+
where
|
|
608
|
+
B: StorageBackend + Clone + Send + Sync + 'static,
|
|
609
|
+
for<'backend> B::Read<'backend>: Clone + Send + Sync + 'static,
|
|
610
|
+
for<'backend> B::Write<'backend>: Send,
|
|
611
|
+
{
|
|
612
|
+
transaction.prepare_sql_visible_schemas().await?;
|
|
613
|
+
let tx_plan = sql2::create_write_logical_plan_from_parsed(transaction, statement).await?;
|
|
614
|
+
let (affected_rows, path) =
|
|
615
|
+
sql2::execute_write_logical_plan_with_mode_and_trace(transaction, tx_plan, params, mode)
|
|
616
|
+
.await?;
|
|
617
|
+
Ok((ExecuteResult::from_rows_affected(affected_rows), Some(path)))
|
|
618
|
+
}
|
|
619
|
+
|
|
449
620
|
fn normalize_sql_surface_error(error: LixError, sql: &str) -> LixError {
|
|
450
621
|
if error.code.starts_with("LIX_ERROR_PATH_") && sql_uses_public_filesystem_path_surface(sql) {
|
|
451
622
|
return LixError {
|
|
@@ -464,15 +635,6 @@ fn normalize_sql_surface_error(error: LixError, sql: &str) -> LixError {
|
|
|
464
635
|
..error
|
|
465
636
|
};
|
|
466
637
|
}
|
|
467
|
-
if error.code == LixError::CODE_FOREIGN_KEY {
|
|
468
|
-
let lower = error.message.to_ascii_lowercase();
|
|
469
|
-
if lower.contains("schema 'lix_version_ref'") && lower.contains("target 'lix_commit.") {
|
|
470
|
-
return LixError {
|
|
471
|
-
code: LixError::CODE_VERSION_NOT_FOUND.to_string(),
|
|
472
|
-
..error
|
|
473
|
-
};
|
|
474
|
-
}
|
|
475
|
-
}
|
|
476
638
|
error
|
|
477
639
|
}
|
|
478
640
|
|
|
@@ -481,28 +643,6 @@ fn sql_uses_public_filesystem_path_surface(sql: &str) -> bool {
|
|
|
481
643
|
(lower.contains("lix_file") || lower.contains("lix_directory")) && lower.contains("path")
|
|
482
644
|
}
|
|
483
645
|
|
|
484
|
-
fn affected_rows_from_query_result(result: SqlQueryResult) -> Result<u64, LixError> {
|
|
485
|
-
let Some(first_row) = result.rows.first() else {
|
|
486
|
-
return Ok(0);
|
|
487
|
-
};
|
|
488
|
-
let Some(first_value) = first_row.first() else {
|
|
489
|
-
return Ok(0);
|
|
490
|
-
};
|
|
491
|
-
match first_value {
|
|
492
|
-
Value::Integer(value) if *value >= 0 => Ok(*value as u64),
|
|
493
|
-
Value::Text(value) => value.parse::<u64>().map_err(|error| {
|
|
494
|
-
LixError::new(
|
|
495
|
-
"LIX_ERROR_UNKNOWN",
|
|
496
|
-
format!("failed to parse affected row count from SQL result: {error}"),
|
|
497
|
-
)
|
|
498
|
-
}),
|
|
499
|
-
other => Err(LixError::new(
|
|
500
|
-
"LIX_ERROR_UNKNOWN",
|
|
501
|
-
format!("expected affected row count, got {other:?}"),
|
|
502
|
-
)),
|
|
503
|
-
}
|
|
504
|
-
}
|
|
505
|
-
|
|
506
646
|
#[cfg(test)]
|
|
507
647
|
mod tests {
|
|
508
648
|
use super::*;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
use crate::storage::
|
|
1
|
+
use crate::storage::StorageRead;
|
|
2
2
|
use crate::tracked_state::{
|
|
3
3
|
plan_merge, TrackedStateDiff, TrackedStateDiffRequest, TrackedStateMergePlan,
|
|
4
4
|
TrackedStateStoreReader,
|
|
@@ -44,7 +44,7 @@ pub(crate) async fn analyze<S>(
|
|
|
44
44
|
commits: MergeCommits,
|
|
45
45
|
) -> Result<MergeAnalysis, LixError>
|
|
46
46
|
where
|
|
47
|
-
S:
|
|
47
|
+
S: StorageRead + Send + Sync,
|
|
48
48
|
{
|
|
49
49
|
let request = TrackedStateDiffRequest::default();
|
|
50
50
|
let source_diff = reader
|
|
@@ -56,7 +56,13 @@ where
|
|
|
56
56
|
TrackedStateDiff::default()
|
|
57
57
|
} else {
|
|
58
58
|
reader
|
|
59
|
-
.
|
|
59
|
+
.diff_commits_with_validation(
|
|
60
|
+
&commits.base_commit_id,
|
|
61
|
+
&commits.target_commit_id,
|
|
62
|
+
&request,
|
|
63
|
+
false,
|
|
64
|
+
true,
|
|
65
|
+
)
|
|
60
66
|
.await?
|
|
61
67
|
};
|
|
62
68
|
|