Joedb 10.3.0
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
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
77 return checkpoint_position;
78 }
79
80 /////////////////////////////////////////////////////////////////////////////
82 /////////////////////////////////////////////////////////////////////////////
83 {
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
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 neg = -checkpoint_position;
120
122 (
123 reinterpret_cast<const char *>(&neg),
124 sizeof(neg),
125 int64_t(sizeof(neg)) * (2 * (hard_index ^ 1) + soft_index)
126 );
127 }
128
129 /////////////////////////////////////////////////////////////////////////////
131 /////////////////////////////////////////////////////////////////////////////
132 {
133 if (hard_checkpoint_position >= position)
134 return;
135
137
138 hard_index ^= 1;
140
142
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
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 {
167 }
168
169 /////////////////////////////////////////////////////////////////////////////
171 /////////////////////////////////////////////////////////////////////////////
172 {
174 }
175
176 /////////////////////////////////////////////////////////////////////////////
177 void Writable_Journal::create_table(const std::string &name)
178 /////////////////////////////////////////////////////////////////////////////
179 {
182 }
183
184 /////////////////////////////////////////////////////////////////////////////
186 /////////////////////////////////////////////////////////////////////////////
187 {
190 }
191
192 /////////////////////////////////////////////////////////////////////////////
194 /////////////////////////////////////////////////////////////////////////////
195 (
196 Table_Id table_id,
197 const std::string &name
198 )
199 {
200 file_buffer.write<operation_t>(operation_t::rename_table);
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 {
214 file_buffer.write<operation_t>(operation_t::add_field);
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 {
230 file_buffer.write<operation_t>(operation_t::drop_field);
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 {
244 file_buffer.write<operation_t>(operation_t::rename_field);
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 {
256 }
257
258 /////////////////////////////////////////////////////////////////////////////
259 void Writable_Journal::comment(const std::string &comment)
260 /////////////////////////////////////////////////////////////////////////////
261 {
264 }
265
266 /////////////////////////////////////////////////////////////////////////////
267 void Writable_Journal::timestamp(int64_t timestamp)
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 {
292 file_buffer.write<operation_t>(operation_t::append);
293 }
294 else
295 {
296 file_buffer.write<operation_t>(operation_t::insert_into);
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 {
313 file_buffer.write<operation_t>(operation_t::delete_from);
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 {
327 file_buffer.write<operation_t>(operation_t::insert_vector);
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 {
345 file_buffer.write<operation_t>(operation_t::delete_vector);
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((const char *)value, size * sizeof(type));\
443 }
444 #include "joedb/TYPE_MACRO.h"
445
446 /////////////////////////////////////////////////////////////////////////////
448 /////////////////////////////////////////////////////////////////////////////
449 (
450 const std::string &data
451 )
452 {
453 file_buffer.write<operation_t>(operation_t::blob);
454 file_buffer.compact_write<size_t>(data.size());
455 const int64_t blob_position = get_position();
456 file_buffer.flush();
457 file_buffer.File_Iterator::write(data.data(), data.size());
458 return Blob(blob_position, int64_t(data.size()));
459 }
460
461 /////////////////////////////////////////////////////////////////////////////
463 /////////////////////////////////////////////////////////////////////////////
464 {
465 if (file.is_shared())
466 {
468 pull_without_locking();
469 }
470 }
471
472 /////////////////////////////////////////////////////////////////////////////
474 /////////////////////////////////////////////////////////////////////////////
475 {
476 if (file.is_shared())
478 }
479
480 /////////////////////////////////////////////////////////////////////////////
482 /////////////////////////////////////////////////////////////////////////////
483 {
485 (
486 Header::joedb.data(),
487 Header::joedb.size(),
488 offsetof(Header, signature)
489 );
490 }
491
492 /////////////////////////////////////////////////////////////////////////////
494 /////////////////////////////////////////////////////////////////////////////
495 {
496 if (ahead_of_checkpoint() > 0)
497 Destructor_Logger::warning("Writable_Journal: ahead of checkpoint");
498 }
499
500 /////////////////////////////////////////////////////////////////////////////
502 /////////////////////////////////////////////////////////////////////////////
503 journal(journal)
504 {
505 if (journal.get_position() > journal.get_checkpoint())
506 throw Exception("locking journal with uncheckpointed data (try rollback?)");
507 journal.lock_pull();
508 }
509
510 /////////////////////////////////////////////////////////////////////////////
512 /////////////////////////////////////////////////////////////////////////////
513 {
514 journal.unlock();
515 }
516}
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(const std::string &message) noexcept
int64_t get_position() const noexcept
Definition File_Buffer.h:75
void compact_write(T x)
Definition File_Buffer.h:99
void write_string(const std::string &s)
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(const std::string &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