Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 8 additions & 8 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ members = ["crates/*"]
resolver = "2"

[workspace.package]
version = "0.6.8"
version = "0.7.0"
edition = "2024"
rust-version = "1.92"
authors = ["init4"]
Expand Down Expand Up @@ -35,13 +35,13 @@ incremental = false

[workspace.dependencies]
# internal
signet-hot = { version = "0.6.8", path = "./crates/hot" }
signet-hot-mdbx = { version = "0.6.8", path = "./crates/hot-mdbx" }
signet-cold = { version = "0.6.8", path = "./crates/cold" }
signet-cold-mdbx = { version = "0.6.8", path = "./crates/cold-mdbx" }
signet-cold-sql = { version = "0.6.8", path = "./crates/cold-sql" }
signet-storage = { version = "0.6.8", path = "./crates/storage" }
signet-storage-types = { version = "0.6.8", path = "./crates/types" }
signet-hot = { version = "0.7.0", path = "./crates/hot" }
signet-hot-mdbx = { version = "0.7.0", path = "./crates/hot-mdbx" }
signet-cold = { version = "0.7.0", path = "./crates/cold" }
signet-cold-mdbx = { version = "0.7.0", path = "./crates/cold-mdbx" }
signet-cold-sql = { version = "0.7.0", path = "./crates/cold-sql" }
signet-storage = { version = "0.7.0", path = "./crates/storage" }
signet-storage-types = { version = "0.7.0", path = "./crates/types" }

# External, in-house
signet-libmdbx = { version = "0.8.0" }
Expand Down
289 changes: 219 additions & 70 deletions crates/cold-sql/src/backend.rs

Large diffs are not rendered by default.

9 changes: 8 additions & 1 deletion crates/cold-sql/src/columns.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,6 @@ pub(crate) const COL_FROM_ADDRESS: &str = "from_address";
// ── receipt columns ─────────────────────────────────────────────────────────
pub(crate) const COL_SUCCESS: &str = "success";
pub(crate) const COL_CUMULATIVE_GAS_USED: &str = "cumulative_gas_used";
pub(crate) const COL_FIRST_LOG_INDEX: &str = "first_log_index";

// ── log columns ─────────────────────────────────────────────────────────────
pub(crate) const COL_ADDRESS: &str = "address";
Expand Down Expand Up @@ -84,3 +83,11 @@ pub(crate) const COL_PRIOR_GAS: &str = "prior_gas";
pub(crate) const COL_BLOCK_LOG_INDEX: &str = "block_log_index";
pub(crate) const COL_BLOCK_TIMESTAMP: &str = "block_timestamp";
pub(crate) const COL_MAX_BN: &str = "max_bn";

// ── get_receipt combined query aliases ──────────────────────────────────────
pub(crate) const COL_R_TX_TYPE: &str = "r_tx_type";
pub(crate) const COL_R_SUCCESS: &str = "r_success";
pub(crate) const COL_R_CUMULATIVE_GAS_USED: &str = "r_cumulative_gas_used";
pub(crate) const COL_R_FIRST_LOG_INDEX: &str = "r_first_log_index";
pub(crate) const COL_R_TX_HASH: &str = "r_tx_hash";
pub(crate) const COL_R_FROM_ADDRESS: &str = "r_from_address";
46 changes: 39 additions & 7 deletions crates/cold-sql/src/connector.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
//! SQL cold storage connector.

use crate::{SqlColdBackend, SqlColdError};
use crate::{PoolOverrides, SqlColdBackend, SqlColdError};
use signet_cold::ColdConnect;
use std::time::Duration;

/// Errors that can occur when initializing SQL connectors.
#[derive(Debug, thiserror::Error)]
Expand All @@ -21,42 +22,72 @@ pub enum SqlConnectorError {
/// - URLs starting with `postgres://` or `postgresql://` use PostgreSQL
/// - URLs starting with `sqlite:` use SQLite
///
/// Pool settings use backend-specific defaults. Use
/// [`with_max_connections`] and [`with_acquire_timeout`] to override.
///
/// [`with_max_connections`]: Self::with_max_connections
/// [`with_acquire_timeout`]: Self::with_acquire_timeout
///
/// # Example
///
/// ```ignore
/// use signet_cold_sql::SqlConnector;
///
/// // PostgreSQL
/// let pg = SqlConnector::new("postgres://localhost/signet");
/// // PostgreSQL with custom pool size
/// let pg = SqlConnector::new("postgres://localhost/signet")
/// .with_max_connections(20);
/// let backend = pg.connect().await?;
///
/// // SQLite
/// // SQLite (defaults)
/// let sqlite = SqlConnector::new("sqlite::memory:");
/// let backend = sqlite.connect().await?;
/// ```
#[cfg(any(feature = "sqlite", feature = "postgres"))]
#[derive(Debug, Clone)]
pub struct SqlConnector {
url: String,
overrides: PoolOverrides,
}

#[cfg(any(feature = "sqlite", feature = "postgres"))]
impl SqlConnector {
/// Create a new SQL connector.
///
/// The database type is detected from the URL prefix.
/// The database type is detected from the URL prefix. Pool settings
/// use backend-specific defaults. Use [`with_max_connections`] and
/// [`with_acquire_timeout`] to override.
///
/// [`with_max_connections`]: Self::with_max_connections
/// [`with_acquire_timeout`]: Self::with_acquire_timeout
pub fn new(url: impl Into<String>) -> Self {
Self { url: url.into() }
Self { url: url.into(), overrides: PoolOverrides::default() }
}

/// Get a reference to the connection URL.
pub fn url(&self) -> &str {
&self.url
}

/// Override the maximum number of pool connections.
///
/// Default: 1 for SQLite, 10 for PostgreSQL.
pub const fn with_max_connections(mut self, n: u32) -> Self {
self.overrides.max_connections = Some(n);
self
}

/// Override the connection acquire timeout.
///
/// Default: 5 seconds for all backends.
pub const fn with_acquire_timeout(mut self, timeout: Duration) -> Self {
self.overrides.acquire_timeout = Some(timeout);
self
}

/// Create a connector from environment variables.
///
/// Reads the SQL URL from the specified environment variable.
/// Uses default pool settings.
///
/// # Example
///
Expand All @@ -78,6 +109,7 @@ impl ColdConnect for SqlConnector {

fn connect(&self) -> impl std::future::Future<Output = Result<Self::Cold, Self::Error>> + Send {
let url = self.url.clone();
async move { SqlColdBackend::connect(&url).await }
let overrides = self.overrides;
async move { SqlColdBackend::connect_with(&url, overrides).await }
}
}
26 changes: 21 additions & 5 deletions crates/cold-sql/src/convert.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,25 @@ use signet_storage_types::Receipt;
// ============================================================================

/// Convert u64 to i64 for SQL storage.
pub(crate) const fn to_i64(v: u64) -> i64 {
///
/// # Panics
///
/// Debug-asserts that `v` fits in an `i64`. All block numbers, gas
/// values, and indices in Ethereum are well below `i64::MAX`; a value
/// that overflows indicates data corruption.
pub(crate) fn to_i64(v: u64) -> i64 {
debug_assert!(v <= i64::MAX as u64, "u64 value {v} overflows i64");
v as i64
}

/// Convert i64 from SQL back to u64.
pub(crate) const fn from_i64(v: i64) -> u64 {
///
/// # Panics
///
/// Debug-asserts that `v` is non-negative. Negative values from the
/// database indicate data corruption.
pub(crate) fn from_i64(v: i64) -> u64 {
debug_assert!(v >= 0, "negative i64 value {v} cannot represent u64");
v as u64
}

Expand Down Expand Up @@ -208,13 +221,16 @@ pub(crate) fn decode_b256_vec(data: &[u8]) -> Result<Vec<B256>, SqlColdError> {

/// Reconstruct a [`Receipt`] from primitive column values and decoded logs.
pub(crate) fn build_receipt(
tx_type: i16,
tx_type_raw: i32,
success: bool,
cumulative_gas_used: i64,
logs: Vec<Log>,
) -> Result<Receipt, SqlColdError> {
let tx_type = TxType::try_from(tx_type as u8)
.map_err(|_| SqlColdError::Convert(format!("invalid tx_type: {tx_type}")))?;
let tx_type_u8: u8 = tx_type_raw
.try_into()
.map_err(|_| SqlColdError::Convert(format!("tx_type out of u8 range: {tx_type_raw}")))?;
let tx_type = TxType::try_from(tx_type_u8)
.map_err(|_| SqlColdError::Convert(format!("invalid tx_type: {tx_type_u8}")))?;
Ok(Receipt {
tx_type,
inner: AlloyReceipt {
Expand Down
2 changes: 1 addition & 1 deletion crates/cold-sql/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ mod convert;
#[cfg(any(feature = "sqlite", feature = "postgres"))]
mod backend;
#[cfg(any(feature = "sqlite", feature = "postgres"))]
pub use backend::SqlColdBackend;
pub use backend::{PoolOverrides, SqlColdBackend};

#[cfg(any(feature = "sqlite", feature = "postgres"))]
mod connector;
Expand Down
Loading