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