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 50 : _retrieve_guid_ (gpointer pObject, gpointer pValue)
157 : {
158 50 : GncGUID* pGuid = (GncGUID*)pObject;
159 50 : GncGUID* guid = (GncGUID*)pValue;
160 :
161 50 : g_return_if_fail (pObject != NULL);
162 50 : g_return_if_fail (pValue != NULL);
163 :
164 50 : *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 15 : GncSqlSlotsBackend::GncSqlSlotsBackend() :
182 30 : GncSqlObjectBackend(TABLE_VERSION, GNC_ID_ACCOUNT,
183 75 : TABLE_NAME, col_table) {}
184 :
185 : /* ================================================================= */
186 :
187 : static std::string
188 74 : get_key (slot_info_t* pInfo)
189 : {
190 74 : if (!pInfo) return "";
191 :
192 74 : auto path = pInfo->path;
193 74 : path.erase (0, pInfo->parent_path.size());
194 74 : return path;
195 74 : }
196 :
197 : static void
198 64 : set_slot_from_value (slot_info_t* pInfo, KvpValue* pValue)
199 : {
200 64 : g_return_if_fail (pInfo != NULL);
201 64 : g_return_if_fail (pValue != NULL);
202 :
203 64 : switch (pInfo->context)
204 : {
205 64 : case FRAME:
206 : {
207 64 : auto key = get_key (pInfo);
208 192 : pInfo->pKvpFrame->set ({key}, pValue);
209 64 : break;
210 64 : }
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 64 : }
232 :
233 : static gpointer
234 86 : get_obj_guid (gpointer pObject)
235 : {
236 86 : slot_info_t* pInfo = (slot_info_t*)pObject;
237 :
238 86 : g_return_val_if_fail (pObject != NULL, NULL);
239 :
240 86 : return (gpointer)pInfo->guid;
241 : }
242 :
243 : static void
244 74 : set_obj_guid (void)
245 : {
246 : // Nowhere to put the GncGUID
247 74 : }
248 :
249 : static gpointer
250 75 : get_path (gpointer pObject)
251 : {
252 75 : slot_info_t* pInfo = (slot_info_t*)pObject;
253 :
254 75 : g_return_val_if_fail (pObject != NULL, NULL);
255 :
256 75 : return (gpointer)pInfo->path.c_str();
257 : }
258 :
259 : static void
260 74 : set_path (gpointer pObject, gpointer pValue)
261 : {
262 74 : slot_info_t* pInfo = (slot_info_t*)pObject;
263 74 : pInfo->path = static_cast<char*>(pValue);
264 74 : if (pInfo->path.find (pInfo->parent_path) != 0)
265 0 : pInfo->parent_path.clear();
266 74 : }
267 :
268 : static KvpValue::Type
269 75 : get_slot_type (gpointer pObject)
270 : {
271 75 : slot_info_t* pInfo = (slot_info_t*)pObject;
272 :
273 75 : g_return_val_if_fail (pObject != NULL, KvpValue::Type::INVALID);
274 :
275 : // return (gpointer)kvp_value_get_type( pInfo->pKvpValue );
276 75 : return pInfo->value_type;
277 : }
278 :
279 : static void
280 74 : set_slot_type (gpointer pObject, gpointer pValue)
281 : {
282 74 : slot_info_t* pInfo = (slot_info_t*)pObject;
283 :
284 74 : g_return_if_fail (pObject != NULL);
285 74 : g_return_if_fail (pValue != NULL);
286 :
287 74 : pInfo->value_type = static_cast<KvpValue::Type> (GPOINTER_TO_INT (pValue));
288 : }
289 :
290 : static gint64
291 75 : get_int64_val (gpointer pObject)
292 : {
293 75 : slot_info_t* pInfo = (slot_info_t*)pObject;
294 :
295 75 : g_return_val_if_fail (pObject != NULL, 0);
296 :
297 75 : if (pInfo->pKvpValue->get_type () == KvpValue::Type::INT64)
298 : {
299 12 : return pInfo->pKvpValue->get<int64_t> ();
300 : }
301 : else
302 : {
303 63 : return 0;
304 : }
305 : }
306 :
307 : static void
308 74 : set_int64_val (gpointer pObject, gint64 value)
309 : {
310 74 : slot_info_t* pInfo = (slot_info_t*)pObject;
311 74 : KvpValue* pValue = NULL;
312 :
313 74 : g_return_if_fail (pObject != NULL);
314 :
315 74 : if (pInfo->value_type != KvpValue::Type::INT64) return;
316 13 : pValue = new KvpValue {value};
317 13 : set_slot_from_value (pInfo, pValue);
318 : }
319 :
320 : static gpointer
321 75 : get_string_val (gpointer pObject)
322 : {
323 75 : slot_info_t* pInfo = (slot_info_t*)pObject;
324 :
325 75 : g_return_val_if_fail (pObject != NULL, NULL);
326 :
327 75 : if (pInfo->pKvpValue->get_type () == KvpValue::Type::STRING)
328 : {
329 28 : return (gpointer)pInfo->pKvpValue->get<const char*> ();
330 : }
331 : else
332 : {
333 47 : return NULL;
334 : }
335 : }
336 :
337 : static void
338 74 : set_string_val (gpointer pObject, gpointer pValue)
339 : {
340 74 : slot_info_t* pInfo = (slot_info_t*)pObject;
341 74 : g_return_if_fail (pObject != NULL);
342 :
343 74 : if (pInfo->value_type != KvpValue::Type::STRING || pValue == NULL)
344 49 : return;
345 50 : auto value = new KvpValue {g_strdup(static_cast<const char*> (pValue))};
346 25 : set_slot_from_value (pInfo, value);
347 : }
348 :
349 : static gpointer
350 75 : get_double_val (gpointer pObject)
351 : {
352 : static double d_val; /* static so that we can return it. */
353 75 : g_return_val_if_fail (pObject != NULL, NULL);
354 75 : auto pInfo = static_cast<slot_info_t*>(pObject);
355 75 : if (pInfo->pKvpValue->get_type () == KvpValue::Type::DOUBLE)
356 : {
357 4 : d_val = pInfo->pKvpValue->get<double> ();
358 4 : return (gpointer)&d_val;
359 : }
360 : else
361 : {
362 71 : return NULL;
363 : }
364 : }
365 :
366 : static void
367 74 : set_double_val (gpointer pObject, gpointer pValue)
368 : {
369 74 : slot_info_t* pInfo = (slot_info_t*)pObject;
370 74 : KvpValue* value = NULL;
371 :
372 74 : g_return_if_fail (pObject != NULL);
373 :
374 74 : if (pInfo->value_type != KvpValue::Type::DOUBLE || pValue == NULL) return;
375 5 : value = new KvpValue {* (static_cast<double*> (pValue))};
376 5 : set_slot_from_value (pInfo, value);
377 : }
378 :
379 : static time64
380 75 : get_time_val (gpointer pObject)
381 : {
382 75 : slot_info_t* pInfo = (slot_info_t*)pObject;
383 :
384 75 : g_return_val_if_fail (pObject != NULL, 0);
385 :
386 : //if( kvp_value_get_type( pInfo->pKvpValue ) == KvpValue::Type::TIME64 ) {
387 75 : auto t = pInfo->pKvpValue->get<Time64> ();
388 75 : return t.t;
389 : }
390 :
391 : static void
392 74 : set_time_val (gpointer pObject, time64 time)
393 : {
394 74 : slot_info_t* pInfo = (slot_info_t*)pObject;
395 74 : KvpValue* value = NULL;
396 74 : Time64 t{time};
397 142 : g_return_if_fail (pObject != NULL);
398 :
399 74 : if (pInfo->value_type != KvpValue::Type::TIME64) return;
400 6 : value = new KvpValue {t};
401 6 : set_slot_from_value (pInfo, value);
402 : }
403 :
404 : static gpointer
405 75 : get_guid_val (gpointer pObject)
406 : {
407 75 : slot_info_t* pInfo = (slot_info_t*)pObject;
408 :
409 75 : g_return_val_if_fail (pObject != NULL, NULL);
410 :
411 75 : if (pInfo->pKvpValue->get_type () == KvpValue::Type::GUID)
412 : {
413 22 : return (gpointer)pInfo->pKvpValue->get<GncGUID*> ();
414 : }
415 : else
416 : {
417 53 : return NULL;
418 : }
419 : }
420 :
421 : static void
422 20 : set_guid_val (gpointer pObject, gpointer pValue)
423 : {
424 20 : slot_info_t* pInfo = (slot_info_t*)pObject;
425 :
426 20 : g_return_if_fail (pObject != NULL);
427 20 : if (pValue == NULL) return;
428 :
429 20 : switch (pInfo->value_type)
430 : {
431 10 : case KvpValue::Type::GUID:
432 : {
433 10 : auto new_guid = guid_copy (static_cast<GncGUID*> (pValue));
434 10 : set_slot_from_value (pInfo, new KvpValue {new_guid});
435 10 : 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 10 : case KvpValue::Type::FRAME:
452 : {
453 10 : slot_info_t* newInfo = slot_info_copy (pInfo, (GncGUID*)pValue) ;
454 10 : auto newFrame = new KvpFrame;
455 10 : newInfo->pKvpFrame = newFrame;
456 :
457 10 : 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 10 : case FRAME:
467 : default:
468 : {
469 10 : auto key = get_key (pInfo);
470 50 : pInfo->pKvpFrame->set ({key.c_str()}, new KvpValue {newFrame});
471 10 : break;
472 10 : }
473 : }
474 :
475 10 : newInfo->context = FRAME;
476 10 : slots_load_info (newInfo);
477 10 : delete newInfo;
478 10 : break;
479 : }
480 0 : default:
481 0 : break;
482 : }
483 20 : }
484 :
485 : static gnc_numeric
486 75 : get_numeric_val (gpointer pObject)
487 : {
488 75 : slot_info_t* pInfo = (slot_info_t*)pObject;
489 :
490 75 : g_return_val_if_fail (pObject != NULL, gnc_numeric_zero ());
491 :
492 75 : if (pInfo->pKvpValue->get_type () == KvpValue::Type::NUMERIC)
493 : {
494 4 : return pInfo->pKvpValue->get<gnc_numeric> ();
495 : }
496 : else
497 : {
498 71 : return gnc_numeric_zero ();
499 : }
500 : }
501 :
502 : static void
503 74 : set_numeric_val (gpointer pObject, gnc_numeric value)
504 : {
505 74 : slot_info_t* pInfo = (slot_info_t*)pObject;
506 :
507 74 : g_return_if_fail (pObject != NULL);
508 :
509 74 : if (pInfo->value_type != KvpValue::Type::NUMERIC) return;
510 5 : set_slot_from_value (pInfo, new KvpValue {value});
511 : }
512 :
513 : static GDate*
514 75 : get_gdate_val (gpointer pObject)
515 : {
516 75 : slot_info_t* pInfo = (slot_info_t*)pObject;
517 : static GDate date;
518 :
519 75 : g_return_val_if_fail (pObject != NULL, NULL);
520 :
521 75 : 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 75 : 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 47 : slot_info_copy (slot_info_t* pInfo, GncGUID* guid)
545 : {
546 47 : g_return_val_if_fail (pInfo != NULL, NULL);
547 47 : auto newSlot = new slot_info_t;
548 :
549 47 : newSlot->be = pInfo->be;
550 47 : newSlot->guid = guid == NULL ? pInfo->guid : guid;
551 47 : newSlot->is_ok = pInfo->is_ok;
552 47 : newSlot->pKvpFrame = pInfo->pKvpFrame;
553 47 : newSlot->value_type = pInfo->value_type;
554 47 : newSlot->pList = pInfo->pList;
555 47 : newSlot->context = pInfo->context;
556 47 : newSlot->pKvpValue = pInfo->pKvpValue;
557 47 : if (!pInfo->path.empty())
558 23 : newSlot->parent_path = pInfo->path + "/";
559 : else
560 24 : newSlot->parent_path = pInfo->parent_path;
561 47 : return newSlot;
562 : }
563 :
564 : static void
565 75 : save_slot (const char* key, KvpValue* value, slot_info_t & slot_info)
566 : {
567 75 : g_return_if_fail (value != NULL);
568 :
569 : // Ignore if we've already run into a failure
570 75 : if (!slot_info.is_ok)
571 : {
572 0 : return;
573 : }
574 75 : slot_info.pKvpValue = value;
575 75 : slot_info.path = slot_info.parent_path + key;
576 75 : slot_info.value_type = value->get_type ();
577 :
578 75 : switch (slot_info.value_type)
579 : {
580 13 : case KvpValue::Type::FRAME:
581 : {
582 13 : auto pKvpFrame = value->get<KvpFrame*> ();
583 13 : auto guid = guid_new ();
584 13 : slot_info_t* pNewInfo = slot_info_copy (&slot_info, guid);
585 13 : KvpValue* oldValue = slot_info.pKvpValue;
586 13 : slot_info.pKvpValue = new KvpValue {guid};
587 13 : 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 13 : g_return_if_fail (slot_info.is_ok);
593 13 : pKvpFrame->for_each_slot_temp (save_slot, *pNewInfo);
594 13 : delete slot_info.pKvpValue;
595 13 : slot_info.pKvpValue = oldValue;
596 13 : delete pNewInfo;
597 : }
598 13 : 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 62 : default:
622 : {
623 62 : 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 62 : break;
630 : }
631 : }
632 :
633 : gboolean
634 107 : gnc_sql_slots_save (GncSqlBackend* sql_be, const GncGUID* guid, gboolean is_infant,
635 : QofInstance* inst)
636 : {
637 107 : slot_info_t slot_info = { NULL, NULL, TRUE, NULL, KvpValue::Type::INVALID,
638 107 : NULL, FRAME, NULL, "" };
639 107 : KvpFrame* pFrame = qof_instance_get_slots (inst);
640 :
641 107 : g_return_val_if_fail (sql_be != NULL, FALSE);
642 107 : g_return_val_if_fail (guid != NULL, FALSE);
643 107 : 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 107 : if (!sql_be->pristine() && !is_infant)
647 : {
648 8 : (void)gnc_sql_slots_delete (sql_be, guid);
649 : }
650 :
651 107 : slot_info.be = sql_be;
652 107 : slot_info.guid = guid;
653 107 : pFrame->for_each_slot_temp (save_slot, slot_info);
654 :
655 107 : return slot_info.is_ok;
656 107 : }
657 :
658 : gboolean
659 11 : gnc_sql_slots_delete (GncSqlBackend* sql_be, const GncGUID* guid)
660 : {
661 : gchar* buf;
662 : gchar guid_buf[GUID_ENCODING_LENGTH + 1];
663 11 : slot_info_t slot_info = { NULL, NULL, TRUE, NULL, KvpValue::Type::INVALID,
664 11 : NULL, FRAME, NULL, "" };
665 :
666 11 : g_return_val_if_fail (sql_be != NULL, FALSE);
667 11 : g_return_val_if_fail (guid != NULL, FALSE);
668 :
669 11 : (void)guid_to_string_buff (guid, guid_buf);
670 :
671 11 : 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 22 : auto stmt = sql_be->create_statement_from_sql(buf);
674 11 : g_free (buf);
675 11 : if (stmt != nullptr)
676 : {
677 11 : auto result = sql_be->execute_select_statement(stmt);
678 12 : 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 12 : }
687 : }
688 :
689 11 : slot_info.be = sql_be;
690 11 : slot_info.guid = guid;
691 11 : slot_info.is_ok = TRUE;
692 11 : 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 11 : return slot_info.is_ok;
697 11 : }
698 :
699 : static void
700 24 : load_slot (slot_info_t* pInfo, GncSqlRow& row)
701 : {
702 : slot_info_t* slot_info;
703 :
704 24 : g_return_if_fail (pInfo != NULL);
705 24 : g_return_if_fail (pInfo->be != NULL);
706 24 : g_return_if_fail (pInfo->pKvpFrame != NULL);
707 :
708 24 : slot_info = slot_info_copy (pInfo, NULL);
709 :
710 24 : gnc_sql_load_object (pInfo->be, row, TABLE_NAME, slot_info, col_table);
711 :
712 24 : 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 24 : delete slot_info;
724 : }
725 :
726 : void
727 9 : gnc_sql_slots_load (GncSqlBackend* sql_be, QofInstance* inst)
728 : {
729 9 : slot_info_t info = { NULL, NULL, TRUE, NULL, KvpValue::Type::INVALID,
730 9 : NULL, FRAME, NULL, "" };
731 9 : g_return_if_fail (sql_be != NULL);
732 9 : g_return_if_fail (inst != NULL);
733 :
734 9 : info.be = sql_be;
735 9 : info.guid = qof_instance_get_guid (inst);
736 9 : info.pKvpFrame = qof_instance_get_slots (inst);
737 9 : info.context = NONE;
738 :
739 9 : slots_load_info (&info);
740 9 : }
741 :
742 : static void
743 19 : slots_load_info (slot_info_t* pInfo)
744 : {
745 19 : g_return_if_fail (pInfo != NULL);
746 19 : g_return_if_fail (pInfo->be != NULL);
747 19 : g_return_if_fail (pInfo->guid != NULL);
748 19 : g_return_if_fail (pInfo->pKvpFrame != NULL);
749 :
750 19 : gnc::GUID guid(*pInfo->guid);
751 19 : std::string sql("SELECT * FROM " TABLE_NAME " WHERE obj_guid='");
752 19 : sql += guid.to_string() + "'";
753 19 : auto stmt = pInfo->be->create_statement_from_sql(sql);
754 19 : if (stmt != nullptr)
755 : {
756 19 : auto result = pInfo->be->execute_select_statement (stmt);
757 43 : for (auto row : *result)
758 43 : load_slot (pInfo, row);
759 19 : delete result;
760 : }
761 19 : }
762 :
763 : static const GncGUID*
764 50 : load_obj_guid (const GncSqlBackend* sql_be, GncSqlRow& row)
765 : {
766 : static GncGUID guid;
767 :
768 50 : g_return_val_if_fail (sql_be != NULL, NULL);
769 :
770 50 : gnc_sql_load_object (sql_be, row, NULL, &guid, obj_guid_col_table);
771 :
772 50 : return &guid;
773 : }
774 :
775 : static void
776 50 : load_slot_for_book_object (GncSqlBackend* sql_be, GncSqlRow& row,
777 : BookLookupFn lookup_fn)
778 : {
779 50 : slot_info_t slot_info = { NULL, NULL, TRUE, NULL, KvpValue::Type::INVALID,
780 50 : NULL, FRAME, NULL, "" };
781 : const GncGUID* guid;
782 : QofInstance* inst;
783 :
784 50 : g_return_if_fail (sql_be != NULL);
785 50 : g_return_if_fail (lookup_fn != NULL);
786 :
787 50 : guid = load_obj_guid (sql_be, row);
788 50 : g_return_if_fail (guid != NULL);
789 50 : inst = lookup_fn (guid, sql_be->book());
790 50 : if (inst == NULL) return; /* Silently bail if the guid isn't loaded yet. */
791 :
792 50 : slot_info.be = sql_be;
793 50 : slot_info.pKvpFrame = qof_instance_get_slots (inst);
794 50 : slot_info.path.clear();
795 :
796 50 : gnc_sql_load_object (sql_be, row, TABLE_NAME, &slot_info, col_table);
797 50 : }
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 97 : void gnc_sql_slots_load_for_sql_subquery (GncSqlBackend* sql_be,
809 : const std::string subquery,
810 : BookLookupFn lookup_fn)
811 : {
812 97 : g_return_if_fail (sql_be != NULL);
813 :
814 : // Ignore empty subquery
815 97 : if (subquery.empty()) return;
816 :
817 194 : std::string pkey(obj_guid_col_table[0]->name());
818 97 : std::string sql("SELECT * FROM " TABLE_NAME " WHERE ");
819 97 : sql += pkey + " IN (" + subquery + ")";
820 :
821 : // Execute the query and load the slots
822 97 : auto stmt = sql_be->create_statement_from_sql(sql);
823 97 : if (stmt == nullptr)
824 : {
825 0 : PERR ("stmt == NULL, SQL = '%s'\n", sql.c_str());
826 0 : return;
827 : }
828 97 : auto result = sql_be->execute_select_statement(stmt);
829 147 : for (auto row : *result)
830 147 : load_slot_for_book_object (sql_be, row, lookup_fn);
831 97 : delete result;
832 97 : }
833 :
834 : /* ================================================================= */
835 : void
836 13 : GncSqlSlotsBackend::create_tables (GncSqlBackend* sql_be)
837 : {
838 : gint version;
839 : gboolean ok;
840 :
841 13 : g_return_if_fail (sql_be != NULL);
842 :
843 26 : version = sql_be->get_table_version( TABLE_NAME);
844 13 : if (version == 0)
845 : {
846 12 : (void)sql_be->create_table(TABLE_NAME, TABLE_VERSION, col_table);
847 :
848 24 : ok = sql_be->create_index ("slots_guid_index", TABLE_NAME,
849 : obj_guid_col_table);
850 6 : if (!ok)
851 : {
852 0 : PERR ("Unable to create index\n");
853 : }
854 : }
855 7 : 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 ===================== */
|