Joedb 10.4.3
The Journal-Only Embedded Database
Loading...
Searching...
No Matches
Readonly_Journal.cpp
Go to the documentation of this file.
3
4#include <vector>
5
6namespace joedb
7{
8 /////////////////////////////////////////////////////////////////////////////
10 /////////////////////////////////////////////////////////////////////////////
11 {
12 private:
13 Abstract_File &file;
14
15 public:
17 {
18 file.shared_lock_head();
19 }
20
23
25 {
26 file.unlock_head();
27 }
28 };
29}
30
31/////////////////////////////////////////////////////////////////////////////
32#define TYPE_MACRO(cpp_type, return_type, type_id, read_method, W)\
33void joedb::Readonly_Journal::perform_update_##type_id(Writable &writable)\
34{\
35 const cpp_type value = read_method();\
36 writable.update_##type_id\
37 (\
38 table_of_last_operation,\
39 record_of_last_operation,\
40 field_of_last_update,\
41 value\
42 );\
43}
44#include "joedb/TYPE_MACRO.h"
45
46/////////////////////////////////////////////////////////////////////////////
48/////////////////////////////////////////////////////////////////////////////
49{
53}
54
55/////////////////////////////////////////////////////////////////////////////
57/////////////////////////////////////////////////////////////////////////////
58 file(lock.file),
59 file_buffer(file),
60 hard_index(0),
61 soft_index(0),
62 checkpoint_position(Header::size),
63 hard_checkpoint_position(Header::size)
64{
65 if (lock.size != 0)
66 {
67 Header header;
68 file.full_pread((char *)(&header), Header::size, 0);
69
71
73 {
74 if (lock.size > checkpoint_position)
76 }
77 else
78 {
79 if (header.signature != Header::joedb)
80 throw Exception("missing joedb signature");
81
82 if (header.version != format_version)
83 throw Exception("unsupported file format version");
84
85 read_checkpoint(header.checkpoint, lock.size);
86
87 if (lock.size > 0 && lock.size < checkpoint_position)
88 throw Exception("Checkpoint is bigger than file size");
89 }
90 }
91 else if (!lock.is_for_writable_journal())
92 throw Exception("file is empty");
93
95}
96
97/////////////////////////////////////////////////////////////////////////////
98void joedb::Readonly_Journal::read_checkpoint
99/////////////////////////////////////////////////////////////////////////////
100(
101 const std::array<int64_t, 4> &pos,
102 int64_t file_size
103)
104{
105 for (int i = 0; i < 2; i++)
106 {
107 if (pos[2 * i] == pos[2 * i + 1] && pos[2 * i] >= hard_checkpoint_position)
108 {
109 hard_checkpoint_position = pos[2 * i];
110 hard_index = i;
111 }
112
113 for (int j = 0; j < 2; j++)
114 {
115 const int64_t p = pos[2 * i + j];
116 const int64_t neg = (p == INT64_MIN) ? INT64_MIN : -p; // avoid UB
117 if (neg >= checkpoint_position && (file_size < 0 || neg <= file_size))
118 {
119 checkpoint_position = neg;
120 hard_index = i ^ 1;
121 soft_index = j;
122 }
123 }
124 }
125
126 if (hard_checkpoint_position > checkpoint_position)
127 checkpoint_position = hard_checkpoint_position;
128}
129
130/////////////////////////////////////////////////////////////////////////////
131void joedb::Readonly_Journal::pull_without_locking()
132/////////////////////////////////////////////////////////////////////////////
133{
134 std::array<int64_t, 4> pos;
135 file.full_pread((char *)&pos, sizeof(pos), 0);
136 read_checkpoint(pos, -1);
137}
138
139/////////////////////////////////////////////////////////////////////////////
141/////////////////////////////////////////////////////////////////////////////
142{
143 Head_Shared_Lock lock(file);
144 const int64_t old_checkpoint = checkpoint_position;
145 pull_without_locking();
146 return checkpoint_position - old_checkpoint;
147}
148
149/////////////////////////////////////////////////////////////////////////////
151/////////////////////////////////////////////////////////////////////////////
152{
153 rewind();
154 play_until_checkpoint(writable);
155}
156
157/////////////////////////////////////////////////////////////////////////////
159/////////////////////////////////////////////////////////////////////////////
160(
161 Writable &writable
162)
163{
164 rewind();
165 writable.start_writing(get_position());
166 while(get_position() < checkpoint_position)
167 {
168 one_step(writable);
169 writable.comment(std::to_string(get_position()));
170 }
171 writable.end_writing(get_position());
172 file_buffer.flush();
173}
174
175/////////////////////////////////////////////////////////////////////////////
177/////////////////////////////////////////////////////////////////////////////
178{
179 file_buffer.set_position(Header::size);
180 reset_context();
181}
182
183/////////////////////////////////////////////////////////////////////////////
185/////////////////////////////////////////////////////////////////////////////
186{
187 while (get_position() < end)
188 one_step(writable);
189}
190
191/////////////////////////////////////////////////////////////////////////////
193/////////////////////////////////////////////////////////////////////////////
194{
195 if (get_position() < end)
196 {
197 const int64_t writable_position = writable.get_position();
198 if (get_position() < writable_position)
199 {
200 Dummy_Writable dummy_writable;
201 raw_play_until(dummy_writable, writable_position);
202 }
203 writable.start_writing(get_position());
204 raw_play_until(writable, end);
205 writable.end_writing(get_position());
206 writable.soft_checkpoint();
207 }
208
209 file_buffer.flush();
210}
211
212/////////////////////////////////////////////////////////////////////////////
214/////////////////////////////////////////////////////////////////////////////
215{
216 switch(file_buffer.read<operation_t>())
217 {
218 case operation_t::create_table:
219 {
220 const std::string name = safe_read_string();
221 writable.create_table(name);
222 }
223 break;
224
225 case operation_t::drop_table:
226 {
227 const Table_Id table_id = file_buffer.read_strong_type<Table_Id>();
228 writable.drop_table(table_id);
229 }
230 break;
231
232 case operation_t::rename_table:
233 {
234 const Table_Id table_id = file_buffer.read_strong_type<Table_Id>();
235 const std::string name = safe_read_string();
236 writable.rename_table(table_id, name);
237 }
238 break;
239
240 case operation_t::add_field:
241 {
242 const Table_Id table_id = file_buffer.read_strong_type<Table_Id>();
243 const std::string name = safe_read_string();
244 const Type type = read_type();
245 writable.add_field(table_id, name, type);
246 }
247 break;
248
249 case operation_t::drop_field:
250 {
251 const Table_Id table_id = file_buffer.read_strong_type<Table_Id>();
252 const Field_Id field_id = file_buffer.read_strong_type<Field_Id>();
253 writable.drop_field(table_id, field_id);
254 }
255 break;
256
257 case operation_t::rename_field:
258 {
259 const Table_Id table_id = file_buffer.read_strong_type<Table_Id>();
260 const Field_Id field_id = file_buffer.read_strong_type<Field_Id>();
261 const std::string name = safe_read_string();
262 writable.rename_field(table_id, field_id, name);
263 }
264 break;
265
266 case operation_t::insert_into:
267 {
268 const Table_Id table_id = file_buffer.read_strong_type<Table_Id>();
269 const Record_Id record_id = file_buffer.read_reference();
270 writable.insert_into(table_id, record_id);
271 table_of_last_operation = table_id;
272 record_of_last_operation = record_id;
273 }
274 break;
275
276 case operation_t::delete_from:
277 {
278 const Table_Id table_id = file_buffer.read_strong_type<Table_Id>();
279 const Record_Id record_id = file_buffer.read_reference();
280 writable.delete_from(table_id, record_id);
281 }
282 break;
283
284 case operation_t::insert_vector:
285 {
286 const Table_Id table_id = file_buffer.read_strong_type<Table_Id>();
287 const Record_Id record_id = file_buffer.read_reference();
288 const size_t size = file_buffer.compact_read<size_t>();
289 writable.insert_vector(table_id, record_id, size);
290 table_of_last_operation = table_id;
291 record_of_last_operation = record_id;
292 }
293 break;
294
295 case operation_t::delete_vector:
296 {
297 const Table_Id table_id = file_buffer.read_strong_type<Table_Id>();
298 const Record_Id record_id = file_buffer.read_reference();
299 const size_t size = file_buffer.compact_read<size_t>();
300 writable.delete_vector(table_id, record_id, size);
301 }
302 break;
303
304 case operation_t::append:
305 writable.insert_into(table_of_last_operation, ++record_of_last_operation);
306 break;
307
308 #define TYPE_MACRO(cpp_type, return_type, type_id, read_method, W)\
309 case operation_t::update_##type_id:\
310 table_of_last_operation = file_buffer.read_strong_type<Table_Id>();\
311 record_of_last_operation = file_buffer.read_reference();\
312 field_of_last_update = file_buffer.read_strong_type<Field_Id>();\
313 perform_update_##type_id(writable);\
314 break;\
315\
316 case operation_t::update_last_##type_id:\
317 field_of_last_update = file_buffer.read_strong_type<Field_Id>();\
318 perform_update_##type_id(writable);\
319 break;\
320\
321 case operation_t::update_next_##type_id:\
322 ++record_of_last_operation;\
323 perform_update_##type_id(writable);\
324 break;
325 #include "joedb/TYPE_MACRO.h"
326
327 #define TYPE_MACRO(cpp_type, return_type, type_id, read_method, W)\
328 case operation_t::update_vector_##type_id:\
329 {\
330 table_of_last_operation = file_buffer.read_strong_type<Table_Id>();\
331 record_of_last_operation = file_buffer.read_reference();\
332 field_of_last_update = file_buffer.read_strong_type<Field_Id>();\
333 const size_t size = file_buffer.compact_read<size_t>();\
334 if (size == 0)\
335 break;\
336 if (int64_t(size) > checkpoint_position)\
337 throw Exception("update_vector too big");\
338 size_t capacity;\
339 cpp_type *data = writable.get_own_##type_id##_storage\
340 (\
341 table_of_last_operation,\
342 record_of_last_operation,\
343 field_of_last_update,\
344 capacity\
345 );\
346 std::vector<cpp_type> buffer;\
347 if (!data)\
348 {\
349 buffer.resize(size);\
350 data = &buffer[0];\
351 }\
352 else if (to_underlying(record_of_last_operation) < 0 || to_underlying(record_of_last_operation) + size > capacity)\
353 throw Exception("update_vector out of range");\
354 read_vector_of_##type_id(data, size);\
355 writable.update_vector_##type_id\
356 (\
357 table_of_last_operation,\
358 record_of_last_operation,\
359 field_of_last_update,\
360 size,\
361 data\
362 );\
363 }\
364 break;
365 #include "joedb/TYPE_MACRO.h"
366
367 case operation_t::custom:
368 {
369 const std::string name = safe_read_string();
370 writable.custom(name);
371 }
372 break;
373
374 case operation_t::comment:
375 {
376 const std::string comment = safe_read_string();
377 writable.comment(comment);
378 }
379 break;
380
381 case operation_t::timestamp:
382 {
383 const int64_t timestamp = file_buffer.read<int64_t>();
384 writable.timestamp(timestamp);
385 }
386 break;
387
388 case operation_t::valid_data:
389 writable.valid_data();
390 break;
391
392 case operation_t::blob:
393 {
394 const int64_t size = file_buffer.compact_read<int64_t>();
395 writable.on_blob(Blob(get_position(), size));
396
397 if (writable.wants_blob_data() && size < checkpoint_position)
398 {
399 std::string s;
400 if (size > 0)
401 {
402 s.resize(size);
403 file_buffer.read_data(s.data(), s.size());
404 }
405 writable.write_blob(s);
406 }
407 else
408 file_buffer.ignore(size);
409 }
410 break;
411
412 default:
413 {
414 throw Exception("Unexpected operation: get_position() = " + std::to_string(get_position()));
415 }
416 }
417}
418
419/////////////////////////////////////////////////////////////////////////////
421/////////////////////////////////////////////////////////////////////////////
422{
423 const Type::Type_Id type_id = Type::Type_Id(file_buffer.read<Type_Id_Storage>());
424 if (type_id == Type::Type_Id::reference)
425 return Type::reference(file_buffer.read_strong_type<Table_Id>());
426 else
427 return Type(type_id);
428}
429
430/////////////////////////////////////////////////////////////////////////////
432/////////////////////////////////////////////////////////////////////////////
433{
434 return file_buffer.safe_read_string(checkpoint_position);
435}
436
437#define TYPE_MACRO(cpp_type, return_type, type_id, read_method, W)\
438void joedb::Readonly_Journal::read_vector_of_##type_id(cpp_type *data, size_t size)\
439{\
440 for (size_t i = 0; i < size; i++)\
441 data[i] = read_method();\
442}
443#define TYPE_MACRO_NO_INT
444#define TYPE_MACRO_NO_FLOAT
445#include "joedb/TYPE_MACRO.h"
446
447#define TYPE_MACRO(cpp_type, return_type, type_id, read_method, W)\
448void joedb::Readonly_Journal::read_vector_of_##type_id(cpp_type *data, size_t size)\
449{\
450 file_buffer.read_data((char *)data, size * sizeof(cpp_type));\
451}
452#define TYPE_MACRO_NO_STRING
453#define TYPE_MACRO_NO_REFERENCE
454#define TYPE_MACRO_NO_BLOB
455#include "joedb/TYPE_MACRO.h"
void full_pread(char *data, size_t size, int64_t offset) const
Fill the buffer, or throw if the end of the file is reached.
void unlock_head() noexcept
Writable with empty insert_vector and delete_vector.
Definition Writable.h:120
void set_position(int64_t position)
Head_Shared_Lock(const Head_Shared_Lock &)=delete
Head_Shared_Lock(Abstract_File &file)
Head_Shared_Lock & operator=(const Head_Shared_Lock &)=delete
void play_until(Writable &writable, int64_t end)
void one_step(Writable &writable)
static constexpr uint32_t format_version
void replay_with_checkpoint_comments(Writable &writable)
void replay_log(Writable &writable)
void raw_play_until(Writable &writable, int64_t end)
Readonly_Journal(Journal_Construction_Lock &lock)
static Type reference(Table_Id table_id)
Definition Type.h:52
Superclass with all joedb journal event listeners as virtual functions.
Definition Writable.h:17
virtual void insert_vector(Table_Id table_id, Record_Id record_id, size_t size)=0
Definition Writable.cpp:33
virtual void insert_into(Table_Id table_id, Record_Id record_id)
Definition Writable.h:50
virtual void drop_table(Table_Id table_id)
Definition Writable.h:27
virtual void create_table(const std::string &name)
Definition Writable.h:26
virtual void drop_field(Table_Id table_id, Field_Id field_id)
Definition Writable.h:36
virtual void rename_table(Table_Id table_id, const std::string &name)
Definition Writable.h:28
virtual void delete_vector(Table_Id table_id, Record_Id record_id, size_t size)=0
Definition Writable.cpp:44
virtual void soft_checkpoint()
Definition Writable.h:23
virtual bool wants_blob_data() const
Definition Writable.h:110
virtual void custom(const std::string &name)
Definition Writable.h:44
virtual void start_writing(int64_t position)
Definition Writable.h:20
virtual Blob write_blob(std::string_view data)
Definition Writable.h:111
virtual void add_field(Table_Id table_id, const std::string &name, Type type)
Definition Writable.h:31
virtual void delete_from(Table_Id table_id, Record_Id record_id)
Definition Writable.h:51
virtual void end_writing(int64_t position)
Definition Writable.h:21
virtual int64_t get_position() const
Definition Writable.h:19
virtual void comment(const std::string &comment)
Definition Writable.h:45
virtual void on_blob(Blob blob)
Definition Writable.h:109
virtual void valid_data()
Definition Writable.h:47
virtual void rename_field(Table_Id table_id, Field_Id field_id, const std::string &name)
Definition Writable.h:38
virtual void timestamp(int64_t timestamp)
Definition Writable.h:46
@ ignore_header
use file size as checkpoint
uint8_t Type_Id_Storage
Definition Type.h:11
static constexpr size_t size
Definition Header.h:19
std::array< char, 5 > signature
Definition Header.h:15
static constexpr std::array< char, 5 > joedb
Definition Header.h:17
uint32_t version
Definition Header.h:14
std::array< int64_t, 4 > checkpoint
Definition Header.h:13