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