Line data Source code
1 : // Copyright (c) 2019 Made to Order Software Corp. All Rights Reserved
2 : //
3 : // https://snapwebsites.org/project/snapdatabase
4 : // contact@m2osw.com
5 : //
6 : // This program is free software; you can redistribute it and/or modify
7 : // it under the terms of the GNU General Public License as published by
8 : // the Free Software Foundation; either version 2 of the License, or
9 : // (at your option) any later version.
10 : //
11 : // This program is distributed in the hope that it will be useful,
12 : // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 : // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 : // GNU General Public License for more details.
15 : //
16 : // You should have received a copy of the GNU General Public License along
17 : // with this program; if not, write to the Free Software Foundation, Inc.,
18 : // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19 :
20 :
21 : /** \file
22 : * \brief Base block implementation.
23 : *
24 : * The block base class handles the loading of the block in memory using
25 : * mmap() and gives information such as its type and location.
26 : */
27 :
28 : // self
29 : //
30 : #include "snapdatabase/block/block.h"
31 :
32 : #include "snapdatabase/exception.h"
33 : #include "snapdatabase/database/table.h"
34 : #include "snapdatabase/data/structure.h"
35 :
36 :
37 : // snaplogger lib
38 : //
39 : #include <snaplogger/message.h>
40 :
41 :
42 : // C++ lib
43 : //
44 : #include <iostream>
45 :
46 :
47 : // last include
48 : //
49 : #include <snapdev/poison.h>
50 :
51 :
52 :
53 : namespace snapdatabase
54 : {
55 :
56 :
57 :
58 : namespace
59 : {
60 :
61 :
62 : constexpr char const * g_errmsg_table = "block::~block() called with an f_data pointer, but f_table == nullptr.";
63 : constexpr char const * g_errmsg_exception = "block::~block() tried to release the f_data by it threw an exception.";
64 :
65 :
66 : }
67 :
68 :
69 :
70 3 : block::block(descriptions_by_version_t const * structure_descriptions, dbfile::pointer_t f, reference_t offset)
71 : : f_file(f)
72 : , f_structure_descriptions(structure_descriptions)
73 3 : , f_offset(offset)
74 : {
75 : #ifdef _DEBUG
76 : // verify that the versions are in order
77 : //
78 3 : descriptions_by_version_t const * p(nullptr);
79 3 : descriptions_by_version_t const * d = f_structure_descriptions;
80 9 : for(; d->f_description != nullptr; ++d)
81 : {
82 3 : if(p != nullptr)
83 : {
84 0 : if(p->f_version <= d->f_version)
85 : {
86 : throw snapdatabase_logic_error(
87 : "The versions in a structure definition array must be in order ("
88 0 : + p->f_version.to_string()
89 0 : + " <= "
90 0 : + d->f_version.to_string()
91 0 : + " when it should be '>').");
92 : }
93 : }
94 3 : p = d;
95 : }
96 3 : if(d == f_structure_descriptions)
97 : {
98 0 : throw snapdatabase_logic_error("The array of structure descriptions cannot be empty.");
99 : }
100 : #endif
101 :
102 : // create the structure we want to use in memory
103 : //
104 : // this may not be the one used when loading data from a file.
105 : //
106 3 : f_structure = std::make_shared<structure>(f_structure_descriptions->f_description);
107 3 : }
108 :
109 :
110 0 : block::~block()
111 : {
112 0 : if(f_data != nullptr)
113 : {
114 0 : if(f_table == nullptr)
115 : {
116 0 : SNAP_LOG_FATAL
117 0 : << g_errmsg_table
118 : << SNAP_LOG_SEND;
119 0 : std::cerr << g_errmsg_table << std::endl;
120 0 : std::terminate();
121 : }
122 :
123 : try
124 : {
125 0 : f_table->get_dbfile()->release_data(f_data);
126 : //f_data = nullptr;
127 : }
128 0 : catch(page_not_found const & e)
129 : {
130 0 : SNAP_LOG_FATAL
131 0 : << g_errmsg_exception
132 : << " ("
133 0 : << e.what()
134 : << ")."
135 : << SNAP_LOG_SEND;
136 : std::cerr
137 : << g_errmsg_exception
138 : << " ("
139 0 : << e.what()
140 0 : << ")."
141 0 : << std::endl;
142 0 : std::terminate();
143 : }
144 : }
145 0 : }
146 :
147 :
148 28 : table_pointer_t block::get_table() const
149 : {
150 28 : if(f_table == nullptr)
151 : {
152 0 : throw invalid_xml("block::get_table() called before the table was defined.");
153 : }
154 :
155 28 : return f_table;
156 : }
157 :
158 :
159 3 : void block::set_table(table_pointer_t table)
160 : {
161 3 : if(f_table != nullptr)
162 : {
163 0 : throw invalid_xml("block::set_table() called twice.");
164 : }
165 :
166 3 : f_table = table;
167 3 : }
168 :
169 :
170 3 : structure::pointer_t block::get_structure() const
171 : {
172 3 : return f_structure;
173 : }
174 :
175 :
176 0 : structure::pointer_t block::get_structure(version_t version) const
177 : {
178 0 : for(descriptions_by_version_t const * d = f_structure_descriptions;
179 0 : d->f_description != nullptr;
180 : ++d)
181 : {
182 0 : if(d->f_version == version)
183 : {
184 0 : return std::make_shared<structure>(f_structure_descriptions->f_description);
185 : }
186 : }
187 :
188 : throw snapdatabase_logic_error(
189 : "Block of type \""
190 0 : + to_string(get_dbtype())
191 0 : + "\" has no structure version "
192 0 : + version.to_string()
193 0 : + ".");
194 : }
195 :
196 :
197 4 : dbtype_t block::get_dbtype() const
198 : {
199 4 : return *reinterpret_cast<dbtype_t const *>(data(0));
200 : }
201 :
202 :
203 3 : void block::set_dbtype(dbtype_t type)
204 : {
205 : // TODO: add verifications (i.e. go from FREE to any or any to FREE
206 : // and maybe a few others)
207 : //
208 3 : if(*reinterpret_cast<dbtype_t *>(data(0)) != type)
209 : {
210 2 : *reinterpret_cast<dbtype_t *>(data(0)) = type;
211 : }
212 3 : }
213 :
214 :
215 5 : version_t block::get_structure_version() const
216 : {
217 5 : return version_t(static_cast<std::uint32_t>(f_structure->get_uinteger("header.version")));
218 : }
219 :
220 :
221 2 : void block::set_structure_version()
222 : {
223 : // avoid a write if not required
224 : //
225 2 : if(get_structure_version() != f_structure_descriptions->f_version)
226 : {
227 0 : f_structure->set_uinteger("header.version", f_structure_descriptions->f_version.to_binary());
228 : }
229 2 : }
230 :
231 :
232 0 : reference_t block::get_offset() const
233 : {
234 0 : return f_offset;
235 : }
236 :
237 :
238 3 : void block::set_data(data_t data)
239 : {
240 : // the table retrieves the data pointer because it needs to determine
241 : // the block type (using the first 4 bytes) and so the data pointer
242 : // is already locked once and we can immediately save it in the block
243 : //
244 3 : f_data = data;
245 3 : }
246 :
247 :
248 21 : data_t block::data(reference_t offset)
249 : {
250 21 : if(f_data == nullptr)
251 : {
252 : //f_data = get_table()->get_dbfile()->data(f_offset);
253 0 : throw snapdatabase_logic_error("block::data() called before set_data().");
254 : }
255 :
256 21 : return f_data + (offset % get_table()->get_page_size());
257 : }
258 :
259 :
260 4 : const_data_t block::data(reference_t offset) const
261 : {
262 4 : if(f_data == nullptr)
263 : {
264 : //f_data = get_table()->get_dbfile()->data(f_offset);
265 0 : throw snapdatabase_logic_error("block::data() called before set_data().");
266 : }
267 :
268 4 : return f_data + (offset % get_table()->get_page_size());
269 : }
270 :
271 :
272 2 : void block::sync(bool immediate)
273 : {
274 2 : get_table()->get_dbfile()->sync(f_data, immediate);
275 2 : }
276 :
277 :
278 3 : void block::from_current_file_version()
279 : {
280 3 : version_t current_version(get_structure_version());
281 3 : if(f_structure_descriptions->f_version == current_version)
282 : {
283 : // same version, no conversion necessary
284 : //
285 6 : return;
286 : }
287 :
288 0 : throw snapdatabase_logic_error("from_current_file_version() not fully implemented yet.");
289 : }
290 :
291 :
292 :
293 6 : } // namespace snapdatabase
294 : // vim: ts=4 sw=4 et
|