GnuCash  5.6-150-g038405b370+
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 
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 
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 _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
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 GncSqlSlotsBackend::GncSqlSlotsBackend() :
182  GncSqlObjectBackend(TABLE_VERSION, GNC_ID_ACCOUNT,
183  TABLE_NAME, col_table) {}
184 
185 /* ================================================================= */
186 
187 static std::string
188 get_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 
197 static void
198 set_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 
233 static gpointer
234 get_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 
243 static void
244 set_obj_guid (void)
245 {
246  // Nowhere to put the GncGUID
247 }
248 
249 static gpointer
250 get_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 
259 static void
260 set_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 
268 static KvpValue::Type
269 get_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 
279 static void
280 set_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 
290 static gint64
291 get_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 
307 static void
308 set_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 
320 static gpointer
321 get_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 
337 static void
338 set_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 
349 static gpointer
350 get_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 
366 static void
367 set_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 
379 static time64
380 get_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 
391 static void
392 set_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 
404 static gpointer
405 get_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 
421 static void
422 set_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 
485 static gnc_numeric
486 get_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 
502 static void
503 set_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 
513 static GDate*
514 get_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 
532 static void
533 set_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 
543 static slot_info_t*
544 slot_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 
564 static void
565 save_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 
633 gboolean
634 gnc_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 
658 gboolean
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 
699 static void
700 load_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 
726 void
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 
742 static void
743 slots_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 
763 static const GncGUID*
764 load_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 
775 static void
776 load_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 
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 /* ================================================================= */
835 void
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 ===================== */
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 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.
bool set_table_version(const std::string &table_name, uint_t version) noexcept
Registers the version for a table.
GncSqlResultPtr execute_select_statement(const GncSqlStatementPtr &stmt) const noexcept
Executes an SQL SELECT statement and returns the result rows.
const GncGUID * qof_instance_get_guid(gconstpointer inst)
Return the GncGUID of this instance.
void gnc_sql_slots_load_for_sql_subquery(GncSqlBackend *sql_be, const std::string subquery, BookLookupFn lookup_fn)
gnc_sql_slots_load_for_sql_subquery - Loads slots for all objects whose guid is supplied by a subquer...
#define G_LOG_DOMAIN
Functions providing the SX List as a plugin page.
#define PINFO(format, args...)
Print an informational note.
Definition: qoflog.h:256
load and save accounts data to SQL
GncGUID * guid_copy(const GncGUID *guid)
Returns a newly allocated GncGUID that matches the passed-in GUID.
Definition: guid.cpp:120
gboolean string_to_guid(const gchar *string, GncGUID *guid)
Given a string, replace the given guid with the parsed one unless the given value is null...
GncGUID * guid_new(void)
Allocate and construct a new GUID.
Definition: guid.cpp:151
gboolean gnc_sql_slots_save(GncSqlBackend *sql_be, const GncGUID *guid, gboolean is_infant, QofInstance *inst)
gnc_sql_slots_save - Saves slots for an object to the db.
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:173
#define PERR(format, args...)
Log a serious error.
Definition: qoflog.h:244
void(* QofSetterFunc)(gpointer, gpointer)
The QofSetterFunc defines an function pointer for parameter setters.
Definition: qofclass.h:185
#define PWARN(format, args...)
Log a warning.
Definition: qoflog.h:250
void create_tables(GncSqlBackend *) override
Conditionally create or update a database table from m_col_table.
Row of SQL Query results.
void upgrade_table(const std::string &table_name, const EntryVec &col_table) noexcept
Upgrades a table to a new structure.
#define GUID_ENCODING_LENGTH
Number of characters needed to encode a guid as a string not including the null terminator.
Definition: guid.h:84
gpointer(* QofAccessFunc)(gpointer object, const QofParam *param)
The QofAccessFunc defines an arbitrary function pointer for access functions.
Definition: qofclass.h:178
Encapsulates per-class table schema with functions to load, create a table, commit a changed front-en...
All type declarations for the whole Gnucash engine.
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.
gboolean gnc_sql_slots_delete(GncSqlBackend *sql_be, const GncGUID *guid)
gnc_sql_slots_delete - Deletes slots for an object from the db.
void gnc_sql_slots_load(GncSqlBackend *sql_be, QofInstance *inst)
Loads slots for an object from the db.
gint64 time64
Most systems that are currently maintained, including Microsoft Windows, BSD-derived Unixes and Linux...
Definition: gnc-date.h:87
The type used to store guids in C.
Definition: guid.h:75
uint_t get_table_version(const std::string &table_name) const noexcept
Returns the version number for a DB table.
Main SQL backend structure.