mirror of
https://github.com/Gnucash/gnucash.git
synced 2025-02-25 18:55:30 -06:00
992 lines
28 KiB
C++
992 lines
28 KiB
C++
/********************************************************************
|
|
* gnc-slots-sql.c: load and save data to SQL *
|
|
* *
|
|
* This program is free software; you can redistribute it and/or *
|
|
* modify it under the terms of the GNU General Public License as *
|
|
* published by the Free Software Foundation; either version 2 of *
|
|
* the License, or (at your option) any later version. *
|
|
* *
|
|
* This program is distributed in the hope that it will be useful, *
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
|
* GNU General Public License for more details. *
|
|
* *
|
|
* You should have received a copy of the GNU General Public License*
|
|
* along with this program; if not, contact: *
|
|
* *
|
|
* Free Software Foundation Voice: +1-617-542-5942 *
|
|
* 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 *
|
|
* Boston, MA 02110-1301, USA gnu@gnu.org *
|
|
\********************************************************************/
|
|
/** @file gnc-slots-sql.c
|
|
* @brief load and save data to SQL
|
|
* @author Copyright (c) 2006-2008 Phil Longstaff <plongstaff@rogers.com>
|
|
*
|
|
* This file implements the top-level QofBackend API for saving/
|
|
* restoring data to/from an SQL db
|
|
*/
|
|
#include <guid.hpp>
|
|
extern "C"
|
|
{
|
|
#include "config.h"
|
|
|
|
#include <glib.h>
|
|
|
|
#include <qof.h>
|
|
#include <gnc-engine.h>
|
|
|
|
#ifdef S_SPLINT_S
|
|
#include "splint-defs.h"
|
|
#endif
|
|
}
|
|
|
|
#include <string>
|
|
#include <sstream>
|
|
|
|
#include "gnc-sql-connection.hpp"
|
|
#include "gnc-sql-backend.hpp"
|
|
#include "gnc-sql-object-backend.hpp"
|
|
#include "gnc-sql-column-table-entry.hpp"
|
|
#include "gnc-slots-sql.h"
|
|
|
|
#include <kvp-frame.hpp>
|
|
|
|
static QofLogModule log_module = G_LOG_DOMAIN;
|
|
|
|
#define TABLE_NAME "slots"
|
|
#define TABLE_VERSION 3
|
|
|
|
typedef enum
|
|
{
|
|
NONE,
|
|
FRAME,
|
|
LIST
|
|
} context_t;
|
|
|
|
struct slot_info_t
|
|
{
|
|
GncSqlBackend* be;
|
|
const GncGUID* guid;
|
|
gboolean is_ok;
|
|
KvpFrame* pKvpFrame;
|
|
KvpValue::Type value_type;
|
|
GList* pList;
|
|
context_t context;
|
|
KvpValue* pKvpValue;
|
|
std::string path;
|
|
};
|
|
|
|
|
|
static gpointer get_obj_guid (gpointer pObject);
|
|
static void set_obj_guid (void);
|
|
static gpointer get_path (gpointer pObject);
|
|
static void set_path (gpointer pObject, gpointer pValue);
|
|
static KvpValue::Type get_slot_type (gpointer pObject);
|
|
static void set_slot_type (gpointer pObject, gpointer pValue);
|
|
static gint64 get_int64_val (gpointer pObject);
|
|
static void set_int64_val (gpointer pObject, gint64 pValue);
|
|
static gpointer get_string_val (gpointer pObject);
|
|
static void set_string_val (gpointer pObject, gpointer pValue);
|
|
static gpointer get_double_val (gpointer pObject);
|
|
static void set_double_val (gpointer pObject, gpointer pValue);
|
|
static Timespec get_timespec_val (gpointer pObject);
|
|
static void set_timespec_val (gpointer pObject, Timespec *ts);
|
|
static gpointer get_guid_val (gpointer pObject);
|
|
static void set_guid_val (gpointer pObject, gpointer pValue);
|
|
static gnc_numeric get_numeric_val (gpointer pObject);
|
|
static void set_numeric_val (gpointer pObject, gnc_numeric *value);
|
|
static GDate* get_gdate_val (gpointer pObject);
|
|
static void set_gdate_val (gpointer pObject, GDate* value);
|
|
static slot_info_t* slot_info_copy (slot_info_t* pInfo, GncGUID* guid);
|
|
static void slots_load_info (slot_info_t* pInfo);
|
|
|
|
#define SLOT_MAX_PATHNAME_LEN 4096
|
|
#define SLOT_MAX_STRINGVAL_LEN 4096
|
|
enum
|
|
{
|
|
id_col = 0,
|
|
obj_guid_col,
|
|
name_col,
|
|
slot_type_col,
|
|
int64_val_col,
|
|
string_val_col,
|
|
double_val_col,
|
|
timespec_val_col,
|
|
guid_val_col,
|
|
numeric_val_col,
|
|
gdate_val_col
|
|
};
|
|
|
|
static const EntryVec col_table
|
|
{
|
|
/* col_name, col_type, size, flags, g0bj_param_name, qof_param_name, getter, setter */
|
|
gnc_sql_make_table_entry<CT_INT>(
|
|
"id", 0, COL_PKEY | COL_NNUL | COL_AUTOINC),
|
|
gnc_sql_make_table_entry<CT_GUID>("obj_guid", 0, COL_NNUL,
|
|
(QofAccessFunc)get_obj_guid,
|
|
(QofSetterFunc)set_obj_guid),
|
|
gnc_sql_make_table_entry<CT_STRING>("name", SLOT_MAX_PATHNAME_LEN, COL_NNUL,
|
|
(QofAccessFunc)get_path, set_path),
|
|
gnc_sql_make_table_entry<CT_INT>("slot_type", 0, COL_NNUL,
|
|
(QofAccessFunc)get_slot_type,
|
|
set_slot_type),
|
|
gnc_sql_make_table_entry<CT_INT64>("int64_val", 0, 0,
|
|
(QofAccessFunc)get_int64_val,
|
|
(QofSetterFunc)set_int64_val),
|
|
gnc_sql_make_table_entry<CT_STRING>("string_val", SLOT_MAX_PATHNAME_LEN, 0,
|
|
(QofAccessFunc)get_string_val,
|
|
set_string_val),
|
|
gnc_sql_make_table_entry<CT_DOUBLE>("double_val", 0, 0,
|
|
(QofAccessFunc)get_double_val,
|
|
set_double_val),
|
|
gnc_sql_make_table_entry<CT_TIMESPEC>("timespec_val", 0, 0,
|
|
(QofAccessFunc)get_timespec_val,
|
|
(QofSetterFunc)set_timespec_val),
|
|
gnc_sql_make_table_entry<CT_GUID>("guid_val", 0, 0,
|
|
(QofAccessFunc)get_guid_val,
|
|
set_guid_val),
|
|
gnc_sql_make_table_entry<CT_NUMERIC>("numeric_val", 0, 0,
|
|
(QofAccessFunc)get_numeric_val,
|
|
(QofSetterFunc)set_numeric_val),
|
|
gnc_sql_make_table_entry<CT_GDATE>("gdate_val", 0, 0,
|
|
(QofAccessFunc)get_gdate_val,
|
|
(QofSetterFunc)set_gdate_val),
|
|
};
|
|
|
|
static void
|
|
_retrieve_guid_ (gpointer pObject, gpointer pValue)
|
|
{
|
|
GncGUID* pGuid = (GncGUID*)pObject;
|
|
GncGUID* guid = (GncGUID*)pValue;
|
|
|
|
g_return_if_fail (pObject != NULL);
|
|
g_return_if_fail (pValue != NULL);
|
|
|
|
memcpy (pGuid, guid, sizeof (GncGUID));
|
|
}
|
|
|
|
/* Special column table because we need to be able to access the table by
|
|
a column other than the primary key */
|
|
static const EntryVec obj_guid_col_table
|
|
{
|
|
gnc_sql_make_table_entry<CT_GUID>("obj_guid", 0, 0,
|
|
(QofAccessFunc)get_obj_guid,
|
|
_retrieve_guid_),
|
|
};
|
|
|
|
static const EntryVec gdate_col_table
|
|
{
|
|
gnc_sql_make_table_entry<CT_GDATE>("gdate_val", 0, 0),
|
|
};
|
|
|
|
GncSqlSlotsBackend::GncSqlSlotsBackend() :
|
|
GncSqlObjectBackend(GNC_SQL_BACKEND_VERSION, GNC_ID_ACCOUNT,
|
|
TABLE_NAME, col_table) {}
|
|
|
|
/* ================================================================= */
|
|
|
|
inline static std::string::size_type
|
|
get_final_delim(std::string& path)
|
|
{
|
|
auto idx = path.rfind('/');
|
|
while (idx == path.length())
|
|
{
|
|
path.erase(idx);
|
|
idx = path.rfind('/');
|
|
}
|
|
return idx;
|
|
}
|
|
|
|
static std::string
|
|
get_key_from_path (std::string path)
|
|
{
|
|
auto idx = get_final_delim(path);
|
|
if (idx == std::string::npos)
|
|
return path;
|
|
return path.substr(idx);
|
|
}
|
|
|
|
static std::string
|
|
get_path_from_path (std::string path)
|
|
{
|
|
auto idx = get_final_delim(path);
|
|
if (idx == std::string::npos)
|
|
return "";
|
|
return path.substr(0, idx);
|
|
}
|
|
|
|
static void
|
|
set_slot_from_value (slot_info_t* pInfo, KvpValue* pValue)
|
|
{
|
|
g_return_if_fail (pInfo != NULL);
|
|
g_return_if_fail (pValue != NULL);
|
|
|
|
switch (pInfo->context)
|
|
{
|
|
case FRAME:
|
|
{
|
|
auto key = get_key_from_path (pInfo->path);
|
|
pInfo->pKvpFrame->set (key.c_str(), pValue);
|
|
break;
|
|
}
|
|
case LIST:
|
|
{
|
|
pInfo->pList = g_list_append (pInfo->pList, pValue);
|
|
break;
|
|
}
|
|
case NONE:
|
|
default:
|
|
{
|
|
auto key = get_key_from_path (pInfo->path);
|
|
auto path = get_path_from_path (pInfo->path);
|
|
auto frame = pInfo->pKvpFrame;
|
|
if (!path.empty())
|
|
{
|
|
frame->set_path ({path.c_str(), key.c_str()}, pValue);
|
|
}
|
|
else
|
|
frame->set (key.c_str(), pValue);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
static gpointer
|
|
get_obj_guid (gpointer pObject)
|
|
{
|
|
slot_info_t* pInfo = (slot_info_t*)pObject;
|
|
|
|
g_return_val_if_fail (pObject != NULL, NULL);
|
|
|
|
return (gpointer)pInfo->guid;
|
|
}
|
|
|
|
static void
|
|
set_obj_guid (void)
|
|
{
|
|
// Nowhere to put the GncGUID
|
|
}
|
|
|
|
static gpointer
|
|
get_path (gpointer pObject)
|
|
{
|
|
slot_info_t* pInfo = (slot_info_t*)pObject;
|
|
|
|
g_return_val_if_fail (pObject != NULL, NULL);
|
|
|
|
return (gpointer)pInfo->path.c_str();
|
|
}
|
|
|
|
static void
|
|
set_path (gpointer pObject, gpointer pValue)
|
|
{
|
|
slot_info_t* pInfo = (slot_info_t*)pObject;
|
|
pInfo->path = static_cast<char*>(pValue);
|
|
}
|
|
|
|
static KvpValue::Type
|
|
get_slot_type (gpointer pObject)
|
|
{
|
|
slot_info_t* pInfo = (slot_info_t*)pObject;
|
|
|
|
g_return_val_if_fail (pObject != NULL, KvpValue::Type::INVALID);
|
|
|
|
// return (gpointer)kvp_value_get_type( pInfo->pKvpValue );
|
|
return pInfo->value_type;
|
|
}
|
|
|
|
static void
|
|
set_slot_type (gpointer pObject, gpointer pValue)
|
|
{
|
|
slot_info_t* pInfo = (slot_info_t*)pObject;
|
|
|
|
g_return_if_fail (pObject != NULL);
|
|
g_return_if_fail (pValue != NULL);
|
|
|
|
pInfo->value_type = static_cast<KvpValue::Type> (GPOINTER_TO_INT (pValue));
|
|
}
|
|
|
|
static gint64
|
|
get_int64_val (gpointer pObject)
|
|
{
|
|
slot_info_t* pInfo = (slot_info_t*)pObject;
|
|
|
|
g_return_val_if_fail (pObject != NULL, 0);
|
|
|
|
if (pInfo->pKvpValue->get_type () == KvpValue::Type::INT64)
|
|
{
|
|
return pInfo->pKvpValue->get<int64_t> ();
|
|
}
|
|
else
|
|
{
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
static void
|
|
set_int64_val (gpointer pObject, gint64 value)
|
|
{
|
|
slot_info_t* pInfo = (slot_info_t*)pObject;
|
|
KvpValue* pValue = NULL;
|
|
|
|
g_return_if_fail (pObject != NULL);
|
|
|
|
if (pInfo->value_type != KvpValue::Type::INT64) return;
|
|
pValue = new KvpValue {value};
|
|
set_slot_from_value (pInfo, pValue);
|
|
}
|
|
|
|
static gpointer
|
|
get_string_val (gpointer pObject)
|
|
{
|
|
slot_info_t* pInfo = (slot_info_t*)pObject;
|
|
|
|
g_return_val_if_fail (pObject != NULL, NULL);
|
|
|
|
if (pInfo->pKvpValue->get_type () == KvpValue::Type::STRING)
|
|
{
|
|
return (gpointer)pInfo->pKvpValue->get<const char*> ();
|
|
}
|
|
else
|
|
{
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
static void
|
|
set_string_val (gpointer pObject, gpointer pValue)
|
|
{
|
|
slot_info_t* pInfo = (slot_info_t*)pObject;
|
|
g_return_if_fail (pObject != NULL);
|
|
|
|
if (pInfo->value_type != KvpValue::Type::STRING || pValue == NULL)
|
|
return;
|
|
auto value = new KvpValue {g_strdup(static_cast<const char*> (pValue))};
|
|
set_slot_from_value (pInfo, value);
|
|
}
|
|
|
|
static gpointer
|
|
get_double_val (gpointer pObject)
|
|
{
|
|
slot_info_t* pInfo = (slot_info_t*)pObject;
|
|
static double d_val;
|
|
|
|
g_return_val_if_fail (pObject != NULL, NULL);
|
|
|
|
if (pInfo->pKvpValue->get_type () == KvpValue::Type::DOUBLE)
|
|
{
|
|
d_val = pInfo->pKvpValue->get<double> ();
|
|
return (gpointer)&d_val;
|
|
}
|
|
else
|
|
{
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
static void
|
|
set_double_val (gpointer pObject, gpointer pValue)
|
|
{
|
|
slot_info_t* pInfo = (slot_info_t*)pObject;
|
|
KvpValue* value = NULL;
|
|
|
|
g_return_if_fail (pObject != NULL);
|
|
|
|
if (pInfo->value_type != KvpValue::Type::DOUBLE || pValue == NULL) return;
|
|
value = new KvpValue {* (static_cast<double*> (pValue))};
|
|
set_slot_from_value (pInfo, value);
|
|
}
|
|
|
|
static Timespec
|
|
get_timespec_val (gpointer pObject)
|
|
{
|
|
slot_info_t* pInfo = (slot_info_t*)pObject;
|
|
|
|
g_return_val_if_fail (pObject != NULL, gnc_dmy2timespec (1, 1, 1970));
|
|
|
|
//if( kvp_value_get_type( pInfo->pKvpValue ) == KvpValue::Type::TIMESPEC ) {
|
|
return pInfo->pKvpValue->get<Timespec> ();
|
|
}
|
|
|
|
static void
|
|
set_timespec_val (gpointer pObject, Timespec *ts)
|
|
{
|
|
slot_info_t* pInfo = (slot_info_t*)pObject;
|
|
KvpValue* value = NULL;
|
|
|
|
g_return_if_fail (pObject != NULL);
|
|
|
|
if (pInfo->value_type != KvpValue::Type::TIMESPEC) return;
|
|
value = new KvpValue {*ts};
|
|
set_slot_from_value (pInfo, value);
|
|
}
|
|
|
|
static gpointer
|
|
get_guid_val (gpointer pObject)
|
|
{
|
|
slot_info_t* pInfo = (slot_info_t*)pObject;
|
|
|
|
g_return_val_if_fail (pObject != NULL, NULL);
|
|
|
|
if (pInfo->pKvpValue->get_type () == KvpValue::Type::GUID)
|
|
{
|
|
return (gpointer)pInfo->pKvpValue->get<GncGUID*> ();
|
|
}
|
|
else
|
|
{
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
static void
|
|
set_guid_val (gpointer pObject, gpointer pValue)
|
|
{
|
|
slot_info_t* pInfo = (slot_info_t*)pObject;
|
|
|
|
g_return_if_fail (pObject != NULL);
|
|
if (pValue == NULL) return;
|
|
|
|
switch (pInfo->value_type)
|
|
{
|
|
case KvpValue::Type::GUID:
|
|
{
|
|
auto new_guid = guid_copy (static_cast<GncGUID*> (pValue));
|
|
set_slot_from_value (pInfo, new KvpValue {new_guid});
|
|
break;
|
|
}
|
|
case KvpValue::Type::GLIST:
|
|
{
|
|
slot_info_t* newInfo = slot_info_copy (pInfo, (GncGUID*)pValue);
|
|
KvpValue* pValue = NULL;
|
|
auto key = get_key_from_path (pInfo->path);
|
|
|
|
newInfo->context = LIST;
|
|
|
|
slots_load_info (newInfo);
|
|
pValue = new KvpValue {newInfo->pList};
|
|
pInfo->pKvpFrame->set (key.c_str(), pValue);
|
|
delete newInfo;
|
|
break;
|
|
}
|
|
case KvpValue::Type::FRAME:
|
|
{
|
|
slot_info_t* newInfo = slot_info_copy (pInfo, (GncGUID*)pValue) ;
|
|
auto newFrame = new KvpFrame;
|
|
newInfo->pKvpFrame = newFrame;
|
|
|
|
switch (pInfo->context)
|
|
{
|
|
case LIST:
|
|
{
|
|
auto value = new KvpValue {newFrame};
|
|
newInfo->path = get_key_from_path (pInfo->path);
|
|
pInfo->pList = g_list_append (pInfo->pList, value);
|
|
break;
|
|
}
|
|
case FRAME:
|
|
default:
|
|
{
|
|
auto key = get_key_from_path (pInfo->path);
|
|
pInfo->pKvpFrame->set (key.c_str(), new KvpValue {newFrame});
|
|
break;
|
|
}
|
|
}
|
|
|
|
newInfo->context = FRAME;
|
|
slots_load_info (newInfo);
|
|
delete newInfo;
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
static gnc_numeric
|
|
get_numeric_val (gpointer pObject)
|
|
{
|
|
slot_info_t* pInfo = (slot_info_t*)pObject;
|
|
|
|
g_return_val_if_fail (pObject != NULL, gnc_numeric_zero ());
|
|
|
|
if (pInfo->pKvpValue->get_type () == KvpValue::Type::NUMERIC)
|
|
{
|
|
return pInfo->pKvpValue->get<gnc_numeric> ();
|
|
}
|
|
else
|
|
{
|
|
return gnc_numeric_zero ();
|
|
}
|
|
}
|
|
|
|
static void
|
|
set_numeric_val (gpointer pObject, gnc_numeric *value)
|
|
{
|
|
slot_info_t* pInfo = (slot_info_t*)pObject;
|
|
KvpValue* pValue = NULL;
|
|
|
|
g_return_if_fail (pObject != NULL);
|
|
|
|
if (pInfo->value_type != KvpValue::Type::NUMERIC) return;
|
|
set_slot_from_value (pInfo, new KvpValue {*value});
|
|
}
|
|
|
|
static GDate*
|
|
get_gdate_val (gpointer pObject)
|
|
{
|
|
slot_info_t* pInfo = (slot_info_t*)pObject;
|
|
static GDate date;
|
|
|
|
g_return_val_if_fail (pObject != NULL, NULL);
|
|
|
|
if (pInfo->pKvpValue->get_type () == KvpValue::Type::GDATE)
|
|
{
|
|
date = pInfo->pKvpValue->get<GDate> ();
|
|
return &date;
|
|
}
|
|
else
|
|
{
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
static void
|
|
set_gdate_val (gpointer pObject, GDate* value)
|
|
{
|
|
slot_info_t* pInfo = (slot_info_t*)pObject;
|
|
KvpValue* pValue = NULL;
|
|
|
|
g_return_if_fail (pObject != NULL);
|
|
|
|
if (pInfo->value_type != KvpValue::Type::GDATE) return;
|
|
set_slot_from_value (pInfo, new KvpValue {*value});
|
|
}
|
|
|
|
static slot_info_t*
|
|
slot_info_copy (slot_info_t* pInfo, GncGUID* guid)
|
|
{
|
|
g_return_val_if_fail (pInfo != NULL, NULL);
|
|
auto newSlot = new slot_info_t;
|
|
|
|
newSlot->be = pInfo->be;
|
|
newSlot->guid = guid == NULL ? pInfo->guid : guid;
|
|
newSlot->is_ok = pInfo->is_ok;
|
|
newSlot->pKvpFrame = pInfo->pKvpFrame;
|
|
newSlot->value_type = pInfo->value_type;
|
|
newSlot->pList = pInfo->pList;
|
|
newSlot->context = pInfo->context;
|
|
newSlot->pKvpValue = pInfo->pKvpValue;
|
|
newSlot->path.clear();
|
|
return newSlot;
|
|
}
|
|
|
|
static void
|
|
save_slot (const char* key, KvpValue* value, gpointer data)
|
|
{
|
|
slot_info_t* pSlot_info = (slot_info_t*)data;
|
|
|
|
g_return_if_fail (value != NULL);
|
|
g_return_if_fail (data != NULL);
|
|
|
|
// Ignore if we've already run into a failure
|
|
if (!pSlot_info->is_ok)
|
|
{
|
|
return;
|
|
}
|
|
auto curlen = pSlot_info->path.length();
|
|
pSlot_info->pKvpValue = value;
|
|
if (curlen != 0)
|
|
pSlot_info->path += "/";
|
|
|
|
pSlot_info->path += key;
|
|
pSlot_info->value_type = value->get_type ();
|
|
|
|
switch (pSlot_info->value_type)
|
|
{
|
|
case KvpValue::Type::FRAME:
|
|
{
|
|
auto pKvpFrame = value->get<KvpFrame*> ();
|
|
auto guid = guid_new ();
|
|
slot_info_t* pNewInfo = slot_info_copy (pSlot_info, guid);
|
|
KvpValue* oldValue = pSlot_info->pKvpValue;
|
|
pSlot_info->pKvpValue = new KvpValue {guid};
|
|
pSlot_info->is_ok = pSlot_info->be->do_db_operation(OP_DB_INSERT,
|
|
TABLE_NAME,
|
|
TABLE_NAME,
|
|
pSlot_info,
|
|
col_table);
|
|
g_return_if_fail (pSlot_info->is_ok);
|
|
pKvpFrame->for_each_slot (save_slot, pNewInfo);
|
|
delete pSlot_info->pKvpValue;
|
|
pSlot_info->pKvpValue = oldValue;
|
|
delete pNewInfo;
|
|
}
|
|
break;
|
|
case KvpValue::Type::GLIST:
|
|
{
|
|
GncGUID* guid = guid_new ();
|
|
slot_info_t* pNewInfo = slot_info_copy (pSlot_info, guid);
|
|
KvpValue* oldValue = pSlot_info->pKvpValue;
|
|
pSlot_info->pKvpValue = new KvpValue {guid}; // Transfer ownership!
|
|
pSlot_info->is_ok = pSlot_info->be->do_db_operation(OP_DB_INSERT,
|
|
TABLE_NAME,
|
|
TABLE_NAME,
|
|
pSlot_info,
|
|
col_table);
|
|
g_return_if_fail (pSlot_info->is_ok);
|
|
for (auto cursor = value->get<GList*> (); cursor; cursor = cursor->next)
|
|
{
|
|
auto val = static_cast<KvpValue*> (cursor->data);
|
|
save_slot ("", val, pNewInfo);
|
|
}
|
|
delete pSlot_info->pKvpValue;
|
|
pSlot_info->pKvpValue = oldValue;
|
|
delete pNewInfo;
|
|
}
|
|
break;
|
|
default:
|
|
{
|
|
pSlot_info->is_ok = pSlot_info->be->do_db_operation (OP_DB_INSERT,
|
|
TABLE_NAME,
|
|
TABLE_NAME,
|
|
pSlot_info,
|
|
col_table);
|
|
}
|
|
break;
|
|
}
|
|
|
|
pSlot_info->path.erase(curlen);
|
|
}
|
|
|
|
gboolean
|
|
gnc_sql_slots_save (GncSqlBackend* sql_be, const GncGUID* guid, gboolean is_infant,
|
|
QofInstance* inst)
|
|
{
|
|
slot_info_t slot_info = { NULL, NULL, TRUE, NULL, KvpValue::Type::INVALID,
|
|
NULL, FRAME, NULL, "" };
|
|
KvpFrame* pFrame = qof_instance_get_slots (inst);
|
|
|
|
g_return_val_if_fail (sql_be != NULL, FALSE);
|
|
g_return_val_if_fail (guid != NULL, FALSE);
|
|
g_return_val_if_fail (pFrame != NULL, FALSE);
|
|
|
|
// If this is not saving into a new db, clear out the old saved slots first
|
|
if (!sql_be->pristine() && !is_infant)
|
|
{
|
|
(void)gnc_sql_slots_delete (sql_be, guid);
|
|
}
|
|
|
|
slot_info.be = sql_be;
|
|
slot_info.guid = guid;
|
|
pFrame->for_each_slot (save_slot, &slot_info);
|
|
|
|
return slot_info.is_ok;
|
|
}
|
|
|
|
gboolean
|
|
gnc_sql_slots_delete (GncSqlBackend* sql_be, const GncGUID* guid)
|
|
{
|
|
gchar* buf;
|
|
gchar guid_buf[GUID_ENCODING_LENGTH + 1];
|
|
slot_info_t slot_info = { NULL, NULL, TRUE, NULL, KvpValue::Type::INVALID,
|
|
NULL, FRAME, NULL, "" };
|
|
|
|
g_return_val_if_fail (sql_be != NULL, FALSE);
|
|
g_return_val_if_fail (guid != NULL, FALSE);
|
|
|
|
(void)guid_to_string_buff (guid, guid_buf);
|
|
|
|
buf = g_strdup_printf ("SELECT * FROM %s WHERE obj_guid='%s' and slot_type in ('%d', '%d') and not guid_val is null",
|
|
TABLE_NAME, guid_buf, KvpValue::Type::FRAME, KvpValue::Type::GLIST);
|
|
auto stmt = sql_be->create_statement_from_sql(buf);
|
|
g_free (buf);
|
|
if (stmt != nullptr)
|
|
{
|
|
auto result = sql_be->execute_select_statement(stmt);
|
|
for (auto row : *result)
|
|
{
|
|
try
|
|
{
|
|
const GncSqlColumnTableEntryPtr table_row =
|
|
col_table[guid_val_col];
|
|
GncGUID child_guid;
|
|
auto val = row.get_string_at_col (table_row->name());
|
|
(void)string_to_guid (val.c_str(), &child_guid);
|
|
gnc_sql_slots_delete (sql_be, &child_guid);
|
|
}
|
|
catch (std::invalid_argument)
|
|
{
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
|
|
slot_info.be = sql_be;
|
|
slot_info.guid = guid;
|
|
slot_info.is_ok = TRUE;
|
|
slot_info.is_ok = sql_be->do_db_operation(OP_DB_DELETE, TABLE_NAME,
|
|
TABLE_NAME, &slot_info,
|
|
obj_guid_col_table);
|
|
|
|
return slot_info.is_ok;
|
|
}
|
|
|
|
static void
|
|
load_slot (slot_info_t* pInfo, GncSqlRow& row)
|
|
{
|
|
slot_info_t* slot_info;
|
|
|
|
g_return_if_fail (pInfo != NULL);
|
|
g_return_if_fail (pInfo->be != NULL);
|
|
g_return_if_fail (pInfo->pKvpFrame != NULL);
|
|
|
|
slot_info = slot_info_copy (pInfo, NULL);
|
|
|
|
gnc_sql_load_object (pInfo->be, row, TABLE_NAME, slot_info, col_table);
|
|
|
|
if (slot_info->pList != pInfo->pList)
|
|
{
|
|
if (pInfo->pList != NULL)
|
|
{
|
|
PWARN ("Load slot returned a different list than the original");
|
|
}
|
|
else
|
|
{
|
|
pInfo->pList = slot_info->pList;
|
|
}
|
|
}
|
|
delete slot_info;
|
|
}
|
|
|
|
void
|
|
gnc_sql_slots_load (GncSqlBackend* sql_be, QofInstance* inst)
|
|
{
|
|
slot_info_t info = { NULL, NULL, TRUE, NULL, KvpValue::Type::INVALID,
|
|
NULL, FRAME, NULL, "" };
|
|
g_return_if_fail (sql_be != NULL);
|
|
g_return_if_fail (inst != NULL);
|
|
|
|
info.be = sql_be;
|
|
info.guid = qof_instance_get_guid (inst);
|
|
info.pKvpFrame = qof_instance_get_slots (inst);
|
|
info.context = NONE;
|
|
|
|
slots_load_info (&info);
|
|
}
|
|
|
|
static void
|
|
slots_load_info (slot_info_t* pInfo)
|
|
{
|
|
gchar guid_buf[GUID_ENCODING_LENGTH + 1];
|
|
|
|
g_return_if_fail (pInfo != NULL);
|
|
g_return_if_fail (pInfo->be != NULL);
|
|
g_return_if_fail (pInfo->guid != NULL);
|
|
g_return_if_fail (pInfo->pKvpFrame != NULL);
|
|
|
|
(void)guid_to_string_buff (pInfo->guid, guid_buf);
|
|
|
|
std::stringstream buf;
|
|
buf << "SELECT * FROM " << TABLE_NAME <<
|
|
" WHERE obj_guid='" << guid_buf << "'";
|
|
auto stmt = pInfo->be->create_statement_from_sql (buf.str());
|
|
if (stmt != nullptr)
|
|
{
|
|
auto result = pInfo->be->execute_select_statement (stmt);
|
|
for (auto row : *result)
|
|
load_slot (pInfo, row);
|
|
delete result;
|
|
}
|
|
}
|
|
|
|
static const GncGUID*
|
|
load_obj_guid (const GncSqlBackend* sql_be, GncSqlRow& row)
|
|
{
|
|
static GncGUID guid;
|
|
|
|
g_return_val_if_fail (sql_be != NULL, NULL);
|
|
|
|
gnc_sql_load_object (sql_be, row, NULL, &guid, obj_guid_col_table);
|
|
|
|
return &guid;
|
|
}
|
|
|
|
static void
|
|
load_slot_for_list_item (GncSqlBackend* sql_be, GncSqlRow& row,
|
|
QofCollection* coll)
|
|
{
|
|
slot_info_t slot_info = { NULL, NULL, TRUE, NULL, KvpValue::Type::INVALID,
|
|
NULL, FRAME, NULL, "" };
|
|
const GncGUID* guid;
|
|
QofInstance* inst;
|
|
|
|
g_return_if_fail (sql_be != NULL);
|
|
g_return_if_fail (coll != NULL);
|
|
|
|
guid = load_obj_guid (sql_be, row);
|
|
g_assert (guid != NULL);
|
|
inst = qof_collection_lookup_entity (coll, guid);
|
|
|
|
slot_info.be = sql_be;
|
|
slot_info.pKvpFrame = qof_instance_get_slots (inst);
|
|
slot_info.context = NONE;
|
|
|
|
gnc_sql_load_object (sql_be, row, TABLE_NAME, &slot_info, col_table);
|
|
|
|
|
|
}
|
|
|
|
void
|
|
gnc_sql_slots_load_for_instancevec (GncSqlBackend* sql_be, InstanceVec& instances)
|
|
{
|
|
QofCollection* coll;
|
|
std::stringstream sql;
|
|
|
|
g_return_if_fail (sql_be != NULL);
|
|
|
|
// Ignore empty list
|
|
if (instances.empty()) return;
|
|
|
|
coll = qof_instance_get_collection (instances[0]);
|
|
|
|
// Create the query for all slots for all items on the list
|
|
|
|
sql << "SELECT * FROM " << TABLE_NAME << " WHERE " <<
|
|
obj_guid_col_table[0]->name();
|
|
if (instances.size() != 1)
|
|
sql << " IN (";
|
|
else
|
|
sql << " = ";
|
|
|
|
gnc_sql_append_guids_to_sql (sql, instances);
|
|
if (instances.size() > 1)
|
|
sql << ")";
|
|
|
|
// Execute the query and load the slots
|
|
auto stmt = sql_be->create_statement_from_sql(sql.str());
|
|
if (stmt == nullptr)
|
|
{
|
|
PERR ("stmt == NULL, SQL = '%s'\n", sql.str().c_str());
|
|
return;
|
|
}
|
|
auto result = sql_be->execute_select_statement (stmt);
|
|
for (auto row : *result)
|
|
load_slot_for_list_item (sql_be, row, coll);
|
|
}
|
|
|
|
static void
|
|
load_slot_for_book_object (GncSqlBackend* sql_be, GncSqlRow& row,
|
|
BookLookupFn lookup_fn)
|
|
{
|
|
slot_info_t slot_info = { NULL, NULL, TRUE, NULL, KvpValue::Type::INVALID,
|
|
NULL, FRAME, NULL, "" };
|
|
const GncGUID* guid;
|
|
QofInstance* inst;
|
|
|
|
g_return_if_fail (sql_be != NULL);
|
|
g_return_if_fail (lookup_fn != NULL);
|
|
|
|
guid = load_obj_guid (sql_be, row);
|
|
g_return_if_fail (guid != NULL);
|
|
inst = lookup_fn (guid, sql_be->book());
|
|
g_return_if_fail (inst != NULL);
|
|
|
|
slot_info.be = sql_be;
|
|
slot_info.pKvpFrame = qof_instance_get_slots (inst);
|
|
slot_info.path.clear();
|
|
|
|
gnc_sql_load_object (sql_be, row, TABLE_NAME, &slot_info, col_table);
|
|
}
|
|
|
|
/**
|
|
* gnc_sql_slots_load_for_sql_subquery - Loads slots for all objects whose guid is
|
|
* supplied by a subquery. The subquery should be of the form "SELECT DISTINCT guid FROM ...".
|
|
* This is faster than loading for one object at a time because fewer SQL queries * are used.
|
|
*
|
|
* @param sql_be SQL backend
|
|
* @param subquery Subquery SQL string
|
|
* @param lookup_fn Lookup function
|
|
*/
|
|
void gnc_sql_slots_load_for_sql_subquery (GncSqlBackend* sql_be,
|
|
const gchar* subquery,
|
|
BookLookupFn lookup_fn)
|
|
{
|
|
gchar* sql;
|
|
|
|
g_return_if_fail (sql_be != NULL);
|
|
|
|
// Ignore empty subquery
|
|
if (subquery == NULL) return;
|
|
|
|
sql = g_strdup_printf ("SELECT * FROM %s WHERE %s IN (%s)",
|
|
TABLE_NAME, obj_guid_col_table[0]->name(),
|
|
subquery);
|
|
|
|
// Execute the query and load the slots
|
|
auto stmt = sql_be->create_statement_from_sql(sql);
|
|
if (stmt == nullptr)
|
|
{
|
|
PERR ("stmt == NULL, SQL = '%s'\n", sql);
|
|
g_free (sql);
|
|
return;
|
|
}
|
|
g_free (sql);
|
|
auto result = sql_be->execute_select_statement(stmt);
|
|
for (auto row : *result)
|
|
load_slot_for_book_object (sql_be, row, lookup_fn);
|
|
delete result;
|
|
}
|
|
|
|
/* ================================================================= */
|
|
void
|
|
GncSqlSlotsBackend::create_tables (GncSqlBackend* sql_be)
|
|
{
|
|
gint version;
|
|
gboolean ok;
|
|
|
|
g_return_if_fail (sql_be != NULL);
|
|
|
|
version = sql_be->get_table_version( TABLE_NAME);
|
|
if (version == 0)
|
|
{
|
|
(void)sql_be->create_table(TABLE_NAME, TABLE_VERSION, col_table);
|
|
|
|
ok = sql_be->create_index ("slots_guid_index", TABLE_NAME,
|
|
obj_guid_col_table);
|
|
if (!ok)
|
|
{
|
|
PERR ("Unable to create index\n");
|
|
}
|
|
}
|
|
else if (version < TABLE_VERSION)
|
|
{
|
|
/* Upgrade:
|
|
1->2: 64-bit int values to proper definition, add index
|
|
2->3: Add gdate field
|
|
*/
|
|
if (version == 1)
|
|
{
|
|
sql_be->upgrade_table(TABLE_NAME, col_table);
|
|
ok = sql_be->create_index ("slots_guid_index", TABLE_NAME,
|
|
obj_guid_col_table);
|
|
if (!ok)
|
|
{
|
|
PERR ("Unable to create index\n");
|
|
}
|
|
}
|
|
else if (version == 2)
|
|
{
|
|
ok = sql_be->add_columns_to_table(TABLE_NAME, gdate_col_table);
|
|
if (!ok)
|
|
{
|
|
PERR ("Unable to add gdate column\n");
|
|
}
|
|
}
|
|
sql_be->set_table_version (TABLE_NAME, TABLE_VERSION);
|
|
PINFO ("Slots table upgraded from version %d to version %d\n", version,
|
|
TABLE_VERSION);
|
|
}
|
|
}
|
|
|
|
/* ========================== END OF FILE ===================== */
|