GnuCash c935c2f+
Loading...
Searching...
No Matches
gnc-slots-sql.cpp
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\********************************************************************/
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
52static QofLogModule log_module = G_LOG_DOMAIN;
53
54#define TABLE_NAME "slots"
55#define TABLE_VERSION 4
56
57typedef enum
58{
59 NONE,
60 FRAME,
61 LIST
62} context_t;
63
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
79static gpointer get_obj_guid (gpointer pObject);
80static void set_obj_guid (void);
81static gpointer get_path (gpointer pObject);
82static void set_path (gpointer pObject, gpointer pValue);
83static KvpValue::Type get_slot_type (gpointer pObject);
84static void set_slot_type (gpointer pObject, gpointer pValue);
85static gint64 get_int64_val (gpointer pObject);
86static void set_int64_val (gpointer pObject, gint64 pValue);
87static gpointer get_string_val (gpointer pObject);
88static void set_string_val (gpointer pObject, gpointer pValue);
89static gpointer get_double_val (gpointer pObject);
90static void set_double_val (gpointer pObject, gpointer pValue);
91static time64 get_time_val (gpointer pObject);
92static void set_time_val (gpointer pObject, time64 t);
93static gpointer get_guid_val (gpointer pObject);
94static void set_guid_val (gpointer pObject, gpointer pValue);
95static gnc_numeric get_numeric_val (gpointer pObject);
96static void set_numeric_val (gpointer pObject, gnc_numeric value);
97static GDate* get_gdate_val (gpointer pObject);
98static void set_gdate_val (gpointer pObject, GDate* value);
99static slot_info_t* slot_info_copy (slot_info_t* pInfo, GncGUID* guid);
100static void slots_load_info (slot_info_t* pInfo);
101
102#define SLOT_MAX_PATHNAME_LEN 4096
103#define SLOT_MAX_STRINGVAL_LEN 4096
104enum
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
119static 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
155static void
156_retrieve_guid_ (gpointer pObject, gpointer pValue)
157{
158 GncGUID* pGuid = (GncGUID*)pObject;
159 GncGUID* guid = (GncGUID*)pValue;
160
161 g_return_if_fail (pObject != NULL);
162 g_return_if_fail (pValue != NULL);
163
164 *pGuid = *guid;
165}
166
167/* Special column table because we need to be able to access the table by
168a column other than the primary key */
169static 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
176static const EntryVec gdate_col_table
177{
178 gnc_sql_make_table_entry<CT_GDATE>("gdate_val", 0, 0),
179};
180
181GncSqlSlotsBackend::GncSqlSlotsBackend() :
182 GncSqlObjectBackend(TABLE_VERSION, GNC_ID_ACCOUNT,
183 TABLE_NAME, col_table) {}
184
185/* ================================================================= */
186
187static std::string
188get_key (slot_info_t* pInfo)
189{
190 if (!pInfo) return "";
191
192 auto path = pInfo->path;
193 path.erase (0, pInfo->parent_path.size());
194 return path;
195}
196
197static void
198set_slot_from_value (slot_info_t* pInfo, KvpValue* pValue)
199{
200 g_return_if_fail (pInfo != NULL);
201 g_return_if_fail (pValue != NULL);
202
203 switch (pInfo->context)
204 {
205 case FRAME:
206 {
207 auto key = get_key (pInfo);
208 pInfo->pKvpFrame->set ({key}, pValue);
209 break;
210 }
211 case LIST:
212 {
213 pInfo->pList = g_list_append (pInfo->pList, pValue);
214 break;
215 }
216 case NONE:
217 default:
218 {
219 auto key = get_key (pInfo);
220 auto path = pInfo->parent_path;
221 auto frame = pInfo->pKvpFrame;
222 if (!path.empty())
223 {
224 frame->set_path ({path, key}, pValue);
225 }
226 else
227 frame->set ({key}, pValue);
228 break;
229 }
230 }
231}
232
233static gpointer
234get_obj_guid (gpointer pObject)
235{
236 slot_info_t* pInfo = (slot_info_t*)pObject;
237
238 g_return_val_if_fail (pObject != NULL, NULL);
239
240 return (gpointer)pInfo->guid;
241}
242
243static void
244set_obj_guid (void)
245{
246 // Nowhere to put the GncGUID
247}
248
249static gpointer
250get_path (gpointer pObject)
251{
252 slot_info_t* pInfo = (slot_info_t*)pObject;
253
254 g_return_val_if_fail (pObject != NULL, NULL);
255
256 return (gpointer)pInfo->path.c_str();
257}
258
259static void
260set_path (gpointer pObject, gpointer pValue)
261{
262 slot_info_t* pInfo = (slot_info_t*)pObject;
263 pInfo->path = static_cast<char*>(pValue);
264 if (pInfo->path.find (pInfo->parent_path) != 0)
265 pInfo->parent_path.clear();
266}
267
268static KvpValue::Type
269get_slot_type (gpointer pObject)
270{
271 slot_info_t* pInfo = (slot_info_t*)pObject;
272
273 g_return_val_if_fail (pObject != NULL, KvpValue::Type::INVALID);
274
275// return (gpointer)kvp_value_get_type( pInfo->pKvpValue );
276 return pInfo->value_type;
277}
278
279static void
280set_slot_type (gpointer pObject, gpointer pValue)
281{
282 slot_info_t* pInfo = (slot_info_t*)pObject;
283
284 g_return_if_fail (pObject != NULL);
285 g_return_if_fail (pValue != NULL);
286
287 pInfo->value_type = static_cast<KvpValue::Type> (GPOINTER_TO_INT (pValue));
288}
289
290static gint64
291get_int64_val (gpointer pObject)
292{
293 slot_info_t* pInfo = (slot_info_t*)pObject;
294
295 g_return_val_if_fail (pObject != NULL, 0);
296
297 if (pInfo->pKvpValue->get_type () == KvpValue::Type::INT64)
298 {
299 return pInfo->pKvpValue->get<int64_t> ();
300 }
301 else
302 {
303 return 0;
304 }
305}
306
307static void
308set_int64_val (gpointer pObject, gint64 value)
309{
310 slot_info_t* pInfo = (slot_info_t*)pObject;
311 KvpValue* pValue = NULL;
312
313 g_return_if_fail (pObject != NULL);
314
315 if (pInfo->value_type != KvpValue::Type::INT64) return;
316 pValue = new KvpValue {value};
317 set_slot_from_value (pInfo, pValue);
318}
319
320static gpointer
321get_string_val (gpointer pObject)
322{
323 slot_info_t* pInfo = (slot_info_t*)pObject;
324
325 g_return_val_if_fail (pObject != NULL, NULL);
326
327 if (pInfo->pKvpValue->get_type () == KvpValue::Type::STRING)
328 {
329 return (gpointer)pInfo->pKvpValue->get<const char*> ();
330 }
331 else
332 {
333 return NULL;
334 }
335}
336
337static void
338set_string_val (gpointer pObject, gpointer pValue)
339{
340 slot_info_t* pInfo = (slot_info_t*)pObject;
341 g_return_if_fail (pObject != NULL);
342
343 if (pInfo->value_type != KvpValue::Type::STRING || pValue == NULL)
344 return;
345 auto value = new KvpValue {g_strdup(static_cast<const char*> (pValue))};
346 set_slot_from_value (pInfo, value);
347}
348
349static gpointer
350get_double_val (gpointer pObject)
351{
352 static double d_val; /* static so that we can return it. */
353 g_return_val_if_fail (pObject != NULL, NULL);
354 auto pInfo = static_cast<slot_info_t*>(pObject);
355 if (pInfo->pKvpValue->get_type () == KvpValue::Type::DOUBLE)
356 {
357 d_val = pInfo->pKvpValue->get<double> ();
358 return (gpointer)&d_val;
359 }
360 else
361 {
362 return NULL;
363 }
364}
365
366static void
367set_double_val (gpointer pObject, gpointer pValue)
368{
369 slot_info_t* pInfo = (slot_info_t*)pObject;
370 KvpValue* value = NULL;
371
372 g_return_if_fail (pObject != NULL);
373
374 if (pInfo->value_type != KvpValue::Type::DOUBLE || pValue == NULL) return;
375 value = new KvpValue {* (static_cast<double*> (pValue))};
376 set_slot_from_value (pInfo, value);
377}
378
379static time64
380get_time_val (gpointer pObject)
381{
382 slot_info_t* pInfo = (slot_info_t*)pObject;
383
384 g_return_val_if_fail (pObject != NULL, 0);
385
386//if( kvp_value_get_type( pInfo->pKvpValue ) == KvpValue::Type::TIME64 ) {
387 auto t = pInfo->pKvpValue->get<Time64> ();
388 return t.t;
389}
390
391static void
392set_time_val (gpointer pObject, time64 time)
393{
394 slot_info_t* pInfo = (slot_info_t*)pObject;
395 KvpValue* value = NULL;
396 Time64 t{time};
397 g_return_if_fail (pObject != NULL);
398
399 if (pInfo->value_type != KvpValue::Type::TIME64) return;
400 value = new KvpValue {t};
401 set_slot_from_value (pInfo, value);
402}
403
404static gpointer
405get_guid_val (gpointer pObject)
406{
407 slot_info_t* pInfo = (slot_info_t*)pObject;
408
409 g_return_val_if_fail (pObject != NULL, NULL);
410
411 if (pInfo->pKvpValue->get_type () == KvpValue::Type::GUID)
412 {
413 return (gpointer)pInfo->pKvpValue->get<GncGUID*> ();
414 }
415 else
416 {
417 return NULL;
418 }
419}
420
421static void
422set_guid_val (gpointer pObject, gpointer pValue)
423{
424 slot_info_t* pInfo = (slot_info_t*)pObject;
425
426 g_return_if_fail (pObject != NULL);
427 if (pValue == NULL) return;
428
429 switch (pInfo->value_type)
430 {
431 case KvpValue::Type::GUID:
432 {
433 auto new_guid = guid_copy (static_cast<GncGUID*> (pValue));
434 set_slot_from_value (pInfo, new KvpValue {new_guid});
435 break;
436 }
437 case KvpValue::Type::GLIST:
438 {
439 slot_info_t* newInfo = slot_info_copy (pInfo, (GncGUID*)pValue);
440 KvpValue* pValue = NULL;
441 auto key = get_key (pInfo);
442
443 newInfo->context = LIST;
444
445 slots_load_info (newInfo);
446 pValue = new KvpValue {newInfo->pList};
447 pInfo->pKvpFrame->set ({key.c_str()}, pValue);
448 delete newInfo;
449 break;
450 }
451 case KvpValue::Type::FRAME:
452 {
453 slot_info_t* newInfo = slot_info_copy (pInfo, (GncGUID*)pValue) ;
454 auto newFrame = new KvpFrame;
455 newInfo->pKvpFrame = newFrame;
456
457 switch (pInfo->context)
458 {
459 case LIST:
460 {
461 auto value = new KvpValue {newFrame};
462 newInfo->path = get_key (pInfo);
463 pInfo->pList = g_list_append (pInfo->pList, value);
464 break;
465 }
466 case FRAME:
467 default:
468 {
469 auto key = get_key (pInfo);
470 pInfo->pKvpFrame->set ({key.c_str()}, new KvpValue {newFrame});
471 break;
472 }
473 }
474
475 newInfo->context = FRAME;
476 slots_load_info (newInfo);
477 delete newInfo;
478 break;
479 }
480 default:
481 break;
482 }
483}
484
485static gnc_numeric
486get_numeric_val (gpointer pObject)
487{
488 slot_info_t* pInfo = (slot_info_t*)pObject;
489
490 g_return_val_if_fail (pObject != NULL, gnc_numeric_zero ());
491
492 if (pInfo->pKvpValue->get_type () == KvpValue::Type::NUMERIC)
493 {
494 return pInfo->pKvpValue->get<gnc_numeric> ();
495 }
496 else
497 {
498 return gnc_numeric_zero ();
499 }
500}
501
502static void
503set_numeric_val (gpointer pObject, gnc_numeric value)
504{
505 slot_info_t* pInfo = (slot_info_t*)pObject;
506
507 g_return_if_fail (pObject != NULL);
508
509 if (pInfo->value_type != KvpValue::Type::NUMERIC) return;
510 set_slot_from_value (pInfo, new KvpValue {value});
511}
512
513static GDate*
514get_gdate_val (gpointer pObject)
515{
516 slot_info_t* pInfo = (slot_info_t*)pObject;
517 static GDate date;
518
519 g_return_val_if_fail (pObject != NULL, NULL);
520
521 if (pInfo->pKvpValue->get_type () == KvpValue::Type::GDATE)
522 {
523 date = pInfo->pKvpValue->get<GDate> ();
524 return &date;
525 }
526 else
527 {
528 return NULL;
529 }
530}
531
532static void
533set_gdate_val (gpointer pObject, GDate* value)
534{
535 slot_info_t* pInfo = (slot_info_t*)pObject;
536
537 g_return_if_fail (pObject != NULL);
538
539 if (pInfo->value_type != KvpValue::Type::GDATE) return;
540 set_slot_from_value (pInfo, new KvpValue {*value});
541}
542
543static slot_info_t*
544slot_info_copy (slot_info_t* pInfo, GncGUID* guid)
545{
546 g_return_val_if_fail (pInfo != NULL, NULL);
547 auto newSlot = new slot_info_t;
548
549 newSlot->be = pInfo->be;
550 newSlot->guid = guid == NULL ? pInfo->guid : guid;
551 newSlot->is_ok = pInfo->is_ok;
552 newSlot->pKvpFrame = pInfo->pKvpFrame;
553 newSlot->value_type = pInfo->value_type;
554 newSlot->pList = pInfo->pList;
555 newSlot->context = pInfo->context;
556 newSlot->pKvpValue = pInfo->pKvpValue;
557 if (!pInfo->path.empty())
558 newSlot->parent_path = pInfo->path + "/";
559 else
560 newSlot->parent_path = pInfo->parent_path;
561 return newSlot;
562}
563
564static void
565save_slot (const char* key, KvpValue* value, slot_info_t & slot_info)
566{
567 g_return_if_fail (value != NULL);
568
569 // Ignore if we've already run into a failure
570 if (!slot_info.is_ok)
571 {
572 return;
573 }
574 slot_info.pKvpValue = value;
575 slot_info.path = slot_info.parent_path + key;
576 slot_info.value_type = value->get_type ();
577
578 switch (slot_info.value_type)
579 {
580 case KvpValue::Type::FRAME:
581 {
582 auto pKvpFrame = value->get<KvpFrame*> ();
583 auto guid = guid_new ();
584 slot_info_t* pNewInfo = slot_info_copy (&slot_info, guid);
585 KvpValue* oldValue = slot_info.pKvpValue;
586 slot_info.pKvpValue = new KvpValue {guid};
587 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 g_return_if_fail (slot_info.is_ok);
593 pKvpFrame->for_each_slot_temp (save_slot, *pNewInfo);
594 delete slot_info.pKvpValue;
595 slot_info.pKvpValue = oldValue;
596 delete pNewInfo;
597 }
598 break;
599 case KvpValue::Type::GLIST:
600 {
601 GncGUID* guid = guid_new ();
602 slot_info_t* pNewInfo = slot_info_copy (&slot_info, guid);
603 KvpValue* oldValue = slot_info.pKvpValue;
604 slot_info.pKvpValue = new KvpValue {guid}; // Transfer ownership!
605 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 g_return_if_fail (slot_info.is_ok);
611 for (auto cursor = value->get<GList*> (); cursor; cursor = cursor->next)
612 {
613 auto val = static_cast<KvpValue*> (cursor->data);
614 save_slot ("", val, *pNewInfo);
615 }
616 delete slot_info.pKvpValue;
617 slot_info.pKvpValue = oldValue;
618 delete pNewInfo;
619 }
620 break;
621 default:
622 {
623 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 break;
630 }
631}
632
633gboolean
634gnc_sql_slots_save (GncSqlBackend* sql_be, const GncGUID* guid, gboolean is_infant,
635 QofInstance* inst)
636{
637 slot_info_t slot_info = { NULL, NULL, TRUE, NULL, KvpValue::Type::INVALID,
638 NULL, FRAME, NULL, "" };
639 KvpFrame* pFrame = qof_instance_get_slots (inst);
640
641 g_return_val_if_fail (sql_be != NULL, FALSE);
642 g_return_val_if_fail (guid != NULL, FALSE);
643 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 if (!sql_be->pristine() && !is_infant)
647 {
648 (void)gnc_sql_slots_delete (sql_be, guid);
649 }
650
651 slot_info.be = sql_be;
652 slot_info.guid = guid;
653 pFrame->for_each_slot_temp (save_slot, slot_info);
654
655 return slot_info.is_ok;
656}
657
658gboolean
659gnc_sql_slots_delete (GncSqlBackend* sql_be, const GncGUID* guid)
660{
661 gchar* buf;
662 gchar guid_buf[GUID_ENCODING_LENGTH + 1];
663 slot_info_t slot_info = { NULL, NULL, TRUE, NULL, KvpValue::Type::INVALID,
664 NULL, FRAME, NULL, "" };
665
666 g_return_val_if_fail (sql_be != NULL, FALSE);
667 g_return_val_if_fail (guid != NULL, FALSE);
668
669 (void)guid_to_string_buff (guid, guid_buf);
670
671 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 auto stmt = sql_be->create_statement_from_sql(buf);
674 g_free (buf);
675 if (stmt != nullptr)
676 {
677 auto result = sql_be->execute_select_statement(stmt);
678 for (auto row : *result)
679 {
680 const GncSqlColumnTableEntryPtr table_row =
681 col_table[guid_val_col];
682 GncGUID child_guid;
683 auto val = row.get_string_at_col (table_row->name());
684 if (val && string_to_guid (val->c_str(), &child_guid))
685 gnc_sql_slots_delete (sql_be, &child_guid);
686 }
687 }
688
689 slot_info.be = sql_be;
690 slot_info.guid = guid;
691 slot_info.is_ok = TRUE;
692 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 return slot_info.is_ok;
697}
698
699static void
700load_slot (slot_info_t* pInfo, GncSqlRow& row)
701{
702 slot_info_t* slot_info;
703
704 g_return_if_fail (pInfo != NULL);
705 g_return_if_fail (pInfo->be != NULL);
706 g_return_if_fail (pInfo->pKvpFrame != NULL);
707
708 slot_info = slot_info_copy (pInfo, NULL);
709
710 gnc_sql_load_object (pInfo->be, row, TABLE_NAME, slot_info, col_table);
711
712 if (slot_info->pList != pInfo->pList)
713 {
714 if (pInfo->pList != NULL)
715 {
716 PWARN ("Load slot returned a different list than the original");
717 }
718 else
719 {
720 pInfo->pList = slot_info->pList;
721 }
722 }
723 delete slot_info;
724}
725
726void
727gnc_sql_slots_load (GncSqlBackend* sql_be, QofInstance* inst)
728{
729 slot_info_t info = { NULL, NULL, TRUE, NULL, KvpValue::Type::INVALID,
730 NULL, FRAME, NULL, "" };
731 g_return_if_fail (sql_be != NULL);
732 g_return_if_fail (inst != NULL);
733
734 info.be = sql_be;
735 info.guid = qof_instance_get_guid (inst);
736 info.pKvpFrame = qof_instance_get_slots (inst);
737 info.context = NONE;
738
739 slots_load_info (&info);
740}
741
742static void
743slots_load_info (slot_info_t* pInfo)
744{
745 g_return_if_fail (pInfo != NULL);
746 g_return_if_fail (pInfo->be != NULL);
747 g_return_if_fail (pInfo->guid != NULL);
748 g_return_if_fail (pInfo->pKvpFrame != NULL);
749
750 gnc::GUID guid(*pInfo->guid);
751 std::string sql("SELECT * FROM " TABLE_NAME " WHERE obj_guid='");
752 sql += guid.to_string() + "'";
753 auto stmt = pInfo->be->create_statement_from_sql(sql);
754 if (stmt != nullptr)
755 {
756 auto result = pInfo->be->execute_select_statement (stmt);
757 for (auto row : *result)
758 load_slot (pInfo, row);
759 delete result;
760 }
761}
762
763static const GncGUID*
764load_obj_guid (const GncSqlBackend* sql_be, GncSqlRow& row)
765{
766 static GncGUID guid;
767
768 g_return_val_if_fail (sql_be != NULL, NULL);
769
770 gnc_sql_load_object (sql_be, row, NULL, &guid, obj_guid_col_table);
771
772 return &guid;
773}
774
775static void
776load_slot_for_book_object (GncSqlBackend* sql_be, GncSqlRow& row,
777 BookLookupFn lookup_fn)
778{
779 slot_info_t slot_info = { NULL, NULL, TRUE, NULL, KvpValue::Type::INVALID,
780 NULL, FRAME, NULL, "" };
781 const GncGUID* guid;
782 QofInstance* inst;
783
784 g_return_if_fail (sql_be != NULL);
785 g_return_if_fail (lookup_fn != NULL);
786
787 guid = load_obj_guid (sql_be, row);
788 g_return_if_fail (guid != NULL);
789 inst = lookup_fn (guid, sql_be->book());
790 if (inst == NULL) return; /* Silently bail if the guid isn't loaded yet. */
791
792 slot_info.be = sql_be;
793 slot_info.pKvpFrame = qof_instance_get_slots (inst);
794 slot_info.path.clear();
795
796 gnc_sql_load_object (sql_be, row, TABLE_NAME, &slot_info, col_table);
797}
798
808void gnc_sql_slots_load_for_sql_subquery (GncSqlBackend* sql_be,
809 const std::string subquery,
810 BookLookupFn lookup_fn)
811{
812 g_return_if_fail (sql_be != NULL);
813
814 // Ignore empty subquery
815 if (subquery.empty()) return;
816
817 std::string pkey(obj_guid_col_table[0]->name());
818 std::string sql("SELECT * FROM " TABLE_NAME " WHERE ");
819 sql += pkey + " IN (" + subquery + ")";
820
821 // Execute the query and load the slots
822 auto stmt = sql_be->create_statement_from_sql(sql);
823 if (stmt == nullptr)
824 {
825 PERR ("stmt == NULL, SQL = '%s'\n", sql.c_str());
826 return;
827 }
828 auto result = sql_be->execute_select_statement(stmt);
829 for (auto row : *result)
830 load_slot_for_book_object (sql_be, row, lookup_fn);
831 delete result;
832}
833
834/* ================================================================= */
835void
837{
838 gint version;
839 gboolean ok;
840
841 g_return_if_fail (sql_be != NULL);
842
843 version = sql_be->get_table_version( TABLE_NAME);
844 if (version == 0)
845 {
846 (void)sql_be->create_table(TABLE_NAME, TABLE_VERSION, col_table);
847
848 ok = sql_be->create_index ("slots_guid_index", TABLE_NAME,
849 obj_guid_col_table);
850 if (!ok)
851 {
852 PERR ("Unable to create index\n");
853 }
854 }
855 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 if (version == 1)
863 {
864 sql_be->upgrade_table(TABLE_NAME, col_table);
865 ok = sql_be->create_index ("slots_guid_index", TABLE_NAME,
866 obj_guid_col_table);
867 if (!ok)
868 {
869 PERR ("Unable to create index\n");
870 }
871 }
872 else if (version == 2)
873 {
874 ok = sql_be->add_columns_to_table(TABLE_NAME, gdate_col_table);
875 if (!ok)
876 {
877 PERR ("Unable to add gdate column\n");
878 }
879 }
880 else
881 {
882 sql_be->upgrade_table(TABLE_NAME, col_table);
883 }
884 sql_be->set_table_version (TABLE_NAME, TABLE_VERSION);
885 PINFO ("Slots table upgraded from version %d to version %d\n", version,
886 TABLE_VERSION);
887 }
888}
889
890/* ========================== END OF FILE ===================== */
Main SQL backend structure.
uint_t get_table_version(const std::string &table_name) const noexcept
Returns the version number for a DB table.
bool create_index(const std::string &index_name, const std::string &table_name, const EntryVec &col_table) const noexcept
Creates an index in the database.
bool add_columns_to_table(const std::string &table_name, const EntryVec &col_table) const noexcept
Adds one or more columns to an existing table.
bool create_table(const std::string &table_name, const EntryVec &col_table) const noexcept
Creates a table in the database.
GncSqlResultPtr execute_select_statement(const GncSqlStatementPtr &stmt) const noexcept
Executes an SQL SELECT statement and returns the result rows.
void upgrade_table(const std::string &table_name, const EntryVec &col_table) noexcept
Upgrades a table to a new structure.
bool do_db_operation(E_DB_OPERATION op, const char *table_name, QofIdTypeConst obj_name, gpointer pObject, const EntryVec &table) const noexcept
Performs an operation on the database.
bool set_table_version(const std::string &table_name, uint_t version) noexcept
Registers the version for a table.
Encapsulates per-class table schema with functions to load, create a table, commit a changed front-en...
Row of SQL Query results.
void create_tables(GncSqlBackend *) override
Conditionally create or update a database table from m_col_table.
All type declarations for the whole Gnucash engine.
load and save accounts data to SQL
gpointer(* QofAccessFunc)(gpointer object, const QofParam *param)
The QofAccessFunc defines an arbitrary function pointer for access functions.
Definition qofclass.h:123
void(* QofSetterFunc)(gpointer, gpointer)
The QofSetterFunc defines an function pointer for parameter setters.
Definition qofclass.h:130
gint64 time64
Most systems that are currently maintained, including Microsoft Windows, BSD-derived Unixes and Linux...
Definition gnc-date.h:87
#define GUID_ENCODING_LENGTH
Number of characters needed to encode a guid as a string not including the null terminator.
Definition guid.h:84
GncGUID * guid_copy(const GncGUID *guid)
Returns a newly allocated GncGUID that matches the passed-in GUID.
Definition guid.cpp:155
gchar * guid_to_string_buff(const GncGUID *guid, gchar *str)
The guid_to_string_buff() routine puts a null-terminated string encoding of the id into the memory po...
Definition guid.cpp:208
GncGUID * guid_new(void)
Allocate and construct a new GUID.
Definition guid.cpp:186
const GncGUID * qof_instance_get_guid(gconstpointer inst)
Return the GncGUID of this instance.
#define PINFO(format, args...)
Print an informational note.
Definition qoflog.h:256
#define PERR(format, args...)
Log a serious error.
Definition qoflog.h:244
#define PWARN(format, args...)
Log a warning.
Definition qoflog.h:250
The type used to store guids in C.
Definition guid.h:75