Joedb 9.1.4
The Journal-Only Embedded Database
Loading...
Searching...
No Matches
Readonly_Journal.cpp
Go to the documentation of this file.
4
5#include <vector>
6
7#if __cplusplus < 201703L
11#endif
12
13/////////////////////////////////////////////////////////////////////////////
14#define TYPE_MACRO(cpp_type, return_type, type_id, read_method, W)\
15void joedb::Readonly_Journal::perform_update_##type_id(Writable &writable)\
16{\
17 const cpp_type value = read_method();\
18 writable.update_##type_id\
19 (\
20 table_of_last_operation,\
21 record_of_last_operation,\
22 field_of_last_update,\
23 value\
24 );\
25}
26#include "joedb/TYPE_MACRO.h"
27
28/////////////////////////////////////////////////////////////////////////////
30/////////////////////////////////////////////////////////////////////////////
31(
33 Check check
34):
35 file(lock.get_file()),
36 file_version(0),
37 checkpoint_index(0),
38 checkpoint_position(header_size),
39 table_of_last_operation(Table_Id(0)),
40 record_of_last_operation(Record_Id(0)),
41 field_of_last_update(Field_Id(0))
42{
44
45 //
46 // Check the format of an existing joedb file
47 //
48 if (!lock.is_creating_new())
49 {
50 //
51 // First, check for initial "joedb"
52 //
53 if (file.read<uint8_t>() != 'j' ||
54 file.read<uint8_t>() != 'o' ||
55 file.read<uint8_t>() != 'e' ||
56 file.read<uint8_t>() != 'd' ||
57 file.read<uint8_t>() != 'b')
58 {
59 if (check_flag(check, Check::joedb))
60 throw Exception("File does not start by 'joedb'");
61 }
62 else
63 {
64 //
65 // Check version number
66 //
67 file_version = file.read<uint32_t>();
68
69 if (check_flag(check, Check::version))
70 {
71 if (file_version < compatible_version || file_version > version_number)
72 throw Exception("Unsupported format version");
73 }
74
76
77 for (int i = 0; i < 4; i++)
78 lock.pos[i] = file.read<int64_t>();
79
80 read_checkpoint(lock.pos);
81
82 //
83 // Compare to file size (if available)
84 //
85 const int64_t file_size = file.get_size();
86
87 if (file_size > 0)
88 {
90 checkpoint_position = file_size;
91 else
92 {
93 if
94 (
96 file_size > checkpoint_position
97 )
98 {
99 throw Exception
100 (
101 "Checkpoint (" + std::to_string(checkpoint_position) + ") is smaller than file size (" +
102 std::to_string(file_size) + "). This file may contain an aborted transaction. "
103 "'joedb_push file.joedb file fixed.joedb' can be used to truncate it."
104 );
105 }
106
107 if
108 (
110 file_size < checkpoint_position
111 )
112 {
113 throw Exception("Checkpoint is bigger than file size");
114 }
115 }
116 }
117 }
118 }
119}
120
121/////////////////////////////////////////////////////////////////////////////
122void joedb::Readonly_Journal::read_checkpoint
123/////////////////////////////////////////////////////////////////////////////
124(
125 const std::array<int64_t, 4> &pos
126)
127{
128 for (unsigned i = 0; i < 2; i++)
129 {
130 if (pos[2 * i] == pos[2 * i + 1] && pos[2 * i] >= checkpoint_position)
131 {
132 checkpoint_position = pos[2 * i];
133 checkpoint_index = i;
134 }
135 }
136}
137
138/////////////////////////////////////////////////////////////////////////////
139void joedb::Readonly_Journal::pull_without_locking()
140/////////////////////////////////////////////////////////////////////////////
141{
142 const int64_t old_position = file.get_position();
143 std::array<int64_t, 4> pos;
144 file.pread((char *)&pos, sizeof(pos), checkpoint_offset);
145 read_checkpoint(pos);
146 file.set_position(old_position);
147}
148
149/////////////////////////////////////////////////////////////////////////////
151/////////////////////////////////////////////////////////////////////////////
152{
153 file.shared_lock_head();
154 pull_without_locking();
155 file.unlock_head();
156}
157
158/////////////////////////////////////////////////////////////////////////////
160/////////////////////////////////////////////////////////////////////////////
161{
162 rewind();
163 play_until_checkpoint(writable);
164}
165
166/////////////////////////////////////////////////////////////////////////////
168/////////////////////////////////////////////////////////////////////////////
169(
170 Writable &writable
171)
172{
173 rewind();
174 while(file.get_position() < checkpoint_position)
175 {
176 one_step(writable);
177 writable.comment(std::to_string(file.get_position()));
178 }
179 writable.default_checkpoint();
180}
181
182/////////////////////////////////////////////////////////////////////////////
184/////////////////////////////////////////////////////////////////////////////
185{
186 file.set_position(header_size);
187}
188
189/////////////////////////////////////////////////////////////////////////////
191/////////////////////////////////////////////////////////////////////////////
192{
193 file.set_position(position);
194}
195
196/////////////////////////////////////////////////////////////////////////////
198/////////////////////////////////////////////////////////////////////////////
199{
200 while(file.get_position() < end)
201 one_step(writable);
202 file.set_position(file.get_position()); // get ready for writing
203}
204
205/////////////////////////////////////////////////////////////////////////////
207/////////////////////////////////////////////////////////////////////////////
208{
209 return file.get_position() >= checkpoint_position;
210}
211
212/////////////////////////////////////////////////////////////////////////////
214/////////////////////////////////////////////////////////////////////////////
215{
216 switch(file.read<operation_t>())
217 {
218 case operation_t::create_table:
219 {
220 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.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.read_strong_type<Table_Id>();
235 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.read_strong_type<Table_Id>();
243 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.read_strong_type<Table_Id>();
252 const Field_Id field_id = file.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.read_strong_type<Table_Id>();
260 const Field_Id field_id = file.read_strong_type<Field_Id>();
261 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.read_strong_type<Table_Id>();
269 const Record_Id record_id = file.read_strong_type<Record_Id>();
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::insert_vector:
277 {
278 const Table_Id table_id = file.read_strong_type<Table_Id>();
279 const Record_Id record_id = file.read_strong_type<Record_Id>();
280 const size_t size = file.compact_read<size_t>();
281 writable.insert_vector(table_id, record_id, size);
282 table_of_last_operation = table_id;
283 record_of_last_operation = record_id;
284 }
285 break;
286
287 case operation_t::append:
288 writable.insert_into(table_of_last_operation,
289 ++record_of_last_operation);
290 break;
291
292 case operation_t::delete_from:
293 {
294 const Table_Id table_id = file.read_strong_type<Table_Id>();
295 const Record_Id record_id = file.read_strong_type<Record_Id>();
296 writable.delete_from(table_id, record_id);
297 }
298 break;
299
300 #define TYPE_MACRO(cpp_type, return_type, type_id, read_method, W)\
301 case operation_t::update_##type_id:\
302 table_of_last_operation = file.read_strong_type<Table_Id>();\
303 record_of_last_operation = file.read_strong_type<Record_Id>();\
304 field_of_last_update = file.read_strong_type<Field_Id>();\
305 perform_update_##type_id(writable);\
306 break;\
307\
308 case operation_t::update_last_##type_id:\
309 field_of_last_update = file.read_strong_type<Field_Id>();\
310 perform_update_##type_id(writable);\
311 break;\
312\
313 case operation_t::update_next_##type_id:\
314 ++record_of_last_operation;\
315 perform_update_##type_id(writable);\
316 break;
317 #include "joedb/TYPE_MACRO.h"
318
319 #define TYPE_MACRO(cpp_type, return_type, type_id, read_method, W)\
320 case operation_t::update_vector_##type_id:\
321 {\
322 table_of_last_operation = file.read_strong_type<Table_Id>();\
323 record_of_last_operation = file.read_strong_type<Record_Id>();\
324 field_of_last_update = file.read_strong_type<Field_Id>();\
325 const size_t size = file.compact_read<size_t>();\
326 if (int64_t(size) > checkpoint_position)\
327 throw Exception("update_vector too big");\
328 size_t capacity;\
329 cpp_type *data = writable.get_own_##type_id##_storage\
330 (\
331 table_of_last_operation,\
332 record_of_last_operation,\
333 field_of_last_update,\
334 capacity\
335 );\
336 std::vector<cpp_type> buffer;\
337 if (!data)\
338 {\
339 buffer.resize(size);\
340 data = &buffer[0];\
341 }\
342 else if (to_underlying(record_of_last_operation) <= 0 || to_underlying(record_of_last_operation) + size - 1 > capacity)\
343 throw Exception("update_vector out of range");\
344 read_vector_of_##type_id(data, size);\
345 writable.update_vector_##type_id\
346 (\
347 table_of_last_operation,\
348 record_of_last_operation,\
349 field_of_last_update,\
350 size,\
351 data\
352 );\
353 }\
354 break;
355 #include "joedb/TYPE_MACRO.h"
356
357 case operation_t::custom:
358 {
359 const std::string name = safe_read_string();
360 writable.custom(name);
361 }
362 break;
363
364 case operation_t::comment:
365 {
366 const std::string comment = safe_read_string();
367 writable.comment(comment);
368 }
369 break;
370
371 case operation_t::timestamp:
372 {
373 const int64_t timestamp = file.read<int64_t>();
374 writable.timestamp(timestamp);
375 }
376 break;
377
378 case operation_t::valid_data:
379 writable.valid_data();
380 break;
381
382 case operation_t::blob:
383 {
384 writable.on_blob(Blob(get_position()));
385
386 if (writable.wants_blob_data())
387 writable.write_blob_data(safe_read_string());
388 else
389 {
390 const int64_t size = file.compact_read<int64_t>();
391 file.ignore(size);
392 }
393 }
394 break;
395
396 default:
397 {
398 throw Exception("Unexpected operation: file.get_position() = " + std::to_string(file.get_position()));
399 }
400 }
401}
402
403/////////////////////////////////////////////////////////////////////////////
405/////////////////////////////////////////////////////////////////////////////
406{
407 const Type::Type_Id type_id = Type::Type_Id(file.read<Type_Id_Storage>());
408 if (type_id == Type::Type_Id::reference)
409 return Type::reference(file.read_strong_type<Table_Id>());
410 else
411 return Type(type_id);
412}
413
414/////////////////////////////////////////////////////////////////////////////
416/////////////////////////////////////////////////////////////////////////////
417{
418 return file.safe_read_string(checkpoint_position);
419}
420
421#define TYPE_MACRO(cpp_type, return_type, type_id, read_method, W)\
422void joedb::Readonly_Journal::read_vector_of_##type_id(cpp_type *data, size_t size)\
423{\
424 for (size_t i = 0; i < size; i++)\
425 data[i] = read_method();\
426}
427#define TYPE_MACRO_NO_INT
428#define TYPE_MACRO_NO_FLOAT
429#include "joedb/TYPE_MACRO.h"
430
431#define TYPE_MACRO(cpp_type, return_type, type_id, read_method, W)\
432void joedb::Readonly_Journal::read_vector_of_##type_id(cpp_type *data, size_t size)\
433{\
434 file.read_data((char *)data, size * sizeof(cpp_type));\
435}
436#define TYPE_MACRO_NO_STRING
437#define TYPE_MACRO_NO_REFERENCE
438#define TYPE_MACRO_NO_BLOB
439#include "joedb/TYPE_MACRO.h"
virtual int64_t get_size() const
void set_position(int64_t position)
static constexpr uint32_t version_number
void play_until(Writable &writable, int64_t end)
void one_step(Writable &writable)
void replay_with_checkpoint_comments(Writable &writable)
Readonly_Journal(Journal_Construction_Lock &lock, Check check)
static constexpr uint32_t compatible_version
void set_position(int64_t position)
void replay_log(Writable &writable)
static bool check_flag(Check check, Check flag)
static constexpr int64_t header_size
static Type reference(Table_Id table_id)
Definition Type.h:51
virtual void insert_into(Table_Id table_id, Record_Id record_id)
Definition Writable.h:60
virtual void drop_table(Table_Id table_id)
Definition Writable.h:33
virtual void create_table(const std::string &name)
Definition Writable.h:32
virtual void drop_field(Table_Id table_id, Field_Id field_id)
Definition Writable.h:42
virtual Blob write_blob_data(const std::string &data)
Definition Writable.h:113
virtual void rename_table(Table_Id table_id, const std::string &name)
Definition Writable.h:34
virtual void insert_vector(Table_Id table_id, Record_Id record_id, size_t size)
Definition Writable.h:62
virtual bool wants_blob_data() const
Definition Writable.h:112
virtual void custom(const std::string &name)
Definition Writable.h:50
virtual void add_field(Table_Id table_id, const std::string &name, Type type)
Definition Writable.h:37
virtual void delete_from(Table_Id table_id, Record_Id record_id)
Definition Writable.h:67
virtual void comment(const std::string &comment)
Definition Writable.h:51
virtual void on_blob(Blob blob)
Definition Writable.h:111
virtual void valid_data()
Definition Writable.h:53
virtual void rename_field(Table_Id table_id, Field_Id field_id, const std::string &name)
Definition Writable.h:44
virtual void timestamp(int64_t timestamp)
Definition Writable.h:52
uint8_t Type_Id_Storage
Definition Type.h:11