• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

stacks-network / stacks-core / 26250451051-1

21 May 2026 08:11PM UTC coverage: 85.585% (-0.1%) from 85.712%
26250451051-1

Pull #7215

github

ec9d4c
web-flow
Merge 9487bf852 into af1280aac
Pull Request #7215: Chore: fix flake in non_blocking_minority_configured_to_favour_...

188844 of 220651 relevant lines covered (85.58%)

18975267.44 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

82.9
/stackslib/src/util_lib/db.rs
1
// Copyright (C) 2013-2020 Blockstack PBC, a public benefit corporation
2
// Copyright (C) 2020 Stacks Open Internet Foundation
3
//
4
// This program is free software: you can redistribute it and/or modify
5
// it under the terms of the GNU General Public License as published by
6
// the Free Software Foundation, either version 3 of the License, or
7
// (at your option) any later version.
8
//
9
// This program is distributed in the hope that it will be useful,
10
// but WITHOUT ANY WARRANTY; without even the implied warranty of
11
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
// GNU General Public License for more details.
13
//
14
// You should have received a copy of the GNU General Public License
15
// along with this program.  If not, see <http://www.gnu.org/licenses/>.
16

17
use std::io::Error as IOError;
18
use std::ops::{Deref, DerefMut};
19
use std::path::{Path, PathBuf};
20
use std::{error, fmt, fs, io};
21

22
use clarity::vm::types::QualifiedContractIdentifier;
23
use rusqlite::types::ToSql;
24
use rusqlite::{
25
    params, Connection, Error as sqlite_error, OpenFlags, OptionalExtension, Params, Row,
26
    Transaction, TransactionBehavior,
27
};
28
use serde_json::Error as serde_error;
29
use stacks_common::types::chainstate::{SortitionId, StacksAddress, StacksBlockId, TrieHash};
30
use stacks_common::types::sqlite::NO_PARAMS;
31
use stacks_common::types::Address;
32
use stacks_common::util::db::update_lock_table;
33
use stacks_common::util::hash::to_hex;
34
use stacks_common::util::secp256k1::{Secp256k1PrivateKey, Secp256k1PublicKey};
35

36
use crate::chainstate::stacks::index::marf::{MarfConnection, MarfTransaction, MARF};
37
use crate::chainstate::stacks::index::{Error as MARFError, MARFValue, MarfTrieId};
38

39
pub type DBConn = rusqlite::Connection;
40
pub type DBTx<'a> = rusqlite::Transaction<'a>;
41

42
// 256MB
43
pub const SQLITE_MMAP_SIZE: i64 = 256 * 1024 * 1024;
44

45
// 32K
46
pub const SQLITE_MARF_PAGE_SIZE: i64 = 32768;
47

48
#[derive(Debug)]
49
pub enum Error {
50
    /// Not implemented
51
    NotImplemented,
52
    /// Database doesn't exist
53
    NoDBError,
54
    /// Read-only and tried to write
55
    ReadOnly,
56
    /// Type error -- can't represent the given data in the database
57
    TypeError,
58
    /// Database is corrupt -- we got data that shouldn't be there, or didn't get data when we
59
    /// should have
60
    Corruption,
61
    /// Serialization error -- can't serialize data
62
    SerializationError(serde_error),
63
    /// Parse error -- failed to load data we stored directly
64
    ParseError,
65
    /// Operation would overflow
66
    Overflow,
67
    /// Data not found
68
    NotFoundError,
69
    /// Data already exists
70
    ExistsError,
71
    /// Data corresponds to a non-canonical PoX sortition
72
    InvalidPoxSortition,
73
    /// Sqlite3 error
74
    SqliteError(sqlite_error),
75
    /// I/O error
76
    IOError(IOError),
77
    /// MARF index error
78
    IndexError(MARFError),
79
    /// Old schema error
80
    OldSchema(u64),
81
    /// Database is too old for epoch
82
    TooOldForEpoch,
83
    /// Block height is out of range
84
    BlockHeightOutOfRange,
85
    /// Other error
86
    Other(String),
87
}
88

89
impl fmt::Display for Error {
90
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
163✔
91
        match *self {
163✔
92
            Error::NotImplemented => write!(f, "Not implemented"),
×
93
            Error::NoDBError => write!(f, "Database does not exist"),
×
94
            Error::ReadOnly => write!(f, "Database is opened read-only"),
×
95
            Error::TypeError => write!(f, "Invalid or unrepresentable database type"),
×
96
            Error::Corruption => write!(f, "Database is corrupt"),
×
97
            Error::SerializationError(ref e) => fmt::Display::fmt(e, f),
×
98
            Error::ParseError => write!(f, "Parse error"),
×
99
            Error::Overflow => write!(f, "Numeric overflow"),
×
100
            Error::NotFoundError => write!(f, "Not found"),
163✔
101
            Error::ExistsError => write!(f, "Already exists"),
×
102
            Error::InvalidPoxSortition => write!(f, "Invalid PoX sortition"),
×
103
            Error::IOError(ref e) => fmt::Display::fmt(e, f),
×
104
            Error::SqliteError(ref e) => fmt::Display::fmt(e, f),
×
105
            Error::IndexError(ref e) => fmt::Display::fmt(e, f),
×
106
            Error::OldSchema(ref s) => write!(f, "Old database schema: {}", s),
×
107
            Error::TooOldForEpoch => {
108
                write!(f, "Database is not compatible with current system epoch")
×
109
            }
110
            Error::BlockHeightOutOfRange => write!(f, "Block height is out of range"),
×
111
            Error::Other(ref s) => fmt::Display::fmt(s, f),
×
112
        }
113
    }
163✔
114
}
115

116
impl error::Error for Error {
117
    fn cause(&self) -> Option<&dyn error::Error> {
×
118
        match *self {
×
119
            Error::NotImplemented => None,
×
120
            Error::NoDBError => None,
×
121
            Error::ReadOnly => None,
×
122
            Error::TypeError => None,
×
123
            Error::Corruption => None,
×
124
            Error::SerializationError(ref e) => Some(e),
×
125
            Error::ParseError => None,
×
126
            Error::Overflow => None,
×
127
            Error::NotFoundError => None,
×
128
            Error::ExistsError => None,
×
129
            Error::InvalidPoxSortition => None,
×
130
            Error::SqliteError(ref e) => Some(e),
×
131
            Error::IOError(ref e) => Some(e),
×
132
            Error::IndexError(ref e) => Some(e),
×
133
            Error::OldSchema(ref _s) => None,
×
134
            Error::TooOldForEpoch => None,
×
135
            Error::BlockHeightOutOfRange => None,
×
136
            Error::Other(ref _s) => None,
×
137
        }
138
    }
×
139
}
140

141
impl From<serde_error> for Error {
142
    #[cfg_attr(test, mutants::skip)]
143
    fn from(e: serde_error) -> Self {
×
144
        Self::SerializationError(e)
×
145
    }
×
146
}
147

148
impl From<sqlite_error> for Error {
149
    #[cfg_attr(test, mutants::skip)]
150
    fn from(e: sqlite_error) -> Self {
29,942,009✔
151
        Self::SqliteError(e)
29,942,009✔
152
    }
29,942,009✔
153
}
154

155
impl From<MARFError> for Error {
156
    #[cfg_attr(test, mutants::skip)]
157
    fn from(e: MARFError) -> Self {
×
158
        Self::IndexError(e)
×
159
    }
×
160
}
161

162
pub trait FromRow<T> {
163
    fn from_row(row: &Row) -> Result<T, Error>;
164
}
165

166
pub trait FromColumn<T> {
167
    fn from_column(row: &Row, column_name: &str) -> Result<T, Error>;
168
}
169

170
impl FromRow<u64> for u64 {
171
    fn from_row(row: &Row) -> Result<u64, Error> {
159,317,330✔
172
        let x: i64 = row.get(0)?;
159,317,330✔
173
        if x < 0 {
159,317,330✔
174
            return Err(Error::ParseError);
×
175
        }
159,317,330✔
176
        Ok(x as u64)
159,317,330✔
177
    }
159,317,330✔
178
}
179

180
impl FromRow<u32> for u32 {
181
    fn from_row(row: &Row) -> Result<u32, Error> {
47,112,415✔
182
        let x: u32 = row.get(0)?;
47,112,415✔
183
        Ok(x)
47,112,415✔
184
    }
47,112,415✔
185
}
186

187
impl FromRow<String> for String {
188
    fn from_row(row: &Row) -> Result<String, Error> {
2,293,314✔
189
        let x: String = row.get(0)?;
2,293,314✔
190
        Ok(x)
2,293,314✔
191
    }
2,293,314✔
192
}
193

194
impl FromRow<Vec<u8>> for Vec<u8> {
195
    fn from_row(row: &Row) -> Result<Vec<u8>, Error> {
283,802✔
196
        let x: Vec<u8> = row.get(0)?;
283,802✔
197
        Ok(x)
283,802✔
198
    }
283,802✔
199
}
200

201
impl FromColumn<u64> for u64 {
202
    fn from_column(row: &Row, column_name: &str) -> Result<u64, Error> {
2,147,483,647✔
203
        let x: i64 = row.get(column_name)?;
2,147,483,647✔
204
        if x < 0 {
2,147,483,647✔
205
            return Err(Error::ParseError);
×
206
        }
2,147,483,647✔
207
        Ok(x as u64)
2,147,483,647✔
208
    }
2,147,483,647✔
209
}
210

211
impl FromRow<StacksAddress> for StacksAddress {
212
    fn from_row(row: &Row) -> Result<StacksAddress, Error> {
555,875✔
213
        let addr_str: String = row.get(0)?;
555,875✔
214
        let addr = StacksAddress::from_string(&addr_str).ok_or(Error::ParseError)?;
555,875✔
215
        Ok(addr)
555,875✔
216
    }
555,875✔
217
}
218

219
impl FromColumn<Option<u64>> for u64 {
220
    fn from_column(row: &Row, column_name: &str) -> Result<Option<u64>, Error> {
6,189,879✔
221
        let x: Option<i64> = row.get(column_name)?;
6,189,879✔
222
        match x {
6,189,879✔
223
            Some(x) => {
3✔
224
                if x < 0 {
3✔
225
                    return Err(Error::ParseError);
×
226
                }
3✔
227
                Ok(Some(x as u64))
3✔
228
            }
229
            None => Ok(None),
6,189,876✔
230
        }
231
    }
6,189,879✔
232
}
233

234
impl FromRow<i64> for i64 {
235
    fn from_row(row: &Row) -> Result<i64, Error> {
249,224,625✔
236
        let x: i64 = row.get(0)?;
249,224,625✔
237
        Ok(x)
249,224,625✔
238
    }
249,224,625✔
239
}
240

241
impl FromColumn<i64> for i64 {
242
    fn from_column(row: &Row, column_name: &str) -> Result<i64, Error> {
×
243
        let x: i64 = row.get(column_name)?;
×
244
        Ok(x)
×
245
    }
×
246
}
247

248
impl FromColumn<QualifiedContractIdentifier> for QualifiedContractIdentifier {
249
    fn from_column(row: &Row, column_name: &str) -> Result<QualifiedContractIdentifier, Error> {
12✔
250
        let value: String = row.get(column_name)?;
12✔
251
        QualifiedContractIdentifier::parse(&value).map_err(|_| Error::ParseError)
12✔
252
    }
12✔
253
}
254

255
impl FromRow<bool> for bool {
256
    fn from_row(row: &Row) -> Result<bool, Error> {
26✔
257
        let x: bool = row.get(0)?;
26✔
258
        Ok(x)
26✔
259
    }
26✔
260
}
261

262
/// Make public keys loadable from a sqlite database
263
impl FromColumn<Secp256k1PublicKey> for Secp256k1PublicKey {
264
    fn from_column(row: &Row, column_name: &str) -> Result<Secp256k1PublicKey, Error> {
19,737,274✔
265
        let pubkey_hex: String = row.get(column_name)?;
19,737,274✔
266
        let pubkey = Secp256k1PublicKey::from_hex(&pubkey_hex).map_err(|_e| Error::ParseError)?;
19,737,274✔
267
        Ok(pubkey)
19,737,274✔
268
    }
19,737,274✔
269
}
270

271
/// Make private keys loadable from a sqlite database
272
impl FromColumn<Secp256k1PrivateKey> for Secp256k1PrivateKey {
273
    fn from_column(row: &Row, column_name: &str) -> Result<Secp256k1PrivateKey, Error> {
16,074,748✔
274
        let privkey_hex: String = row.get(column_name)?;
16,074,748✔
275
        let privkey =
16,074,748✔
276
            Secp256k1PrivateKey::from_hex(&privkey_hex).map_err(|_e| Error::ParseError)?;
16,074,748✔
277
        Ok(privkey)
16,074,748✔
278
    }
16,074,748✔
279
}
280

281
pub fn u64_to_sql(x: u64) -> Result<i64, Error> {
1,277,485,076✔
282
    if x > (i64::MAX as u64) {
1,277,485,076✔
283
        return Err(Error::ParseError);
×
284
    }
1,277,485,076✔
285
    Ok(x as i64)
1,277,485,076✔
286
}
1,277,485,076✔
287

288
pub fn opt_u64_to_sql(x: Option<u64>) -> Result<Option<i64>, Error> {
218,241✔
289
    match x {
218,241✔
290
        Some(num) => {
2✔
291
            if num > (i64::MAX as u64) {
2✔
292
                return Err(Error::ParseError);
×
293
            }
2✔
294
            Ok(Some(num as i64))
2✔
295
        }
296
        None => Ok(None),
218,239✔
297
    }
298
}
218,241✔
299

300
macro_rules! impl_byte_array_from_column_only {
301
    ($thing:ident) => {
302
        impl crate::util_lib::db::FromColumn<$thing> for $thing {
303
            fn from_column(
2,147,483,647✔
304
                row: &rusqlite::Row,
2,147,483,647✔
305
                column_name: &str,
2,147,483,647✔
306
            ) -> Result<Self, crate::util_lib::db::Error> {
2,147,483,647✔
307
                Ok(row.get::<_, Self>(column_name)?)
2,147,483,647✔
308
            }
2,147,483,647✔
309
        }
310
    };
311
}
312

313
impl_byte_array_from_column_only!(SortitionId);
314
impl_byte_array_from_column_only!(StacksBlockId);
315

316
macro_rules! impl_byte_array_from_column {
317
    ($thing:ident) => {
318
        impl rusqlite::types::FromSql for $thing {
319
            fn column_result(
676,238,370✔
320
                value: rusqlite::types::ValueRef,
676,238,370✔
321
            ) -> rusqlite::types::FromSqlResult<Self> {
676,238,370✔
322
                let hex_str = value.as_str()?;
676,238,370✔
323
                let byte_str = stacks_common::util::hash::hex_bytes(hex_str)
676,238,370✔
324
                    .map_err(|_e| rusqlite::types::FromSqlError::InvalidType)?;
676,238,370✔
325
                let inst = $thing::from_bytes(&byte_str)
676,238,370✔
326
                    .ok_or(rusqlite::types::FromSqlError::InvalidType)?;
676,238,370✔
327
                Ok(inst)
676,238,370✔
328
            }
676,238,370✔
329
        }
330

331
        impl crate::util_lib::db::FromColumn<$thing> for $thing {
332
            fn from_column(
676,238,370✔
333
                row: &rusqlite::Row,
676,238,370✔
334
                column_name: &str,
676,238,370✔
335
            ) -> Result<Self, crate::util_lib::db::Error> {
676,238,370✔
336
                Ok(row.get::<_, Self>(column_name)?)
676,238,370✔
337
            }
676,238,370✔
338
        }
339

340
        impl rusqlite::types::ToSql for $thing {
341
            fn to_sql(&self) -> rusqlite::Result<rusqlite::types::ToSqlOutput<'_>> {
1,263,134✔
342
                let hex_str = self.to_hex();
1,263,134✔
343
                Ok(hex_str.into())
1,263,134✔
344
            }
1,263,134✔
345
        }
346
    };
347
}
348

349
/// Load the path of the database from the connection
350
#[cfg(test)]
351
fn get_db_path(conn: &Connection) -> Result<String, Error> {
×
352
    let sql = "PRAGMA database_list";
×
353
    let path: Result<Option<String>, sqlite_error> =
×
354
        conn.query_row_and_then(sql, NO_PARAMS, |row| row.get(2));
×
355
    match path {
×
356
        Ok(Some(path)) => Ok(path),
×
357
        Ok(None) => Ok("<unknown>".to_string()),
×
358
        Err(e) => Err(Error::SqliteError(e)),
×
359
    }
360
}
×
361

362
/// Generate debug output to be fed into an external script to examine query plans.
363
/// TODO: it uses mocked arguments, which it assumes are strings. This does not always result in a
364
/// valid query.
365
#[cfg(test)]
366
fn log_sql_eqp(conn: &Connection, sql_query: &str) {
14,089,975✔
367
    if std::env::var("BLOCKSTACK_DB_TRACE") != Ok("1".to_string()) {
14,089,975✔
368
        return;
14,089,975✔
369
    }
×
370

371
    let mut parts = sql_query.split(" ");
×
372
    let mut full_sql = if let Some(part) = parts.next() {
×
373
        part.to_string()
×
374
    } else {
375
        sql_query.to_string()
×
376
    };
377

378
    while let Some(part) = parts.next() {
×
379
        if part.starts_with("?") {
×
380
            full_sql = format!("{} \"mock_arg\"", full_sql.trim());
×
381
        } else {
×
382
            full_sql = format!("{} {}", full_sql.trim(), part.trim());
×
383
        }
×
384
    }
385

386
    let path = get_db_path(conn).unwrap_or("ERROR!".to_string());
×
387
    let eqp_sql = format!("\"{}\" EXPLAIN QUERY PLAN {}", &path, full_sql.trim());
×
388
    debug!("{}", &eqp_sql);
×
389
}
14,089,975✔
390

391
#[cfg(not(test))]
392
fn log_sql_eqp(_conn: &Connection, _sql_query: &str) {}
1,038,115,629✔
393

394
/// boilerplate code for querying rows
395
pub fn query_rows<T, P>(conn: &Connection, sql_query: &str, sql_args: P) -> Result<Vec<T>, Error>
147,266,660✔
396
where
147,266,660✔
397
    P: Params,
147,266,660✔
398
    T: FromRow<T>,
147,266,660✔
399
{
400
    log_sql_eqp(conn, sql_query);
147,266,660✔
401
    let mut stmt = conn.prepare(sql_query)?;
147,266,660✔
402
    let result = stmt.query_and_then(sql_args, |row| T::from_row(row))?;
344,240,694✔
403

404
    result.collect()
147,266,660✔
405
}
147,266,660✔
406

407
/// boilerplate code for querying a single row
408
///   if more than 1 row is returned, excess rows are ignored.
409
pub fn query_row<T, P>(conn: &Connection, sql_query: &str, sql_args: P) -> Result<Option<T>, Error>
579,955,679✔
410
where
579,955,679✔
411
    P: Params,
579,955,679✔
412
    T: FromRow<T>,
579,955,679✔
413
{
414
    log_sql_eqp(conn, sql_query);
579,955,679✔
415
    let query_result = conn.query_row_and_then(sql_query, sql_args, |row| T::from_row(row));
579,955,679✔
416
    match query_result {
28,257,418✔
417
        Ok(x) => Ok(Some(x)),
551,698,261✔
418
        Err(Error::SqliteError(sqlite_error::QueryReturnedNoRows)) => Ok(None),
28,257,417✔
419
        Err(e) => Err(e),
1✔
420
    }
421
}
579,955,679✔
422

423
/// boilerplate code for querying a single row
424
///   if more than 1 row is returned, panic
425
pub fn query_expect_row<T, P>(
3,321✔
426
    conn: &Connection,
3,321✔
427
    sql_query: &str,
3,321✔
428
    sql_args: P,
3,321✔
429
) -> Result<Option<T>, Error>
3,321✔
430
where
3,321✔
431
    P: Params,
3,321✔
432
    T: FromRow<T>,
3,321✔
433
{
434
    log_sql_eqp(conn, sql_query);
3,321✔
435
    let mut stmt = conn.prepare(sql_query)?;
3,321✔
436
    let mut result = stmt.query_and_then(sql_args, |row| T::from_row(row))?;
3,321✔
437
    let mut return_value = None;
3,321✔
438
    if let Some(value) = result.next() {
3,321✔
439
        return_value = Some(value?);
3,321✔
440
    }
×
441
    assert!(
3,321✔
442
        result.next().is_none(),
3,321✔
443
        "FATAL: Multiple values returned for query that expected a single result:\n {}",
444
        sql_query
445
    );
446
    Ok(return_value)
3,321✔
447
}
3,321✔
448

449
pub fn query_row_panic<T, P, F>(
318,956,457✔
450
    conn: &Connection,
318,956,457✔
451
    sql_query: &str,
318,956,457✔
452
    sql_args: P,
318,956,457✔
453
    panic_message: F,
318,956,457✔
454
) -> Result<Option<T>, Error>
318,956,457✔
455
where
318,956,457✔
456
    P: Params,
318,956,457✔
457
    T: FromRow<T>,
318,956,457✔
458
    F: FnOnce() -> String,
318,956,457✔
459
{
460
    log_sql_eqp(conn, sql_query);
318,956,457✔
461
    let mut stmt = conn.prepare(sql_query)?;
318,956,457✔
462
    let mut result = stmt.query_and_then(sql_args, |row| T::from_row(row))?;
318,956,457✔
463
    let mut return_value = None;
318,956,457✔
464
    if let Some(value) = result.next() {
318,956,457✔
465
        return_value = Some(value?);
273,308,694✔
466
    }
45,647,763✔
467
    if result.next().is_some() {
318,956,457✔
468
        panic!("{}", &panic_message());
36✔
469
    }
318,956,421✔
470
    Ok(return_value)
318,956,421✔
471
}
318,956,421✔
472

473
/// boilerplate code for querying a column out of a sequence of rows
474
pub fn query_row_columns<T, P>(
1,547,268✔
475
    conn: &Connection,
1,547,268✔
476
    sql_query: &str,
1,547,268✔
477
    sql_args: P,
1,547,268✔
478
    column_name: &str,
1,547,268✔
479
) -> Result<Vec<T>, Error>
1,547,268✔
480
where
1,547,268✔
481
    P: Params,
1,547,268✔
482
    T: FromColumn<T>,
1,547,268✔
483
{
484
    log_sql_eqp(conn, sql_query);
1,547,268✔
485
    let mut stmt = conn.prepare(sql_query)?;
1,547,268✔
486
    let mut rows = stmt.query(sql_args)?;
1,547,268✔
487

488
    // gather
489
    let mut row_data = vec![];
1,547,268✔
490
    while let Some(row) = rows.next().map_err(Error::SqliteError)? {
2,981,955✔
491
        let next_row = T::from_column(row, column_name)?;
1,434,687✔
492
        row_data.push(next_row);
1,434,687✔
493
    }
494

495
    Ok(row_data)
1,547,268✔
496
}
1,547,268✔
497

498
/// boilerplate code for querying a column out of a sequence of rows,
499
///  expecting exactly 0 or 1 results. panics if more.
500
pub fn query_one_row_column<T, P>(
1,886✔
501
    conn: &Connection,
1,886✔
502
    sql_query: &str,
1,886✔
503
    sql_args: P,
1,886✔
504
    column_name: &str,
1,886✔
505
    panic_msg: &str,
1,886✔
506
) -> Result<Option<T>, Error>
1,886✔
507
where
1,886✔
508
    P: Params,
1,886✔
509
    T: FromColumn<T>,
1,886✔
510
{
511
    log_sql_eqp(conn, sql_query);
1,886✔
512
    let mut stmt = conn.prepare(sql_query)?;
1,886✔
513
    let mut rows = stmt.query(sql_args)?;
1,886✔
514

515
    // gather
516
    let mut result = None;
1,886✔
517
    while let Some(row) = rows.next().map_err(Error::SqliteError)? {
3,311✔
518
        if result.is_some() {
1,425✔
519
            panic!("{panic_msg}");
×
520
        }
1,425✔
521
        let next_row = T::from_column(row, column_name)?;
1,425✔
522
        result = Some(next_row);
1,425✔
523
    }
524

525
    Ok(result)
1,886✔
526
}
1,886✔
527

528
/// Boilerplate for querying a single integer (first and only item of the query must be an int)
529
pub fn query_int<P>(conn: &Connection, sql_query: &str, sql_args: P) -> Result<i64, Error>
996,553✔
530
where
996,553✔
531
    P: Params,
996,553✔
532
{
533
    log_sql_eqp(conn, sql_query);
996,553✔
534
    let mut stmt = conn.prepare(sql_query)?;
996,553✔
535
    let mut rows = stmt.query(sql_args)?;
996,553✔
536
    let mut row_data = None;
996,553✔
537
    while let Some(row) = rows.next().map_err(Error::SqliteError)? {
1,993,103✔
538
        if row_data.is_some() {
996,550✔
539
            return Err(Error::Overflow);
×
540
        }
996,550✔
541
        let i: i64 = row.get(0)?;
996,550✔
542
        row_data = Some(i);
996,550✔
543
    }
544

545
    row_data.ok_or_else(|| Error::NotFoundError)
996,553✔
546
}
996,553✔
547

548
pub fn query_count<P>(conn: &Connection, sql_query: &str, sql_args: P) -> Result<i64, Error>
746,036✔
549
where
746,036✔
550
    P: Params,
746,036✔
551
{
552
    query_int(conn, sql_query, sql_args)
746,036✔
553
}
746,036✔
554

555
/// Run a PRAGMA statement.  This can't always be done via execute(), because it may return a result (and
556
/// rusqlite does not like this).
557
pub fn sql_pragma(
80,535,758✔
558
    conn: &Connection,
80,535,758✔
559
    pragma_name: &str,
80,535,758✔
560
    pragma_value: &dyn ToSql,
80,535,758✔
561
) -> Result<(), Error> {
80,535,758✔
562
    inner_sql_pragma(conn, pragma_name, pragma_value).map_err(Error::SqliteError)
80,535,758✔
563
}
80,535,758✔
564

565
fn inner_sql_pragma(
336,279,649✔
566
    conn: &Connection,
336,279,649✔
567
    pragma_name: &str,
336,279,649✔
568
    pragma_value: &dyn ToSql,
336,279,649✔
569
) -> Result<(), sqlite_error> {
336,279,649✔
570
    conn.pragma_update(None, pragma_name, pragma_value)
336,279,649✔
571
}
336,279,649✔
572

573
/// Run a VACUUM command
574
pub fn sql_vacuum(conn: &Connection) -> Result<(), Error> {
9,743✔
575
    conn.execute("VACUUM", NO_PARAMS)
9,743✔
576
        .map_err(Error::SqliteError)
9,743✔
577
        .map(|_| ())
9,743✔
578
}
9,743✔
579

580
/// Returns true if the database table `table_name` exists in the active
581
///  database of the provided SQLite connection.
582
pub fn table_exists(conn: &Connection, table_name: &str) -> Result<bool, sqlite_error> {
44,999,166✔
583
    let sql = "SELECT name FROM sqlite_master WHERE type='table' AND name=?";
44,999,166✔
584
    conn.query_row(sql, &[table_name], |row| row.get::<_, String>(0))
44,999,166✔
585
        .optional()
44,999,166✔
586
        .map(|r| r.is_some())
44,999,166✔
587
}
44,999,166✔
588

589
/// Set up an on-disk database with a MARF index if they don't exist yet.
590
/// Either way, returns the MARF path
591
pub fn db_mkdirs(path_str: &str) -> Result<String, Error> {
14,042,402✔
592
    let mut path = PathBuf::from(path_str);
14,042,402✔
593
    match fs::metadata(path_str) {
14,042,402✔
594
        Ok(md) => {
14,038,662✔
595
            if !md.is_dir() {
14,038,662✔
596
                error!("Not a directory: {:?}", path);
×
597
                return Err(Error::ExistsError);
×
598
            }
14,038,662✔
599
        }
600
        Err(e) => {
3,740✔
601
            if e.kind() != io::ErrorKind::NotFound {
3,740✔
602
                return Err(Error::IOError(e));
×
603
            }
3,740✔
604
            fs::create_dir_all(path_str).map_err(Error::IOError)?;
3,740✔
605
        }
606
    }
607

608
    path.push("marf.sqlite");
14,042,402✔
609
    let marf_path = path.to_str().ok_or_else(|| Error::ParseError)?.to_string();
14,042,402✔
610

611
    Ok(marf_path)
14,042,402✔
612
}
14,042,402✔
613

614
/// Read-only connection to a MARF-indexed DB
615
pub struct IndexDBConn<'a, C, T: MarfTrieId> {
616
    pub index: &'a MARF<T>,
617
    pub context: C,
618
}
619

620
impl<'a, C, T: MarfTrieId> IndexDBConn<'a, C, T> {
621
    pub fn new(index: &'a MARF<T>, context: C) -> IndexDBConn<'a, C, T> {
71,963,852✔
622
        IndexDBConn { index, context }
71,963,852✔
623
    }
71,963,852✔
624

625
    /// Get the ancestor block hash of a block of a given height, given a descendent block hash.
626
    pub fn get_ancestor_block_hash(
28,541,752✔
627
        &self,
28,541,752✔
628
        block_height: u64,
28,541,752✔
629
        tip_block_hash: &T,
28,541,752✔
630
    ) -> Result<Option<T>, Error> {
28,541,752✔
631
        get_ancestor_block_hash(self.index, block_height, tip_block_hash)
28,541,752✔
632
    }
28,541,752✔
633

634
    /// Get the height of an ancestor block, if it is indeed the ancestor.
635
    pub fn get_ancestor_block_height(
44✔
636
        &self,
44✔
637
        ancestor_block_hash: &T,
44✔
638
        tip_block_hash: &T,
44✔
639
    ) -> Result<Option<u64>, Error> {
44✔
640
        get_ancestor_block_height(&self.index, ancestor_block_hash, tip_block_hash)
44✔
641
    }
44✔
642

643
    /// Get a value from the fork index
644
    pub fn get_indexed(&self, header_hash: &T, key: &str) -> Result<Option<String>, Error> {
15,131,629✔
645
        let mut connection = self.index.reopen_connection()?;
15,131,629✔
646
        get_indexed(&mut connection, header_hash, key)
15,131,629✔
647
    }
15,131,629✔
648

649
    pub fn conn(&self) -> &DBConn {
134,516,866✔
650
        self.index.sqlite_conn()
134,516,866✔
651
    }
134,516,866✔
652
}
653

654
impl<C, T: MarfTrieId> Deref for IndexDBConn<'_, C, T> {
655
    type Target = DBConn;
656
    fn deref(&self) -> &DBConn {
80,703,281✔
657
        self.conn()
80,703,281✔
658
    }
80,703,281✔
659
}
660

661
pub struct IndexDBTx<'a, C: Clone, T: MarfTrieId> {
662
    _index: Option<MarfTransaction<'a, T>>,
663
    pub context: C,
664
    block_linkage: Option<(T, T)>,
665
}
666

667
impl<'a, C: Clone, T: MarfTrieId> Deref for IndexDBTx<'a, C, T> {
668
    type Target = DBTx<'a>;
669
    fn deref(&self) -> &DBTx<'a> {
15,252,760✔
670
        self.tx()
15,252,760✔
671
    }
15,252,760✔
672
}
673

674
impl<'a, C: Clone, T: MarfTrieId> DerefMut for IndexDBTx<'a, C, T> {
675
    fn deref_mut(&mut self) -> &mut DBTx<'a> {
567,202✔
676
        self.tx_mut()
567,202✔
677
    }
567,202✔
678
}
679

680
/// Called by `rusqlite` if we are waiting too long on a database lock
681
pub fn tx_busy_handler(run_count: i32) -> bool {
199,318✔
682
    stacks_common::util::db::tx_busy_handler(run_count)
199,318✔
683
}
199,318✔
684

685
/// Begin an immediate-mode transaction, and handle busy errors with exponential backoff.
686
/// Handling busy errors when the tx begins is preferable to doing it when the tx commits, since
687
/// then we don't have to worry about any extra rollback logic.
688
pub fn tx_begin_immediate(conn: &mut Connection) -> Result<DBTx<'_>, Error> {
60,134,395✔
689
    tx_begin_immediate_sqlite(conn).map_err(Error::from)
60,134,395✔
690
}
60,134,395✔
691

692
/// Begin an immediate-mode transaction, and handle busy errors with exponential backoff.
693
/// Handling busy errors when the tx begins is preferable to doing it when the tx commits, since
694
/// then we don't have to worry about any extra rollback logic.
695
/// Sames as `tx_begin_immediate` except that it returns a rusqlite error.
696
pub fn tx_begin_immediate_sqlite(conn: &mut Connection) -> Result<DBTx<'_>, sqlite_error> {
61,010,010✔
697
    conn.busy_handler(Some(tx_busy_handler))?;
61,010,010✔
698
    let tx = Transaction::new(conn, TransactionBehavior::Immediate)?;
61,010,010✔
699
    update_lock_table(tx.deref());
61,010,010✔
700
    Ok(tx)
61,010,010✔
701
}
61,010,010✔
702

703
#[cfg(feature = "profile-sqlite")]
704
fn trace_profile(query: &str, duration: std::time::Duration) {
705
    use serde_json::json;
706
    let obj = json!({"millis":duration.as_millis(), "query":query});
707
    debug!(
708
        "sqlite trace profile {}",
709
        serde_json::to_string(&obj).unwrap()
710
    );
711
}
712

713
#[cfg(feature = "profile-sqlite")]
714
fn inner_connection_open<P: AsRef<Path>>(
715
    path: P,
716
    flags: OpenFlags,
717
) -> Result<Connection, sqlite_error> {
718
    let mut db = Connection::open_with_flags(path, flags)?;
719
    db.profile(Some(trace_profile));
720
    Ok(db)
721
}
722

723
#[cfg(not(feature = "profile-sqlite"))]
724
fn inner_connection_open<P: AsRef<Path>>(
121,899,047✔
725
    path: P,
121,899,047✔
726
    flags: OpenFlags,
121,899,047✔
727
) -> Result<Connection, sqlite_error> {
121,899,047✔
728
    Connection::open_with_flags(path, flags)
121,899,047✔
729
}
121,899,047✔
730

731
/// Open a database connection and set some typically-used pragmas
732
pub fn sqlite_open<P: AsRef<Path>>(
121,899,047✔
733
    path: P,
121,899,047✔
734
    flags: OpenFlags,
121,899,047✔
735
    foreign_keys: bool,
121,899,047✔
736
) -> Result<Connection, sqlite_error> {
121,899,047✔
737
    let db = inner_connection_open(path, flags)?;
121,899,047✔
738
    db.busy_handler(Some(tx_busy_handler))?;
121,870,756✔
739
    inner_sql_pragma(&db, "journal_mode", &"WAL")?;
121,870,756✔
740
    inner_sql_pragma(&db, "synchronous", &"NORMAL")?;
121,870,756✔
741
    if foreign_keys {
121,870,756✔
742
        inner_sql_pragma(&db, "foreign_keys", &true)?;
11,952,057✔
743
    }
109,918,699✔
744
    Ok(db)
121,870,756✔
745
}
121,899,047✔
746

747
/// Get the ancestor block hash of a block of a given height, given a descendent block hash.
748
pub fn get_ancestor_block_hash<T: MarfTrieId>(
28,541,752✔
749
    index: &MARF<T>,
28,541,752✔
750
    block_height: u64,
28,541,752✔
751
    tip_block_hash: &T,
28,541,752✔
752
) -> Result<Option<T>, Error> {
28,541,752✔
753
    let block_height = block_height
28,541,752✔
754
        .try_into()
28,541,752✔
755
        .map_err(|_e| Error::BlockHeightOutOfRange)?;
28,541,752✔
756
    let mut read_only = index.reopen_connection()?;
28,541,751✔
757
    let bh = read_only.get_block_at_height(block_height, tip_block_hash)?;
28,541,751✔
758
    Ok(bh)
28,541,751✔
759
}
28,541,752✔
760

761
/// Get the height of an ancestor block, if it is indeed the ancestor.
762
pub fn get_ancestor_block_height<T: MarfTrieId>(
44✔
763
    index: &MARF<T>,
44✔
764
    ancestor_block_hash: &T,
44✔
765
    tip_block_hash: &T,
44✔
766
) -> Result<Option<u64>, Error> {
44✔
767
    let mut read_only = index.reopen_connection()?;
44✔
768
    let height_opt = read_only
44✔
769
        .get_block_height(ancestor_block_hash, tip_block_hash)?
44✔
770
        .map(|height| height as u64);
44✔
771
    Ok(height_opt)
44✔
772
}
44✔
773

774
/// Load some index data
775
fn load_indexed(conn: &DBConn, marf_value: &MARFValue) -> Result<Option<String>, Error> {
15,607,853✔
776
    let mut stmt = conn
15,607,853✔
777
        .prepare("SELECT value FROM __fork_storage WHERE value_hash = ?1 LIMIT 2")
15,607,853✔
778
        .map_err(Error::SqliteError)?;
15,607,853✔
779
    let mut rows = stmt
15,607,853✔
780
        .query(params![marf_value.to_hex()])
15,607,853✔
781
        .map_err(Error::SqliteError)?;
15,607,853✔
782
    let mut value = None;
15,607,853✔
783

784
    while let Some(row) = rows.next()? {
31,215,706✔
785
        let value_str: String = row.get(0)?;
15,607,853✔
786
        if value.is_some() {
15,607,853✔
787
            // should be impossible
788
            panic!(
×
789
                "FATAL: two or more values for {}",
790
                &to_hex(&marf_value.to_vec())
×
791
            );
792
        }
15,607,853✔
793
        value = Some(value_str);
15,607,853✔
794
    }
795

796
    Ok(value)
15,607,853✔
797
}
15,607,853✔
798

799
/// Get a value from the fork index
800
fn get_indexed<T: MarfTrieId, M: MarfConnection<T>>(
21,185,298✔
801
    index: &mut M,
21,185,298✔
802
    header_hash: &T,
21,185,298✔
803
    key: &str,
21,185,298✔
804
) -> Result<Option<String>, Error> {
21,185,298✔
805
    match index.get(header_hash, key) {
21,185,298✔
806
        Ok(Some(marf_value)) => {
15,607,844✔
807
            let value = load_indexed(index.sqlite_conn(), &marf_value)?
15,607,844✔
808
                .unwrap_or_else(|| panic!("FATAL: corrupt index: key '{}' from {} is present in the index but missing a value in the DB", &key, &header_hash));
15,607,844✔
809
            Ok(Some(value))
15,607,844✔
810
        }
811
        Ok(None) => Ok(None),
5,577,454✔
812
        Err(MARFError::NotFoundError) => Ok(None),
×
813
        Err(e) => {
×
814
            error!(
×
815
                "Failed to fetch '{}' off of {}: {:?}",
816
                key, &header_hash, &e
×
817
            );
818
            Err(Error::Corruption)
×
819
        }
820
    }
821
}
21,185,298✔
822

823
impl<'a, C: Clone, T: MarfTrieId> IndexDBTx<'a, C, T> {
824
    pub fn new(index: &'a mut MARF<T>, context: C) -> IndexDBTx<'a, C, T> {
4,558,709✔
825
        let tx = index
4,558,709✔
826
            .begin_tx()
4,558,709✔
827
            .expect("BUG: failure to begin MARF transaction");
4,558,709✔
828
        IndexDBTx {
4,558,709✔
829
            _index: Some(tx),
4,558,709✔
830
            block_linkage: None,
4,558,709✔
831
            context,
4,558,709✔
832
        }
4,558,709✔
833
    }
4,558,709✔
834

835
    pub fn index(&self) -> &MarfTransaction<'a, T> {
34,051,456✔
836
        self._index
34,051,456✔
837
            .as_ref()
34,051,456✔
838
            .expect("BUG: MarfTransaction lost, but IndexDBTx still exists")
34,051,456✔
839
    }
34,051,456✔
840

841
    fn index_mut(&mut self) -> &mut MarfTransaction<'a, T> {
16,440,419✔
842
        self._index
16,440,419✔
843
            .as_mut()
16,440,419✔
844
            .expect("BUG: MarfTransaction lost, but IndexDBTx still exists")
16,440,419✔
845
    }
16,440,419✔
846

847
    pub fn tx(&self) -> &DBTx<'a> {
33,994,317✔
848
        self.index().sqlite_tx()
33,994,317✔
849
    }
33,994,317✔
850

851
    pub fn tx_mut(&mut self) -> &mut DBTx<'a> {
567,202✔
852
        self.index_mut().sqlite_tx_mut()
567,202✔
853
    }
567,202✔
854

855
    pub fn instantiate_index(&mut self) -> Result<(), Error> {
7,472✔
856
        self.tx()
7,472✔
857
            .execute(
7,472✔
858
                r#"
7,472✔
859
        -- fork-specific key/value storage, indexed via a MARF.
7,472✔
860
        -- each row is guaranteed to be unique
7,472✔
861
        CREATE TABLE IF NOT EXISTS __fork_storage(
7,472✔
862
            value_hash TEXT NOT NULL,
7,472✔
863
            value TEXT NOT NULL,
7,472✔
864

7,472✔
865
            PRIMARY KEY(value_hash)
7,472✔
866
        );
7,472✔
867
        "#,
7,472✔
868
                NO_PARAMS,
7,472✔
869
            )
7,472✔
870
            .map_err(Error::SqliteError)?;
7,472✔
871
        Ok(())
7,472✔
872
    }
7,472✔
873

874
    /// Get the ancestor block hash of a block of a given height, given a descendent block hash.
875
    pub fn get_ancestor_block_hash(
7,482,962✔
876
        &mut self,
7,482,962✔
877
        block_height: u64,
7,482,962✔
878
        tip_block_hash: &T,
7,482,962✔
879
    ) -> Result<Option<T>, Error> {
7,482,962✔
880
        self.index_mut()
7,482,962✔
881
            .get_block_at_height(
7,482,962✔
882
                block_height.try_into().expect("Height > u32::max()"),
7,482,962✔
883
                tip_block_hash,
7,482,962✔
884
            )
885
            .map_err(Error::from)
7,482,962✔
886
    }
7,482,962✔
887

888
    /// Get the height of an ancestor block, if it is indeed the ancestor.
889
    pub fn get_ancestor_block_height(
×
890
        &mut self,
×
891
        ancestor_block_hash: &T,
×
892
        tip_block_hash: &T,
×
893
    ) -> Result<Option<u64>, Error> {
×
894
        let height_opt = self
×
895
            .index_mut()
×
896
            .get_block_height(ancestor_block_hash, tip_block_hash)?
×
897
            .map(|height| height as u64);
×
898
        Ok(height_opt)
×
899
    }
×
900

901
    /// Store some data to the index storage.
902
    fn store_indexed(&mut self, value: &String) -> Result<MARFValue, Error> {
2,396,774✔
903
        let marf_value = MARFValue::from_value(value);
2,396,774✔
904
        self.tx().execute(
2,396,774✔
905
            "INSERT OR REPLACE INTO __fork_storage (value_hash, value) VALUES (?1, ?2)",
2,396,774✔
906
            &[&to_hex(&marf_value.to_vec()), value],
2,396,774✔
907
        )?;
2,396,774✔
908
        Ok(marf_value)
2,396,774✔
909
    }
2,396,774✔
910

911
    /// Get a value from the fork index
912
    pub fn get_indexed(&mut self, header_hash: &T, key: &str) -> Result<Option<String>, Error> {
6,052,922✔
913
        get_indexed(self.index_mut(), header_hash, key)
6,052,922✔
914
    }
6,052,922✔
915

916
    /// Get a value from the fork index, but with a read-only reference
917
    pub fn get_indexed_ref(&self, header_hash: &T, key: &str) -> Result<Option<String>, Error> {
756✔
918
        let mut ro_index = self.index().reopen_readonly()?;
756✔
919
        get_indexed(&mut ro_index, header_hash, key)
756✔
920
    }
756✔
921

922
    /// Put all keys and values in a single MARF transaction, and seal it.
923
    /// This is a one-time operation; subsequent calls will panic.  You should follow this up with
924
    /// a commit if you want to save the MARF state.
925
    pub fn put_indexed_all(
779,111✔
926
        &mut self,
779,111✔
927
        parent_header_hash: &T,
779,111✔
928
        header_hash: &T,
779,111✔
929
        keys: &[String],
779,111✔
930
        values: &[String],
779,111✔
931
    ) -> Result<TrieHash, Error> {
779,111✔
932
        assert_eq!(keys.len(), values.len());
779,111✔
933
        match self.block_linkage {
779,111✔
934
            None => {
935
                self.index_mut().begin(parent_header_hash, header_hash)?;
779,111✔
936
                self.block_linkage = Some((parent_header_hash.clone(), header_hash.clone()));
779,111✔
937
            }
938
            Some(_) => panic!("Tried to put_indexed_all twice!"),
×
939
        }
940

941
        let mut marf_values = Vec::with_capacity(values.len());
779,111✔
942
        for value in values.iter() {
2,396,783✔
943
            let marf_value = self.store_indexed(value)?;
2,396,774✔
944
            marf_values.push(marf_value);
2,396,774✔
945
        }
946

947
        self.index_mut().insert_batch(keys, marf_values)?;
779,111✔
948
        let root_hash = self.index_mut().seal()?;
779,111✔
949
        Ok(root_hash)
779,111✔
950
    }
779,111✔
951

952
    /// Commit the MARF transaction
953
    pub fn commit(mut self) -> Result<(), Error> {
3,368,085✔
954
        self.block_linkage = None;
3,368,085✔
955
        debug!("Indexed-commit: MARF index");
3,368,085✔
956
        let index_tx = self
3,368,085✔
957
            ._index
3,368,085✔
958
            .take()
3,368,085✔
959
            .expect("BUG: MarfTransaction lost, but IndexDBTx still exists");
3,368,085✔
960
        index_tx.commit()?;
3,368,085✔
961
        Ok(())
3,368,085✔
962
    }
3,368,085✔
963

964
    /// Get the root hash
965
    pub fn get_root_hash_at(&mut self, bhh: &T) -> Result<TrieHash, Error> {
×
966
        let root_hash = self.index_mut().get_root_hash_at(bhh)?;
×
967
        Ok(root_hash)
×
968
    }
×
969
}
970

971
impl<C: Clone, T: MarfTrieId> Drop for IndexDBTx<'_, C, T> {
972
    fn drop(&mut self) {
4,558,691✔
973
        if let Some((ref parent, ref child)) = self.block_linkage {
4,558,691✔
974
            let index_tx = self
1✔
975
                ._index
1✔
976
                .take()
1✔
977
                .expect("BUG: MarfTransaction lost, but IndexDBTx still exists");
1✔
978
            debug!("Dropping MARF linkage ({},{})", parent, child);
1✔
979
            index_tx.drop_current();
1✔
980
        }
4,558,690✔
981
    }
4,558,691✔
982
}
983

984
#[cfg(test)]
985
mod tests {
986
    use std::fs;
987

988
    use super::*;
989

990
    #[test]
991
    fn test_pragma() {
1✔
992
        let path = "/tmp/blockstack_db_test_pragma.db";
1✔
993
        if fs::metadata(path).is_ok() {
1✔
994
            fs::remove_file(path).unwrap();
×
995
        }
1✔
996

997
        // calls pragma_update with both journal_mode and foreign_keys
998
        let db = sqlite_open(
1✔
999
            path,
1✔
1000
            OpenFlags::SQLITE_OPEN_CREATE | OpenFlags::SQLITE_OPEN_READ_WRITE,
1✔
1001
            true,
1002
        )
1003
        .unwrap();
1✔
1004

1005
        // journal mode must be WAL
1006
        db.pragma_query(None, "journal_mode", |row| {
1✔
1007
            let value: String = row.get(0)?;
1✔
1008
            assert_eq!(value, "wal");
1✔
1009
            Ok(())
1✔
1010
        })
1✔
1011
        .unwrap();
1✔
1012

1013
        // foreign keys must be on
1014
        db.pragma_query(None, "foreign_keys", |row| {
1✔
1015
            let value: i64 = row.get(0)?;
1✔
1016
            assert_eq!(value, 1);
1✔
1017
            Ok(())
1✔
1018
        })
1✔
1019
        .unwrap();
1✔
1020
    }
1✔
1021
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc