Skip to main content

khive_db/
sql_bridge.rs

1//! SqlAccess bridge: connects `ConnectionPool` to `khive_storage::SqlAccess`.
2//!
3//! Two modes:
4//! - **File-backed**: ordinary reads check out pooled readers per operation.
5//!   The only standalone-reader exception is an explicitly admitted multi-call
6//!   deferred read transaction; standalone writer handles remain capped at one.
7//!   Cross-statement write atomicity goes through `atomic_unit`, which drives a
8//!   single registered raw transaction span rather than a caller-held per-tx
9//!   connection.
10//! - **Memory**: Uses pool-backed approach (acquire pool connection per-query inside `spawn_blocking`).
11
12#[path = "sql_bridge/write_errors.rs"]
13mod write_errors;
14
15#[path = "sql_bridge/manual_atomic.rs"]
16mod manual_atomic;
17use manual_atomic::run_manual_atomic_unit;
18#[path = "sql_bridge/standalone_admission.rs"]
19mod standalone_admission;
20use standalone_admission::{
21    acquire_standalone_lease, acquire_unit_lease, admit_standalone_operation, StandaloneWriteError,
22};
23mod standalone_batch;
24#[cfg(test)]
25use standalone_batch::BatchPoisonReason;
26use standalone_batch::{
27    run_standalone_batch, run_standalone_script, run_standalone_statement,
28    run_standalone_top_level, BatchFailure, PoisonedBatchError,
29};
30
31use std::any::Any;
32use std::sync::atomic::{AtomicU64, Ordering};
33use std::sync::Arc;
34
35use async_trait::async_trait;
36
37use khive_storage::error::StorageError;
38use khive_storage::types::{PageRequest, SqlColumn, SqlRow, SqlStatement, SqlValue};
39use khive_storage::{AtomicUnitOp, StorageCapability, TopLevelMaintenance};
40use tokio::sync::{OwnedSemaphorePermit, Semaphore};
41
42use crate::error::SqliteError;
43use crate::pool::{ConnectionPool, SharedReaderTransactionGuard, StandaloneReaderPurpose};
44
45// =============================================================================
46// Shared helpers
47// =============================================================================
48
49mod batch_exec;
50use batch_exec::{
51    cached_read_transaction_control, execute_prepared_batch, next_sqlite_token,
52    reject_transaction_control_statements, skip_sqlite_empty_prefix, transaction_control_head,
53    CachedReadTransactionControl,
54};
55
56mod rows;
57
58pub(crate) use rows::bind_params;
59use rows::{
60    prepare_batch_statements, prepare_cached_sql_statement, prepare_sql_statement, row_to_sql_row,
61    AtomicEventRows,
62};
63#[cfg(test)]
64use rows::{COUNTED_EVENT_INSERT_LABELS, ROW_CONVERSIONS};
65
66#[cfg(test)]
67#[path = "atomic_event_usage_tests.rs"]
68mod atomic_event_usage_tests;
69
70mod query_execution;
71use query_execution::{
72    execute_prepared_query, execute_prepared_query_page, execute_prepared_query_row, execute_query,
73    execute_query_page, execute_query_row, map_rusqlite_err, map_write_boundary_err, map_write_err,
74    prepare_bound_statement, settle_pooled_call,
75};
76
77#[cfg(test)]
78#[path = "sql_bridge/sync_executor_tests.rs"]
79mod sync_executor_tests;
80
81/// Execute trusted statements inside a caller-owned active transaction.
82///
83/// The caller owns writer admission, commit, rollback, and connection settlement.
84/// This function starts no transaction or savepoint, installs no hooks, retries
85/// nothing, and does not account committed events. The returned affected-row sum
86/// is execution evidence, not a commit receipt; zero affected rows is successful.
87///
88/// Explicit transaction-control heads are rejected across the list before
89/// preparation. Each element must contain only one SQL statement. Other prepare
90/// failures may be deferred until execution to allow earlier schema changes, so
91/// an error can follow earlier successful writes. Missing raw parameters bind as
92/// NULL; excess parameters fail binding before that element steps.
93///
94/// This is not a SQL sandbox: PRAGMAs, UDFs, triggers, and virtual tables may have
95/// connection or external effects, and SQLite errors can end the transaction.
96/// The owner must handle rollback and verify settlement on error.
97///
98/// # Errors
99/// Returns [`SqliteError::InvalidData`] for autocommit connections (even with an
100/// empty list) or explicit transaction control. Native prepare, bind, and step
101/// failures are preserved as [`SqliteError::Rusqlite`].
102pub fn execute_statements(
103    conn: &rusqlite::Connection,
104    statements: &[SqlStatement],
105) -> Result<u64, SqliteError> {
106    if conn.is_autocommit() {
107        return Err(SqliteError::InvalidData(
108            "execute_statements requires a caller-owned active transaction".into(),
109        ));
110    }
111    for (index, statement) in statements.iter().enumerate() {
112        if let Some(keyword) = transaction_control_head(&statement.sql) {
113            return Err(SqliteError::InvalidData(format!(
114                "statement at index {index} is transaction control ({keyword}); \
115                 the caller owns the transaction boundary"
116            )));
117        }
118    }
119    let prepared = prepare_batch_statements(conn, statements)?;
120    Ok(execute_prepared_batch(conn, prepared, statements, None)?)
121}
122
123mod reader_capability;
124pub(crate) use reader_capability::reader_capability_admits;
125use reader_capability::{
126    admit_reader_capability_sql, execute_query_interruptibly, execute_query_page_interruptibly,
127    execute_query_row_interruptibly,
128};
129
130mod handles;
131pub(crate) use handles::acquire_in_memory_write_unit;
132use handles::{
133    acquire_handle_slot, acquire_reader_handle_slot, open_standalone_reader_on_blocking,
134    open_standalone_writer_on_blocking, SlotTimeoutClass,
135};
136#[cfg(test)]
137use handles::{open_standalone_on_blocking, open_standalone_reader, open_standalone_writer};
138
139mod sqlite_reader;
140#[cfg(test)]
141use sqlite_reader::CACHED_READ_TRANSACTION_LABEL;
142use sqlite_reader::{
143    execute_standalone_read, open_explicit_read_transaction_handle, SqliteReader, StandaloneHandle,
144};
145
146mod sqlite_writer;
147use sqlite_writer::{execute_top_level_maintenance, SqliteWriter};
148
149mod pool_backed;
150use pool_backed::{run_pool_reader_query, PoolBackedReader, PoolBackedWriter};
151
152mod inline_bridge;
153pub use inline_bridge::SqlBridge;
154#[cfg(test)]
155use inline_bridge::{block_on_sync, InlineWriter};
156
157#[cfg(test)]
158#[path = "sql_bridge_tests.rs"]
159mod tests;
160
161#[cfg(test)]
162#[path = "sql_bridge/direct_busy_tests.rs"]
163mod direct_busy_tests;
164
165#[cfg(test)]
166#[path = "sql_bridge/settlement_hazard_tests.rs"]
167mod settlement_hazard_tests;