Skip to content

Commit 4b8c2bf

Browse files
committed
Add read-only mode, do not allow inserts
1 parent 37ee4fd commit 4b8c2bf

File tree

2 files changed

+157
-0
lines changed

2 files changed

+157
-0
lines changed

include/nudb/basic_store.hpp

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,9 +90,14 @@ class basic_store
9090
path_type const& dp_, path_type const& kp_,
9191
path_type const& lp_,
9292
detail::key_file_header const& kh_);
93+
94+
state(File&& df_, File&& kf_,
95+
path_type const& dp_, path_type const& kp_,
96+
detail::key_file_header const& kh_);
9397
};
9498

9599
bool open_ = false;
100+
bool read_only_ = false;
96101

97102
// Use optional because some
98103
// members cannot be default-constructed.
@@ -173,6 +178,19 @@ class basic_store
173178
return open_;
174179
}
175180

181+
/** Returns `true` if the database is read only.
182+
183+
@par Thread safety
184+
185+
Safe to call concurrently with any function
186+
except @ref open.
187+
*/
188+
bool
189+
is_read_only() const
190+
{
191+
return read_only_;
192+
}
193+
176194
/** Return the path to the data file.
177195
178196
@par Requirements
@@ -373,6 +391,31 @@ class basic_store
373391
error_code& ec,
374392
Args&&... args);
375393

394+
/** Open a database in read only mode.
395+
396+
Takes same parameters as open but opens
397+
all files in read mode and disallows
398+
insertions.
399+
400+
*/
401+
template<class... Args>
402+
void
403+
open_read_only(
404+
path_type const& dat_path,
405+
path_type const& key_path,
406+
error_code& ec,
407+
Args&&... args);
408+
409+
/** Open a database directory in read only mode.
410+
411+
*/
412+
template<class... Args>
413+
void
414+
open_read_only(
415+
path_type const& dir_path,
416+
error_code& ec,
417+
Args&&... args);
418+
376419
/** Fetch a value.
377420
378421
The function checks the database for the specified

include/nudb/impl/basic_store.ipp

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,26 @@ state(File&& df_, File&& kf_, File&& lf_,
4747
"File requirements not met");
4848
}
4949

50+
template<class Hasher, class File>
51+
basic_store<Hasher, File>::state::
52+
state(File&& df_, File&& kf_,
53+
path_type const& dp_, path_type const& kp_,
54+
detail::key_file_header const& kh_)
55+
: df(std::move(df_))
56+
, kf(std::move(kf_))
57+
, dp(dp_)
58+
, kp(kp_)
59+
, hasher(kh_.salt)
60+
, p0(kh_.key_size, "p0")
61+
, p1(kh_.key_size, "p1")
62+
, c1(kh_.key_size, kh_.block_size, "c1")
63+
, kh(kh_)
64+
{
65+
static_assert(is_File<File>::value,
66+
"File requirements not met");
67+
}
68+
69+
5070
//------------------------------------------------------------------------------
5171

5272
template<class Hasher, class File>
@@ -214,6 +234,92 @@ open(
214234
ec, args...);
215235
}
216236

237+
template<class Hasher, class File>
238+
template<class... Args>
239+
void
240+
basic_store<Hasher, File>::
241+
open_read_only(
242+
path_type const& dat_path,
243+
path_type const& key_path,
244+
error_code& ec,
245+
Args&&... args)
246+
{
247+
static_assert(is_Hasher<Hasher>::value,
248+
"Hasher requirements not met");
249+
using namespace detail;
250+
BOOST_ASSERT(! is_open());
251+
ec_ = {};
252+
ecb_.store(false);
253+
254+
File df(args...);
255+
File kf(args...);
256+
df.open(file_mode::read, dat_path, ec);
257+
if(ec)
258+
return;
259+
kf.open(file_mode::read, key_path, ec);
260+
if(ec)
261+
return;
262+
263+
dat_file_header dh;
264+
read(df, dh, ec);
265+
if(ec)
266+
return;
267+
verify(dh, ec);
268+
if(ec)
269+
return;
270+
271+
key_file_header kh;
272+
read(kf, kh, ec);
273+
if(ec)
274+
return;
275+
verify<Hasher>(kh, ec);
276+
if(ec)
277+
return;
278+
279+
verify<Hasher>(dh, kh, ec);
280+
if(ec)
281+
return;
282+
283+
boost::optional<state> s;
284+
s.emplace(std::move(df), std::move(kf),
285+
dat_path, key_path, kh);
286+
thresh_ = std::max<std::size_t>(65536UL,
287+
kh.load_factor * kh.capacity);
288+
frac_ = thresh_ / 2;
289+
buckets_ = kh.buckets;
290+
modulus_ = ceil_pow2(kh.buckets);
291+
// VFALCO TODO This could be better
292+
if(buckets_ < 1)
293+
{
294+
ec = error::short_key_file;
295+
return;
296+
}
297+
s_.emplace(std::move(*s));
298+
open_ = true;
299+
read_only_ = true;
300+
ctx_->insert(*this);
301+
}
302+
303+
template<class Hasher, class File>
304+
template<class... Args>
305+
void
306+
basic_store<Hasher, File>::
307+
open_read_only(
308+
path_type const& dir_path,
309+
error_code& ec,
310+
Args&&... args){
311+
BOOST_ASSERT(boost::filesystem::exists(dir_path));
312+
313+
boost::filesystem::path fs_dir_path(dir_path);
314+
315+
boost::filesystem::path dat_path =
316+
fs_dir_path / detail::default_dat_file();
317+
boost::filesystem::path key_path =
318+
fs_dir_path / detail::default_key_file();
319+
320+
open_read_only(dat_path.string(), key_path.string(), ec, args...);
321+
}
322+
217323
template<class Hasher, class File>
218324
void
219325
basic_store<Hasher, File>::
@@ -223,6 +329,10 @@ close(error_code& ec)
223329
{
224330
open_ = false;
225331
ctx_->erase(*this);
332+
333+
if(read_only_)
334+
return;
335+
226336
if(! s_->p1.empty())
227337
{
228338
std::size_t work;
@@ -302,6 +412,7 @@ insert(
302412
using namespace detail;
303413
using namespace std::chrono;
304414
BOOST_ASSERT(is_open());
415+
BOOST_ASSERT(!is_read_only());
305416
if(ecb_)
306417
{
307418
ec = ec_;
@@ -776,6 +887,9 @@ flush()
776887
using namespace std::chrono;
777888
using namespace detail;
778889

890+
if(is_read_only())
891+
return;
892+
779893
#if NUDB_DEBUG_LOG
780894
beast::unit_test::dstream dout{std::cout};
781895
#endif

0 commit comments

Comments
 (0)