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

stacks-network / stacks-core / 25903914664-1

15 May 2026 06:28AM UTC coverage: 47.122% (-38.8%) from 85.959%
25903914664-1

Pull #7199

github

94e391
web-flow
Merge 109f2828c into 1c7b8e6ac
Pull Request #7199: Feat: L1 and L2 early unlocks, updating signer

103343 of 219309 relevant lines covered (47.12%)

12880462.62 hits per line

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

74.68
/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 {
81✔
91
        match *self {
81✔
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"),
81✔
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
    }
81✔
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 {
27,547,427✔
151
        Self::SqliteError(e)
27,547,427✔
152
    }
27,547,427✔
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> {
149,256,856✔
172
        let x: i64 = row.get(0)?;
149,256,856✔
173
        if x < 0 {
149,256,856✔
174
            return Err(Error::ParseError);
×
175
        }
149,256,856✔
176
        Ok(x as u64)
149,256,856✔
177
    }
149,256,856✔
178
}
179

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

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

194
impl FromRow<Vec<u8>> for Vec<u8> {
195
    fn from_row(row: &Row) -> Result<Vec<u8>, Error> {
34,403✔
196
        let x: Vec<u8> = row.get(0)?;
34,403✔
197
        Ok(x)
34,403✔
198
    }
34,403✔
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> {
542,340✔
213
        let addr_str: String = row.get(0)?;
542,340✔
214
        let addr = StacksAddress::from_string(&addr_str).ok_or(Error::ParseError)?;
542,340✔
215
        Ok(addr)
542,340✔
216
    }
542,340✔
217
}
218

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

234
impl FromRow<i64> for i64 {
235
    fn from_row(row: &Row) -> Result<i64, Error> {
85,437,641✔
236
        let x: i64 = row.get(0)?;
85,437,641✔
237
        Ok(x)
85,437,641✔
238
    }
85,437,641✔
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> {
×
250
        let value: String = row.get(column_name)?;
×
251
        QualifiedContractIdentifier::parse(&value).map_err(|_| Error::ParseError)
×
252
    }
×
253
}
254

255
impl FromRow<bool> for bool {
256
    fn from_row(row: &Row) -> Result<bool, Error> {
×
257
        let x: bool = row.get(0)?;
×
258
        Ok(x)
×
259
    }
×
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> {
21,773,194✔
265
        let pubkey_hex: String = row.get(column_name)?;
21,773,194✔
266
        let pubkey = Secp256k1PublicKey::from_hex(&pubkey_hex).map_err(|_e| Error::ParseError)?;
21,773,194✔
267
        Ok(pubkey)
21,773,194✔
268
    }
21,773,194✔
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,624,329✔
274
        let privkey_hex: String = row.get(column_name)?;
16,624,329✔
275
        let privkey =
16,624,329✔
276
            Secp256k1PrivateKey::from_hex(&privkey_hex).map_err(|_e| Error::ParseError)?;
16,624,329✔
277
        Ok(privkey)
16,624,329✔
278
    }
16,624,329✔
279
}
280

281
pub fn u64_to_sql(x: u64) -> Result<i64, Error> {
1,184,209,854✔
282
    if x > (i64::MAX as u64) {
1,184,209,854✔
283
        return Err(Error::ParseError);
81✔
284
    }
1,184,209,773✔
285
    Ok(x as i64)
1,184,209,773✔
286
}
1,184,209,854✔
287

288
pub fn opt_u64_to_sql(x: Option<u64>) -> Result<Option<i64>, Error> {
184,340✔
289
    match x {
184,340✔
290
        Some(num) => {
×
291
            if num > (i64::MAX as u64) {
×
292
                return Err(Error::ParseError);
×
293
            }
×
294
            Ok(Some(num as i64))
×
295
        }
296
        None => Ok(None),
184,340✔
297
    }
298
}
184,340✔
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(
1,009,287,333✔
320
                value: rusqlite::types::ValueRef,
1,009,287,333✔
321
            ) -> rusqlite::types::FromSqlResult<Self> {
1,009,287,333✔
322
                let hex_str = value.as_str()?;
1,009,287,333✔
323
                let byte_str = stacks_common::util::hash::hex_bytes(hex_str)
1,009,287,333✔
324
                    .map_err(|_e| rusqlite::types::FromSqlError::InvalidType)?;
1,009,287,333✔
325
                let inst = $thing::from_bytes(&byte_str)
1,009,287,333✔
326
                    .ok_or(rusqlite::types::FromSqlError::InvalidType)?;
1,009,287,333✔
327
                Ok(inst)
1,009,287,333✔
328
            }
1,009,287,333✔
329
        }
330

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

340
        impl rusqlite::types::ToSql for $thing {
341
            fn to_sql(&self) -> rusqlite::Result<rusqlite::types::ToSqlOutput<'_>> {
20,505,224✔
342
                let hex_str = self.to_hex();
20,505,224✔
343
                Ok(hex_str.into())
20,505,224✔
344
            }
20,505,224✔
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) {
2,533,163✔
367
    if std::env::var("BLOCKSTACK_DB_TRACE") != Ok("1".to_string()) {
2,533,163✔
368
        return;
2,533,163✔
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
}
2,533,163✔
390

391
#[cfg(not(test))]
392
fn log_sql_eqp(_conn: &Connection, _sql_query: &str) {}
1,016,172,918✔
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>
144,761,755✔
396
where
144,761,755✔
397
    P: Params,
144,761,755✔
398
    T: FromRow<T>,
144,761,755✔
399
{
400
    log_sql_eqp(conn, sql_query);
144,761,755✔
401
    let mut stmt = conn.prepare(sql_query)?;
144,761,755✔
402
    let result = stmt.query_and_then(sql_args, |row| T::from_row(row))?;
185,981,263✔
403

404
    result.collect()
144,761,755✔
405
}
144,761,755✔
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>
558,595,919✔
410
where
558,595,919✔
411
    P: Params,
558,595,919✔
412
    T: FromRow<T>,
558,595,919✔
413
{
414
    log_sql_eqp(conn, sql_query);
558,595,919✔
415
    let query_result = conn.query_row_and_then(sql_query, sql_args, |row| T::from_row(row));
558,595,919✔
416
    match query_result {
26,240,390✔
417
        Ok(x) => Ok(Some(x)),
532,355,529✔
418
        Err(Error::SqliteError(sqlite_error::QueryReturnedNoRows)) => Ok(None),
26,240,390✔
419
        Err(e) => Err(e),
×
420
    }
421
}
558,595,919✔
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>(
2,730✔
426
    conn: &Connection,
2,730✔
427
    sql_query: &str,
2,730✔
428
    sql_args: P,
2,730✔
429
) -> Result<Option<T>, Error>
2,730✔
430
where
2,730✔
431
    P: Params,
2,730✔
432
    T: FromRow<T>,
2,730✔
433
{
434
    log_sql_eqp(conn, sql_query);
2,730✔
435
    let mut stmt = conn.prepare(sql_query)?;
2,730✔
436
    let mut result = stmt.query_and_then(sql_args, |row| T::from_row(row))?;
2,730✔
437
    let mut return_value = None;
2,730✔
438
    if let Some(value) = result.next() {
2,730✔
439
        return_value = Some(value?);
2,730✔
440
    }
×
441
    assert!(
2,730✔
442
        result.next().is_none(),
2,730✔
443
        "FATAL: Multiple values returned for query that expected a single result:\n {}",
444
        sql_query
445
    );
446
    Ok(return_value)
2,730✔
447
}
2,730✔
448

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

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

488
    // gather
489
    let mut row_data = vec![];
1,350,250✔
490
    while let Some(row) = rows.next().map_err(Error::SqliteError)? {
2,606,826✔
491
        let next_row = T::from_column(row, column_name)?;
1,256,576✔
492
        row_data.push(next_row);
1,256,576✔
493
    }
494

495
    Ok(row_data)
1,350,250✔
496
}
1,350,250✔
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>(
45✔
501
    conn: &Connection,
45✔
502
    sql_query: &str,
45✔
503
    sql_args: P,
45✔
504
    column_name: &str,
45✔
505
    panic_msg: &str,
45✔
506
) -> Result<Option<T>, Error>
45✔
507
where
45✔
508
    P: Params,
45✔
509
    T: FromColumn<T>,
45✔
510
{
511
    log_sql_eqp(conn, sql_query);
45✔
512
    let mut stmt = conn.prepare(sql_query)?;
45✔
513
    let mut rows = stmt.query(sql_args)?;
45✔
514

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

525
    Ok(result)
45✔
526
}
45✔
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>
843,545✔
530
where
843,545✔
531
    P: Params,
843,545✔
532
{
533
    log_sql_eqp(conn, sql_query);
843,545✔
534
    let mut stmt = conn.prepare(sql_query)?;
843,545✔
535
    let mut rows = stmt.query(sql_args)?;
843,545✔
536
    let mut row_data = None;
843,545✔
537
    while let Some(row) = rows.next().map_err(Error::SqliteError)? {
1,687,090✔
538
        if row_data.is_some() {
843,545✔
539
            return Err(Error::Overflow);
×
540
        }
843,545✔
541
        let i: i64 = row.get(0)?;
843,545✔
542
        row_data = Some(i);
843,545✔
543
    }
544

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

548
pub fn query_count<P>(conn: &Connection, sql_query: &str, sql_args: P) -> Result<i64, Error>
738,262✔
549
where
738,262✔
550
    P: Params,
738,262✔
551
{
552
    query_int(conn, sql_query, sql_args)
738,262✔
553
}
738,262✔
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(
73,664,943✔
558
    conn: &Connection,
73,664,943✔
559
    pragma_name: &str,
73,664,943✔
560
    pragma_value: &dyn ToSql,
73,664,943✔
561
) -> Result<(), Error> {
73,664,943✔
562
    inner_sql_pragma(conn, pragma_name, pragma_value).map_err(Error::SqliteError)
73,664,943✔
563
}
73,664,943✔
564

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

573
/// Run a VACUUM command
574
pub fn sql_vacuum(conn: &Connection) -> Result<(), Error> {
5,478✔
575
    conn.execute("VACUUM", NO_PARAMS)
5,478✔
576
        .map_err(Error::SqliteError)
5,478✔
577
        .map(|_| ())
5,478✔
578
}
5,478✔
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> {
10,391,375✔
583
    let sql = "SELECT name FROM sqlite_master WHERE type='table' AND name=?";
10,391,375✔
584
    conn.query_row(sql, &[table_name], |row| row.get::<_, String>(0))
10,391,375✔
585
        .optional()
10,391,375✔
586
        .map(|r| r.is_some())
10,391,375✔
587
}
10,391,375✔
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> {
12,668,697✔
592
    let mut path = PathBuf::from(path_str);
12,668,697✔
593
    match fs::metadata(path_str) {
12,668,697✔
594
        Ok(md) => {
12,665,886✔
595
            if !md.is_dir() {
12,665,886✔
596
                error!("Not a directory: {:?}", path);
×
597
                return Err(Error::ExistsError);
×
598
            }
12,665,886✔
599
        }
600
        Err(e) => {
2,811✔
601
            if e.kind() != io::ErrorKind::NotFound {
2,811✔
602
                return Err(Error::IOError(e));
×
603
            }
2,811✔
604
            fs::create_dir_all(path_str).map_err(Error::IOError)?;
2,811✔
605
        }
606
    }
607

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

611
    Ok(marf_path)
12,668,697✔
612
}
12,668,697✔
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,497,407✔
622
        IndexDBConn { index, context }
71,497,407✔
623
    }
71,497,407✔
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(
25,842,822✔
627
        &self,
25,842,822✔
628
        block_height: u64,
25,842,822✔
629
        tip_block_hash: &T,
25,842,822✔
630
    ) -> Result<Option<T>, Error> {
25,842,822✔
631
        get_ancestor_block_hash(self.index, block_height, tip_block_hash)
25,842,822✔
632
    }
25,842,822✔
633

634
    /// Get the height of an ancestor block, if it is indeed the ancestor.
635
    pub fn get_ancestor_block_height(
36✔
636
        &self,
36✔
637
        ancestor_block_hash: &T,
36✔
638
        tip_block_hash: &T,
36✔
639
    ) -> Result<Option<u64>, Error> {
36✔
640
        get_ancestor_block_height(&self.index, ancestor_block_hash, tip_block_hash)
36✔
641
    }
36✔
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,108,233✔
645
        let mut connection = self.index.reopen_connection()?;
15,108,233✔
646
        get_indexed(&mut connection, header_hash, key)
15,108,233✔
647
    }
15,108,233✔
648

649
    pub fn conn(&self) -> &DBConn {
128,063,592✔
650
        self.index.sqlite_conn()
128,063,592✔
651
    }
128,063,592✔
652
}
653

654
impl<C, T: MarfTrieId> Deref for IndexDBConn<'_, C, T> {
655
    type Target = DBConn;
656
    fn deref(&self) -> &DBConn {
77,854,636✔
657
        self.conn()
77,854,636✔
658
    }
77,854,636✔
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> {
13,819,122✔
670
        self.tx()
13,819,122✔
671
    }
13,819,122✔
672
}
673

674
impl<'a, C: Clone, T: MarfTrieId> DerefMut for IndexDBTx<'a, C, T> {
675
    fn deref_mut(&mut self) -> &mut DBTx<'a> {
445,963✔
676
        self.tx_mut()
445,963✔
677
    }
445,963✔
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 {
182,484✔
682
    stacks_common::util::db::tx_busy_handler(run_count)
182,484✔
683
}
182,484✔
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> {
57,502,803✔
689
    tx_begin_immediate_sqlite(conn).map_err(Error::from)
57,502,803✔
690
}
57,502,803✔
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> {
58,362,519✔
697
    conn.busy_handler(Some(tx_busy_handler))?;
58,362,519✔
698
    let tx = Transaction::new(conn, TransactionBehavior::Immediate)?;
58,362,519✔
699
    update_lock_table(tx.deref());
58,362,519✔
700
    Ok(tx)
58,362,519✔
701
}
58,362,519✔
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>>(
112,575,374✔
725
    path: P,
112,575,374✔
726
    flags: OpenFlags,
112,575,374✔
727
) -> Result<Connection, sqlite_error> {
112,575,374✔
728
    Connection::open_with_flags(path, flags)
112,575,374✔
729
}
112,575,374✔
730

731
/// Open a database connection and set some typically-used pragmas
732
pub fn sqlite_open<P: AsRef<Path>>(
112,575,374✔
733
    path: P,
112,575,374✔
734
    flags: OpenFlags,
112,575,374✔
735
    foreign_keys: bool,
112,575,374✔
736
) -> Result<Connection, sqlite_error> {
112,575,374✔
737
    let db = inner_connection_open(path, flags)?;
112,575,374✔
738
    db.busy_handler(Some(tx_busy_handler))?;
112,572,845✔
739
    inner_sql_pragma(&db, "journal_mode", &"WAL")?;
112,572,845✔
740
    inner_sql_pragma(&db, "synchronous", &"NORMAL")?;
112,572,845✔
741
    if foreign_keys {
112,572,845✔
742
        inner_sql_pragma(&db, "foreign_keys", &true)?;
10,861,532✔
743
    }
101,711,313✔
744
    Ok(db)
112,572,845✔
745
}
112,575,374✔
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>(
25,842,822✔
749
    index: &MARF<T>,
25,842,822✔
750
    block_height: u64,
25,842,822✔
751
    tip_block_hash: &T,
25,842,822✔
752
) -> Result<Option<T>, Error> {
25,842,822✔
753
    let block_height = block_height
25,842,822✔
754
        .try_into()
25,842,822✔
755
        .map_err(|_e| Error::BlockHeightOutOfRange)?;
25,842,822✔
756
    let mut read_only = index.reopen_connection()?;
25,842,822✔
757
    let bh = read_only.get_block_at_height(block_height, tip_block_hash)?;
25,842,822✔
758
    Ok(bh)
25,842,822✔
759
}
25,842,822✔
760

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

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

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

796
    Ok(value)
14,857,685✔
797
}
14,857,685✔
798

799
/// Get a value from the fork index
800
fn get_indexed<T: MarfTrieId, M: MarfConnection<T>>(
20,749,771✔
801
    index: &mut M,
20,749,771✔
802
    header_hash: &T,
20,749,771✔
803
    key: &str,
20,749,771✔
804
) -> Result<Option<String>, Error> {
20,749,771✔
805
    match index.get(header_hash, key) {
20,749,771✔
806
        Ok(Some(marf_value)) => {
14,857,685✔
807
            let value = load_indexed(index.sqlite_conn(), &marf_value)?
14,857,685✔
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));
14,857,685✔
809
            Ok(Some(value))
14,857,685✔
810
        }
811
        Ok(None) => Ok(None),
5,892,086✔
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
}
20,749,771✔
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,154,001✔
825
        let tx = index
4,154,001✔
826
            .begin_tx()
4,154,001✔
827
            .expect("BUG: failure to begin MARF transaction");
4,154,001✔
828
        IndexDBTx {
4,154,001✔
829
            _index: Some(tx),
4,154,001✔
830
            block_linkage: None,
4,154,001✔
831
            context,
4,154,001✔
832
        }
4,154,001✔
833
    }
4,154,001✔
834

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

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

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

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

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

5,550✔
865
            PRIMARY KEY(value_hash)
5,550✔
866
        );
5,550✔
867
        "#,
5,550✔
868
                NO_PARAMS,
5,550✔
869
            )
5,550✔
870
            .map_err(Error::SqliteError)?;
5,550✔
871
        Ok(())
5,550✔
872
    }
5,550✔
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,029,570✔
876
        &mut self,
7,029,570✔
877
        block_height: u64,
7,029,570✔
878
        tip_block_hash: &T,
7,029,570✔
879
    ) -> Result<Option<T>, Error> {
7,029,570✔
880
        self.index_mut()
7,029,570✔
881
            .get_block_at_height(
7,029,570✔
882
                block_height.try_into().expect("Height > u32::max()"),
7,029,570✔
883
                tip_block_hash,
7,029,570✔
884
            )
885
            .map_err(Error::from)
7,029,570✔
886
    }
7,029,570✔
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,093,898✔
903
        let marf_value = MARFValue::from_value(value);
2,093,898✔
904
        self.tx().execute(
2,093,898✔
905
            "INSERT OR REPLACE INTO __fork_storage (value_hash, value) VALUES (?1, ?2)",
2,093,898✔
906
            &[&to_hex(&marf_value.to_vec()), value],
2,093,898✔
907
        )?;
2,093,898✔
908
        Ok(marf_value)
2,093,898✔
909
    }
2,093,898✔
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> {
5,641,538✔
913
        get_indexed(self.index_mut(), header_hash, key)
5,641,538✔
914
    }
5,641,538✔
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> {
×
918
        let mut ro_index = self.index().reopen_readonly()?;
×
919
        get_indexed(&mut ro_index, header_hash, key)
×
920
    }
×
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(
688,415✔
926
        &mut self,
688,415✔
927
        parent_header_hash: &T,
688,415✔
928
        header_hash: &T,
688,415✔
929
        keys: &[String],
688,415✔
930
        values: &[String],
688,415✔
931
    ) -> Result<TrieHash, Error> {
688,415✔
932
        assert_eq!(keys.len(), values.len());
688,415✔
933
        match self.block_linkage {
688,415✔
934
            None => {
935
                self.index_mut().begin(parent_header_hash, header_hash)?;
688,415✔
936
                self.block_linkage = Some((parent_header_hash.clone(), header_hash.clone()));
688,415✔
937
            }
938
            Some(_) => panic!("Tried to put_indexed_all twice!"),
×
939
        }
940

941
        let mut marf_values = Vec::with_capacity(values.len());
688,415✔
942
        for value in values.iter() {
2,093,907✔
943
            let marf_value = self.store_indexed(value)?;
2,093,898✔
944
            marf_values.push(marf_value);
2,093,898✔
945
        }
946

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

952
    /// Commit the MARF transaction
953
    pub fn commit(mut self) -> Result<(), Error> {
3,059,615✔
954
        self.block_linkage = None;
3,059,615✔
955
        debug!("Indexed-commit: MARF index");
3,059,615✔
956
        let index_tx = self
3,059,615✔
957
            ._index
3,059,615✔
958
            .take()
3,059,615✔
959
            .expect("BUG: MarfTransaction lost, but IndexDBTx still exists");
3,059,615✔
960
        index_tx.commit()?;
3,059,615✔
961
        Ok(())
3,059,615✔
962
    }
3,059,615✔
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,153,965✔
973
        if let Some((ref parent, ref child)) = self.block_linkage {
4,153,965✔
974
            let index_tx = self
×
975
                ._index
×
976
                .take()
×
977
                .expect("BUG: MarfTransaction lost, but IndexDBTx still exists");
×
978
            debug!("Dropping MARF linkage ({},{})", parent, child);
×
979
            index_tx.drop_current();
×
980
        }
4,153,965✔
981
    }
4,153,965✔
982
}
983

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

988
    use super::*;
989

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

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

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

1013
        // foreign keys must be on
1014
        db.pragma_query(None, "foreign_keys", |row| {
×
1015
            let value: i64 = row.get(0)?;
×
1016
            assert_eq!(value, 1);
×
1017
            Ok(())
×
1018
        })
×
1019
        .unwrap();
×
1020
    }
×
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