Joedb 10.0.0
The Journal-Only Embedded Database
Loading...
Searching...
No Matches
Database_Writable_h.cpp
Go to the documentation of this file.
3#include "joedb/ui/type_io.h"
4
5#include <set>
6
7namespace joedb::generator
8{
9 ////////////////////////////////////////////////////////////////////////////
11 ////////////////////////////////////////////////////////////////////////////
12 (
13 const Compiler_Options &options
14 ):
15 Generator(".", "Database_Writable.h", options)
16 {
17 }
18
19 ////////////////////////////////////////////////////////////////////////////
21 ////////////////////////////////////////////////////////////////////////////
22 {
23 const Database_Schema &db = options.get_db();
24 auto tables = db.get_tables();
25
26 namespace_include_guard(out, "Database_Writable", options.get_name_space());
27
28 out << R"RRR(
29#include "joedb/journal/Writable_Journal.h"
30#include "joedb/journal/Memory_File.h"
31#include "joedb/error/Out_Of_Date.h"
32#include "Database.h"
33
34#include <string>
35#include <stdint.h>
36#include <cstring>
37#include <vector>
38
39)RRR";
40
42
43 out << R"RRR(
44 namespace detail
45 {
46 extern const char * schema_string;
47 inline constexpr size_t schema_string_size = )RRR";
48
49 out << options.schema_file.get_size() << ";\n }\n\n";
50
51 for (const auto &[tid, tname]: tables)
52 out << " class container_of_" << tname << ";\n";
53
54 out << R"RRR(
55 /// implement @ref joedb::Writable in a @ref Database
56 class Database_Writable: public Database, public joedb::Writable
57 {
58 protected:
59 joedb::index_t max_record_id;
60 Table_Id current_table_id = Table_Id{0};
61)RRR";
62
63 //
64 // delete_from writable function
65 //
66 out << '\n';
67 out << " void delete_from(Table_Id table_id, Record_Id record_id) override\n";
68 out << " {\n";
69 {
70 bool first = true;
71 for (const auto &[tid, tname]: tables)
72 {
73 out << " ";
74 if (first)
75 first = false;
76 else
77 out << "else ";
78
79 out << "if (table_id == Table_Id(" << tid << "))\n";
80 out << " internal_delete_" << tname << "(record_id);\n";
81 }
82 }
83 out << " }\n";
84
85 //
86 // insert_into
87 //
88 out << '\n';
89 out << " void insert_into(Table_Id table_id, Record_Id record_id) override\n";
90 out << " {\n";
91 out << " if (to_underlying(record_id) < 0 || (max_record_id && to_underlying(record_id) >= max_record_id))\n";
92 out << " throw_exception(\"insert_into: too big\");\n";
93 {
94 bool first = true;
95 for (const auto &[tid, tname]: tables)
96 {
97 out << " ";
98 if (first)
99 first = false;
100 else
101 out << "else ";
102
103 out << "if (table_id == Table_Id(" << tid << "))\n";
104 out << " {\n";
105 out << " if (is_valid_record_id_for_" << tname << "(record_id))\n";
106 out << " throw_exception(\"Duplicate insert into table " << tname << "\");\n";
107 out << " if (storage_of_" << tname << ".size() <= size_t(record_id))\n";
108 out << " storage_of_" << tname << ".resize(to_underlying(record_id) + 1);\n";
109 out << " internal_insert_" << tname << "(record_id);\n";
110 out << " }\n";
111 }
112 }
113 out << " }\n";
114
115 //
116 // insert_vector
117 //
118 out << R"RRR(
119 void delete_vector
120 (
121 Table_Id table_id,
122 Record_Id record_id,
123 size_t size
124 ) override
125 {
126 joedb::Freedom_Keeper *fk = nullptr;
127
128)RRR";
129
130 {
131 bool first = true;
132 for (const auto &[tid, tname]: tables)
133 {
134 out << " ";
135 if (first)
136 first = false;
137 else
138 out << "else ";
139
140 out << "if (table_id == Table_Id(" << tid << "))\n";
141 out << " fk = &storage_of_" << tname << ".freedom_keeper;\n";
142 }
143 }
144
145 out << R"RRR(
146 if (fk)
147 {
148 JOEDB_RELEASE_ASSERT(fk->is_used_vector(record_id, size));
149 joedb::Writable::delete_vector(table_id, record_id, size);
150 }
151 }
152
153 void insert_vector
154 (
155 Table_Id table_id,
156 Record_Id record_id,
157 size_t size
158 ) override
159 {
160 if
161 (
162 to_underlying(record_id) < 0 ||
163 (max_record_id && (to_underlying(record_id) >= max_record_id || joedb::index_t(size) >= max_record_id))
164 )
165 {
166 throw_exception("insert_vector: null record_id, or too big");
167 }
168)RRR";
169
170 {
171 bool first = true;
172 for (const auto &[tid, tname]: tables)
173 {
174 out << " ";
175 if (first)
176 first = false;
177 else
178 out << "else ";
179
180 out << "if (table_id == Table_Id(" << tid << "))\n";
181 out << " {\n";
182 out << " if (storage_of_" << tname << ".size() < size_t(record_id) + size)\n";
183 out << " storage_of_" << tname << ".resize(to_underlying(record_id) + size);\n";
184 out << " internal_vector_insert_" << tname << "(record_id, size);\n";
185 out << " }\n";
186 }
187 }
188 out << " }\n";
189
190 //
191 // set of existing types in the database
192 //
193 std::set<Type::Type_Id> db_types;
194
195 for (const auto &[tid, tname]: tables)
196 for (const auto &[fid, fname]: db.get_fields(tid))
197 db_types.insert(db.get_field_type(tid, fid).get_type_id());
198
199 //
200 // update
201 //
202 {
203 for (int type_index = 1; type_index < int(Type::type_ids); type_index++)
204 {
205 const Type::Type_Id type_id = Type::Type_Id(type_index);
206 if (db_types.find(type_id) == db_types.end())
207 continue;
208
209 out << '\n';
210 out << " void update_" << get_type_string(type_id) << '\n';
211 out << " (\n";
212 out << " Table_Id table_id,\n";
213 out << " Record_Id record_id,\n";
214 out << " Field_Id field_id,\n";
215 out << " " << get_cpp_type_string(type_id) << " value\n";
216 out << " )\n";
217 out << " override\n";
218 out << " {\n";
219
220 for (const auto &[tid, tname]: tables)
221 {
222 bool has_typed_field = false;
223
224 for (const auto &[fid, fname]: db.get_fields(tid))
225 {
226 const Type &type = db.get_field_type(tid, fid);
227 if (type.get_type_id() == type_id)
228 {
229 has_typed_field = true;
230 break;
231 }
232 }
233
234 if (has_typed_field)
235 {
236 out << " if (table_id == Table_Id(" << tid << "))\n";
237 out << " {\n";
238
239 for (const auto &[fid, fname]: db.get_fields(tid))
240 {
241 const Type &type = db.get_field_type(tid, fid);
242 if (type.get_type_id() == type_id)
243 {
244 out << " if (field_id == Field_Id(" << fid << "))\n";
245 out << " {\n";
246 out << " internal_update_" << tname;
247 out << "__" << fname << "(record_id, ";
248 if (type.get_type_id() != Type::Type_Id::reference)
249 out << "value";
250 else
251 {
252 out << "id_of_" << db.get_table_name(type.get_table_id());
253 out << "(value)";
254 }
255 out << ");\n";
256 out << " return;\n";
257 out << " }\n";
258 }
259 }
260
261 out << " return;\n";
262 out << " }\n";
263 }
264 }
265
266 out << " }\n";
267 }
268 }
269
270 //
271 // update_vector
272 //
273 {
274 for (int type_index = 1; type_index < int(Type::type_ids); type_index++)
275 {
276 const auto type_id = Type::Type_Id(type_index);
277 if (db_types.find(Type::Type_Id(type_id)) == db_types.end())
278 continue;
279
280 out << '\n';
281 out << " void update_vector_" << get_type_string(type_id) << '\n';
282 out << " (\n";
283 out << " Table_Id table_id,\n";
284 out << " Record_Id record_id,\n";
285 out << " Field_Id field_id,\n";
286 out << " size_t size,\n";
287 out << " const " << get_storage_type_string(type_id) << " *value\n";
288 out << " )\n";
289 out << " override\n";
290 out << " {\n";
291
292 for (const auto &[tid, tname]: tables)
293 {
294 bool has_typed_field = false;
295
296 for (const auto &[fid, fname]: db.get_fields(tid))
297 {
298 const Type &type = db.get_field_type(tid, fid);
299 if (type.get_type_id() == type_id)
300 {
301 has_typed_field = true;
302 break;
303 }
304 }
305
306 if (has_typed_field)
307 {
308 out << " if (table_id == Table_Id(" << tid << "))\n";
309 out << " {\n";
310
311 for (const auto &[fid, fname]: db.get_fields(tid))
312 {
313 const Type &type = db.get_field_type(tid, fid);
314 if (type.get_type_id() == type_id)
315 {
316 out << " if (field_id == Field_Id(" << fid << "))\n";
317 out << " {\n";
318 out << " internal_update_vector_" << tname;
319 out << "__" << fname << "(record_id, size, ";
320
321 if (type_id != joedb::Type::Type_Id::reference)
322 out << "value";
323 else
324 {
325 out << "reinterpret_cast<const ";
326 write_type(type, false, false);
327 out << "*>(value)";
328 }
329
330 out << ");\n";
331 out << " return;\n";
332 out << " }\n";
333 }
334 }
335
336 out << " return;\n";
337 out << " }\n";
338 }
339 }
340
341 out << " }\n";
342 }
343 }
344
345 //
346 // get_own_storage
347 //
348 {
349 for (int type_index = 1; type_index < int(Type::type_ids); type_index++)
350 {
351 const Type::Type_Id type_id = Type::Type_Id(type_index);
352 if (db_types.find(Type::Type_Id(type_id)) == db_types.end())
353 continue;
354
355 out << '\n';
356 out << " " << get_storage_type_string(type_id);
357 out << " *get_own_" << get_type_string(type_id) << "_storage\n";
358 out << " (\n";
359 out << " Table_Id table_id,\n";
360 out << " Record_Id record_id,\n";
361 out << " Field_Id field_id,\n";
362 out << " size_t &capacity\n";
363 out << " )\n";
364 out << " override\n";
365 out << " {\n";
366
367 for (const auto &[tid, tname]: tables)
368 {
369 bool has_typed_field = false;
370
371 for (const auto &[fid, fname]: db.get_fields(tid))
372 {
373 const Type &type = db.get_field_type(tid, fid);
374 if (type.get_type_id() == type_id)
375 {
376 has_typed_field = true;
377 break;
378 }
379 }
380
381 if (has_typed_field)
382 {
383 out << " if (table_id == Table_Id(" << tid << "))\n";
384 out << " {\n";
385 out << " capacity = size_t(storage_of_" << tname << ".freedom_keeper.size());\n";
386
387 for (const auto &[fid, fname]: db.get_fields(tid))
388 {
389 const Type &type = db.get_field_type(tid, fid);
390 if (type.get_type_id() == type_id)
391 {
392 out << " if (field_id == Field_Id(" << fid << "))\n"
393 << " {\n"
394 << " return ";
395
396 if (type_id == Type::Type_Id::reference)
397 out << "reinterpret_cast<Record_Id *>";
398
399 out << "(storage_of_" << tname;
400 out << ".field_value_of_" << fname << ".data() + to_underlying(record_id));\n"
401 << " }\n";
402 }
403 }
404
405 out << " return nullptr;\n";
406 out << " }\n";
407 }
408 }
409
410 out << " return nullptr;\n";
411 out << " }\n";
412 }
413 }
414
415 //
416 // Informative events are ignored
417 //
418 out << R"RRR(
419 void comment(const std::string &comment) override {}
420 void timestamp(int64_t timestamp) override {}
421 void valid_data() override {}
422)RRR";
423
424 //
425 // Schema changes are forwarded to the schema string
426 //
427 out << R"RRR(
428 bool upgrading_schema = false;
429 joedb::Memory_File schema_file;
430 joedb::Writable_Journal schema_journal;
431
432 bool requires_schema_upgrade() const
433 {
434 return schema_file.get_data().size() < detail::schema_string_size;
435 }
436
437 void check_schema()
438 {
439 constexpr size_t pos = joedb::Header::size;
440 const size_t schema_file_size = schema_file.get_data().size();
441
442 if
443 (
444 schema_file_size < pos ||
445 schema_file_size > detail::schema_string_size ||
446 std::memcmp
447 (
448 schema_file.get_data().data() + pos,
449 detail::schema_string + pos,
450 schema_file_size - pos
451 ) != 0
452 )
453 {
454 throw_exception("Trying to open a file with incompatible schema");
455 }
456 }
457
458 void create_table(const std::string &name) override
459 {
460 ++current_table_id;
461 schema_journal.create_table(name);
462 schema_journal.soft_checkpoint();
463 }
464
465 void drop_table(Table_Id table_id) override
466 {
467 schema_journal.drop_table(table_id);
468 schema_journal.soft_checkpoint();
469 }
470
471 void rename_table
472 (
473 Table_Id table_id,
474 const std::string &name
475 ) override
476 {
477 schema_journal.rename_table(table_id, name);
478 schema_journal.soft_checkpoint();
479 }
480
481 void add_field
482 (
483 Table_Id table_id,
484 const std::string &name,
485 joedb::Type type
486 ) override
487 {
488 schema_journal.add_field(table_id, name, type);
489 schema_journal.soft_checkpoint();
490 }
491
492 void drop_field(Table_Id table_id, Field_Id field_id) override
493 {
494 schema_journal.drop_field(table_id, field_id);
495 schema_journal.soft_checkpoint();
496 }
497
498 void rename_field
499 (
500 Table_Id table_id,
501 Field_Id field_id,
502 const std::string &name
503 ) override
504 {
505 schema_journal.rename_field(table_id, field_id, name);
506 schema_journal.soft_checkpoint();
507 }
508
509 void custom(const std::string &name) override
510 {
511 schema_journal.custom(name);
512 schema_journal.soft_checkpoint();
513 }
514)RRR";
515
516 //
517 // Public stuff
518 //
519 out << R"RRR(
520 public:
521 Database_Writable():
522 max_record_id(0),
523 schema_journal(schema_file)
524 {}
525
526 void set_max_record_id(joedb::index_t record_id)
527 {
528 max_record_id = record_id;
529 }
530
531 int64_t get_schema_checkpoint() const
532 {
533 return schema_journal.get_checkpoint();
534 }
535
536 void initialize_with_readonly_journal(joedb::Readonly_Journal &journal)
537 {
538 max_record_id = size_t(journal.get_checkpoint());
539 journal.replay_log(*this);
540 max_record_id = 0;
541
542 check_schema();
543
544 if (requires_schema_upgrade())
545 throw_exception<joedb::Out_Of_Date>("Schema is out of date. Can't upgrade a read-only database.");
546 }
547 };
548)RRR";
549
551 out << "\n#endif\n";
552 }
553}
const std::vector< std::string > & get_name_space() const
const Database & get_db() const
const std::map< Table_Id, std::string > & get_tables() const override
const Type & get_field_type(Table_Id table_id, Field_Id field_id) const override
const std::map< Field_Id, std::string > & get_fields(Table_Id table_id) const override
int64_t get_size() const override
Get the size of the file, or -1 if it is unknown.
Definition Memory_File.h:24
const std::string & get_table_name(Table_Id table_id) const
Definition Readable.cpp:38
Table_Id get_table_id() const
Definition Type.h:42
@ type_ids
Definition Type.h:25
Type_Id get_type_id() const
Definition Type.h:41
Database_Writable_h(const Compiler_Options &options)
static const char * get_storage_type_string(Type type)
static const char * get_type_string(Type type)
void write_type(Type type, bool return_type, bool setter_type)
Definition Generator.cpp:42
static const char * get_cpp_type_string(Type type)
const Compiler_Options & options
Definition Generator.h:14
void namespace_open(std::ostream &out, const std::vector< std::string > &n)
void namespace_close(std::ostream &out, const std::vector< std::string > &n)
void namespace_include_guard(std::ostream &out, const char *name, const std::vector< std::string > &n)
One code generator for each of the file generated by joedbc.
Definition Client_h.cpp:5