@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
|
@@ -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,12 @@ 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};
|
|
10
|
+
use super::transaction::SessionTransaction;
|
|
9
11
|
|
|
10
12
|
/// Result of executing one SQL statement through engine.
|
|
11
13
|
///
|
|
@@ -286,30 +288,38 @@ impl RowRef<'_> {
|
|
|
286
288
|
}
|
|
287
289
|
}
|
|
288
290
|
|
|
289
|
-
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
|
+
{
|
|
290
297
|
/// Executes one DataFusion SQL statement against this Lix session.
|
|
291
298
|
///
|
|
292
299
|
/// The SQL dialect is DataFusion SQL, not SQLite SQL. Positional
|
|
293
|
-
/// placeholders use `$1`, `$2`, and so on. SQLite-specific catalog tables
|
|
300
|
+
/// placeholders use `?` or `$1`, `$2`, and so on. SQLite-specific catalog tables
|
|
294
301
|
/// and transaction statements such as `sqlite_master`, `BEGIN`, and
|
|
295
302
|
/// `COMMIT` are not part of this contract; use `information_schema` for
|
|
296
303
|
/// catalog inspection. Lix owns transaction boundaries for each statement.
|
|
297
304
|
pub async fn execute(&self, sql: &str, params: &[Value]) -> Result<ExecuteResult, LixError> {
|
|
298
305
|
self.ensure_open()?;
|
|
299
|
-
let
|
|
300
|
-
if
|
|
301
|
-
let
|
|
302
|
-
let sql_for_error = sql.
|
|
306
|
+
let statement = sql2::parse_statement(sql)?;
|
|
307
|
+
if sql2::bind_statement_route(&statement)? == sql2::BoundStatementRoute::Write {
|
|
308
|
+
let write_access = self.begin_session_write_access().await?;
|
|
309
|
+
let sql_for_error = sql.to_string();
|
|
303
310
|
let params = params.to_vec();
|
|
304
311
|
return self
|
|
305
|
-
.
|
|
312
|
+
.with_write_transaction_reserved(write_access, |transaction| {
|
|
306
313
|
Box::pin(async move {
|
|
307
314
|
// Re-plan against the transaction-backed write
|
|
308
315
|
// session so provider hooks read and stage through the
|
|
309
316
|
// transaction-owned SQL write context.
|
|
310
|
-
|
|
311
|
-
let
|
|
312
|
-
|
|
317
|
+
transaction.prepare_sql_visible_schemas().await?;
|
|
318
|
+
let tx_plan =
|
|
319
|
+
sql2::create_write_logical_plan_from_parsed(transaction, statement)
|
|
320
|
+
.await?;
|
|
321
|
+
let affected_rows =
|
|
322
|
+
sql2::execute_write_logical_plan(transaction, tx_plan, ¶ms).await?;
|
|
313
323
|
Ok(ExecuteResult::from_rows_affected(affected_rows))
|
|
314
324
|
})
|
|
315
325
|
})
|
|
@@ -317,30 +327,39 @@ impl SessionContext {
|
|
|
317
327
|
.map_err(|error| normalize_sql_surface_error(error, &sql_for_error));
|
|
318
328
|
}
|
|
319
329
|
|
|
320
|
-
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())?;
|
|
321
341
|
let read_result = async {
|
|
322
342
|
let mut read_store = read_scope.store();
|
|
323
343
|
let live_state: Arc<dyn crate::live_state::LiveStateReader> =
|
|
324
344
|
Arc::new(self.live_state.reader(read_store.clone()));
|
|
325
345
|
let runtime_functions = FunctionContext::prepare(live_state.as_ref()).await?;
|
|
326
346
|
let functions = runtime_functions.provider();
|
|
327
|
-
let
|
|
347
|
+
let active_branch_id = self.active_branch_id_from_reader(&mut read_store).await?;
|
|
328
348
|
let visible_schemas = self
|
|
329
349
|
.catalog_context
|
|
330
|
-
.schema_jsons_for_sql_read_planning(live_state.as_ref(), &
|
|
350
|
+
.schema_jsons_for_sql_read_planning(live_state.as_ref(), &active_branch_id)
|
|
331
351
|
.await?;
|
|
332
352
|
let ctx = SessionSqlExecutionContext {
|
|
333
|
-
|
|
353
|
+
active_branch_id: &active_branch_id,
|
|
334
354
|
read_store,
|
|
335
355
|
live_state: Arc::clone(&self.live_state),
|
|
336
356
|
binary_cas: Arc::clone(&self.binary_cas),
|
|
337
|
-
|
|
338
|
-
version_ctx: Arc::clone(&self.version_ctx),
|
|
357
|
+
branch_ctx: Arc::clone(&self.branch_ctx),
|
|
339
358
|
visible_schemas,
|
|
340
359
|
functions: functions.clone(),
|
|
341
360
|
};
|
|
342
361
|
|
|
343
|
-
let plan = sql2::
|
|
362
|
+
let plan = sql2::create_logical_plan_from_parsed(&ctx, sql, statement).await?;
|
|
344
363
|
let result = sql2::execute_logical_plan(plan, params).await?;
|
|
345
364
|
drop(ctx);
|
|
346
365
|
drop(live_state);
|
|
@@ -348,19 +367,55 @@ impl SessionContext {
|
|
|
348
367
|
};
|
|
349
368
|
let (runtime_functions, result) = match read_result.await {
|
|
350
369
|
Ok(result) => {
|
|
351
|
-
read_scope.
|
|
370
|
+
read_scope.close()?;
|
|
352
371
|
result
|
|
353
372
|
}
|
|
354
373
|
Err(error) => {
|
|
355
|
-
let _ = read_scope.
|
|
374
|
+
let _ = read_scope.close();
|
|
356
375
|
return Err(normalize_sql_surface_error(error, sql));
|
|
357
376
|
}
|
|
358
377
|
};
|
|
359
|
-
self.persist_runtime_functions_if_needed(&runtime_functions)
|
|
378
|
+
self.persist_runtime_functions_if_needed(&runtime_functions, runtime_write_access.as_ref())
|
|
360
379
|
.await?;
|
|
361
380
|
Ok(ExecuteResult::from_sql_query_result(result))
|
|
362
381
|
}
|
|
363
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
|
+
|
|
364
419
|
/// Persists execution-scoped runtime function state after a successful read.
|
|
365
420
|
///
|
|
366
421
|
/// Reads do not otherwise own a write transaction, but SQL functions such as
|
|
@@ -370,19 +425,198 @@ impl SessionContext {
|
|
|
370
425
|
async fn persist_runtime_functions_if_needed(
|
|
371
426
|
&self,
|
|
372
427
|
runtime_functions: &FunctionContext,
|
|
428
|
+
runtime_write_access: Option<&SessionWriteAccess>,
|
|
373
429
|
) -> Result<(), LixError> {
|
|
374
|
-
let mut transaction = self.storage.begin_write_transaction().await?;
|
|
375
430
|
let mut writes = StorageWriteSet::new();
|
|
376
431
|
runtime_functions
|
|
377
432
|
.stage_persist_if_needed(&mut writes)
|
|
378
433
|
.await?;
|
|
379
|
-
if
|
|
380
|
-
|
|
434
|
+
if writes.is_empty() {
|
|
435
|
+
return Ok(());
|
|
436
|
+
}
|
|
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
|
+
));
|
|
381
442
|
}
|
|
382
|
-
|
|
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(())
|
|
383
451
|
}
|
|
384
452
|
}
|
|
385
453
|
|
|
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
|
+
{
|
|
460
|
+
/// Executes one SQL statement inside this transaction.
|
|
461
|
+
///
|
|
462
|
+
/// Write statements are staged until `commit()`. Read statements use the
|
|
463
|
+
/// transaction overlay, so they can observe writes staged by earlier calls
|
|
464
|
+
/// on this transaction handle.
|
|
465
|
+
pub async fn execute(
|
|
466
|
+
&mut self,
|
|
467
|
+
sql: &str,
|
|
468
|
+
params: &[Value],
|
|
469
|
+
) -> Result<ExecuteResult, LixError> {
|
|
470
|
+
let _operation_guard = self.begin_session_operation()?;
|
|
471
|
+
let statement = sql2::parse_statement(sql)?;
|
|
472
|
+
let transaction = self.transaction_mut()?;
|
|
473
|
+
match sql2::bind_statement_route(&statement)? {
|
|
474
|
+
sql2::BoundStatementRoute::Write => {
|
|
475
|
+
execute_transaction_write_auto(transaction, statement, params)
|
|
476
|
+
.await
|
|
477
|
+
.map_err(|error| normalize_sql_surface_error(error, sql))
|
|
478
|
+
}
|
|
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
|
+
};
|
|
503
|
+
Ok(ExecuteResult::from_sql_query_result(result))
|
|
504
|
+
}
|
|
505
|
+
}
|
|
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))
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
#[cfg(test)]
|
|
582
|
+
async fn execute_transaction_write_with_mode<B>(
|
|
583
|
+
transaction: &mut crate::transaction::Transaction<B>,
|
|
584
|
+
statement: datafusion::sql::parser::Statement,
|
|
585
|
+
params: &[Value],
|
|
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?;
|
|
594
|
+
let tx_plan = sql2::create_write_logical_plan_from_parsed(transaction, statement).await?;
|
|
595
|
+
let affected_rows =
|
|
596
|
+
sql2::execute_write_logical_plan_with_mode(transaction, tx_plan, params, mode).await?;
|
|
597
|
+
Ok(ExecuteResult::from_rows_affected(affected_rows))
|
|
598
|
+
}
|
|
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
|
+
|
|
386
620
|
fn normalize_sql_surface_error(error: LixError, sql: &str) -> LixError {
|
|
387
621
|
if error.code.starts_with("LIX_ERROR_PATH_") && sql_uses_public_filesystem_path_surface(sql) {
|
|
388
622
|
return LixError {
|
|
@@ -401,15 +635,6 @@ fn normalize_sql_surface_error(error: LixError, sql: &str) -> LixError {
|
|
|
401
635
|
..error
|
|
402
636
|
};
|
|
403
637
|
}
|
|
404
|
-
if error.code == LixError::CODE_FOREIGN_KEY {
|
|
405
|
-
let lower = error.message.to_ascii_lowercase();
|
|
406
|
-
if lower.contains("schema 'lix_version_ref'") && lower.contains("target 'lix_commit.") {
|
|
407
|
-
return LixError {
|
|
408
|
-
code: LixError::CODE_VERSION_NOT_FOUND.to_string(),
|
|
409
|
-
..error
|
|
410
|
-
};
|
|
411
|
-
}
|
|
412
|
-
}
|
|
413
638
|
error
|
|
414
639
|
}
|
|
415
640
|
|
|
@@ -418,28 +643,6 @@ fn sql_uses_public_filesystem_path_surface(sql: &str) -> bool {
|
|
|
418
643
|
(lower.contains("lix_file") || lower.contains("lix_directory")) && lower.contains("path")
|
|
419
644
|
}
|
|
420
645
|
|
|
421
|
-
fn affected_rows_from_query_result(result: SqlQueryResult) -> Result<u64, LixError> {
|
|
422
|
-
let Some(first_row) = result.rows.first() else {
|
|
423
|
-
return Ok(0);
|
|
424
|
-
};
|
|
425
|
-
let Some(first_value) = first_row.first() else {
|
|
426
|
-
return Ok(0);
|
|
427
|
-
};
|
|
428
|
-
match first_value {
|
|
429
|
-
Value::Integer(value) if *value >= 0 => Ok(*value as u64),
|
|
430
|
-
Value::Text(value) => value.parse::<u64>().map_err(|error| {
|
|
431
|
-
LixError::new(
|
|
432
|
-
"LIX_ERROR_UNKNOWN",
|
|
433
|
-
format!("failed to parse affected row count from SQL result: {error}"),
|
|
434
|
-
)
|
|
435
|
-
}),
|
|
436
|
-
other => Err(LixError::new(
|
|
437
|
-
"LIX_ERROR_UNKNOWN",
|
|
438
|
-
format!("expected affected row count, got {other:?}"),
|
|
439
|
-
)),
|
|
440
|
-
}
|
|
441
|
-
}
|
|
442
|
-
|
|
443
646
|
#[cfg(test)]
|
|
444
647
|
mod tests {
|
|
445
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
|
|