@lix-js/sdk 0.6.0-preview.2 → 0.6.0-preview.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/SKILL.md +46 -8
- package/dist/engine-wasm/wasm/lix_engine.d.ts +25 -1
- package/dist/engine-wasm/wasm/lix_engine.js +60 -2
- package/dist/engine-wasm/wasm/lix_engine.wasm +0 -0
- package/dist/engine-wasm/wasm/lix_engine.wasm.d.ts +5 -0
- package/dist/generated/builtin-schemas.d.ts +87 -162
- package/dist/generated/builtin-schemas.js +139 -236
- package/dist/open-lix.d.ts +10 -3
- package/dist/open-lix.js +39 -0
- package/dist-engine-src/src/binary_cas/types.rs +0 -6
- package/dist-engine-src/src/catalog/context.rs +412 -0
- package/dist-engine-src/src/catalog/mod.rs +10 -0
- package/dist-engine-src/src/catalog/schema.rs +4 -0
- package/dist-engine-src/src/catalog/snapshot.rs +1114 -0
- package/dist-engine-src/src/cel/mod.rs +1 -1
- package/dist-engine-src/src/cel/provider.rs +1 -1
- package/dist-engine-src/src/commit_graph/context.rs +328 -1015
- package/dist-engine-src/src/commit_graph/mod.rs +2 -3
- package/dist-engine-src/src/commit_graph/types.rs +7 -43
- package/dist-engine-src/src/commit_graph/walker.rs +57 -81
- package/dist-engine-src/src/commit_store/codec.rs +887 -0
- package/dist-engine-src/src/commit_store/context.rs +944 -0
- package/dist-engine-src/src/commit_store/materialization.rs +84 -0
- package/dist-engine-src/src/commit_store/mod.rs +16 -0
- package/dist-engine-src/src/commit_store/storage.rs +600 -0
- package/dist-engine-src/src/commit_store/types.rs +215 -0
- package/dist-engine-src/src/common/identity.rs +15 -5
- package/dist-engine-src/src/common/json_pointer.rs +67 -0
- package/dist-engine-src/src/common/metadata.rs +17 -12
- package/dist-engine-src/src/common/mod.rs +5 -5
- package/dist-engine-src/src/domain.rs +324 -0
- package/dist-engine-src/src/engine.rs +29 -43
- package/dist-engine-src/src/entity_identity.rs +238 -118
- package/dist-engine-src/src/functions/context.rs +17 -52
- package/dist-engine-src/src/functions/deterministic.rs +1 -1
- package/dist-engine-src/src/functions/mod.rs +1 -1
- package/dist-engine-src/src/functions/provider.rs +4 -4
- package/dist-engine-src/src/functions/state.rs +39 -66
- package/dist-engine-src/src/functions/types.rs +1 -1
- package/dist-engine-src/src/init.rs +204 -151
- package/dist-engine-src/src/json_store/context.rs +354 -60
- package/dist-engine-src/src/json_store/encoded.rs +6 -6
- package/dist-engine-src/src/json_store/mod.rs +4 -1
- package/dist-engine-src/src/json_store/store.rs +884 -11
- package/dist-engine-src/src/json_store/types.rs +166 -1
- package/dist-engine-src/src/lib.rs +11 -10
- package/dist-engine-src/src/live_state/context.rs +608 -830
- package/dist-engine-src/src/live_state/mod.rs +3 -3
- package/dist-engine-src/src/live_state/overlay.rs +7 -7
- package/dist-engine-src/src/live_state/reader.rs +5 -5
- package/dist-engine-src/src/live_state/types.rs +19 -36
- package/dist-engine-src/src/live_state/visibility.rs +19 -14
- package/dist-engine-src/src/plugin/archive.rs +3 -6
- package/dist-engine-src/src/plugin/install.rs +0 -18
- package/dist-engine-src/src/plugin/plugin_manifest.json +0 -1
- package/dist-engine-src/src/schema/annotations/defaults.rs +2 -7
- package/dist-engine-src/src/schema/builtin/lix_account.json +0 -1
- package/dist-engine-src/src/schema/builtin/lix_active_account.json +0 -1
- package/dist-engine-src/src/schema/builtin/lix_binary_blob_ref.json +0 -1
- package/dist-engine-src/src/schema/builtin/lix_change.json +11 -10
- package/dist-engine-src/src/schema/builtin/lix_change_author.json +0 -1
- package/dist-engine-src/src/schema/builtin/lix_commit.json +8 -46
- package/dist-engine-src/src/schema/builtin/lix_commit_edge.json +29 -22
- package/dist-engine-src/src/schema/builtin/lix_directory_descriptor.json +0 -1
- package/dist-engine-src/src/schema/builtin/lix_file_descriptor.json +0 -1
- package/dist-engine-src/src/schema/builtin/lix_key_value.json +0 -1
- package/dist-engine-src/src/schema/builtin/lix_label.json +10 -3
- package/dist-engine-src/src/schema/builtin/lix_label_assignment.json +74 -0
- package/dist-engine-src/src/schema/builtin/lix_registered_schema.json +2 -8
- package/dist-engine-src/src/schema/builtin/lix_version_descriptor.json +0 -1
- package/dist-engine-src/src/schema/builtin/lix_version_ref.json +0 -1
- package/dist-engine-src/src/schema/builtin/mod.rs +10 -59
- package/dist-engine-src/src/schema/compatibility.rs +787 -0
- package/dist-engine-src/src/schema/definition.json +47 -17
- package/dist-engine-src/src/schema/definition.rs +202 -96
- package/dist-engine-src/src/schema/key.rs +9 -77
- package/dist-engine-src/src/schema/mod.rs +4 -4
- package/dist-engine-src/src/schema/tests.rs +133 -92
- package/dist-engine-src/src/session/context.rs +86 -48
- package/dist-engine-src/src/session/create_version.rs +22 -14
- package/dist-engine-src/src/session/execute.rs +117 -23
- package/dist-engine-src/src/session/merge/apply.rs +4 -4
- package/dist-engine-src/src/session/merge/conflicts.rs +3 -2
- package/dist-engine-src/src/session/merge/stats.rs +1 -1
- package/dist-engine-src/src/session/merge/version.rs +35 -45
- package/dist-engine-src/src/session/mod.rs +9 -7
- package/dist-engine-src/src/session/optimization9_sql2_bench.rs +100 -0
- package/dist-engine-src/src/session/switch_version.rs +17 -28
- package/dist-engine-src/src/session/transaction.rs +76 -0
- package/dist-engine-src/src/sql2/change_provider.rs +14 -20
- package/dist-engine-src/src/sql2/classify.rs +75 -48
- package/dist-engine-src/src/sql2/context.rs +22 -18
- package/dist-engine-src/src/sql2/directory_history_provider.rs +28 -20
- package/dist-engine-src/src/sql2/directory_provider.rs +131 -83
- package/dist-engine-src/src/sql2/entity_history_provider.rs +10 -14
- package/dist-engine-src/src/sql2/entity_provider.rs +680 -169
- package/dist-engine-src/src/sql2/error.rs +24 -5
- package/dist-engine-src/src/sql2/execute.rs +426 -272
- package/dist-engine-src/src/sql2/file_history_provider.rs +29 -21
- package/dist-engine-src/src/sql2/file_provider.rs +533 -108
- package/dist-engine-src/src/sql2/filesystem_planner.rs +58 -94
- package/dist-engine-src/src/sql2/filesystem_visibility.rs +37 -23
- package/dist-engine-src/src/sql2/history_projection.rs +3 -27
- package/dist-engine-src/src/sql2/history_provider.rs +11 -17
- package/dist-engine-src/src/sql2/history_route.rs +22 -8
- package/dist-engine-src/src/sql2/lix_state_provider.rs +178 -96
- package/dist-engine-src/src/sql2/mod.rs +8 -4
- package/dist-engine-src/src/sql2/predicate_typecheck.rs +246 -0
- package/dist-engine-src/src/sql2/public_bind/assignment.rs +46 -0
- package/dist-engine-src/src/sql2/public_bind/capability.rs +41 -0
- package/dist-engine-src/src/sql2/public_bind/dml.rs +172 -0
- package/dist-engine-src/src/sql2/public_bind/mod.rs +26 -0
- package/dist-engine-src/src/sql2/public_bind/table.rs +168 -0
- package/dist-engine-src/src/sql2/read_only.rs +10 -12
- package/dist-engine-src/src/sql2/session.rs +7 -10
- package/dist-engine-src/src/sql2/udfs/lix_timestamp.rs +76 -0
- package/dist-engine-src/src/sql2/udfs/mod.rs +8 -1
- package/dist-engine-src/src/sql2/udfs/public_call.rs +238 -0
- package/dist-engine-src/src/sql2/version_provider.rs +46 -31
- package/dist-engine-src/src/sql2/version_scope.rs +4 -4
- package/dist-engine-src/src/storage_bench.rs +1782 -325
- package/dist-engine-src/src/test_support.rs +183 -36
- package/dist-engine-src/src/tracked_state/by_file_index.rs +20 -24
- package/dist-engine-src/src/tracked_state/codec.rs +1519 -181
- package/dist-engine-src/src/tracked_state/context.rs +1155 -271
- package/dist-engine-src/src/tracked_state/diff.rs +249 -57
- package/dist-engine-src/src/tracked_state/materialization.rs +365 -103
- package/dist-engine-src/src/tracked_state/materializer.rs +488 -0
- package/dist-engine-src/src/tracked_state/merge.rs +37 -19
- package/dist-engine-src/src/tracked_state/mod.rs +8 -7
- package/dist-engine-src/src/tracked_state/storage.rs +138 -6
- package/dist-engine-src/src/tracked_state/tree.rs +695 -252
- package/dist-engine-src/src/tracked_state/types.rs +176 -6
- package/dist-engine-src/src/transaction/commit.rs +695 -435
- package/dist-engine-src/src/transaction/context.rs +551 -310
- package/dist-engine-src/src/transaction/live_state_overlay.rs +9 -8
- package/dist-engine-src/src/transaction/mod.rs +2 -0
- package/dist-engine-src/src/transaction/normalization.rs +311 -447
- package/dist-engine-src/src/transaction/prep.rs +37 -0
- package/dist-engine-src/src/transaction/schema_resolver.rs +93 -71
- package/dist-engine-src/src/transaction/staging.rs +701 -406
- package/dist-engine-src/src/transaction/types.rs +231 -122
- package/dist-engine-src/src/transaction/validation.rs +2717 -1698
- package/dist-engine-src/src/untracked_state/codec.rs +40 -96
- package/dist-engine-src/src/untracked_state/context.rs +21 -5
- package/dist-engine-src/src/untracked_state/materialization.rs +10 -104
- package/dist-engine-src/src/untracked_state/mod.rs +3 -5
- package/dist-engine-src/src/untracked_state/storage.rs +105 -57
- package/dist-engine-src/src/untracked_state/types.rs +63 -13
- package/dist-engine-src/src/version/context.rs +1 -13
- package/dist-engine-src/src/version/lifecycle.rs +221 -0
- package/dist-engine-src/src/version/mod.rs +3 -2
- package/dist-engine-src/src/version/refs.rs +12 -103
- package/dist-engine-src/src/version/stage_rows.rs +15 -19
- package/package.json +1 -1
- package/dist-engine-src/src/changelog/codec.rs +0 -321
- package/dist-engine-src/src/changelog/context.rs +0 -92
- package/dist-engine-src/src/changelog/materialization.rs +0 -121
- package/dist-engine-src/src/changelog/mod.rs +0 -13
- package/dist-engine-src/src/changelog/reader.rs +0 -20
- package/dist-engine-src/src/changelog/storage.rs +0 -220
- package/dist-engine-src/src/changelog/types.rs +0 -38
- package/dist-engine-src/src/schema/builtin/lix_change_set.json +0 -18
- package/dist-engine-src/src/schema/builtin/lix_change_set_element.json +0 -75
- package/dist-engine-src/src/schema/builtin/lix_entity_label.json +0 -63
- package/dist-engine-src/src/schema_registry.rs +0 -294
- package/dist-engine-src/src/sql2/commit_derived_provider.rs +0 -591
- package/dist-engine-src/src/tracked_state/rebuild.rs +0 -771
- package/dist-engine-src/src/tracked_state/tree_types.rs +0 -176
|
@@ -6,23 +6,27 @@ use std::sync::Arc;
|
|
|
6
6
|
use serde_json::Value as JsonValue;
|
|
7
7
|
|
|
8
8
|
use crate::binary_cas::{BinaryCasContext, BlobDataReader};
|
|
9
|
-
use crate::
|
|
9
|
+
use crate::catalog::CatalogContext;
|
|
10
10
|
use crate::commit_graph::{CommitGraphContext, CommitGraphReader};
|
|
11
|
+
use crate::commit_store::CommitStoreContext;
|
|
11
12
|
use crate::entity_identity::EntityIdentity;
|
|
12
13
|
use crate::functions::FunctionProviderHandle;
|
|
13
14
|
use crate::json_store::JsonStoreContext;
|
|
14
15
|
use crate::live_state::{LiveStateContext, LiveStateReader, LiveStateRowRequest};
|
|
15
|
-
use crate::
|
|
16
|
-
use crate::sql2::{ChangelogQuerySource, SqlChangelogQuerySource, SqlExecutionContext};
|
|
16
|
+
use crate::sql2::{CommitStoreQuerySource, SqlCommitStoreQuerySource, SqlExecutionContext};
|
|
17
17
|
use crate::storage::{
|
|
18
18
|
ScopedStorageReader, StorageContext, StorageReadScope, StorageReadTransaction, StorageReader,
|
|
19
19
|
};
|
|
20
20
|
use crate::tracked_state::TrackedStateContext;
|
|
21
21
|
use crate::transaction::{open_transaction, Transaction};
|
|
22
|
-
use crate::version::{
|
|
22
|
+
use crate::version::{
|
|
23
|
+
VersionContext, VersionLifecycle, VersionOperation, VersionRefReader, VersionReferenceRole,
|
|
24
|
+
};
|
|
23
25
|
use crate::GLOBAL_VERSION_ID;
|
|
24
26
|
use crate::{LixError, NullableKeyFilter};
|
|
25
27
|
|
|
28
|
+
use super::transaction::transaction_state_error;
|
|
29
|
+
|
|
26
30
|
pub(crate) const WORKSPACE_VERSION_KEY: &str = "lix_workspace_version_id";
|
|
27
31
|
|
|
28
32
|
#[derive(Clone)]
|
|
@@ -31,16 +35,13 @@ pub(crate) enum SessionMode {
|
|
|
31
35
|
Workspace,
|
|
32
36
|
}
|
|
33
37
|
|
|
34
|
-
/// Session-context state for
|
|
38
|
+
/// Session-context state for engine execution.
|
|
35
39
|
///
|
|
36
40
|
/// A session context pins the active version selector and shared execution
|
|
37
|
-
/// services.
|
|
38
|
-
///
|
|
39
|
-
///
|
|
40
|
-
///
|
|
41
|
-
/// through `SessionContext::with_write_transaction`. Reads that influence writes
|
|
42
|
-
/// are only available from that transaction capability, not from session-level
|
|
43
|
-
/// helpers.
|
|
41
|
+
/// services. Parent-handle `execute(...)` runs as an implicit single-statement
|
|
42
|
+
/// transaction. Explicit transactions hold the session execution lease until
|
|
43
|
+
/// commit or rollback, so all SQL during that window must run through the
|
|
44
|
+
/// transaction handle.
|
|
44
45
|
#[derive(Clone)]
|
|
45
46
|
pub struct SessionContext {
|
|
46
47
|
pub(super) mode: SessionMode,
|
|
@@ -48,10 +49,11 @@ pub struct SessionContext {
|
|
|
48
49
|
pub(super) live_state: Arc<LiveStateContext>,
|
|
49
50
|
pub(super) tracked_state: Arc<TrackedStateContext>,
|
|
50
51
|
pub(super) binary_cas: Arc<BinaryCasContext>,
|
|
51
|
-
pub(super)
|
|
52
|
+
pub(super) commit_store: Arc<CommitStoreContext>,
|
|
52
53
|
pub(super) version_ctx: Arc<VersionContext>,
|
|
53
|
-
pub(super)
|
|
54
|
+
pub(super) catalog_context: Arc<CatalogContext>,
|
|
54
55
|
closed: Arc<AtomicBool>,
|
|
56
|
+
active_transaction: Arc<AtomicBool>,
|
|
55
57
|
}
|
|
56
58
|
|
|
57
59
|
impl SessionContext {
|
|
@@ -60,9 +62,9 @@ impl SessionContext {
|
|
|
60
62
|
live_state: Arc<LiveStateContext>,
|
|
61
63
|
tracked_state: Arc<TrackedStateContext>,
|
|
62
64
|
binary_cas: Arc<BinaryCasContext>,
|
|
63
|
-
|
|
65
|
+
commit_store: Arc<CommitStoreContext>,
|
|
64
66
|
version_ctx: Arc<VersionContext>,
|
|
65
|
-
|
|
67
|
+
catalog_context: Arc<CatalogContext>,
|
|
66
68
|
) -> Result<Self, LixError> {
|
|
67
69
|
let session = Self::new(
|
|
68
70
|
SessionMode::Workspace,
|
|
@@ -70,9 +72,9 @@ impl SessionContext {
|
|
|
70
72
|
live_state,
|
|
71
73
|
tracked_state,
|
|
72
74
|
binary_cas,
|
|
73
|
-
|
|
75
|
+
commit_store,
|
|
74
76
|
version_ctx,
|
|
75
|
-
|
|
77
|
+
catalog_context,
|
|
76
78
|
);
|
|
77
79
|
session.active_version_id().await?;
|
|
78
80
|
Ok(session)
|
|
@@ -84,9 +86,9 @@ impl SessionContext {
|
|
|
84
86
|
live_state: Arc<LiveStateContext>,
|
|
85
87
|
tracked_state: Arc<TrackedStateContext>,
|
|
86
88
|
binary_cas: Arc<BinaryCasContext>,
|
|
87
|
-
|
|
89
|
+
commit_store: Arc<CommitStoreContext>,
|
|
88
90
|
version_ctx: Arc<VersionContext>,
|
|
89
|
-
|
|
91
|
+
catalog_context: Arc<CatalogContext>,
|
|
90
92
|
) -> Result<Self, LixError> {
|
|
91
93
|
Ok(Self::new(
|
|
92
94
|
SessionMode::Pinned {
|
|
@@ -96,9 +98,9 @@ impl SessionContext {
|
|
|
96
98
|
live_state,
|
|
97
99
|
tracked_state,
|
|
98
100
|
binary_cas,
|
|
99
|
-
|
|
101
|
+
commit_store,
|
|
100
102
|
version_ctx,
|
|
101
|
-
|
|
103
|
+
catalog_context,
|
|
102
104
|
))
|
|
103
105
|
}
|
|
104
106
|
|
|
@@ -108,9 +110,9 @@ impl SessionContext {
|
|
|
108
110
|
live_state: Arc<LiveStateContext>,
|
|
109
111
|
tracked_state: Arc<TrackedStateContext>,
|
|
110
112
|
binary_cas: Arc<BinaryCasContext>,
|
|
111
|
-
|
|
113
|
+
commit_store: Arc<CommitStoreContext>,
|
|
112
114
|
version_ctx: Arc<VersionContext>,
|
|
113
|
-
|
|
115
|
+
catalog_context: Arc<CatalogContext>,
|
|
114
116
|
) -> Self {
|
|
115
117
|
Self::new_with_closed(
|
|
116
118
|
mode,
|
|
@@ -118,9 +120,10 @@ impl SessionContext {
|
|
|
118
120
|
live_state,
|
|
119
121
|
tracked_state,
|
|
120
122
|
binary_cas,
|
|
121
|
-
|
|
123
|
+
commit_store,
|
|
122
124
|
version_ctx,
|
|
123
|
-
|
|
125
|
+
catalog_context,
|
|
126
|
+
Arc::new(AtomicBool::new(false)),
|
|
124
127
|
Arc::new(AtomicBool::new(false)),
|
|
125
128
|
)
|
|
126
129
|
}
|
|
@@ -131,10 +134,11 @@ impl SessionContext {
|
|
|
131
134
|
live_state: Arc<LiveStateContext>,
|
|
132
135
|
tracked_state: Arc<TrackedStateContext>,
|
|
133
136
|
binary_cas: Arc<BinaryCasContext>,
|
|
134
|
-
|
|
137
|
+
commit_store: Arc<CommitStoreContext>,
|
|
135
138
|
version_ctx: Arc<VersionContext>,
|
|
136
|
-
|
|
139
|
+
catalog_context: Arc<CatalogContext>,
|
|
137
140
|
closed: Arc<AtomicBool>,
|
|
141
|
+
active_transaction: Arc<AtomicBool>,
|
|
138
142
|
) -> Self {
|
|
139
143
|
Self {
|
|
140
144
|
mode,
|
|
@@ -142,10 +146,11 @@ impl SessionContext {
|
|
|
142
146
|
live_state,
|
|
143
147
|
tracked_state,
|
|
144
148
|
binary_cas,
|
|
145
|
-
|
|
149
|
+
commit_store,
|
|
146
150
|
version_ctx,
|
|
147
|
-
|
|
151
|
+
catalog_context,
|
|
148
152
|
closed,
|
|
153
|
+
active_transaction,
|
|
149
154
|
}
|
|
150
155
|
}
|
|
151
156
|
|
|
@@ -164,6 +169,10 @@ impl SessionContext {
|
|
|
164
169
|
Arc::clone(&self.closed)
|
|
165
170
|
}
|
|
166
171
|
|
|
172
|
+
pub(crate) fn active_transaction_flag(&self) -> Arc<AtomicBool> {
|
|
173
|
+
Arc::clone(&self.active_transaction)
|
|
174
|
+
}
|
|
175
|
+
|
|
167
176
|
pub(crate) fn ensure_open(&self) -> Result<(), LixError> {
|
|
168
177
|
if self.is_closed() {
|
|
169
178
|
return Err(closed_error());
|
|
@@ -256,18 +265,14 @@ impl SessionContext {
|
|
|
256
265
|
})?
|
|
257
266
|
.to_string();
|
|
258
267
|
|
|
259
|
-
let
|
|
260
|
-
|
|
261
|
-
.
|
|
262
|
-
|
|
268
|
+
let version_ref = self.version_ctx.ref_reader(&mut *reader);
|
|
269
|
+
VersionLifecycle::new(&version_ref)
|
|
270
|
+
.require_existing_ref(
|
|
271
|
+
&version_id,
|
|
272
|
+
VersionOperation::LoadWorkspaceSelector,
|
|
273
|
+
VersionReferenceRole::WorkspaceSelector,
|
|
274
|
+
)
|
|
263
275
|
.await?;
|
|
264
|
-
if head.is_none() {
|
|
265
|
-
return Err(LixError::version_not_found(
|
|
266
|
-
version_id,
|
|
267
|
-
"load_workspace_version_id",
|
|
268
|
-
"workspace_selector",
|
|
269
|
-
));
|
|
270
|
-
}
|
|
271
276
|
|
|
272
277
|
Ok(version_id)
|
|
273
278
|
}
|
|
@@ -279,15 +284,25 @@ impl SessionContext {
|
|
|
279
284
|
) -> Pin<Box<dyn Future<Output = Result<T, LixError>> + 'tx>>,
|
|
280
285
|
{
|
|
281
286
|
self.ensure_open()?;
|
|
287
|
+
let _transaction_guard = self.reserve_session_transaction()?;
|
|
288
|
+
self.with_write_transaction_reserved(f).await
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
pub(crate) async fn with_write_transaction_reserved<T, F>(&self, f: F) -> Result<T, LixError>
|
|
292
|
+
where
|
|
293
|
+
F: for<'tx> FnOnce(
|
|
294
|
+
&'tx mut Transaction,
|
|
295
|
+
) -> Pin<Box<dyn Future<Output = Result<T, LixError>> + 'tx>>,
|
|
296
|
+
{
|
|
282
297
|
let opened = open_transaction(
|
|
283
298
|
&self.mode,
|
|
284
299
|
self.storage.clone(),
|
|
285
300
|
Arc::clone(&self.live_state),
|
|
286
301
|
Arc::clone(&self.tracked_state),
|
|
287
302
|
Arc::clone(&self.binary_cas),
|
|
288
|
-
Arc::clone(&self.
|
|
303
|
+
Arc::clone(&self.commit_store),
|
|
289
304
|
Arc::clone(&self.version_ctx),
|
|
290
|
-
Arc::clone(&self.
|
|
305
|
+
Arc::clone(&self.catalog_context),
|
|
291
306
|
)
|
|
292
307
|
.await?;
|
|
293
308
|
let mut transaction = opened.transaction;
|
|
@@ -304,6 +319,19 @@ impl SessionContext {
|
|
|
304
319
|
}
|
|
305
320
|
}
|
|
306
321
|
}
|
|
322
|
+
|
|
323
|
+
pub(super) fn reserve_session_transaction(&self) -> Result<SessionTransactionGuard, LixError> {
|
|
324
|
+
let active_transaction = self.active_transaction_flag();
|
|
325
|
+
if active_transaction
|
|
326
|
+
.compare_exchange(false, true, Ordering::SeqCst, Ordering::SeqCst)
|
|
327
|
+
.is_err()
|
|
328
|
+
{
|
|
329
|
+
return Err(transaction_state_error(
|
|
330
|
+
"Lix handle has an active transaction; use the transaction handle for reads and writes until it is committed or rolled back",
|
|
331
|
+
));
|
|
332
|
+
}
|
|
333
|
+
Ok(SessionTransactionGuard { active_transaction })
|
|
334
|
+
}
|
|
307
335
|
}
|
|
308
336
|
|
|
309
337
|
fn closed_error() -> LixError {
|
|
@@ -311,6 +339,16 @@ fn closed_error() -> LixError {
|
|
|
311
339
|
.with_hint("Open a new Lix handle before calling this method.")
|
|
312
340
|
}
|
|
313
341
|
|
|
342
|
+
pub(super) struct SessionTransactionGuard {
|
|
343
|
+
active_transaction: Arc<AtomicBool>,
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
impl Drop for SessionTransactionGuard {
|
|
347
|
+
fn drop(&mut self) {
|
|
348
|
+
self.active_transaction.store(false, Ordering::SeqCst);
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
|
|
314
352
|
/// Read-only SQL execution context derived from a session.
|
|
315
353
|
///
|
|
316
354
|
/// Write statements re-plan against `Transaction`; this context intentionally
|
|
@@ -321,7 +359,7 @@ pub(super) struct SessionSqlExecutionContext<'a> {
|
|
|
321
359
|
ScopedStorageReader<Box<dyn StorageReadTransaction + Send + Sync + 'static>>,
|
|
322
360
|
pub(super) live_state: Arc<LiveStateContext>,
|
|
323
361
|
pub(super) binary_cas: Arc<BinaryCasContext>,
|
|
324
|
-
pub(super)
|
|
362
|
+
pub(super) commit_store: Arc<CommitStoreContext>,
|
|
325
363
|
pub(super) version_ctx: Arc<VersionContext>,
|
|
326
364
|
pub(super) visible_schemas: Vec<JsonValue>,
|
|
327
365
|
pub(super) functions: FunctionProviderHandle,
|
|
@@ -336,16 +374,16 @@ impl SqlExecutionContext for SessionSqlExecutionContext<'_> {
|
|
|
336
374
|
Arc::new(self.live_state.reader(self.read_store.clone())) as Arc<dyn LiveStateReader>
|
|
337
375
|
}
|
|
338
376
|
|
|
339
|
-
fn
|
|
377
|
+
fn commit_store_query_source(&self) -> SqlCommitStoreQuerySource {
|
|
340
378
|
let read_scope = StorageReadScope::new(self.read_store.clone());
|
|
341
|
-
|
|
342
|
-
|
|
379
|
+
CommitStoreQuerySource {
|
|
380
|
+
commit_store_reader: Arc::new(self.commit_store.reader(read_scope.store())),
|
|
343
381
|
json_reader: JsonStoreContext::new().reader(read_scope.store()),
|
|
344
382
|
}
|
|
345
383
|
}
|
|
346
384
|
|
|
347
385
|
fn commit_graph(&self) -> Box<dyn CommitGraphReader> {
|
|
348
|
-
Box::new(CommitGraphContext::new(
|
|
386
|
+
Box::new(CommitGraphContext::new().reader(self.read_store.clone()))
|
|
349
387
|
}
|
|
350
388
|
|
|
351
389
|
fn version_ref(&self) -> Arc<dyn VersionRefReader> {
|
|
@@ -1,5 +1,8 @@
|
|
|
1
|
-
use crate::transaction::types::{
|
|
2
|
-
use crate::version::{
|
|
1
|
+
use crate::transaction::types::{TransactionWrite, TransactionWriteMode};
|
|
2
|
+
use crate::version::{
|
|
3
|
+
version_descriptor_stage_row, version_ref_stage_row, VersionLifecycle, VersionOperation,
|
|
4
|
+
VersionReferenceRole,
|
|
5
|
+
};
|
|
3
6
|
use crate::LixError;
|
|
4
7
|
|
|
5
8
|
use super::context::SessionContext;
|
|
@@ -7,7 +10,7 @@ use super::context::SessionContext;
|
|
|
7
10
|
/// Options for creating a new version from the session's active version.
|
|
8
11
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
|
9
12
|
pub struct CreateVersionOptions {
|
|
10
|
-
/// Optional caller-provided version id. If omitted,
|
|
13
|
+
/// Optional caller-provided version id. If omitted, engine generates one.
|
|
11
14
|
pub id: Option<String>,
|
|
12
15
|
/// User-facing version name.
|
|
13
16
|
pub name: String,
|
|
@@ -41,25 +44,30 @@ impl SessionContext {
|
|
|
41
44
|
.id
|
|
42
45
|
.unwrap_or_else(|| transaction.functions().call_uuid_v7());
|
|
43
46
|
let source_head = if let Some(from_commit_id) = options.from_commit_id {
|
|
47
|
+
let mut commit_graph = transaction.commit_graph_reader();
|
|
48
|
+
VersionLifecycle::require_existing_commit(
|
|
49
|
+
&mut commit_graph,
|
|
50
|
+
&from_commit_id,
|
|
51
|
+
VersionOperation::CreateVersion,
|
|
52
|
+
VersionReferenceRole::CommitSource,
|
|
53
|
+
)
|
|
54
|
+
.await?;
|
|
44
55
|
from_commit_id
|
|
45
56
|
} else {
|
|
46
57
|
let active_version_id = transaction.active_version_id().to_string();
|
|
47
58
|
let reader = transaction.version_ref_reader();
|
|
48
|
-
reader
|
|
49
|
-
.
|
|
59
|
+
VersionLifecycle::new(&reader)
|
|
60
|
+
.require_existing_commit_id(
|
|
61
|
+
&active_version_id,
|
|
62
|
+
VersionOperation::CreateVersion,
|
|
63
|
+
VersionReferenceRole::Source,
|
|
64
|
+
)
|
|
50
65
|
.await?
|
|
51
|
-
.ok_or_else(|| {
|
|
52
|
-
LixError::version_not_found(
|
|
53
|
-
active_version_id.clone(),
|
|
54
|
-
"create_version",
|
|
55
|
-
"source",
|
|
56
|
-
)
|
|
57
|
-
})?
|
|
58
66
|
};
|
|
59
67
|
|
|
60
68
|
transaction
|
|
61
|
-
.stage_write(
|
|
62
|
-
mode:
|
|
69
|
+
.stage_write(TransactionWrite::Rows {
|
|
70
|
+
mode: TransactionWriteMode::Insert,
|
|
63
71
|
rows: vec![
|
|
64
72
|
version_descriptor_stage_row(&version_id, &options.name, false),
|
|
65
73
|
version_ref_stage_row(&version_id, &source_head),
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
use std::sync::Arc;
|
|
2
2
|
|
|
3
3
|
use crate::functions::FunctionContext;
|
|
4
|
-
use crate::json_store::JsonStoreContext;
|
|
5
4
|
use crate::sql2;
|
|
6
5
|
use crate::storage::{StorageReadScope, StorageWriteSet};
|
|
7
6
|
use crate::{LixError, LixNotice, SqlQueryResult, Value};
|
|
8
7
|
|
|
9
8
|
use super::context::{SessionContext, SessionSqlExecutionContext};
|
|
9
|
+
use super::transaction::SessionTransaction;
|
|
10
10
|
|
|
11
|
-
/// Result of executing one SQL statement through
|
|
11
|
+
/// Result of executing one SQL statement through engine.
|
|
12
12
|
///
|
|
13
13
|
/// Column names live once at the result-set level. Individual rows only own
|
|
14
14
|
/// values, which keeps the public API row-oriented without copying schema
|
|
@@ -251,7 +251,7 @@ fn value_type_error(expected: &str, actual: &Value) -> LixError {
|
|
|
251
251
|
)
|
|
252
252
|
}
|
|
253
253
|
|
|
254
|
-
///
|
|
254
|
+
/// Zero-copy row view with access to the result-set column names.
|
|
255
255
|
///
|
|
256
256
|
/// This is the ergonomic path for callers that want `row.get("column")`
|
|
257
257
|
/// without storing column metadata on every owned row.
|
|
@@ -291,29 +291,40 @@ impl SessionContext {
|
|
|
291
291
|
/// Executes one DataFusion SQL statement against this Lix session.
|
|
292
292
|
///
|
|
293
293
|
/// The SQL dialect is DataFusion SQL, not SQLite SQL. Positional
|
|
294
|
-
/// placeholders use `$1`, `$2`, and so on. SQLite-specific catalog tables
|
|
294
|
+
/// placeholders use `?` or `$1`, `$2`, and so on. SQLite-specific catalog tables
|
|
295
295
|
/// and transaction statements such as `sqlite_master`, `BEGIN`, and
|
|
296
296
|
/// `COMMIT` are not part of this contract; use `information_schema` for
|
|
297
297
|
/// catalog inspection. Lix owns transaction boundaries for each statement.
|
|
298
298
|
pub async fn execute(&self, sql: &str, params: &[Value]) -> Result<ExecuteResult, LixError> {
|
|
299
299
|
self.ensure_open()?;
|
|
300
|
-
let
|
|
300
|
+
let _transaction_guard = self.reserve_session_transaction()?;
|
|
301
|
+
let statement = sql2::parse_statement(sql)?;
|
|
302
|
+
let kind = sql2::classify_datafusion_statement(&statement);
|
|
301
303
|
if kind == sql2::SqlStatementKind::Write {
|
|
302
|
-
let
|
|
304
|
+
let sql_for_error = sql.to_string();
|
|
303
305
|
let params = params.to_vec();
|
|
304
306
|
return self
|
|
305
|
-
.
|
|
307
|
+
.with_write_transaction_reserved(|transaction| {
|
|
306
308
|
Box::pin(async move {
|
|
307
309
|
// Re-plan against the transaction-backed write
|
|
308
310
|
// session so provider hooks read and stage through the
|
|
309
311
|
// transaction-owned SQL write context.
|
|
310
|
-
let tx_plan =
|
|
312
|
+
let tx_plan =
|
|
313
|
+
sql2::create_write_logical_plan_from_parsed(transaction, statement)
|
|
314
|
+
.await?;
|
|
311
315
|
let result = sql2::execute_logical_plan(tx_plan, ¶ms).await?;
|
|
312
316
|
let affected_rows = affected_rows_from_query_result(result)?;
|
|
313
317
|
Ok(ExecuteResult::from_rows_affected(affected_rows))
|
|
314
318
|
})
|
|
315
319
|
})
|
|
316
|
-
.await
|
|
320
|
+
.await
|
|
321
|
+
.map_err(|error| normalize_sql_surface_error(error, &sql_for_error));
|
|
322
|
+
}
|
|
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
|
+
));
|
|
317
328
|
}
|
|
318
329
|
|
|
319
330
|
let read_scope = StorageReadScope::new(self.storage.begin_read_transaction().await?);
|
|
@@ -325,21 +336,21 @@ impl SessionContext {
|
|
|
325
336
|
let functions = runtime_functions.provider();
|
|
326
337
|
let active_version_id = self.active_version_id_from_reader(&mut read_store).await?;
|
|
327
338
|
let visible_schemas = self
|
|
328
|
-
.
|
|
329
|
-
.
|
|
339
|
+
.catalog_context
|
|
340
|
+
.schema_jsons_for_sql_read_planning(live_state.as_ref(), &active_version_id)
|
|
330
341
|
.await?;
|
|
331
342
|
let ctx = SessionSqlExecutionContext {
|
|
332
343
|
active_version_id: &active_version_id,
|
|
333
344
|
read_store,
|
|
334
345
|
live_state: Arc::clone(&self.live_state),
|
|
335
346
|
binary_cas: Arc::clone(&self.binary_cas),
|
|
336
|
-
|
|
347
|
+
commit_store: Arc::clone(&self.commit_store),
|
|
337
348
|
version_ctx: Arc::clone(&self.version_ctx),
|
|
338
349
|
visible_schemas,
|
|
339
350
|
functions: functions.clone(),
|
|
340
351
|
};
|
|
341
352
|
|
|
342
|
-
let plan = sql2::
|
|
353
|
+
let plan = sql2::create_logical_plan_from_parsed(&ctx, sql, statement).await?;
|
|
343
354
|
let result = sql2::execute_logical_plan(plan, params).await?;
|
|
344
355
|
drop(ctx);
|
|
345
356
|
drop(live_state);
|
|
@@ -352,7 +363,7 @@ impl SessionContext {
|
|
|
352
363
|
}
|
|
353
364
|
Err(error) => {
|
|
354
365
|
let _ = read_scope.rollback().await;
|
|
355
|
-
return Err(error);
|
|
366
|
+
return Err(normalize_sql_surface_error(error, sql));
|
|
356
367
|
}
|
|
357
368
|
};
|
|
358
369
|
self.persist_runtime_functions_if_needed(&runtime_functions)
|
|
@@ -370,23 +381,106 @@ impl SessionContext {
|
|
|
370
381
|
&self,
|
|
371
382
|
runtime_functions: &FunctionContext,
|
|
372
383
|
) -> Result<(), LixError> {
|
|
373
|
-
let mut transaction = self.storage.begin_write_transaction().await?;
|
|
374
384
|
let mut writes = StorageWriteSet::new();
|
|
375
|
-
let mut json_writer = JsonStoreContext::new().writer();
|
|
376
385
|
runtime_functions
|
|
377
|
-
.stage_persist_if_needed(
|
|
378
|
-
&mut self.live_state.writer(transaction.as_mut()),
|
|
379
|
-
&mut writes,
|
|
380
|
-
&mut json_writer,
|
|
381
|
-
)
|
|
386
|
+
.stage_persist_if_needed(&mut writes)
|
|
382
387
|
.await?;
|
|
383
|
-
if
|
|
384
|
-
|
|
388
|
+
if writes.is_empty() {
|
|
389
|
+
return Ok(());
|
|
385
390
|
}
|
|
391
|
+
let mut transaction = self.storage.begin_write_transaction().await?;
|
|
392
|
+
writes.apply(&mut transaction.as_mut()).await?;
|
|
386
393
|
transaction.commit().await
|
|
387
394
|
}
|
|
388
395
|
}
|
|
389
396
|
|
|
397
|
+
impl SessionTransaction {
|
|
398
|
+
/// Executes one SQL statement inside this transaction.
|
|
399
|
+
///
|
|
400
|
+
/// Write statements are staged until `commit()`. Read statements use the
|
|
401
|
+
/// transaction overlay, so they can observe writes staged by earlier calls
|
|
402
|
+
/// on this transaction handle.
|
|
403
|
+
pub async fn execute(
|
|
404
|
+
&mut self,
|
|
405
|
+
sql: &str,
|
|
406
|
+
params: &[Value],
|
|
407
|
+
) -> Result<ExecuteResult, LixError> {
|
|
408
|
+
let statement = sql2::parse_statement(sql)?;
|
|
409
|
+
let kind = sql2::classify_datafusion_statement(&statement);
|
|
410
|
+
let transaction = self.transaction_mut()?;
|
|
411
|
+
match kind {
|
|
412
|
+
sql2::SqlStatementKind::Write => {
|
|
413
|
+
execute_transaction_write(transaction, statement, params)
|
|
414
|
+
.await
|
|
415
|
+
.map_err(|error| normalize_sql_surface_error(error, sql))
|
|
416
|
+
}
|
|
417
|
+
sql2::SqlStatementKind::Read => {
|
|
418
|
+
let plan = sql2::create_transaction_read_logical_plan_from_parsed(
|
|
419
|
+
transaction,
|
|
420
|
+
sql,
|
|
421
|
+
statement,
|
|
422
|
+
)
|
|
423
|
+
.await
|
|
424
|
+
.map_err(|error| normalize_sql_surface_error(error, sql))?;
|
|
425
|
+
let result = sql2::execute_logical_plan(plan, params)
|
|
426
|
+
.await
|
|
427
|
+
.map_err(|error| normalize_sql_surface_error(error, sql))?;
|
|
428
|
+
Ok(ExecuteResult::from_sql_query_result(result))
|
|
429
|
+
}
|
|
430
|
+
sql2::SqlStatementKind::Other => Err(LixError::new(
|
|
431
|
+
LixError::CODE_UNSUPPORTED_SQL,
|
|
432
|
+
"SQL statement is not supported by Lix SQL",
|
|
433
|
+
)),
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
async fn execute_transaction_write(
|
|
439
|
+
transaction: &mut crate::transaction::Transaction,
|
|
440
|
+
statement: datafusion::sql::parser::Statement,
|
|
441
|
+
params: &[Value],
|
|
442
|
+
) -> Result<ExecuteResult, LixError> {
|
|
443
|
+
let tx_plan = sql2::create_write_logical_plan_from_parsed(transaction, statement).await?;
|
|
444
|
+
let result = sql2::execute_logical_plan(tx_plan, params).await?;
|
|
445
|
+
let affected_rows = affected_rows_from_query_result(result)?;
|
|
446
|
+
Ok(ExecuteResult::from_rows_affected(affected_rows))
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
fn normalize_sql_surface_error(error: LixError, sql: &str) -> LixError {
|
|
450
|
+
if error.code.starts_with("LIX_ERROR_PATH_") && sql_uses_public_filesystem_path_surface(sql) {
|
|
451
|
+
return LixError {
|
|
452
|
+
code: LixError::CODE_INVALID_PARAM.to_string(),
|
|
453
|
+
..error
|
|
454
|
+
};
|
|
455
|
+
}
|
|
456
|
+
if error.code == LixError::CODE_INVALID_JSON_PATH
|
|
457
|
+
&& error
|
|
458
|
+
.message
|
|
459
|
+
.to_ascii_lowercase()
|
|
460
|
+
.contains("uses variadic path segments")
|
|
461
|
+
{
|
|
462
|
+
return LixError {
|
|
463
|
+
code: LixError::CODE_INVALID_PARAM.to_string(),
|
|
464
|
+
..error
|
|
465
|
+
};
|
|
466
|
+
}
|
|
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
|
+
error
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
fn sql_uses_public_filesystem_path_surface(sql: &str) -> bool {
|
|
480
|
+
let lower = sql.to_ascii_lowercase();
|
|
481
|
+
(lower.contains("lix_file") || lower.contains("lix_directory")) && lower.contains("path")
|
|
482
|
+
}
|
|
483
|
+
|
|
390
484
|
fn affected_rows_from_query_result(result: SqlQueryResult) -> Result<u64, LixError> {
|
|
391
485
|
let Some(first_row) = result.rows.first() else {
|
|
392
486
|
return Ok(0);
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
use crate::tracked_state::TrackedStateMergePlan;
|
|
2
|
-
use crate::transaction::types::
|
|
2
|
+
use crate::transaction::types::TransactionAdoptedChange;
|
|
3
3
|
|
|
4
4
|
pub(crate) fn adopted_changes_from_merge_plan(
|
|
5
5
|
plan: &TrackedStateMergePlan,
|
|
6
6
|
target_version_id: &str,
|
|
7
|
-
) -> Vec<
|
|
7
|
+
) -> Vec<TransactionAdoptedChange> {
|
|
8
8
|
plan.patches
|
|
9
9
|
.iter()
|
|
10
10
|
.map(|patch| stage_adopted_change_from_patch(patch, target_version_id))
|
|
@@ -14,8 +14,8 @@ pub(crate) fn adopted_changes_from_merge_plan(
|
|
|
14
14
|
fn stage_adopted_change_from_patch(
|
|
15
15
|
patch: &crate::tracked_state::TrackedStateMergePatch,
|
|
16
16
|
target_version_id: &str,
|
|
17
|
-
) ->
|
|
18
|
-
|
|
17
|
+
) -> TransactionAdoptedChange {
|
|
18
|
+
TransactionAdoptedChange {
|
|
19
19
|
version_id: target_version_id.to_string(),
|
|
20
20
|
change_id: patch.change_id().to_string(),
|
|
21
21
|
projected_row: patch.projected_row().clone(),
|
|
@@ -2,12 +2,13 @@ use crate::tracked_state::{
|
|
|
2
2
|
TrackedStateDiffEntry, TrackedStateDiffKind, TrackedStateMergeConflict, TrackedStateMergePlan,
|
|
3
3
|
};
|
|
4
4
|
use crate::LixError;
|
|
5
|
+
use serde_json::Value as JsonValue;
|
|
5
6
|
|
|
6
7
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
|
7
8
|
pub(crate) struct MergeConflict {
|
|
8
9
|
pub(crate) kind: MergeConflictKind,
|
|
9
10
|
pub(crate) schema_key: String,
|
|
10
|
-
pub(crate) entity_id:
|
|
11
|
+
pub(crate) entity_id: JsonValue,
|
|
11
12
|
pub(crate) file_id: Option<String>,
|
|
12
13
|
pub(crate) target: MergeConflictSide,
|
|
13
14
|
pub(crate) source: MergeConflictSide,
|
|
@@ -42,7 +43,7 @@ fn conflict_from_tracked(conflict: &TrackedStateMergeConflict) -> Result<MergeCo
|
|
|
42
43
|
Ok(MergeConflict {
|
|
43
44
|
kind: MergeConflictKind::SameEntityChanged,
|
|
44
45
|
schema_key: conflict.identity.schema_key.clone(),
|
|
45
|
-
entity_id: conflict.identity.entity_id.
|
|
46
|
+
entity_id: conflict.identity.entity_id.as_json_array_value()?,
|
|
46
47
|
file_id: conflict.identity.file_id.clone(),
|
|
47
48
|
target: conflict_side_from_diff_entry(&conflict.target),
|
|
48
49
|
source: conflict_side_from_diff_entry(&conflict.source),
|