ckb_db/
read_only_db.rs

1//! ReadOnlyDB wrapper base on rocksdb read_only_open mode
2use crate::{Result, internal_error};
3use ckb_db_schema::Col;
4use ckb_logger::info;
5use rocksdb::ops::{GetColumnFamilys, GetPinned, GetPinnedCF, OpenCF};
6use rocksdb::{DBPinnableSlice, Options, ReadOnlyDB as RawReadOnlyDB};
7use std::path::Path;
8use std::sync::Arc;
9
10/// ReadOnlyDB wrapper
11pub struct ReadOnlyDB {
12    pub(crate) inner: Arc<RawReadOnlyDB>,
13}
14
15impl ReadOnlyDB {
16    /// The behavior is similar to DB::Open,
17    /// except that it opens DB in read-only mode.
18    /// One big difference is that when opening the DB as read-only,
19    /// you don't need to specify all Column Families
20    /// -- you can only open a subset of Column Families.
21    pub fn open_cf<P, I, N>(path: P, cf_names: I) -> Result<Option<Self>>
22    where
23        P: AsRef<Path>,
24        I: IntoIterator<Item = N>,
25        N: AsRef<str>,
26    {
27        let opts = Options::default();
28        RawReadOnlyDB::open_cf(&opts, path, cf_names).map_or_else(
29            |err| {
30                let err_str = err.as_ref();
31                // notice: err msg difference
32                if err_str.starts_with("IO error: No such file or directory") {
33                    Ok(None)
34                } else if err_str.starts_with("Corruption:") {
35                    info!("DB corrupted: {err_str}.");
36                    Err(internal_error("DB corrupted"))
37                } else {
38                    Err(internal_error(format!(
39                        "failed to open the database: {err}"
40                    )))
41                }
42            },
43            |db| {
44                Ok(Some(ReadOnlyDB {
45                    inner: Arc::new(db),
46                }))
47            },
48        )
49    }
50
51    /// Return the value associated with a key using RocksDB's PinnableSlice from the default column
52    /// so as to avoid unnecessary memory copy.
53    pub fn get_pinned_default(&self, key: &[u8]) -> Result<Option<DBPinnableSlice>> {
54        self.inner.get_pinned(key).map_err(internal_error)
55    }
56
57    /// Return the value associated with a key using RocksDB's PinnableSlice from the given column
58    /// so as to avoid unnecessary memory copy.
59    pub fn get_pinned(&self, col: Col, key: &[u8]) -> Result<Option<DBPinnableSlice>> {
60        let cf = self
61            .inner
62            .cf_handle(col)
63            .ok_or_else(|| internal_error(format!("column {col} not found")))?;
64        self.inner.get_pinned_cf(cf, key).map_err(internal_error)
65    }
66}