ckb_rocksdb/
options.rs

1// Copyright 2020 Nervos Core Dev
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use std::{ffi::CStr, path};
16
17use crate::{
18    db_options::{Cache, OptionsMustOutliveDB},
19    ffi, ffi_util, ColumnFamilyDescriptor, Error, Options,
20};
21
22#[derive(Clone)]
23pub struct FullOptions {
24    pub db_opts: Options,
25    pub cf_descriptors: Vec<ColumnFamilyDescriptor>,
26}
27
28impl FullOptions {
29    pub fn load_from_file<P>(
30        file: P,
31        cache_size: Option<usize>,
32        ignore_unknown_options: bool,
33    ) -> Result<Self, Error>
34    where
35        P: AsRef<path::Path>,
36    {
37        Self::load_from_file_with_cache(
38            file,
39            cache_size.map(Cache::new_lru_cache),
40            ignore_unknown_options,
41        )
42    }
43
44    pub fn load_from_file_with_cache<P>(
45        file: P,
46        cache: Option<Cache>,
47        ignore_unknown_options: bool,
48    ) -> Result<Self, Error>
49    where
50        P: AsRef<path::Path>,
51    {
52        let cpath = ffi_util::to_cpath(
53            file,
54            "Failed to convert path to CString when load config file.",
55        )?;
56
57        unsafe {
58            let env = ffi::rocksdb_create_default_env();
59            let result = ffi_try!(ffi::rocksdb_options_load_from_file(
60                cpath.as_ptr(),
61                env,
62                ignore_unknown_options,
63                cache
64                    .as_ref()
65                    .map(|c| c.0.inner.as_ptr())
66                    .unwrap_or_else(|| ffi::rocksdb_null_cache()),
67            ));
68            ffi::rocksdb_env_destroy(env);
69            let db_opts = result.db_opts;
70            let cf_descs = result.cf_descs;
71            let cf_descs_size = ffi::rocksdb_column_family_descriptors_count(cf_descs);
72            let mut cf_descriptors = Vec::new();
73            for index in 0..cf_descs_size {
74                let name_raw = ffi::rocksdb_column_family_descriptors_name(cf_descs, index);
75                let name_cstr = CStr::from_ptr(name_raw as *const _);
76                let name = String::from_utf8_lossy(name_cstr.to_bytes());
77                let cf_opts_inner = ffi::rocksdb_column_family_descriptors_options(cf_descs, index);
78                let outlive = OptionsMustOutliveDB {
79                    row_cache: cache.clone(),
80                    ..Default::default()
81                };
82                let cf_opts = Options {
83                    inner: cf_opts_inner,
84                    outlive,
85                };
86                cf_descriptors.push(ColumnFamilyDescriptor::new(name, cf_opts));
87            }
88            ffi::rocksdb_column_family_descriptors_destroy(cf_descs);
89
90            let outlive = OptionsMustOutliveDB {
91                row_cache: cache,
92                ..Default::default()
93            };
94
95            Ok(Self {
96                db_opts: Options {
97                    inner: db_opts,
98                    outlive,
99                },
100                cf_descriptors,
101            })
102        }
103    }
104
105    /* This method is used to check those column families which are ignored in the options file,
106     * and create column family descriptors with default options for them.
107     *
108     * For example:
109     * If there is only 'cf_A' in the options file, but in fact we need both of 'cf_A' and 'cf_B',
110     * after we use `Self::load_from_file(..)`, we will get only two `ColumnFamilyDescriptors`:
111     * 'default' and 'cf_A'.
112     * Then we can call `full_options.complete_column_families(&["cf_A", "cf_B"])` to add the
113     * `ColumnFamilyDescriptor` for "cf_B" with the "default" column family options.
114     *
115     * Notice:
116     * The "default" column family options is not default column family options.
117     * They are same only if no "default" column family options was provided in the options file.
118     *
119     * If `ignore_unknown_column_families` is `false` and there has column families which were
120     * provided in the options file but not in the `cf_names`, this method will return an error.
121     */
122    pub fn complete_column_families(
123        &mut self,
124        cf_names: &[&str],
125        ignore_unknown_column_families: bool,
126    ) -> Result<(), Error> {
127        let cf_name_default = "default";
128        let mut options_default = None;
129        for cfd in &self.cf_descriptors {
130            if cfd.name == cf_name_default {
131                options_default = Some(cfd.options.clone());
132                continue;
133            }
134            if cf_names.iter().any(|cf_name| &cfd.name == cf_name) {
135                continue;
136            }
137            if !ignore_unknown_column_families {
138                return Err(Error::new(format!(
139                    "an unknown column family named \"{}\"",
140                    cfd.name
141                )));
142            }
143        }
144        if options_default.is_none() {
145            let cf = ColumnFamilyDescriptor::new(cf_name_default, Options::default());
146            options_default = Some(cf.options.clone());
147            self.cf_descriptors.insert(0, cf);
148        }
149        let options_default = options_default.unwrap();
150        for cf_name in cf_names {
151            if cf_name == &cf_name_default {
152                return Err(Error::new(format!(
153                    "don't name a user-defined column family as \"{}\"",
154                    cf_name
155                )));
156            }
157            if self.cf_descriptors.iter().all(|cfd| &cfd.name != cf_name) {
158                let cf = ColumnFamilyDescriptor::new(cf_name.to_owned(), options_default.clone());
159                self.cf_descriptors.push(cf);
160            }
161        }
162        Ok(())
163    }
164}