37#include "splint-defs.h"
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"
50#include <kvp-frame.hpp>
52static QofLogModule log_module = G_LOG_DOMAIN;
54#define TABLE_NAME "slots"
55#define TABLE_VERSION 4
70 KvpValue::Type value_type;
75 std::string parent_path;
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);
102#define SLOT_MAX_PATHNAME_LEN 4096
103#define SLOT_MAX_STRINGVAL_LEN 4096
119static const EntryVec col_table
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,
127 gnc_sql_make_table_entry<CT_STRING>(
"name", SLOT_MAX_PATHNAME_LEN, COL_NNUL,
129 gnc_sql_make_table_entry<CT_INT>(
"slot_type", 0, COL_NNUL,
132 gnc_sql_make_table_entry<CT_INT64>(
"int64_val", 0, 0,
135 gnc_sql_make_table_entry<CT_STRING>(
"string_val", SLOT_MAX_PATHNAME_LEN, 0,
138 gnc_sql_make_table_entry<CT_DOUBLE>(
"double_val", 0, 0,
141 gnc_sql_make_table_entry<CT_TIME>(
"timespec_val", 0, 0,
144 gnc_sql_make_table_entry<CT_GUID>(
"guid_val", 0, 0,
147 gnc_sql_make_table_entry<CT_NUMERIC>(
"numeric_val", 0, 0,
150 gnc_sql_make_table_entry<CT_GDATE>(
"gdate_val", 0, 0,
156_retrieve_guid_ (gpointer pObject, gpointer pValue)
161 g_return_if_fail (pObject != NULL);
162 g_return_if_fail (pValue != NULL);
169static const EntryVec obj_guid_col_table
171 gnc_sql_make_table_entry<CT_GUID>(
"obj_guid", 0, 0,
176static const EntryVec gdate_col_table
178 gnc_sql_make_table_entry<CT_GDATE>(
"gdate_val", 0, 0),
181GncSqlSlotsBackend::GncSqlSlotsBackend() :
183 TABLE_NAME, col_table) {}
190 if (!pInfo)
return "";
192 auto path = pInfo->path;
193 path.erase (0, pInfo->parent_path.size());
198set_slot_from_value (
slot_info_t* pInfo, KvpValue* pValue)
200 g_return_if_fail (pInfo != NULL);
201 g_return_if_fail (pValue != NULL);
203 switch (pInfo->context)
207 auto key = get_key (pInfo);
208 pInfo->pKvpFrame->set ({key}, pValue);
213 pInfo->pList = g_list_append (pInfo->pList, pValue);
219 auto key = get_key (pInfo);
220 auto path = pInfo->parent_path;
221 auto frame = pInfo->pKvpFrame;
224 frame->set_path ({path, key}, pValue);
227 frame->set ({key}, pValue);
234get_obj_guid (gpointer pObject)
238 g_return_val_if_fail (pObject != NULL, NULL);
240 return (gpointer)pInfo->guid;
250get_path (gpointer pObject)
254 g_return_val_if_fail (pObject != NULL, NULL);
256 return (gpointer)pInfo->path.c_str();
260set_path (gpointer pObject, gpointer pValue)
263 pInfo->path =
static_cast<char*
>(pValue);
264 if (pInfo->path.find (pInfo->parent_path) != 0)
265 pInfo->parent_path.clear();
269get_slot_type (gpointer pObject)
273 g_return_val_if_fail (pObject != NULL, KvpValue::Type::INVALID);
276 return pInfo->value_type;
280set_slot_type (gpointer pObject, gpointer pValue)
284 g_return_if_fail (pObject != NULL);
285 g_return_if_fail (pValue != NULL);
287 pInfo->value_type =
static_cast<KvpValue::Type
> (GPOINTER_TO_INT (pValue));
291get_int64_val (gpointer pObject)
295 g_return_val_if_fail (pObject != NULL, 0);
297 if (pInfo->pKvpValue->get_type () == KvpValue::Type::INT64)
299 return pInfo->pKvpValue->get<int64_t> ();
308set_int64_val (gpointer pObject, gint64 value)
311 KvpValue* pValue = NULL;
313 g_return_if_fail (pObject != NULL);
315 if (pInfo->value_type != KvpValue::Type::INT64)
return;
316 pValue =
new KvpValue {value};
317 set_slot_from_value (pInfo, pValue);
321get_string_val (gpointer pObject)
325 g_return_val_if_fail (pObject != NULL, NULL);
327 if (pInfo->pKvpValue->get_type () == KvpValue::Type::STRING)
329 return (gpointer)pInfo->pKvpValue->get<
const char*> ();
338set_string_val (gpointer pObject, gpointer pValue)
341 g_return_if_fail (pObject != NULL);
343 if (pInfo->value_type != KvpValue::Type::STRING || pValue == NULL)
345 auto value =
new KvpValue {g_strdup(
static_cast<const char*
> (pValue))};
346 set_slot_from_value (pInfo, value);
350get_double_val (gpointer pObject)
353 g_return_val_if_fail (pObject != NULL, NULL);
355 if (pInfo->pKvpValue->get_type () == KvpValue::Type::DOUBLE)
357 d_val = pInfo->pKvpValue->get<
double> ();
358 return (gpointer)&d_val;
367set_double_val (gpointer pObject, gpointer pValue)
370 KvpValue* value = NULL;
372 g_return_if_fail (pObject != NULL);
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);
380get_time_val (gpointer pObject)
384 g_return_val_if_fail (pObject != NULL, 0);
387 auto t = pInfo->pKvpValue->get<
Time64> ();
392set_time_val (gpointer pObject,
time64 time)
395 KvpValue* value = NULL;
397 g_return_if_fail (pObject != NULL);
399 if (pInfo->value_type != KvpValue::Type::TIME64)
return;
400 value =
new KvpValue {t};
401 set_slot_from_value (pInfo, value);
405get_guid_val (gpointer pObject)
409 g_return_val_if_fail (pObject != NULL, NULL);
411 if (pInfo->pKvpValue->get_type () == KvpValue::Type::GUID)
413 return (gpointer)pInfo->pKvpValue->get<
GncGUID*> ();
422set_guid_val (gpointer pObject, gpointer pValue)
426 g_return_if_fail (pObject != NULL);
427 if (pValue == NULL)
return;
429 switch (pInfo->value_type)
431 case KvpValue::Type::GUID:
434 set_slot_from_value (pInfo,
new KvpValue {new_guid});
437 case KvpValue::Type::GLIST:
440 KvpValue* pValue = NULL;
441 auto key = get_key (pInfo);
443 newInfo->context = LIST;
445 slots_load_info (newInfo);
446 pValue =
new KvpValue {newInfo->pList};
447 pInfo->pKvpFrame->set ({key.c_str()}, pValue);
451 case KvpValue::Type::FRAME:
454 auto newFrame =
new KvpFrame;
455 newInfo->pKvpFrame = newFrame;
457 switch (pInfo->context)
461 auto value =
new KvpValue {newFrame};
462 newInfo->path = get_key (pInfo);
463 pInfo->pList = g_list_append (pInfo->pList, value);
469 auto key = get_key (pInfo);
470 pInfo->pKvpFrame->set ({key.c_str()},
new KvpValue {newFrame});
475 newInfo->context = FRAME;
476 slots_load_info (newInfo);
486get_numeric_val (gpointer pObject)
490 g_return_val_if_fail (pObject != NULL, gnc_numeric_zero ());
492 if (pInfo->pKvpValue->get_type () == KvpValue::Type::NUMERIC)
494 return pInfo->pKvpValue->get<gnc_numeric> ();
498 return gnc_numeric_zero ();
503set_numeric_val (gpointer pObject, gnc_numeric value)
507 g_return_if_fail (pObject != NULL);
509 if (pInfo->value_type != KvpValue::Type::NUMERIC)
return;
510 set_slot_from_value (pInfo,
new KvpValue {value});
514get_gdate_val (gpointer pObject)
519 g_return_val_if_fail (pObject != NULL, NULL);
521 if (pInfo->pKvpValue->get_type () == KvpValue::Type::GDATE)
523 date = pInfo->pKvpValue->get<GDate> ();
533set_gdate_val (gpointer pObject, GDate* value)
537 g_return_if_fail (pObject != NULL);
539 if (pInfo->value_type != KvpValue::Type::GDATE)
return;
540 set_slot_from_value (pInfo,
new KvpValue {*value});
546 g_return_val_if_fail (pInfo != NULL, NULL);
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 +
"/";
560 newSlot->parent_path = pInfo->parent_path;
565save_slot (
const char* key, KvpValue* value,
slot_info_t & slot_info)
567 g_return_if_fail (value != NULL);
570 if (!slot_info.is_ok)
574 slot_info.pKvpValue = value;
575 slot_info.path = slot_info.parent_path + key;
576 slot_info.value_type = value->get_type ();
578 switch (slot_info.value_type)
580 case KvpValue::Type::FRAME:
582 auto pKvpFrame = value->get<KvpFrame*> ();
584 slot_info_t* pNewInfo = slot_info_copy (&slot_info, guid);
585 KvpValue* oldValue = slot_info.pKvpValue;
586 slot_info.pKvpValue =
new KvpValue {guid};
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;
599 case KvpValue::Type::GLIST:
602 slot_info_t* pNewInfo = slot_info_copy (&slot_info, guid);
603 KvpValue* oldValue = slot_info.pKvpValue;
604 slot_info.pKvpValue =
new KvpValue {guid};
610 g_return_if_fail (slot_info.is_ok);
611 for (
auto cursor = value->get<GList*> (); cursor; cursor = cursor->next)
613 auto val =
static_cast<KvpValue*
> (cursor->data);
614 save_slot (
"", val, *pNewInfo);
616 delete slot_info.pKvpValue;
617 slot_info.pKvpValue = oldValue;
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);
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);
646 if (!sql_be->pristine() && !is_infant)
648 (void)gnc_sql_slots_delete (sql_be, guid);
651 slot_info.be = sql_be;
652 slot_info.guid = guid;
653 pFrame->for_each_slot_temp (save_slot, slot_info);
655 return slot_info.is_ok;
663 slot_info_t slot_info = { NULL, NULL, TRUE, NULL, KvpValue::Type::INVALID,
664 NULL, FRAME, NULL,
"" };
666 g_return_val_if_fail (sql_be != NULL, FALSE);
667 g_return_val_if_fail (guid != NULL, FALSE);
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);
678 for (
auto row : *result)
680 const GncSqlColumnTableEntryPtr table_row =
681 col_table[guid_val_col];
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);
689 slot_info.be = sql_be;
690 slot_info.guid = guid;
691 slot_info.is_ok = TRUE;
693 TABLE_NAME, &slot_info,
696 return slot_info.is_ok;
704 g_return_if_fail (pInfo != NULL);
705 g_return_if_fail (pInfo->be != NULL);
706 g_return_if_fail (pInfo->pKvpFrame != NULL);
708 slot_info = slot_info_copy (pInfo, NULL);
710 gnc_sql_load_object (pInfo->be, row, TABLE_NAME, slot_info, col_table);
712 if (slot_info->pList != pInfo->pList)
714 if (pInfo->pList != NULL)
716 PWARN (
"Load slot returned a different list than the original");
720 pInfo->pList = slot_info->pList;
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);
736 info.pKvpFrame = qof_instance_get_slots (inst);
739 slots_load_info (&info);
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);
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);
757 for (
auto row : *result)
758 load_slot (pInfo, row);
768 g_return_val_if_fail (sql_be != NULL, NULL);
770 gnc_sql_load_object (sql_be, row, NULL, &guid, obj_guid_col_table);
777 BookLookupFn lookup_fn)
779 slot_info_t slot_info = { NULL, NULL, TRUE, NULL, KvpValue::Type::INVALID,
780 NULL, FRAME, NULL,
"" };
784 g_return_if_fail (sql_be != NULL);
785 g_return_if_fail (lookup_fn != NULL);
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;
792 slot_info.be = sql_be;
793 slot_info.pKvpFrame = qof_instance_get_slots (inst);
794 slot_info.path.clear();
796 gnc_sql_load_object (sql_be, row, TABLE_NAME, &slot_info, col_table);
809 const std::string subquery,
810 BookLookupFn lookup_fn)
812 g_return_if_fail (sql_be != NULL);
815 if (subquery.empty())
return;
817 std::string pkey(obj_guid_col_table[0]->name());
818 std::string sql(
"SELECT * FROM " TABLE_NAME
" WHERE ");
819 sql += pkey +
" IN (" + subquery +
")";
822 auto stmt = sql_be->create_statement_from_sql(sql);
825 PERR (
"stmt == NULL, SQL = '%s'\n", sql.c_str());
829 for (
auto row : *result)
830 load_slot_for_book_object (sql_be, row, lookup_fn);
841 g_return_if_fail (sql_be != NULL);
846 (void)sql_be->
create_table(TABLE_NAME, TABLE_VERSION, col_table);
848 ok = sql_be->
create_index (
"slots_guid_index", TABLE_NAME,
852 PERR (
"Unable to create index\n");
855 else if (version < m_version)
865 ok = sql_be->
create_index (
"slots_guid_index", TABLE_NAME,
869 PERR (
"Unable to create index\n");
872 else if (version == 2)
877 PERR (
"Unable to add gdate column\n");
885 PINFO (
"Slots table upgraded from version %d to version %d\n", version,
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.
void(* QofSetterFunc)(gpointer, gpointer)
The QofSetterFunc defines an function pointer for parameter setters.
gint64 time64
Most systems that are currently maintained, including Microsoft Windows, BSD-derived Unixes and Linux...
#define GUID_ENCODING_LENGTH
Number of characters needed to encode a guid as a string not including the null terminator.
GncGUID * guid_copy(const GncGUID *guid)
Returns a newly allocated GncGUID that matches the passed-in GUID.
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...
GncGUID * guid_new(void)
Allocate and construct a new GUID.
const GncGUID * qof_instance_get_guid(gconstpointer inst)
Return the GncGUID of this instance.
#define PINFO(format, args...)
Print an informational note.
#define PERR(format, args...)
Log a serious error.
#define PWARN(format, args...)
Log a warning.
The type used to store guids in C.