Joedb 10.2.1
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 Readonly_Journal(lock.set_for_writable_journal())
12 {
13 if (file.is_readonly())
14 throw Exception("Cannot create Writable_Journal with read-only file");
15 else if (lock.size == 0)
16 {
17 Header header;
18 header.checkpoint.fill(Header::size);
19 header.version = format_version;
20 header.signature = Header::joedb;
21 file_buffer.File_Iterator::write((const char *)(&header), Header::size);
22 }
23 else if
24 (
25 lock.size > 0 &&
28 )
29 {
30 throw Exception
31 (
32 "Checkpoint (" + std::to_string(checkpoint_position) +
33 ") is smaller than file size (" + std::to_string(lock.size) +
34 "). This file may contain an aborted transaction. "
35 "'joedb_push file.joedb file fixed.joedb' can be used to truncate it."
36 );
37 }
38 }
39
40 /////////////////////////////////////////////////////////////////////////////
42 /////////////////////////////////////////////////////////////////////////////
43 (
44 const Readonly_Journal &journal,
45 const int64_t until
46 )
47 {
48 if (checkpoint_position < until)
49 {
50 const int64_t size = until - checkpoint_position;
51 journal.get_file().copy_to(file, checkpoint_position, size);
52 soft_checkpoint_at(until);
53 }
54
55 return checkpoint_position;
56 }
57
58 /////////////////////////////////////////////////////////////////////////////
60 /////////////////////////////////////////////////////////////////////////////
61 {
63 }
64
65 /////////////////////////////////////////////////////////////////////////////
66 void Writable_Journal::start_writing(int64_t position)
67 /////////////////////////////////////////////////////////////////////////////
68 {
69 if (position != checkpoint_position)
70 throw Exception("writing must start at checkpoint position");
71 }
72
73 /////////////////////////////////////////////////////////////////////////////
74 void Writable_Journal::end_writing(int64_t position)
75 /////////////////////////////////////////////////////////////////////////////
76 {
77 if (position != file_buffer.get_position())
78 throw Exception("end_writing position is not matching file position");
79 }
80
81 /////////////////////////////////////////////////////////////////////////////
83 /////////////////////////////////////////////////////////////////////////////
84 {
85 if (checkpoint_position >= position)
86 return;
87
89
90 JOEDB_DEBUG_ASSERT(file.get_size() < 0 || position <= file.get_size());
91
92 soft_index ^= 1;
93 checkpoint_position = position;
94
96
97 const int64_t neg = -checkpoint_position;
98
100 (
101 reinterpret_cast<const char *>(&neg),
102 sizeof(neg),
103 int64_t(sizeof(neg)) * (2 * (hard_index ^ 1) + soft_index)
104 );
105 }
106
107 /////////////////////////////////////////////////////////////////////////////
109 /////////////////////////////////////////////////////////////////////////////
110 {
111 if (hard_checkpoint_position >= position)
112 return;
113
115
116 hard_index ^= 1;
118
120
122 (
123 reinterpret_cast<const char *>(&checkpoint_position),
124 sizeof(checkpoint_position),
125 int64_t(sizeof(checkpoint_position)) * (2 * hard_index)
126 );
127
128 file.sync();
129
131 (
132 reinterpret_cast<const char *>(&checkpoint_position),
133 sizeof(checkpoint_position),
134 int64_t(sizeof(checkpoint_position)) * (2 * hard_index + 1)
135 );
136
137 file.datasync();
138 }
139
140 /////////////////////////////////////////////////////////////////////////////
142 /////////////////////////////////////////////////////////////////////////////
143 {
145 }
146
147 /////////////////////////////////////////////////////////////////////////////
149 /////////////////////////////////////////////////////////////////////////////
150 {
152 }
153
154 /////////////////////////////////////////////////////////////////////////////
155 void Writable_Journal::create_table(const std::string &name)
156 /////////////////////////////////////////////////////////////////////////////
157 {
160 }
161
162 /////////////////////////////////////////////////////////////////////////////
164 /////////////////////////////////////////////////////////////////////////////
165 {
168 }
169
170 /////////////////////////////////////////////////////////////////////////////
172 /////////////////////////////////////////////////////////////////////////////
173 (
174 Table_Id table_id,
175 const std::string &name
176 )
177 {
178 file_buffer.write<operation_t>(operation_t::rename_table);
179 file_buffer.compact_write<>(to_underlying(table_id));
180 file_buffer.write_string(name);
181 }
182
183 /////////////////////////////////////////////////////////////////////////////
185 /////////////////////////////////////////////////////////////////////////////
186 (
187 Table_Id table_id,
188 const std::string &name,
189 Type type
190 )
191 {
192 file_buffer.write<operation_t>(operation_t::add_field);
193 file_buffer.compact_write<>(to_underlying(table_id));
194 file_buffer.write_string(name);
195 file_buffer.write<Type_Id_Storage>(Type_Id_Storage(type.get_type_id()));
196 if (type.get_type_id() == Type::Type_Id::reference)
197 file_buffer.compact_write<>(to_underlying(type.get_table_id()));
198 }
199
200 /////////////////////////////////////////////////////////////////////////////
202 /////////////////////////////////////////////////////////////////////////////
203 (
204 Table_Id table_id,
205 Field_Id field_id
206 )
207 {
208 file_buffer.write<operation_t>(operation_t::drop_field);
209 file_buffer.compact_write<>(to_underlying(table_id));
210 file_buffer.compact_write<>(to_underlying(field_id));
211 }
212
213 /////////////////////////////////////////////////////////////////////////////
215 /////////////////////////////////////////////////////////////////////////////
216 (
217 Table_Id table_id,
218 Field_Id field_id,
219 const std::string &name
220 )
221 {
222 file_buffer.write<operation_t>(operation_t::rename_field);
223 file_buffer.compact_write<>(to_underlying(table_id));
224 file_buffer.compact_write<>(to_underlying(field_id));
225 file_buffer.write_string(name);
226 }
227
228 /////////////////////////////////////////////////////////////////////////////
229 void Writable_Journal::custom(const std::string &name)
230 /////////////////////////////////////////////////////////////////////////////
231 {
234 }
235
236 /////////////////////////////////////////////////////////////////////////////
237 void Writable_Journal::comment(const std::string &comment)
238 /////////////////////////////////////////////////////////////////////////////
239 {
242 }
243
244 /////////////////////////////////////////////////////////////////////////////
245 void Writable_Journal::timestamp(int64_t timestamp)
246 /////////////////////////////////////////////////////////////////////////////
247 {
249 file_buffer.write<int64_t>(timestamp);
250 }
251
252 /////////////////////////////////////////////////////////////////////////////
254 /////////////////////////////////////////////////////////////////////////////
255 {
257 }
258
259 /////////////////////////////////////////////////////////////////////////////
261 /////////////////////////////////////////////////////////////////////////////
262 (
263 Table_Id table_id,
264 Record_Id record_id
265 )
266 {
267 if (table_id == table_of_last_operation &&
268 record_id == record_of_last_operation + 1)
269 {
270 file_buffer.write<operation_t>(operation_t::append);
271 }
272 else
273 {
274 file_buffer.write<operation_t>(operation_t::insert_into);
275 file_buffer.compact_write<>(to_underlying(table_id));
276 file_buffer.write_reference(record_id);
277 }
278
279 table_of_last_operation = table_id;
280 record_of_last_operation = record_id;
281 }
282
283 /////////////////////////////////////////////////////////////////////////////
285 /////////////////////////////////////////////////////////////////////////////
286 (
287 Table_Id table_id,
288 Record_Id record_id
289 )
290 {
291 file_buffer.write<operation_t>(operation_t::delete_from);
292 file_buffer.compact_write<>(to_underlying(table_id));
293 file_buffer.write_reference(record_id);
294 }
295
296 /////////////////////////////////////////////////////////////////////////////
298 /////////////////////////////////////////////////////////////////////////////
299 (
300 Table_Id table_id,
301 Record_Id record_id,
302 size_t size
303 )
304 {
305 file_buffer.write<operation_t>(operation_t::insert_vector);
306 file_buffer.compact_write<>(to_underlying(table_id));
307 file_buffer.write_reference(record_id);
308 file_buffer.compact_write<>(size);
309
310 table_of_last_operation = table_id;
311 record_of_last_operation = record_id;
312 }
313
314 /////////////////////////////////////////////////////////////////////////////
316 /////////////////////////////////////////////////////////////////////////////
317 (
318 Table_Id table_id,
319 Record_Id record_id,
320 size_t size
321 )
322 {
323 file_buffer.write<operation_t>(operation_t::delete_vector);
324 file_buffer.compact_write<>(to_underlying(table_id));
325 file_buffer.write_reference(record_id);
326 file_buffer.compact_write<>(size);
327 }
328
329 /////////////////////////////////////////////////////////////////////////////
330 void Writable_Journal::generic_update
331 /////////////////////////////////////////////////////////////////////////////
332 (
333 Table_Id table_id,
334 Record_Id record_id,
335 Field_Id field_id,
336 operation_t operation
337 )
338 {
339 if
340 (
341 table_id == table_of_last_operation &&
342 record_id == record_of_last_operation
343 )
344 {
345 constexpr int last =
346 int(operation_t::update_last_int8) -
347 int(operation_t::update_int8);
348
349 file_buffer.write<operation_t>(operation_t(int(operation) + last));
350 file_buffer.compact_write<>(to_underlying(field_id));
351 field_of_last_update = field_id;
352 }
353 else if
354 (
355 table_id == table_of_last_operation &&
356 record_id == record_of_last_operation + 1 &&
357 field_id == field_of_last_update
358 )
359 {
360 constexpr int next =
361 int(operation_t::update_next_int8) -
362 int(operation_t::update_int8);
363 file_buffer.write<operation_t>(operation_t(int(operation) + next));
364 ++record_of_last_operation;
365 }
366 else
367 {
368 file_buffer.write<operation_t>(operation);
369 file_buffer.compact_write<>(to_underlying(table_id));
370 file_buffer.write_reference(record_id);
371 file_buffer.compact_write<>(to_underlying(field_id));
372 table_of_last_operation = table_id;
373 record_of_last_operation = record_id;
374 field_of_last_update = field_id;
375 }
376 }
377
378 /////////////////////////////////////////////////////////////////////////////
379 #define TYPE_MACRO(type, return_type, type_id, R, write_method)\
380 void Writable_Journal::update_##type_id\
381 (\
382 Table_Id table_id,\
383 Record_Id record_id,\
384 Field_Id field_id,\
385 return_type value\
386 )\
387 {\
388 generic_update(table_id, record_id, field_id, operation_t::update_##type_id);\
389 file_buffer.write_method(value);\
390 }\
391 void Writable_Journal::update_vector_##type_id\
392 (\
393 Table_Id table_id,\
394 Record_Id record_id,\
395 Field_Id field_id,\
396 size_t size,\
397 const type *value\
398 )\
399 {\
400 file_buffer.write<operation_t>(operation_t::update_vector_##type_id);\
401 file_buffer.compact_write<>(to_underlying(table_id));\
402 file_buffer.write_reference(record_id);\
403 file_buffer.compact_write<>(to_underlying(field_id));\
404 file_buffer.compact_write<>(size);\
405 table_of_last_operation = table_id;\
406 record_of_last_operation = record_id;\
407 field_of_last_update = field_id;\
408 \
409 if constexpr\
410 (\
411 Type::Type_Id::type_id == Type::Type_Id::blob ||\
412 Type::Type_Id::type_id == Type::Type_Id::string ||\
413 Type::Type_Id::type_id == Type::Type_Id::reference\
414 )\
415 {\
416 for (size_t i = 0; i < size; i++)\
417 file_buffer.write_method(value[i]);\
418 }\
419 else\
420 file_buffer.write_data((const char *)value, size * sizeof(type));\
421 }
422 #include "joedb/TYPE_MACRO.h"
423
424 /////////////////////////////////////////////////////////////////////////////
426 /////////////////////////////////////////////////////////////////////////////
427 (
428 const std::string &data
429 )
430 {
431 file_buffer.write<operation_t>(operation_t::blob);
432 file_buffer.compact_write<size_t>(data.size());
433 const int64_t blob_position = get_position();
434 file_buffer.flush();
435 file_buffer.File_Iterator::write(data.data(), data.size());
436 return Blob(blob_position, int64_t(data.size()));
437 }
438
439 /////////////////////////////////////////////////////////////////////////////
441 /////////////////////////////////////////////////////////////////////////////
442 {
443 if (file.is_shared())
444 {
446 pull_without_locking();
447 }
448 }
449
450 /////////////////////////////////////////////////////////////////////////////
452 /////////////////////////////////////////////////////////////////////////////
453 {
454 if (file.is_shared())
456 }
457
458 /////////////////////////////////////////////////////////////////////////////
460 /////////////////////////////////////////////////////////////////////////////
461 {
462 if (ahead_of_checkpoint() > 0)
463 Destructor_Logger::warning("Writable_Journal: ahead of checkpoint");
464 }
465
466 /////////////////////////////////////////////////////////////////////////////
468 /////////////////////////////////////////////////////////////////////////////
469 {
470 if (journal.get_position() > journal.get_checkpoint())
471 throw Exception("locking journal with uncheckpointed data (try rollback?)");
472 journal.lock_pull();
473 }
474
475 /////////////////////////////////////////////////////////////////////////////
477 /////////////////////////////////////////////////////////////////////////////
478 {
479 journal.unlock();
480 }
481}
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)
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.
static void warning(std::string_view 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)
Journal_Lock(Writable_Journal &journal)
static constexpr uint32_t format_version
int64_t get_checkpoint() const
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