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