Joedb 10.5.0
The Journal-Only Embedded Database
Loading...
Searching...
No Matches
Writable_Journal.cpp
Go to the documentation of this file.
5#include "joedb/neg.h"
6
7namespace joedb
8{
9 /////////////////////////////////////////////////////////////////////////////
11 /////////////////////////////////////////////////////////////////////////////
12 Readonly_Journal(lock.set_for_writable_journal())
13 {
14 if (file.is_readonly())
15 throw Exception("Cannot create Writable_Journal with read-only file");
16 else if (lock.size == 0)
17 {
18 Header header;
19 header.checkpoint.fill(Header::size);
20 header.version = format_version;
21 header.signature = Header::joedb;
22 file_buffer.File_Iterator::write(reinterpret_cast<const char *>(&header), Header::size);
23 }
24 else if (lock.size > 0)
25 {
26 if
27 (
28 lock.size > checkpoint_position &&
29 lock.recovery != Recovery::overwrite
30 )
31 {
32 throw Exception
33 (
34 "Checkpoint (" + std::to_string(checkpoint_position) +
35 ") is smaller than file size (" + std::to_string(lock.size) +
36 "). This file may contain an aborted transaction. "
37 "'joedb_push file.joedb file fixed.joedb' can be used to truncate it."
38 );
39 }
40
41 // If we have a soft checkpoint that is bigger than file size,
42 // it must not become valid by appending uncheckpointed data to the file.
43 for (size_t i = 0; i < lock.header.checkpoint.size(); ++i)
44 {
45 if (neg(lock.header.checkpoint[i]) > lock.size)
46 {
47 const int64_t fixed = neg(checkpoint_position);
48
49 file.pwrite
50 (
51 reinterpret_cast<const char *>(&fixed),
52 sizeof(fixed),
53 int64_t(sizeof(fixed)) * i
54 );
55
56 file.datasync();
57 }
58 }
59 }
60 }
61
62 /////////////////////////////////////////////////////////////////////////////
64 /////////////////////////////////////////////////////////////////////////////
65 (
66 const Readonly_Journal &journal,
67 const int64_t until
68 )
69 {
70 if (checkpoint_position < until)
71 {
72 const int64_t size = until - checkpoint_position;
73 journal.get_file().copy_to(file, checkpoint_position, size);
74 soft_checkpoint_at(until);
75 }
76
78 }
79
80 /////////////////////////////////////////////////////////////////////////////
82 /////////////////////////////////////////////////////////////////////////////
83 {
84 return file_buffer.get_position() - checkpoint_position;
85 }
86
87 /////////////////////////////////////////////////////////////////////////////
88 void Writable_Journal::start_writing(int64_t position)
89 /////////////////////////////////////////////////////////////////////////////
90 {
91 if (position != checkpoint_position)
92 throw Exception("writing must start at checkpoint position");
93 }
94
95 /////////////////////////////////////////////////////////////////////////////
96 void Writable_Journal::end_writing(int64_t position)
97 /////////////////////////////////////////////////////////////////////////////
98 {
99 if (position != file_buffer.get_position())
100 throw Exception("end_writing position is not matching file position");
101 }
102
103 /////////////////////////////////////////////////////////////////////////////
105 /////////////////////////////////////////////////////////////////////////////
106 {
107 if (checkpoint_position >= position)
108 return;
109
110 file_buffer.flush();
111
112 JOEDB_DEBUG_ASSERT(file.get_size() < 0 || position <= file.get_size());
113
114 soft_index ^= 1;
115 checkpoint_position = position;
116
118
119 const int64_t soft = neg(checkpoint_position);
120
121 file.pwrite
122 (
123 reinterpret_cast<const char *>(&soft),
124 sizeof(soft),
125 int64_t(sizeof(soft)) * (2 * (hard_index ^ 1) + soft_index)
126 );
127 }
128
129 /////////////////////////////////////////////////////////////////////////////
131 /////////////////////////////////////////////////////////////////////////////
132 {
133 if (hard_checkpoint_position >= position)
134 return;
135
136 file_buffer.flush();
137
138 hard_index ^= 1;
140
142
143 file.pwrite
144 (
145 reinterpret_cast<const char *>(&checkpoint_position),
146 sizeof(checkpoint_position),
147 int64_t(sizeof(checkpoint_position)) * (2 * hard_index)
148 );
149
150 file.sync();
151
152 file.pwrite
153 (
154 reinterpret_cast<const char *>(&checkpoint_position),
155 sizeof(checkpoint_position),
156 int64_t(sizeof(checkpoint_position)) * (2 * hard_index + 1)
157 );
158
159 file.datasync();
160 }
161
162 /////////////////////////////////////////////////////////////////////////////
164 /////////////////////////////////////////////////////////////////////////////
165 {
166 soft_checkpoint_at(file_buffer.get_position());
167 }
168
169 /////////////////////////////////////////////////////////////////////////////
171 /////////////////////////////////////////////////////////////////////////////
172 {
173 hard_checkpoint_at(file_buffer.get_position());
174 }
175
176 /////////////////////////////////////////////////////////////////////////////
177 void Writable_Journal::create_table(const std::string &name)
178 /////////////////////////////////////////////////////////////////////////////
179 {
181 file_buffer.write_string(name);
182 }
183
184 /////////////////////////////////////////////////////////////////////////////
186 /////////////////////////////////////////////////////////////////////////////
187 {
189 file_buffer.compact_write<>(to_underlying(table_id));
190 }
191
192 /////////////////////////////////////////////////////////////////////////////
194 /////////////////////////////////////////////////////////////////////////////
195 (
196 Table_Id table_id,
197 const std::string &name
198 )
199 {
201 file_buffer.compact_write<>(to_underlying(table_id));
202 file_buffer.write_string(name);
203 }
204
205 /////////////////////////////////////////////////////////////////////////////
207 /////////////////////////////////////////////////////////////////////////////
208 (
209 Table_Id table_id,
210 const std::string &name,
211 Type type
212 )
213 {
215 file_buffer.compact_write<>(to_underlying(table_id));
216 file_buffer.write_string(name);
217 file_buffer.write<Type_Id_Storage>(Type_Id_Storage(type.get_type_id()));
218 if (type.get_type_id() == Type::Type_Id::reference)
219 file_buffer.compact_write<>(to_underlying(type.get_table_id()));
220 }
221
222 /////////////////////////////////////////////////////////////////////////////
224 /////////////////////////////////////////////////////////////////////////////
225 (
226 Table_Id table_id,
227 Field_Id field_id
228 )
229 {
231 file_buffer.compact_write<>(to_underlying(table_id));
232 file_buffer.compact_write<>(to_underlying(field_id));
233 }
234
235 /////////////////////////////////////////////////////////////////////////////
237 /////////////////////////////////////////////////////////////////////////////
238 (
239 Table_Id table_id,
240 Field_Id field_id,
241 const std::string &name
242 )
243 {
245 file_buffer.compact_write<>(to_underlying(table_id));
246 file_buffer.compact_write<>(to_underlying(field_id));
247 file_buffer.write_string(name);
248 }
249
250 /////////////////////////////////////////////////////////////////////////////
251 void Writable_Journal::custom(const std::string &name)
252 /////////////////////////////////////////////////////////////////////////////
253 {
255 file_buffer.write_string(name);
256 }
257
258 /////////////////////////////////////////////////////////////////////////////
259 void Writable_Journal::comment(const std::string &comment)
260 /////////////////////////////////////////////////////////////////////////////
261 {
263 file_buffer.write_string(comment);
264 }
265
266 /////////////////////////////////////////////////////////////////////////////
268 /////////////////////////////////////////////////////////////////////////////
269 {
271 file_buffer.write<int64_t>(timestamp);
272 }
273
274 /////////////////////////////////////////////////////////////////////////////
276 /////////////////////////////////////////////////////////////////////////////
277 {
279 }
280
281 /////////////////////////////////////////////////////////////////////////////
283 /////////////////////////////////////////////////////////////////////////////
284 (
285 Table_Id table_id,
286 Record_Id record_id
287 )
288 {
289 if (table_id == table_of_last_operation &&
290 record_id == record_of_last_operation + 1)
291 {
293 }
294 else
295 {
297 file_buffer.compact_write<>(to_underlying(table_id));
298 file_buffer.write_reference(record_id);
299 }
300
301 table_of_last_operation = table_id;
302 record_of_last_operation = record_id;
303 }
304
305 /////////////////////////////////////////////////////////////////////////////
307 /////////////////////////////////////////////////////////////////////////////
308 (
309 Table_Id table_id,
310 Record_Id record_id
311 )
312 {
314 file_buffer.compact_write<>(to_underlying(table_id));
315 file_buffer.write_reference(record_id);
316 }
317
318 /////////////////////////////////////////////////////////////////////////////
320 /////////////////////////////////////////////////////////////////////////////
321 (
322 Table_Id table_id,
323 Record_Id record_id,
324 size_t size
325 )
326 {
328 file_buffer.compact_write<>(to_underlying(table_id));
329 file_buffer.write_reference(record_id);
330 file_buffer.compact_write<>(size);
331
332 table_of_last_operation = table_id;
333 record_of_last_operation = record_id;
334 }
335
336 /////////////////////////////////////////////////////////////////////////////
338 /////////////////////////////////////////////////////////////////////////////
339 (
340 Table_Id table_id,
341 Record_Id record_id,
342 size_t size
343 )
344 {
346 file_buffer.compact_write<>(to_underlying(table_id));
347 file_buffer.write_reference(record_id);
348 file_buffer.compact_write<>(size);
349 }
350
351 /////////////////////////////////////////////////////////////////////////////
352 void Writable_Journal::generic_update
353 /////////////////////////////////////////////////////////////////////////////
354 (
355 Table_Id table_id,
356 Record_Id record_id,
357 Field_Id field_id,
358 operation_t operation
359 )
360 {
361 if
362 (
363 table_id == table_of_last_operation &&
364 record_id == record_of_last_operation
365 )
366 {
367 constexpr int last =
368 int(operation_t::update_last_int8) -
369 int(operation_t::update_int8);
370
371 file_buffer.write<operation_t>(operation_t(int(operation) + last));
372 file_buffer.compact_write<>(to_underlying(field_id));
373 field_of_last_update = field_id;
374 }
375 else if
376 (
377 table_id == table_of_last_operation &&
378 record_id == record_of_last_operation + 1 &&
379 field_id == field_of_last_update
380 )
381 {
382 constexpr int next =
383 int(operation_t::update_next_int8) -
384 int(operation_t::update_int8);
385 file_buffer.write<operation_t>(operation_t(int(operation) + next));
386 ++record_of_last_operation;
387 }
388 else
389 {
390 file_buffer.write<operation_t>(operation);
391 file_buffer.compact_write<>(to_underlying(table_id));
392 file_buffer.write_reference(record_id);
393 file_buffer.compact_write<>(to_underlying(field_id));
394 table_of_last_operation = table_id;
395 record_of_last_operation = record_id;
396 field_of_last_update = field_id;
397 }
398 }
399
400 /////////////////////////////////////////////////////////////////////////////
401 #define TYPE_MACRO(type, return_type, type_id, R, write_method)\
402 void Writable_Journal::update_##type_id\
403 (\
404 Table_Id table_id,\
405 Record_Id record_id,\
406 Field_Id field_id,\
407 return_type value\
408 )\
409 {\
410 generic_update(table_id, record_id, field_id, operation_t::update_##type_id);\
411 file_buffer.write_method(value);\
412 }\
413 void Writable_Journal::update_vector_##type_id\
414 (\
415 Table_Id table_id,\
416 Record_Id record_id,\
417 Field_Id field_id,\
418 size_t size,\
419 const type *value\
420 )\
421 {\
422 file_buffer.write<operation_t>(operation_t::update_vector_##type_id);\
423 file_buffer.compact_write<>(to_underlying(table_id));\
424 file_buffer.write_reference(record_id);\
425 file_buffer.compact_write<>(to_underlying(field_id));\
426 file_buffer.compact_write<>(size);\
427 table_of_last_operation = table_id;\
428 record_of_last_operation = record_id;\
429 field_of_last_update = field_id;\
430 \
431 if constexpr\
432 (\
433 Type::Type_Id::type_id == Type::Type_Id::blob ||\
434 Type::Type_Id::type_id == Type::Type_Id::string ||\
435 Type::Type_Id::type_id == Type::Type_Id::reference\
436 )\
437 {\
438 for (size_t i = 0; i < size; i++)\
439 file_buffer.write_method(value[i]);\
440 }\
441 else\
442 file_buffer.write_data(reinterpret_cast<const char *>(value), size * sizeof(type));\
443 }
444 #include "joedb/TYPE_MACRO.h"
445
446 /////////////////////////////////////////////////////////////////////////////
447 Blob Writable_Journal::write_blob(std::string_view data)
448 /////////////////////////////////////////////////////////////////////////////
449 {
451 file_buffer.compact_write<size_t>(data.size());
452 const int64_t blob_position = get_position();
453 file_buffer.flush();
454 file_buffer.File_Iterator::write(data.data(), data.size());
455 return Blob(blob_position, int64_t(data.size()));
456 }
457
458 /////////////////////////////////////////////////////////////////////////////
460 /////////////////////////////////////////////////////////////////////////////
461 {
462 if (file.is_shared())
463 {
464 File_Tail_Lock lock(&file);
465 pull_without_locking();
466 lock.release();
467 }
468 }
469
470 /////////////////////////////////////////////////////////////////////////////
472 /////////////////////////////////////////////////////////////////////////////
473 {
474 if (file.is_shared())
475 file.unlock_tail();
476 }
477
478 /////////////////////////////////////////////////////////////////////////////
480 /////////////////////////////////////////////////////////////////////////////
481 {
482 file.pwrite
483 (
484 Header::joedb.data(),
485 Header::joedb.size(),
486 offsetof(Header, signature)
487 );
488 }
489
490 /////////////////////////////////////////////////////////////////////////////
492 /////////////////////////////////////////////////////////////////////////////
493 {
494 if (ahead_of_checkpoint() > 0)
495 Global_Logger::warning("Writable_Journal destructor: ahead of checkpoint");
496 }
497
498 /////////////////////////////////////////////////////////////////////////////
500 /////////////////////////////////////////////////////////////////////////////
501 journal(&journal)
502 {
503 if (journal.get_position() > journal.get_checkpoint())
504 throw Exception("locking journal with uncheckpointed data (try rollback?)");
505 journal.lock_pull();
506 }
507
508 /////////////////////////////////////////////////////////////////////////////
510 /////////////////////////////////////////////////////////////////////////////
511 journal(other.journal)
512 {
513 other.journal = nullptr;
514 }
515
516 /////////////////////////////////////////////////////////////////////////////
518 /////////////////////////////////////////////////////////////////////////////
519 {
520 if (journal)
521 journal->unlock();
522 }
523}
static void warning(beman::cstring_view message) noexcept
Readonly_Journal(Journal_Construction_Lock &lock)
Tail_Exclusive_Lock(Writable_Journal &journal)
int64_t pull_from(const Readonly_Journal &journal, int64_t until)
void add_field(Table_Id table_id, const std::string &name, Type type) override
void delete_vector(Table_Id table_id, Record_Id record_id, size_t size) override
void delete_from(Table_Id table_id, Record_Id record_id) override
int64_t ahead_of_checkpoint() const noexcept
void insert_vector(Table_Id table_id, Record_Id record_id, size_t size) override
void soft_checkpoint_at(int64_t position)
void start_writing(int64_t position) override
int64_t get_position() const override
void custom(const std::string &name) override
void end_writing(int64_t position) override
void create_table(const std::string &name) override
void rename_table(Table_Id table_id, const std::string &name) override
void drop_table(Table_Id table_id) override
void rename_field(Table_Id table_id, Field_Id field_id, const std::string &name) override
void insert_into(Table_Id table_id, Record_Id record_id) override
Blob write_blob(std::string_view data) override
void timestamp(int64_t timestamp) override
void drop_field(Table_Id table_id, Field_Id field_id) override
void hard_checkpoint_at(int64_t position)
void comment(const std::string &comment) override
#define JOEDB_DEBUG_ASSERT(x)
assertion tested in debug mode
Definition assert.h:19
uint8_t Type_Id_Storage
Definition Type.h:11
int64_t neg(int64_t x)
Definition neg.h:8
constexpr index_t to_underlying(Record_Id id)
Definition index_types.h:59
static constexpr std::array< char, 5 > joedb
Definition Header.h:17