Branch data Line data Source code
1 : : /********************************************************************
2 : : * gnc-slots-sql.c: load and save data to SQL *
3 : : * *
4 : : * This program is free software; you can redistribute it and/or *
5 : : * modify it under the terms of the GNU General Public License as *
6 : : * published by the Free Software Foundation; either version 2 of *
7 : : * the License, or (at your option) any later version. *
8 : : * *
9 : : * This program is distributed in the hope that it will be useful, *
10 : : * but WITHOUT ANY WARRANTY; without even the implied warranty of *
11 : : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
12 : : * GNU General Public License for more details. *
13 : : * *
14 : : * You should have received a copy of the GNU General Public License*
15 : : * along with this program; if not, contact: *
16 : : * *
17 : : * Free Software Foundation Voice: +1-617-542-5942 *
18 : : * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 *
19 : : * Boston, MA 02110-1301, USA gnu@gnu.org *
20 : : \********************************************************************/
21 : : /** @file gnc-slots-sql.c
22 : : * @brief load and save data to SQL
23 : : * @author Copyright (c) 2006-2008 Phil Longstaff <plongstaff@rogers.com>
24 : : *
25 : : * This file implements the top-level QofBackend API for saving/
26 : : * restoring data to/from an SQL db
27 : : */
28 : : #include <guid.hpp>
29 : : #include <config.h>
30 : :
31 : : #include <glib.h>
32 : :
33 : : #include <qof.h>
34 : : #include <gnc-engine.h>
35 : :
36 : : #ifdef S_SPLINT_S
37 : : #include "splint-defs.h"
38 : : #endif
39 : :
40 : : #include <string>
41 : : #include <sstream>
42 : : #include <cstdint>
43 : :
44 : : #include "gnc-sql-connection.hpp"
45 : : #include "gnc-sql-backend.hpp"
46 : : #include "gnc-sql-object-backend.hpp"
47 : : #include "gnc-sql-column-table-entry.hpp"
48 : : #include "gnc-slots-sql.h"
49 : :
50 : : #include <kvp-frame.hpp>
51 : :
52 : : static QofLogModule log_module = G_LOG_DOMAIN;
53 : :
54 : : #define TABLE_NAME "slots"
55 : : #define TABLE_VERSION 4
56 : :
57 : : typedef enum
58 : : {
59 : : NONE,
60 : : FRAME,
61 : : LIST
62 : : } context_t;
63 : :
64 : : struct slot_info_t
65 : : {
66 : : GncSqlBackend* be;
67 : : const GncGUID* guid;
68 : : gboolean is_ok;
69 : : KvpFrame* pKvpFrame;
70 : : KvpValue::Type value_type;
71 : : GList* pList;
72 : : context_t context;
73 : : KvpValue* pKvpValue;
74 : : std::string path;
75 : : std::string parent_path;
76 : : };
77 : :
78 : :
79 : : static gpointer get_obj_guid (gpointer pObject);
80 : : static void set_obj_guid (void);
81 : : static gpointer get_path (gpointer pObject);
82 : : static void set_path (gpointer pObject, gpointer pValue);
83 : : static KvpValue::Type get_slot_type (gpointer pObject);
84 : : static void set_slot_type (gpointer pObject, gpointer pValue);
85 : : static gint64 get_int64_val (gpointer pObject);
86 : : static void set_int64_val (gpointer pObject, gint64 pValue);
87 : : static gpointer get_string_val (gpointer pObject);
88 : : static void set_string_val (gpointer pObject, gpointer pValue);
89 : : static gpointer get_double_val (gpointer pObject);
90 : : static void set_double_val (gpointer pObject, gpointer pValue);
91 : : static time64 get_time_val (gpointer pObject);
92 : : static void set_time_val (gpointer pObject, time64 t);
93 : : static gpointer get_guid_val (gpointer pObject);
94 : : static void set_guid_val (gpointer pObject, gpointer pValue);
95 : : static gnc_numeric get_numeric_val (gpointer pObject);
96 : : static void set_numeric_val (gpointer pObject, gnc_numeric value);
97 : : static GDate* get_gdate_val (gpointer pObject);
98 : : static void set_gdate_val (gpointer pObject, GDate* value);
99 : : static slot_info_t* slot_info_copy (slot_info_t* pInfo, GncGUID* guid);
100 : : static void slots_load_info (slot_info_t* pInfo);
101 : :
102 : : #define SLOT_MAX_PATHNAME_LEN 4096
103 : : #define SLOT_MAX_STRINGVAL_LEN 4096
104 : : enum
105 : : {
106 : : id_col = 0,
107 : : obj_guid_col,
108 : : name_col,
109 : : slot_type_col,
110 : : int64_val_col,
111 : : string_val_col,
112 : : double_val_col,
113 : : time_val_col,
114 : : guid_val_col,
115 : : numeric_val_col,
116 : : gdate_val_col
117 : : };
118 : :
119 : : static const EntryVec col_table
120 : : {
121 : : /* col_name, col_type, size, flags, g0bj_param_name, qof_param_name, getter, setter */
122 : : gnc_sql_make_table_entry<CT_INT>(
123 : : "id", 0, COL_PKEY | COL_NNUL | COL_AUTOINC),
124 : : gnc_sql_make_table_entry<CT_GUID>("obj_guid", 0, COL_NNUL,
125 : : (QofAccessFunc)get_obj_guid,
126 : : (QofSetterFunc)set_obj_guid),
127 : : gnc_sql_make_table_entry<CT_STRING>("name", SLOT_MAX_PATHNAME_LEN, COL_NNUL,
128 : : (QofAccessFunc)get_path, set_path),
129 : : gnc_sql_make_table_entry<CT_INT>("slot_type", 0, COL_NNUL,
130 : : (QofAccessFunc)get_slot_type,
131 : : set_slot_type),
132 : : gnc_sql_make_table_entry<CT_INT64>("int64_val", 0, 0,
133 : : (QofAccessFunc)get_int64_val,
134 : : (QofSetterFunc)set_int64_val),
135 : : gnc_sql_make_table_entry<CT_STRING>("string_val", SLOT_MAX_PATHNAME_LEN, 0,
136 : : (QofAccessFunc)get_string_val,
137 : : set_string_val),
138 : : gnc_sql_make_table_entry<CT_DOUBLE>("double_val", 0, 0,
139 : : (QofAccessFunc)get_double_val,
140 : : set_double_val),
141 : : gnc_sql_make_table_entry<CT_TIME>("timespec_val", 0, 0,
142 : : (QofAccessFunc)get_time_val,
143 : : (QofSetterFunc)set_time_val),
144 : : gnc_sql_make_table_entry<CT_GUID>("guid_val", 0, 0,
145 : : (QofAccessFunc)get_guid_val,
146 : : set_guid_val),
147 : : gnc_sql_make_table_entry<CT_NUMERIC>("numeric_val", 0, 0,
148 : : (QofAccessFunc)get_numeric_val,
149 : : (QofSetterFunc)set_numeric_val),
150 : : gnc_sql_make_table_entry<CT_GDATE>("gdate_val", 0, 0,
151 : : (QofAccessFunc)get_gdate_val,
152 : : (QofSetterFunc)set_gdate_val),
153 : : };
154 : :
155 : : static void
156 : 36 : _retrieve_guid_ (gpointer pObject, gpointer pValue)
157 : : {
158 : 36 : GncGUID* pGuid = (GncGUID*)pObject;
159 : 36 : GncGUID* guid = (GncGUID*)pValue;
160 : :
161 : 36 : g_return_if_fail (pObject != NULL);
162 : 36 : g_return_if_fail (pValue != NULL);
163 : :
164 : 36 : *pGuid = *guid;
165 : : }
166 : :
167 : : /* Special column table because we need to be able to access the table by
168 : : a column other than the primary key */
169 : : static const EntryVec obj_guid_col_table
170 : : {
171 : : gnc_sql_make_table_entry<CT_GUID>("obj_guid", 0, 0,
172 : : (QofAccessFunc)get_obj_guid,
173 : : _retrieve_guid_),
174 : : };
175 : :
176 : : static const EntryVec gdate_col_table
177 : : {
178 : : gnc_sql_make_table_entry<CT_GDATE>("gdate_val", 0, 0),
179 : : };
180 : :
181 : 10 : GncSqlSlotsBackend::GncSqlSlotsBackend() :
182 : : GncSqlObjectBackend(TABLE_VERSION, GNC_ID_ACCOUNT,
183 : 50 : TABLE_NAME, col_table) {}
184 : :
185 : : /* ================================================================= */
186 : :
187 : : static std::string
188 : 58 : get_key (slot_info_t* pInfo)
189 : : {
190 : 58 : if (!pInfo) return "";
191 : :
192 : 58 : auto path = pInfo->path;
193 : 58 : path.erase (0, pInfo->parent_path.size());
194 : 58 : return path;
195 : 58 : }
196 : :
197 : : static void
198 : 49 : set_slot_from_value (slot_info_t* pInfo, KvpValue* pValue)
199 : : {
200 : 49 : g_return_if_fail (pInfo != NULL);
201 : 49 : g_return_if_fail (pValue != NULL);
202 : :
203 : 49 : switch (pInfo->context)
204 : : {
205 : 49 : case FRAME:
206 : : {
207 : 49 : auto key = get_key (pInfo);
208 : 147 : pInfo->pKvpFrame->set ({key}, pValue);
209 : 49 : break;
210 : 49 : }
211 : 0 : case LIST:
212 : : {
213 : 0 : pInfo->pList = g_list_append (pInfo->pList, pValue);
214 : 0 : break;
215 : : }
216 : 0 : case NONE:
217 : : default:
218 : : {
219 : 0 : auto key = get_key (pInfo);
220 : 0 : auto path = pInfo->parent_path;
221 : 0 : auto frame = pInfo->pKvpFrame;
222 : 0 : if (!path.empty())
223 : : {
224 : 0 : frame->set_path ({path, key}, pValue);
225 : : }
226 : : else
227 : 0 : frame->set ({key}, pValue);
228 : 0 : break;
229 : 0 : }
230 : : }
231 : 49 : }
232 : :
233 : : static gpointer
234 : 72 : get_obj_guid (gpointer pObject)
235 : : {
236 : 72 : slot_info_t* pInfo = (slot_info_t*)pObject;
237 : :
238 : 72 : g_return_val_if_fail (pObject != NULL, NULL);
239 : :
240 : 72 : return (gpointer)pInfo->guid;
241 : : }
242 : :
243 : : static void
244 : 58 : set_obj_guid (void)
245 : : {
246 : : // Nowhere to put the GncGUID
247 : 58 : }
248 : :
249 : : static gpointer
250 : 65 : get_path (gpointer pObject)
251 : : {
252 : 65 : slot_info_t* pInfo = (slot_info_t*)pObject;
253 : :
254 : 65 : g_return_val_if_fail (pObject != NULL, NULL);
255 : :
256 : 65 : return (gpointer)pInfo->path.c_str();
257 : : }
258 : :
259 : : static void
260 : 58 : set_path (gpointer pObject, gpointer pValue)
261 : : {
262 : 58 : slot_info_t* pInfo = (slot_info_t*)pObject;
263 : 58 : pInfo->path = static_cast<char*>(pValue);
264 : 58 : if (pInfo->path.find (pInfo->parent_path) != 0)
265 : 0 : pInfo->parent_path.clear();
266 : 58 : }
267 : :
268 : : static KvpValue::Type
269 : 65 : get_slot_type (gpointer pObject)
270 : : {
271 : 65 : slot_info_t* pInfo = (slot_info_t*)pObject;
272 : :
273 : 65 : g_return_val_if_fail (pObject != NULL, KvpValue::Type::INVALID);
274 : :
275 : : // return (gpointer)kvp_value_get_type( pInfo->pKvpValue );
276 : 65 : return pInfo->value_type;
277 : : }
278 : :
279 : : static void
280 : 58 : set_slot_type (gpointer pObject, gpointer pValue)
281 : : {
282 : 58 : slot_info_t* pInfo = (slot_info_t*)pObject;
283 : :
284 : 58 : g_return_if_fail (pObject != NULL);
285 : 58 : g_return_if_fail (pValue != NULL);
286 : :
287 : 58 : pInfo->value_type = static_cast<KvpValue::Type> (GPOINTER_TO_INT (pValue));
288 : : }
289 : :
290 : : static gint64
291 : 65 : get_int64_val (gpointer pObject)
292 : : {
293 : 65 : slot_info_t* pInfo = (slot_info_t*)pObject;
294 : :
295 : 65 : g_return_val_if_fail (pObject != NULL, 0);
296 : :
297 : 65 : if (pInfo->pKvpValue->get_type () == KvpValue::Type::INT64)
298 : : {
299 : 14 : return pInfo->pKvpValue->get<int64_t> ();
300 : : }
301 : : else
302 : : {
303 : 51 : return 0;
304 : : }
305 : : }
306 : :
307 : : static void
308 : 58 : set_int64_val (gpointer pObject, gint64 value)
309 : : {
310 : 58 : slot_info_t* pInfo = (slot_info_t*)pObject;
311 : 58 : KvpValue* pValue = NULL;
312 : :
313 : 58 : g_return_if_fail (pObject != NULL);
314 : :
315 : 58 : if (pInfo->value_type != KvpValue::Type::INT64) return;
316 : 14 : pValue = new KvpValue {value};
317 : 14 : set_slot_from_value (pInfo, pValue);
318 : : }
319 : :
320 : : static gpointer
321 : 65 : get_string_val (gpointer pObject)
322 : : {
323 : 65 : slot_info_t* pInfo = (slot_info_t*)pObject;
324 : :
325 : 65 : g_return_val_if_fail (pObject != NULL, NULL);
326 : :
327 : 65 : if (pInfo->pKvpValue->get_type () == KvpValue::Type::STRING)
328 : : {
329 : 24 : return (gpointer)pInfo->pKvpValue->get<const char*> ();
330 : : }
331 : : else
332 : : {
333 : 41 : return NULL;
334 : : }
335 : : }
336 : :
337 : : static void
338 : 58 : set_string_val (gpointer pObject, gpointer pValue)
339 : : {
340 : 58 : slot_info_t* pInfo = (slot_info_t*)pObject;
341 : 58 : g_return_if_fail (pObject != NULL);
342 : :
343 : 58 : if (pInfo->value_type != KvpValue::Type::STRING || pValue == NULL)
344 : 38 : return;
345 : 40 : auto value = new KvpValue {g_strdup(static_cast<const char*> (pValue))};
346 : 20 : set_slot_from_value (pInfo, value);
347 : : }
348 : :
349 : : static gpointer
350 : 65 : get_double_val (gpointer pObject)
351 : : {
352 : : static double d_val; /* static so that we can return it. */
353 : 65 : g_return_val_if_fail (pObject != NULL, NULL);
354 : 65 : auto pInfo = static_cast<slot_info_t*>(pObject);
355 : 65 : if (pInfo->pKvpValue->get_type () == KvpValue::Type::DOUBLE)
356 : : {
357 : 3 : d_val = pInfo->pKvpValue->get<double> ();
358 : 3 : return (gpointer)&d_val;
359 : : }
360 : : else
361 : : {
362 : 62 : return NULL;
363 : : }
364 : : }
365 : :
366 : : static void
367 : 58 : set_double_val (gpointer pObject, gpointer pValue)
368 : : {
369 : 58 : slot_info_t* pInfo = (slot_info_t*)pObject;
370 : 58 : KvpValue* value = NULL;
371 : :
372 : 58 : g_return_if_fail (pObject != NULL);
373 : :
374 : 58 : if (pInfo->value_type != KvpValue::Type::DOUBLE || pValue == NULL) return;
375 : 3 : value = new KvpValue {* (static_cast<double*> (pValue))};
376 : 3 : set_slot_from_value (pInfo, value);
377 : : }
378 : :
379 : : static time64
380 : 65 : get_time_val (gpointer pObject)
381 : : {
382 : 65 : slot_info_t* pInfo = (slot_info_t*)pObject;
383 : :
384 : 65 : g_return_val_if_fail (pObject != NULL, 0);
385 : :
386 : : //if( kvp_value_get_type( pInfo->pKvpValue ) == KvpValue::Type::TIME64 ) {
387 : 65 : auto t = pInfo->pKvpValue->get<Time64> ();
388 : 65 : return t.t;
389 : : }
390 : :
391 : : static void
392 : 58 : set_time_val (gpointer pObject, time64 time)
393 : : {
394 : 58 : slot_info_t* pInfo = (slot_info_t*)pObject;
395 : 58 : KvpValue* value = NULL;
396 : 58 : Time64 t{time};
397 : 115 : g_return_if_fail (pObject != NULL);
398 : :
399 : 58 : if (pInfo->value_type != KvpValue::Type::TIME64) return;
400 : 1 : value = new KvpValue {t};
401 : 1 : set_slot_from_value (pInfo, value);
402 : : }
403 : :
404 : : static gpointer
405 : 65 : get_guid_val (gpointer pObject)
406 : : {
407 : 65 : slot_info_t* pInfo = (slot_info_t*)pObject;
408 : :
409 : 65 : g_return_val_if_fail (pObject != NULL, NULL);
410 : :
411 : 65 : if (pInfo->pKvpValue->get_type () == KvpValue::Type::GUID)
412 : : {
413 : 20 : return (gpointer)pInfo->pKvpValue->get<GncGUID*> ();
414 : : }
415 : : else
416 : : {
417 : 45 : return NULL;
418 : : }
419 : : }
420 : :
421 : : static void
422 : 17 : set_guid_val (gpointer pObject, gpointer pValue)
423 : : {
424 : 17 : slot_info_t* pInfo = (slot_info_t*)pObject;
425 : :
426 : 17 : g_return_if_fail (pObject != NULL);
427 : 17 : if (pValue == NULL) return;
428 : :
429 : 17 : switch (pInfo->value_type)
430 : : {
431 : 8 : case KvpValue::Type::GUID:
432 : : {
433 : 8 : auto new_guid = guid_copy (static_cast<GncGUID*> (pValue));
434 : 8 : set_slot_from_value (pInfo, new KvpValue {new_guid});
435 : 8 : break;
436 : : }
437 : 0 : case KvpValue::Type::GLIST:
438 : : {
439 : 0 : slot_info_t* newInfo = slot_info_copy (pInfo, (GncGUID*)pValue);
440 : 0 : KvpValue* pValue = NULL;
441 : 0 : auto key = get_key (pInfo);
442 : :
443 : 0 : newInfo->context = LIST;
444 : :
445 : 0 : slots_load_info (newInfo);
446 : 0 : pValue = new KvpValue {newInfo->pList};
447 : 0 : pInfo->pKvpFrame->set ({key.c_str()}, pValue);
448 : 0 : delete newInfo;
449 : 0 : break;
450 : 0 : }
451 : 9 : case KvpValue::Type::FRAME:
452 : : {
453 : 9 : slot_info_t* newInfo = slot_info_copy (pInfo, (GncGUID*)pValue) ;
454 : 9 : auto newFrame = new KvpFrame;
455 : 9 : newInfo->pKvpFrame = newFrame;
456 : :
457 : 9 : switch (pInfo->context)
458 : : {
459 : 0 : case LIST:
460 : : {
461 : 0 : auto value = new KvpValue {newFrame};
462 : 0 : newInfo->path = get_key (pInfo);
463 : 0 : pInfo->pList = g_list_append (pInfo->pList, value);
464 : 0 : break;
465 : : }
466 : 9 : case FRAME:
467 : : default:
468 : : {
469 : 9 : auto key = get_key (pInfo);
470 : 36 : pInfo->pKvpFrame->set ({key.c_str()}, new KvpValue {newFrame});
471 : 9 : break;
472 : 9 : }
473 : : }
474 : :
475 : 9 : newInfo->context = FRAME;
476 : 9 : slots_load_info (newInfo);
477 : 9 : delete newInfo;
478 : 9 : break;
479 : : }
480 : 0 : default:
481 : 0 : break;
482 : : }
483 : 27 : }
484 : :
485 : : static gnc_numeric
486 : 65 : get_numeric_val (gpointer pObject)
487 : : {
488 : 65 : slot_info_t* pInfo = (slot_info_t*)pObject;
489 : :
490 : 65 : g_return_val_if_fail (pObject != NULL, gnc_numeric_zero ());
491 : :
492 : 65 : if (pInfo->pKvpValue->get_type () == KvpValue::Type::NUMERIC)
493 : : {
494 : 3 : return pInfo->pKvpValue->get<gnc_numeric> ();
495 : : }
496 : : else
497 : : {
498 : 62 : return gnc_numeric_zero ();
499 : : }
500 : : }
501 : :
502 : : static void
503 : 58 : set_numeric_val (gpointer pObject, gnc_numeric value)
504 : : {
505 : 58 : slot_info_t* pInfo = (slot_info_t*)pObject;
506 : :
507 : 58 : g_return_if_fail (pObject != NULL);
508 : :
509 : 58 : if (pInfo->value_type != KvpValue::Type::NUMERIC) return;
510 : 3 : set_slot_from_value (pInfo, new KvpValue {value});
511 : : }
512 : :
513 : : static GDate*
514 : 65 : get_gdate_val (gpointer pObject)
515 : : {
516 : 65 : slot_info_t* pInfo = (slot_info_t*)pObject;
517 : : static GDate date;
518 : :
519 : 65 : g_return_val_if_fail (pObject != NULL, NULL);
520 : :
521 : 65 : if (pInfo->pKvpValue->get_type () == KvpValue::Type::GDATE)
522 : : {
523 : 0 : date = pInfo->pKvpValue->get<GDate> ();
524 : 0 : return &date;
525 : : }
526 : : else
527 : : {
528 : 65 : return NULL;
529 : : }
530 : : }
531 : :
532 : : static void
533 : 0 : set_gdate_val (gpointer pObject, GDate* value)
534 : : {
535 : 0 : slot_info_t* pInfo = (slot_info_t*)pObject;
536 : :
537 : 0 : g_return_if_fail (pObject != NULL);
538 : :
539 : 0 : if (pInfo->value_type != KvpValue::Type::GDATE) return;
540 : 0 : set_slot_from_value (pInfo, new KvpValue {*value});
541 : : }
542 : :
543 : : static slot_info_t*
544 : 43 : slot_info_copy (slot_info_t* pInfo, GncGUID* guid)
545 : : {
546 : 43 : g_return_val_if_fail (pInfo != NULL, NULL);
547 : 43 : auto newSlot = new slot_info_t;
548 : :
549 : 43 : newSlot->be = pInfo->be;
550 : 43 : newSlot->guid = guid == NULL ? pInfo->guid : guid;
551 : 43 : newSlot->is_ok = pInfo->is_ok;
552 : 43 : newSlot->pKvpFrame = pInfo->pKvpFrame;
553 : 43 : newSlot->value_type = pInfo->value_type;
554 : 43 : newSlot->pList = pInfo->pList;
555 : 43 : newSlot->context = pInfo->context;
556 : 43 : newSlot->pKvpValue = pInfo->pKvpValue;
557 : 43 : if (!pInfo->path.empty())
558 : 21 : newSlot->parent_path = pInfo->path + "/";
559 : : else
560 : 22 : newSlot->parent_path = pInfo->parent_path;
561 : 43 : return newSlot;
562 : : }
563 : :
564 : : static void
565 : 65 : save_slot (const char* key, KvpValue* value, slot_info_t & slot_info)
566 : : {
567 : 65 : g_return_if_fail (value != NULL);
568 : :
569 : : // Ignore if we've already run into a failure
570 : 65 : if (!slot_info.is_ok)
571 : : {
572 : 0 : return;
573 : : }
574 : 65 : slot_info.pKvpValue = value;
575 : 65 : slot_info.path = slot_info.parent_path + key;
576 : 65 : slot_info.value_type = value->get_type ();
577 : :
578 : 65 : switch (slot_info.value_type)
579 : : {
580 : 12 : case KvpValue::Type::FRAME:
581 : : {
582 : 12 : auto pKvpFrame = value->get<KvpFrame*> ();
583 : 12 : auto guid = guid_new ();
584 : 12 : slot_info_t* pNewInfo = slot_info_copy (&slot_info, guid);
585 : 12 : KvpValue* oldValue = slot_info.pKvpValue;
586 : 12 : slot_info.pKvpValue = new KvpValue {guid};
587 : 12 : slot_info.is_ok = slot_info.be->do_db_operation(OP_DB_INSERT,
588 : : TABLE_NAME,
589 : : TABLE_NAME,
590 : : &slot_info,
591 : : col_table);
592 : 12 : g_return_if_fail (slot_info.is_ok);
593 : 12 : pKvpFrame->for_each_slot_temp (save_slot, *pNewInfo);
594 : 12 : delete slot_info.pKvpValue;
595 : 12 : slot_info.pKvpValue = oldValue;
596 : 12 : delete pNewInfo;
597 : : }
598 : 12 : break;
599 : 0 : case KvpValue::Type::GLIST:
600 : : {
601 : 0 : GncGUID* guid = guid_new ();
602 : 0 : slot_info_t* pNewInfo = slot_info_copy (&slot_info, guid);
603 : 0 : KvpValue* oldValue = slot_info.pKvpValue;
604 : 0 : slot_info.pKvpValue = new KvpValue {guid}; // Transfer ownership!
605 : 0 : slot_info.is_ok = slot_info.be->do_db_operation(OP_DB_INSERT,
606 : : TABLE_NAME,
607 : : TABLE_NAME,
608 : : &slot_info,
609 : : col_table);
610 : 0 : g_return_if_fail (slot_info.is_ok);
611 : 0 : for (auto cursor = value->get<GList*> (); cursor; cursor = cursor->next)
612 : : {
613 : 0 : auto val = static_cast<KvpValue*> (cursor->data);
614 : 0 : save_slot ("", val, *pNewInfo);
615 : : }
616 : 0 : delete slot_info.pKvpValue;
617 : 0 : slot_info.pKvpValue = oldValue;
618 : 0 : delete pNewInfo;
619 : : }
620 : 0 : break;
621 : 53 : default:
622 : : {
623 : 53 : slot_info.is_ok = slot_info.be->do_db_operation (OP_DB_INSERT,
624 : : TABLE_NAME,
625 : : TABLE_NAME,
626 : : &slot_info,
627 : : col_table);
628 : : }
629 : 53 : break;
630 : : }
631 : : }
632 : :
633 : : gboolean
634 : 94 : gnc_sql_slots_save (GncSqlBackend* sql_be, const GncGUID* guid, gboolean is_infant,
635 : : QofInstance* inst)
636 : : {
637 : 94 : slot_info_t slot_info = { NULL, NULL, TRUE, NULL, KvpValue::Type::INVALID,
638 : 94 : NULL, FRAME, NULL, "" };
639 : 94 : KvpFrame* pFrame = qof_instance_get_slots (inst);
640 : :
641 : 94 : g_return_val_if_fail (sql_be != NULL, FALSE);
642 : 94 : g_return_val_if_fail (guid != NULL, FALSE);
643 : 94 : g_return_val_if_fail (pFrame != NULL, FALSE);
644 : :
645 : : // If this is not saving into a new db, clear out the old saved slots first
646 : 94 : if (!sql_be->pristine() && !is_infant)
647 : : {
648 : 6 : (void)gnc_sql_slots_delete (sql_be, guid);
649 : : }
650 : :
651 : 94 : slot_info.be = sql_be;
652 : 94 : slot_info.guid = guid;
653 : 94 : pFrame->for_each_slot_temp (save_slot, slot_info);
654 : :
655 : 94 : return slot_info.is_ok;
656 : 94 : }
657 : :
658 : : gboolean
659 : 7 : gnc_sql_slots_delete (GncSqlBackend* sql_be, const GncGUID* guid)
660 : : {
661 : : gchar* buf;
662 : : gchar guid_buf[GUID_ENCODING_LENGTH + 1];
663 : 7 : slot_info_t slot_info = { NULL, NULL, TRUE, NULL, KvpValue::Type::INVALID,
664 : 7 : NULL, FRAME, NULL, "" };
665 : :
666 : 7 : g_return_val_if_fail (sql_be != NULL, FALSE);
667 : 7 : g_return_val_if_fail (guid != NULL, FALSE);
668 : :
669 : 7 : (void)guid_to_string_buff (guid, guid_buf);
670 : :
671 : 7 : buf = g_strdup_printf ("SELECT * FROM %s WHERE obj_guid='%s' and slot_type in ('%d', '%d') and not guid_val is null",
672 : : TABLE_NAME, guid_buf, KvpValue::Type::FRAME, KvpValue::Type::GLIST);
673 : 14 : auto stmt = sql_be->create_statement_from_sql(buf);
674 : 7 : g_free (buf);
675 : 7 : if (stmt != nullptr)
676 : : {
677 : 7 : auto result = sql_be->execute_select_statement(stmt);
678 : 8 : for (auto row : *result)
679 : : {
680 : : const GncSqlColumnTableEntryPtr table_row =
681 : 1 : col_table[guid_val_col];
682 : : GncGUID child_guid;
683 : 1 : auto val = row.get_string_at_col (table_row->name());
684 : 1 : if (val && string_to_guid (val->c_str(), &child_guid))
685 : 1 : gnc_sql_slots_delete (sql_be, &child_guid);
686 : 8 : }
687 : : }
688 : :
689 : 7 : slot_info.be = sql_be;
690 : 7 : slot_info.guid = guid;
691 : 7 : slot_info.is_ok = TRUE;
692 : 7 : slot_info.is_ok = sql_be->do_db_operation(OP_DB_DELETE, TABLE_NAME,
693 : : TABLE_NAME, &slot_info,
694 : : obj_guid_col_table);
695 : :
696 : 7 : return slot_info.is_ok;
697 : 7 : }
698 : :
699 : : static void
700 : 22 : load_slot (slot_info_t* pInfo, GncSqlRow& row)
701 : : {
702 : : slot_info_t* slot_info;
703 : :
704 : 22 : g_return_if_fail (pInfo != NULL);
705 : 22 : g_return_if_fail (pInfo->be != NULL);
706 : 22 : g_return_if_fail (pInfo->pKvpFrame != NULL);
707 : :
708 : 22 : slot_info = slot_info_copy (pInfo, NULL);
709 : :
710 : 22 : gnc_sql_load_object (pInfo->be, row, TABLE_NAME, slot_info, col_table);
711 : :
712 : 22 : if (slot_info->pList != pInfo->pList)
713 : : {
714 : 0 : if (pInfo->pList != NULL)
715 : : {
716 : 0 : PWARN ("Load slot returned a different list than the original");
717 : : }
718 : : else
719 : : {
720 : 0 : pInfo->pList = slot_info->pList;
721 : : }
722 : : }
723 : 22 : delete slot_info;
724 : : }
725 : :
726 : : void
727 : 7 : gnc_sql_slots_load (GncSqlBackend* sql_be, QofInstance* inst)
728 : : {
729 : 7 : slot_info_t info = { NULL, NULL, TRUE, NULL, KvpValue::Type::INVALID,
730 : 7 : NULL, FRAME, NULL, "" };
731 : 7 : g_return_if_fail (sql_be != NULL);
732 : 7 : g_return_if_fail (inst != NULL);
733 : :
734 : 7 : info.be = sql_be;
735 : 7 : info.guid = qof_instance_get_guid (inst);
736 : 7 : info.pKvpFrame = qof_instance_get_slots (inst);
737 : 7 : info.context = NONE;
738 : :
739 : 7 : slots_load_info (&info);
740 : 7 : }
741 : :
742 : : static void
743 : 16 : slots_load_info (slot_info_t* pInfo)
744 : : {
745 : 16 : g_return_if_fail (pInfo != NULL);
746 : 16 : g_return_if_fail (pInfo->be != NULL);
747 : 16 : g_return_if_fail (pInfo->guid != NULL);
748 : 16 : g_return_if_fail (pInfo->pKvpFrame != NULL);
749 : :
750 : 16 : gnc::GUID guid(*pInfo->guid);
751 : 16 : std::string sql("SELECT * FROM " TABLE_NAME " WHERE obj_guid='");
752 : 16 : sql += guid.to_string() + "'";
753 : 16 : auto stmt = pInfo->be->create_statement_from_sql(sql);
754 : 16 : if (stmt != nullptr)
755 : : {
756 : 16 : auto result = pInfo->be->execute_select_statement (stmt);
757 : 38 : for (auto row : *result)
758 : 38 : load_slot (pInfo, row);
759 : 16 : delete result;
760 : : }
761 : 16 : }
762 : :
763 : : static const GncGUID*
764 : 36 : load_obj_guid (const GncSqlBackend* sql_be, GncSqlRow& row)
765 : : {
766 : : static GncGUID guid;
767 : :
768 : 36 : g_return_val_if_fail (sql_be != NULL, NULL);
769 : :
770 : 36 : gnc_sql_load_object (sql_be, row, NULL, &guid, obj_guid_col_table);
771 : :
772 : 36 : return &guid;
773 : : }
774 : :
775 : : static void
776 : 36 : load_slot_for_book_object (GncSqlBackend* sql_be, GncSqlRow& row,
777 : : BookLookupFn lookup_fn)
778 : : {
779 : 36 : slot_info_t slot_info = { NULL, NULL, TRUE, NULL, KvpValue::Type::INVALID,
780 : 36 : NULL, FRAME, NULL, "" };
781 : : const GncGUID* guid;
782 : : QofInstance* inst;
783 : :
784 : 36 : g_return_if_fail (sql_be != NULL);
785 : 36 : g_return_if_fail (lookup_fn != NULL);
786 : :
787 : 36 : guid = load_obj_guid (sql_be, row);
788 : 36 : g_return_if_fail (guid != NULL);
789 : 36 : inst = lookup_fn (guid, sql_be->book());
790 : 36 : if (inst == NULL) return; /* Silently bail if the guid isn't loaded yet. */
791 : :
792 : 36 : slot_info.be = sql_be;
793 : 36 : slot_info.pKvpFrame = qof_instance_get_slots (inst);
794 : 36 : slot_info.path.clear();
795 : :
796 : 36 : gnc_sql_load_object (sql_be, row, TABLE_NAME, &slot_info, col_table);
797 : 36 : }
798 : :
799 : : /**
800 : : * gnc_sql_slots_load_for_sql_subquery - Loads slots for all objects whose guid is
801 : : * supplied by a subquery. The subquery should be of the form "SELECT DISTINCT guid FROM ...".
802 : : * This is faster than loading for one object at a time because fewer SQL queries * are used.
803 : : *
804 : : * @param sql_be SQL backend
805 : : * @param subquery Subquery SQL string
806 : : * @param lookup_fn Lookup function
807 : : */
808 : 69 : void gnc_sql_slots_load_for_sql_subquery (GncSqlBackend* sql_be,
809 : : const std::string subquery,
810 : : BookLookupFn lookup_fn)
811 : : {
812 : 69 : g_return_if_fail (sql_be != NULL);
813 : :
814 : : // Ignore empty subquery
815 : 69 : if (subquery.empty()) return;
816 : :
817 : 138 : std::string pkey(obj_guid_col_table[0]->name());
818 : 69 : std::string sql("SELECT * FROM " TABLE_NAME " WHERE ");
819 : 69 : sql += pkey + " IN (" + subquery + ")";
820 : :
821 : : // Execute the query and load the slots
822 : 69 : auto stmt = sql_be->create_statement_from_sql(sql);
823 : 69 : if (stmt == nullptr)
824 : : {
825 : 0 : PERR ("stmt == NULL, SQL = '%s'\n", sql.c_str());
826 : 0 : return;
827 : : }
828 : 69 : auto result = sql_be->execute_select_statement(stmt);
829 : 105 : for (auto row : *result)
830 : 105 : load_slot_for_book_object (sql_be, row, lookup_fn);
831 : 69 : delete result;
832 : 69 : }
833 : :
834 : : /* ================================================================= */
835 : : void
836 : 10 : GncSqlSlotsBackend::create_tables (GncSqlBackend* sql_be)
837 : : {
838 : : gint version;
839 : : gboolean ok;
840 : :
841 : 10 : g_return_if_fail (sql_be != NULL);
842 : :
843 : 20 : version = sql_be->get_table_version( TABLE_NAME);
844 : 10 : if (version == 0)
845 : : {
846 : 10 : (void)sql_be->create_table(TABLE_NAME, TABLE_VERSION, col_table);
847 : :
848 : 20 : ok = sql_be->create_index ("slots_guid_index", TABLE_NAME,
849 : : obj_guid_col_table);
850 : 5 : if (!ok)
851 : : {
852 : 0 : PERR ("Unable to create index\n");
853 : : }
854 : : }
855 : 5 : else if (version < m_version)
856 : : {
857 : : /* Upgrade:
858 : : 1->2: 64-bit int values to proper definition, add index
859 : : 2->3: Add gdate field
860 : : 3->4: Use DATETIME instead of TIMESTAMP in MySQL
861 : : */
862 : 0 : if (version == 1)
863 : : {
864 : 0 : sql_be->upgrade_table(TABLE_NAME, col_table);
865 : 0 : ok = sql_be->create_index ("slots_guid_index", TABLE_NAME,
866 : : obj_guid_col_table);
867 : 0 : if (!ok)
868 : : {
869 : 0 : PERR ("Unable to create index\n");
870 : : }
871 : : }
872 : 0 : else if (version == 2)
873 : : {
874 : 0 : ok = sql_be->add_columns_to_table(TABLE_NAME, gdate_col_table);
875 : 0 : if (!ok)
876 : : {
877 : 0 : PERR ("Unable to add gdate column\n");
878 : : }
879 : : }
880 : : else
881 : : {
882 : 0 : sql_be->upgrade_table(TABLE_NAME, col_table);
883 : : }
884 : 0 : sql_be->set_table_version (TABLE_NAME, TABLE_VERSION);
885 : 0 : PINFO ("Slots table upgraded from version %d to version %d\n", version,
886 : : TABLE_VERSION);
887 : : }
888 : : }
889 : :
890 : : /* ========================== END OF FILE ===================== */
|