gnucash/libgnucash/backend/sql/gnc-slots-sql.cpp
John Ralls 5781f3445b SQLBackend: Use std::optional return value instead of exceptions
For wrong value type when retrieving a value from the SQL results row.

Profiling showed that most of the SQL load time was spent in handling
these exceptions, and using std::optional instead produced a > 11x
speedup (10 seconds vs. 115 seconds) when loading a large file.
2023-08-10 14:05:10 -07:00

890 lines
26 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>
#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 4
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;
std::string parent_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 time64 get_time_val (gpointer pObject);
static void set_time_val (gpointer pObject, time64 t);
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,
time_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_TIME>("timespec_val", 0, 0,
(QofAccessFunc)get_time_val,
(QofSetterFunc)set_time_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);
*pGuid = *guid;
}
/* 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(TABLE_VERSION, GNC_ID_ACCOUNT,
TABLE_NAME, col_table) {}
/* ================================================================= */
static std::string
get_key (slot_info_t* pInfo)
{
if (!pInfo) return "";
auto path = pInfo->path;
path.erase (0, pInfo->parent_path.size());
return path;
}
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 (pInfo);
pInfo->pKvpFrame->set ({key}, pValue);
break;
}
case LIST:
{
pInfo->pList = g_list_append (pInfo->pList, pValue);
break;
}
case NONE:
default:
{
auto key = get_key (pInfo);
auto path = pInfo->parent_path;
auto frame = pInfo->pKvpFrame;
if (!path.empty())
{
frame->set_path ({path, key}, pValue);
}
else
frame->set ({key}, 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);
if (pInfo->path.find (pInfo->parent_path) != 0)
pInfo->parent_path.clear();
}
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)
{
static double d_val; /* static so that we can return it. */
g_return_val_if_fail (pObject != NULL, NULL);
auto pInfo = static_cast<slot_info_t*>(pObject);
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 time64
get_time_val (gpointer pObject)
{
slot_info_t* pInfo = (slot_info_t*)pObject;
g_return_val_if_fail (pObject != NULL, 0);
//if( kvp_value_get_type( pInfo->pKvpValue ) == KvpValue::Type::TIME64 ) {
auto t = pInfo->pKvpValue->get<Time64> ();
return t.t;
}
static void
set_time_val (gpointer pObject, time64 time)
{
slot_info_t* pInfo = (slot_info_t*)pObject;
KvpValue* value = NULL;
Time64 t{time};
g_return_if_fail (pObject != NULL);
if (pInfo->value_type != KvpValue::Type::TIME64) return;
value = new KvpValue {t};
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 (pInfo);
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 (pInfo);
pInfo->pList = g_list_append (pInfo->pList, value);
break;
}
case FRAME:
default:
{
auto key = get_key (pInfo);
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;
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;
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;
if (!pInfo->path.empty())
newSlot->parent_path = pInfo->path + "/";
else
newSlot->parent_path = pInfo->parent_path;
return newSlot;
}
static void
save_slot (const char* key, KvpValue* value, slot_info_t & slot_info)
{
g_return_if_fail (value != NULL);
// Ignore if we've already run into a failure
if (!slot_info.is_ok)
{
return;
}
slot_info.pKvpValue = value;
slot_info.path = slot_info.parent_path + key;
slot_info.value_type = value->get_type ();
switch (slot_info.value_type)
{
case KvpValue::Type::FRAME:
{
auto pKvpFrame = value->get<KvpFrame*> ();
auto guid = guid_new ();
slot_info_t* pNewInfo = slot_info_copy (&slot_info, guid);
KvpValue* oldValue = slot_info.pKvpValue;
slot_info.pKvpValue = new KvpValue {guid};
slot_info.is_ok = slot_info.be->do_db_operation(OP_DB_INSERT,
TABLE_NAME,
TABLE_NAME,
&slot_info,
col_table);
g_return_if_fail (slot_info.is_ok);
pKvpFrame->for_each_slot_temp (save_slot, *pNewInfo);
delete slot_info.pKvpValue;
slot_info.pKvpValue = oldValue;
delete pNewInfo;
}
break;
case KvpValue::Type::GLIST:
{
GncGUID* guid = guid_new ();
slot_info_t* pNewInfo = slot_info_copy (&slot_info, guid);
KvpValue* oldValue = slot_info.pKvpValue;
slot_info.pKvpValue = new KvpValue {guid}; // Transfer ownership!
slot_info.is_ok = slot_info.be->do_db_operation(OP_DB_INSERT,
TABLE_NAME,
TABLE_NAME,
&slot_info,
col_table);
g_return_if_fail (slot_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 slot_info.pKvpValue;
slot_info.pKvpValue = oldValue;
delete pNewInfo;
}
break;
default:
{
slot_info.is_ok = slot_info.be->do_db_operation (OP_DB_INSERT,
TABLE_NAME,
TABLE_NAME,
&slot_info,
col_table);
}
break;
}
}
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_temp (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)
{
const GncSqlColumnTableEntryPtr table_row =
col_table[guid_val_col];
GncGUID child_guid;
auto val = row.get_string_at_col (table_row->name());
if (val && string_to_guid (val->c_str(), &child_guid))
gnc_sql_slots_delete (sql_be, &child_guid);
}
}
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)
{
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);
gnc::GUID guid(*pInfo->guid);
std::string sql("SELECT * FROM " TABLE_NAME " WHERE obj_guid='");
sql += guid.to_string() + "'";
auto stmt = pInfo->be->create_statement_from_sql(sql);
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_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());
if (inst == NULL) return; /* Silently bail if the guid isn't loaded yet. */
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 std::string subquery,
BookLookupFn lookup_fn)
{
g_return_if_fail (sql_be != NULL);
// Ignore empty subquery
if (subquery.empty()) return;
std::string pkey(obj_guid_col_table[0]->name());
std::string sql("SELECT * FROM " TABLE_NAME " WHERE ");
sql += pkey + " IN (" + 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.c_str());
return;
}
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 < m_version)
{
/* Upgrade:
1->2: 64-bit int values to proper definition, add index
2->3: Add gdate field
3->4: Use DATETIME instead of TIMESTAMP in MySQL
*/
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");
}
}
else
{
sql_be->upgrade_table(TABLE_NAME, col_table);
}
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 ===================== */