From 5781f3445bd8e10c27ba173f259d9b72d0aef299 Mon Sep 17 00:00:00 2001 From: John Ralls Date: Thu, 10 Aug 2023 13:36:39 -0700 Subject: [PATCH] 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. --- libgnucash/backend/dbi/gnc-dbisqlresult.cpp | 45 +++-- libgnucash/backend/dbi/gnc-dbisqlresult.hpp | 12 +- libgnucash/backend/sql/gnc-address-sql.cpp | 15 +- libgnucash/backend/sql/gnc-owner-sql.cpp | 6 +- libgnucash/backend/sql/gnc-slots-sql.cpp | 17 +- libgnucash/backend/sql/gnc-sql-backend.cpp | 5 +- .../sql/gnc-sql-column-table-entry.cpp | 176 ++++++++---------- .../sql/gnc-sql-column-table-entry.hpp | 54 +++--- libgnucash/backend/sql/gnc-sql-result.hpp | 21 ++- .../backend/sql/gnc-transaction-sql.cpp | 38 ++-- .../sql/test/utest-gnc-backend-sql.cpp | 10 +- 11 files changed, 182 insertions(+), 217 deletions(-) diff --git a/libgnucash/backend/dbi/gnc-dbisqlresult.cpp b/libgnucash/backend/dbi/gnc-dbisqlresult.cpp index ba19f73388..5b4581d170 100644 --- a/libgnucash/backend/dbi/gnc-dbisqlresult.cpp +++ b/libgnucash/backend/dbi/gnc-dbisqlresult.cpp @@ -29,6 +29,7 @@ #include #include #include +#include #include "gnc-dbisqlresult.hpp" #include "gnc-dbisqlconnection.hpp" @@ -98,16 +99,16 @@ GncDbiSqlResult::IteratorImpl::operator++() return m_inst->m_sentinel; } -int64_t +std::optional GncDbiSqlResult::IteratorImpl::get_int_at_col(const char* col) const { auto type = dbi_result_get_field_type (m_inst->m_dbi_result, col); if(type != DBI_TYPE_INTEGER) - throw (std::invalid_argument{"Requested integer from non-integer column."}); - return dbi_result_get_longlong (m_inst->m_dbi_result, col); + return std::nullopt; + return std::optional{dbi_result_get_longlong (m_inst->m_dbi_result, col)}; } -double +std::optional GncDbiSqlResult::IteratorImpl::get_float_at_col(const char* col) const { constexpr double float_precision = 1000000.0; @@ -115,56 +116,52 @@ GncDbiSqlResult::IteratorImpl::get_float_at_col(const char* col) const auto attrs = dbi_result_get_field_attribs (m_inst->m_dbi_result, col); if(type != DBI_TYPE_DECIMAL || (attrs & DBI_DECIMAL_SIZEMASK) != DBI_DECIMAL_SIZE4) - throw (std::invalid_argument{"Requested float from non-float column."}); + return std::nullopt; auto locale = gnc_push_locale (LC_NUMERIC, "C"); auto interim = dbi_result_get_float(m_inst->m_dbi_result, col); gnc_pop_locale (LC_NUMERIC, locale); double retval = static_cast(round(interim * float_precision)) / float_precision; - return retval; + return std::optional{retval}; } -double +std::optional GncDbiSqlResult::IteratorImpl::get_double_at_col(const char* col) const { auto type = dbi_result_get_field_type (m_inst->m_dbi_result, col); auto attrs = dbi_result_get_field_attribs (m_inst->m_dbi_result, col); if(type != DBI_TYPE_DECIMAL || (attrs & DBI_DECIMAL_SIZEMASK) != DBI_DECIMAL_SIZE8) - throw (std::invalid_argument{"Requested double from non-double column."}); + return std::nullopt; auto locale = gnc_push_locale (LC_NUMERIC, "C"); auto retval = dbi_result_get_double(m_inst->m_dbi_result, col); gnc_pop_locale (LC_NUMERIC, locale); - return retval; + return std::optional{retval}; } -std::string +std::optional GncDbiSqlResult::IteratorImpl::get_string_at_col(const char* col) const { auto type = dbi_result_get_field_type (m_inst->m_dbi_result, col); dbi_result_get_field_attribs (m_inst->m_dbi_result, col); if(type != DBI_TYPE_STRING) - throw (std::invalid_argument{"Requested string from non-string column."}); + return std::nullopt; auto strval = dbi_result_get_string(m_inst->m_dbi_result, col); - if (strval == nullptr) - { - throw (std::invalid_argument{"Column empty."}); - } - auto retval = std::string{strval}; - return retval; + return std::optional{strval ? strval : ""}; } -time64 + +std::optional GncDbiSqlResult::IteratorImpl::get_time64_at_col (const char* col) const { auto result = (dbi_result_t*) (m_inst->m_dbi_result); auto type = dbi_result_get_field_type (result, col); dbi_result_get_field_attribs (result, col); if (type != DBI_TYPE_DATETIME) - throw (std::invalid_argument{"Requested time64 from non-time64 column."}); + return std::nullopt; #if HAVE_LIBDBI_TO_LONGLONG /* A less evil hack than the one required by libdbi-0.8, but * still necessary to work around the same bug. */ - auto retval = dbi_result_get_as_longlong(result, col); + auto timeval = dbi_result_get_as_longlong(result, col); #else /* A seriously evil hack to work around libdbi bug #15 * https://sourceforge.net/p/libdbi/bugs/15/. When libdbi @@ -174,11 +171,11 @@ GncDbiSqlResult::IteratorImpl::get_time64_at_col (const char* col) const */ auto row = dbi_result_get_currow (result); auto idx = dbi_result_get_field_idx (result, col) - 1; - time64 retval = result->rows[row]->field_values[idx].d_datetime; + time64 timeval = result->rows[row]->field_values[idx].d_datetime; #endif //HAVE_LIBDBI_TO_LONGLONG - if (retval < MINTIME || retval > MAXTIME) - retval = 0; - return retval; + if (timeval < MINTIME || timeval > MAXTIME) + timeval = 0; + return std::optional(timeval); } diff --git a/libgnucash/backend/dbi/gnc-dbisqlresult.hpp b/libgnucash/backend/dbi/gnc-dbisqlresult.hpp index 52da0d228f..8b6aa84188 100644 --- a/libgnucash/backend/dbi/gnc-dbisqlresult.hpp +++ b/libgnucash/backend/dbi/gnc-dbisqlresult.hpp @@ -25,6 +25,8 @@ #ifndef __GNC_DBISQLBACKEND_HPP__ #define __GNC_DBISQLBACKEND_HPP__ +#include + #include "gnc-backend-dbi.h" #include @@ -53,11 +55,11 @@ protected: virtual GncSqlRow& operator++(); virtual GncSqlRow& operator++(int) { return ++(*this); }; virtual GncSqlResult* operator*() { return m_inst; } - virtual int64_t get_int_at_col (const char* col) const; - virtual double get_float_at_col (const char* col) const; - virtual double get_double_at_col (const char* col) const; - virtual std::string get_string_at_col (const char* col)const; - virtual time64 get_time64_at_col (const char* col) const; + virtual std::optional get_int_at_col (const char* col) const; + virtual std::optional get_float_at_col (const char* col) const; + virtual std::optional get_double_at_col (const char* col) const; + virtual std::optional get_string_at_col (const char* col)const; + virtual std::optional get_time64_at_col (const char* col) const; virtual bool is_col_null(const char* col) const noexcept { return dbi_result_field_is_null(m_inst->m_dbi_result, col); diff --git a/libgnucash/backend/sql/gnc-address-sql.cpp b/libgnucash/backend/sql/gnc-address-sql.cpp index 8ada624ffc..cc9d4b6c4f 100644 --- a/libgnucash/backend/sql/gnc-address-sql.cpp +++ b/libgnucash/backend/sql/gnc-address-sql.cpp @@ -85,18 +85,13 @@ GncSqlColumnTableEntryImpl::load (const GncSqlBackend* sql_be, for (auto const& subtable_row : col_table) { auto buf = std::string{m_col_name} + "_" + subtable_row->m_col_name; - try - { - auto val = row.get_string_at_col (buf.c_str()); - auto sub_setter = subtable_row->get_setter(GNC_ID_ADDRESS); - set_parameter (addr, val.c_str(), sub_setter, + auto val = row.get_string_at_col (buf.c_str()); + auto sub_setter = subtable_row->get_setter(GNC_ID_ADDRESS); + if (val) + set_parameter (addr, val->c_str(), sub_setter, subtable_row->m_gobj_param_name); - } - catch (std::invalid_argument&) - { - return; - } } + set_parameter (pObject, addr, reinterpret_cast(get_setter(obj_name)), m_gobj_param_name); diff --git a/libgnucash/backend/sql/gnc-owner-sql.cpp b/libgnucash/backend/sql/gnc-owner-sql.cpp index 2cbf09c090..46b5ca360c 100644 --- a/libgnucash/backend/sql/gnc-owner-sql.cpp +++ b/libgnucash/backend/sql/gnc-owner-sql.cpp @@ -64,10 +64,10 @@ GncSqlColumnTableEntryImpl::load (const GncSqlBackend* sql_be, auto buf = std::string{m_col_name} + "_type"; try { - type = static_cast(row.get_int_at_col (buf.c_str())); + type = static_cast(row.get_int_at_col(buf.c_str()).value_or(0)); buf = std::string{m_col_name} + "_guid"; auto val = row.get_string_at_col (buf.c_str()); - if (string_to_guid (val.c_str(), &guid)) + if (val && string_to_guid (val->c_str(), &guid)) pGuid = &guid; } catch (std::invalid_argument&) @@ -76,7 +76,7 @@ GncSqlColumnTableEntryImpl::load (const GncSqlBackend* sql_be, } if (type == GNC_OWNER_NONE || pGuid == nullptr) return; - + switch (type) { case GNC_OWNER_CUSTOMER: diff --git a/libgnucash/backend/sql/gnc-slots-sql.cpp b/libgnucash/backend/sql/gnc-slots-sql.cpp index dfc43d179c..fd4078c127 100644 --- a/libgnucash/backend/sql/gnc-slots-sql.cpp +++ b/libgnucash/backend/sql/gnc-slots-sql.cpp @@ -676,19 +676,12 @@ gnc_sql_slots_delete (GncSqlBackend* sql_be, const GncGUID* guid) auto result = sql_be->execute_select_statement(stmt); for (auto row : *result) { - try - { - const GncSqlColumnTableEntryPtr table_row = + const GncSqlColumnTableEntryPtr table_row = col_table[guid_val_col]; - GncGUID child_guid; - auto val = row.get_string_at_col (table_row->name()); - if (string_to_guid (val.c_str(), &child_guid)) - gnc_sql_slots_delete (sql_be, &child_guid); - } - catch (std::invalid_argument&) - { - continue; - } + 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); } } diff --git a/libgnucash/backend/sql/gnc-sql-backend.cpp b/libgnucash/backend/sql/gnc-sql-backend.cpp index 4202293cf3..09bd5d318b 100644 --- a/libgnucash/backend/sql/gnc-sql-backend.cpp +++ b/libgnucash/backend/sql/gnc-sql-backend.cpp @@ -669,8 +669,9 @@ GncSqlBackend::init_version_info() noexcept for (const auto& row : *result) { auto name = row.get_string_at_col (TABLE_COL_NAME); - unsigned int version = row.get_int_at_col (VERSION_COL_NAME); - m_versions.push_back(std::make_pair(name, version)); + auto version = row.get_int_at_col (VERSION_COL_NAME); + if (name && version) + m_versions.push_back(std::make_pair(*name, static_cast(*version))); } } else diff --git a/libgnucash/backend/sql/gnc-sql-column-table-entry.cpp b/libgnucash/backend/sql/gnc-sql-column-table-entry.cpp index c1f829e1b0..979d54d5c7 100644 --- a/libgnucash/backend/sql/gnc-sql-column-table-entry.cpp +++ b/libgnucash/backend/sql/gnc-sql-column-table-entry.cpp @@ -124,12 +124,9 @@ GncSqlColumnTableEntryImpl::load (const GncSqlBackend* sql_be, g_return_if_fail (pObject != NULL); g_return_if_fail (m_gobj_param_name != NULL || get_setter(obj_name) != NULL); - try - { - auto s = row.get_string_at_col (m_col_name); - set_parameter(pObject, s.c_str(), get_setter(obj_name), m_gobj_param_name); - } - catch (std::invalid_argument&) {} + auto s = row.get_string_at_col (m_col_name); + if (s) + set_parameter(pObject, s->c_str(), get_setter(obj_name), m_gobj_param_name); } template<> void @@ -174,8 +171,10 @@ GncSqlColumnTableEntryImpl::load (const GncSqlBackend* sql_be, g_return_if_fail (m_gobj_param_name != NULL || get_setter(obj_name) != NULL); auto val = row.get_int_at_col(m_col_name); - set_parameter(pObject, val, - reinterpret_cast(get_setter(obj_name)), m_gobj_param_name); + if (val) + set_parameter(pObject, *val, + reinterpret_cast(get_setter(obj_name)), + m_gobj_param_name); } template<> void @@ -208,9 +207,10 @@ GncSqlColumnTableEntryImpl::load (const GncSqlBackend* sql_be, g_return_if_fail (m_gobj_param_name != NULL || get_setter(obj_name) != NULL); auto val = row.get_int_at_col (m_col_name); - set_parameter(pObject, static_cast(val), - reinterpret_cast(get_setter(obj_name)), - m_gobj_param_name); + if (val) + set_parameter(pObject, static_cast(*val), + reinterpret_cast(get_setter(obj_name)), + m_gobj_param_name); } template<> void @@ -242,9 +242,10 @@ GncSqlColumnTableEntryImpl::load (const GncSqlBackend* sql_be, g_return_if_fail (m_gobj_param_name != nullptr || get_setter(obj_name) != nullptr); auto val = row.get_int_at_col (m_col_name); - set_parameter(pObject, val, - reinterpret_cast(get_setter(obj_name)), - m_gobj_param_name); + if (val) + set_parameter(pObject, *val, + reinterpret_cast(get_setter(obj_name)), + m_gobj_param_name); } template<> void @@ -273,29 +274,15 @@ GncSqlColumnTableEntryImpl::load (const GncSqlBackend* sql_be, { g_return_if_fail (pObject != NULL); g_return_if_fail (m_gobj_param_name != nullptr || get_setter(obj_name) != nullptr); - double val; - try - { - val = static_cast(row.get_int_at_col(m_col_name)); - } - catch (std::invalid_argument&) - { - try - { - val = row.get_float_at_col(m_col_name); - } - catch (std::invalid_argument&) - { - try - { - val = row.get_double_at_col(m_col_name); - } - catch (std::invalid_argument&) - { - val = 0.0; - } - } - } + double val{0.0}; + + if (auto int_val{row.get_int_at_col(m_col_name)}) + val = static_cast(*int_val); + else if (auto float_val{row.get_float_at_col(m_col_name)}) + val = static_cast(*float_val); + else if (auto double_val{row.get_double_at_col(m_col_name)}) + val = *double_val; + set_parameter(pObject, val, get_setter(obj_name), m_gobj_param_name); } @@ -329,16 +316,8 @@ GncSqlColumnTableEntryImpl::load (const GncSqlBackend* sql_be, g_return_if_fail (pObject != NULL); g_return_if_fail (m_gobj_param_name != nullptr || get_setter(obj_name) != nullptr); - std::string str; - try - { - str = row.get_string_at_col(m_col_name); - } - catch (std::invalid_argument&) - { - return; - } - if (string_to_guid (str.c_str(), &guid)) + auto strval{row.get_string_at_col(m_col_name)}; + if (strval && string_to_guid (strval->c_str(), &guid)) set_parameter(pObject, &guid, get_setter(obj_name), m_gobj_param_name); } @@ -378,28 +357,28 @@ GncSqlColumnTableEntryImpl::load (const GncSqlBackend* sql_be, { time64 t{0}; g_return_if_fail (m_gobj_param_name != nullptr || get_setter(obj_name) != nullptr); - try + auto strval = row.get_string_at_col(m_col_name); + if (strval) { - t = row.get_time64_at_col (m_col_name); - } - catch (std::invalid_argument&) - { - try - { - auto val = row.get_string_at_col(m_col_name); - GncDateTime time(val); - t = static_cast(time); - } - catch (const std::invalid_argument& err) - { - if (strcmp(err.what(), "Column empty.") != 0) + if (!strval->empty()) + try { - auto val = row.get_string_at_col (m_col_name); - PWARN("An invalid date %s was found in your database." - "It has been set to 1 January 1970.", val.c_str()); + GncDateTime time(*strval); + t = static_cast(time); + } + catch (const std::invalid_argument& err) + { + PWARN("An invalid date %s was found in your database." + "It has been set to 1 January 1970.", + strval->c_str()); } - } } + else + { + if (auto time64val = row.get_time64_at_col (m_col_name)) + t = *time64val; + } + if (m_gobj_param_name != nullptr) { Time64 t64{t}; @@ -472,37 +451,35 @@ GncSqlColumnTableEntryImpl::load (const GncSqlBackend* sql_be, return; GDate date; g_date_clear (&date, 1); - try + + auto strval{row.get_string_at_col(m_col_name)}; + if (strval) { + if (strval->empty()) + return; + auto year = static_cast(stoi (strval->substr (0,4))); + auto month = static_cast(stoi (strval->substr (4,2))); + auto day = static_cast(stoi (strval->substr (6,2))); + + if (year != 0 || month != 0 || day != (GDateDay)0) + g_date_set_dmy(&date, day, month, year); + } + else + { + auto timeval = row.get_time64_at_col(m_col_name); + if (!timeval) + return; /* time64_to_gdate applies the tz, and gdates are saved * as ymd, so we don't want that. */ - auto time = row.get_time64_at_col(m_col_name); + auto time = *timeval; auto tm = gnc_gmtime(&time); g_date_set_dmy(&date, tm->tm_mday, static_cast(tm->tm_mon + 1), tm->tm_year + 1900); free(tm); } - catch (std::invalid_argument&) - { - try - { - std::string str = row.get_string_at_col(m_col_name); - if (str.empty()) return; - auto year = static_cast(stoi (str.substr (0,4))); - auto month = static_cast(stoi (str.substr (4,2))); - auto day = static_cast(stoi (str.substr (6,2))); - if (year != 0 || month != 0 || day != (GDateDay)0) - g_date_set_dmy(&date, day, month, year); - - } - catch (std::invalid_argument&) - { - return; - } - } set_parameter(pObject, &date, get_setter(obj_name), m_gobj_param_name); } @@ -562,24 +539,21 @@ GncSqlColumnTableEntryImpl::load (const GncSqlBackend* sql_be, g_return_if_fail (pObject != NULL); g_return_if_fail (m_gobj_param_name != nullptr || get_setter(obj_name) != nullptr); - gnc_numeric n; - try + + auto buf = g_strdup_printf ("%s_num", m_col_name); + auto num = row.get_int_at_col (buf); + g_free (buf); + buf = g_strdup_printf ("%s_denom", m_col_name); + auto denom = row.get_int_at_col (buf); + g_free (buf); + + if (num && denom) { - auto buf = g_strdup_printf ("%s_num", m_col_name); - auto num = row.get_int_at_col (buf); - g_free (buf); - buf = g_strdup_printf ("%s_denom", m_col_name); - auto denom = row.get_int_at_col (buf); - n = gnc_numeric_create (num, denom); - g_free (buf); + auto n = gnc_numeric_create (*num, *denom); + set_parameter(pObject, n, + reinterpret_cast(get_setter(obj_name)), + m_gobj_param_name); } - catch (std::invalid_argument&) - { - return; - } - set_parameter(pObject, n, - reinterpret_cast(get_setter(obj_name)), - m_gobj_param_name); } template<> void diff --git a/libgnucash/backend/sql/gnc-sql-column-table-entry.hpp b/libgnucash/backend/sql/gnc-sql-column-table-entry.hpp index ae9c9b90c4..a079751a10 100644 --- a/libgnucash/backend/sql/gnc-sql-column-table-entry.hpp +++ b/libgnucash/backend/sql/gnc-sql-column-table-entry.hpp @@ -183,35 +183,37 @@ public: QofIdTypeConst obj_name, void* pObject, T get_ref) const noexcept - { - static QofLogModule log_module = G_LOG_DOMAIN; - g_return_if_fail (pObject != NULL); + { + static QofLogModule log_module = G_LOG_DOMAIN; + g_return_if_fail (pObject != NULL); - try - { - GncGUID guid; - auto val = row.get_string_at_col (m_col_name); - if (string_to_guid (val.c_str(), &guid)) - { - auto target = get_ref(&guid); - if (target != nullptr) - set_parameter (pObject, target, get_setter(obj_name), - m_gobj_param_name); - else - DEBUG("GUID %s returned null %s reference.", - val.c_str(), m_gobj_param_name); - } - else - { - if (val.empty()) DEBUG("Can't load empty guid string for column %s", m_col_name); - else DEBUG("Invalid GUID %s for column %s", val.c_str(), m_col_name); - } - } - catch (std::invalid_argument& err) { - DEBUG("set_parameter threw %s for column %s", err.what(), m_col_name); - } + GncGUID guid; + auto val = row.get_string_at_col (m_col_name); + if (!val) + { + DEBUG("set parameter: No string in column %s.", m_col_name); + return; } + if (string_to_guid (val->c_str(), &guid)) + { + auto target = get_ref(&guid); + if (target != nullptr) + set_parameter (pObject, target, get_setter(obj_name), + m_gobj_param_name); + else + DEBUG("GUID %s returned null %s reference.", + val->c_str(), m_gobj_param_name); + } + else + { + if (val->empty()) + DEBUG("Can't load empty guid string for column %s", m_col_name); + else + DEBUG("Invalid GUID %s for column %s", val->c_str(), m_col_name); + } + } + protected: template T diff --git a/libgnucash/backend/sql/gnc-sql-result.hpp b/libgnucash/backend/sql/gnc-sql-result.hpp index 757084c3b5..e3adb9fcdd 100644 --- a/libgnucash/backend/sql/gnc-sql-result.hpp +++ b/libgnucash/backend/sql/gnc-sql-result.hpp @@ -27,6 +27,7 @@ #include #include +#include #include #include @@ -49,11 +50,11 @@ protected: virtual ~IteratorImpl() = default; virtual GncSqlRow& operator++() = 0; virtual GncSqlResult* operator*() = 0; - virtual int64_t get_int_at_col (const char* col) const = 0; - virtual double get_float_at_col (const char* col) const = 0; - virtual double get_double_at_col (const char* col) const = 0; - virtual std::string get_string_at_col (const char* col) const = 0; - virtual time64 get_time64_at_col (const char* col) const = 0; + virtual std::optional get_int_at_col (const char* col) const = 0; + virtual std::optional get_float_at_col (const char* col) const = 0; + virtual std::optional get_double_at_col (const char* col) const = 0; + virtual std::optional get_string_at_col (const char* col) const = 0; + virtual std::optional get_time64_at_col (const char* col) const = 0; virtual bool is_col_null (const char* col) const noexcept = 0; }; }; @@ -84,15 +85,15 @@ public: GncSqlRow& operator++(); GncSqlRow& operator*() { return *this; } friend bool operator!=(const GncSqlRow&, const GncSqlRow&); - int64_t get_int_at_col (const char* col) const { + std::optional get_int_at_col (const char* col) const { return m_iter->get_int_at_col (col); } - double get_float_at_col (const char* col) const { + std::optional get_float_at_col (const char* col) const { return m_iter->get_float_at_col (col); } - double get_double_at_col (const char* col) const { + std::optional get_double_at_col (const char* col) const { return m_iter->get_double_at_col (col); } - std::string get_string_at_col (const char* col) const { + std::optional get_string_at_col (const char* col) const { return m_iter->get_string_at_col (col); } - time64 get_time64_at_col (const char* col) const { + std::optional get_time64_at_col (const char* col) const { return m_iter->get_time64_at_col (col); } bool is_col_null (const char* col) const noexcept { return m_iter->is_col_null (col); } diff --git a/libgnucash/backend/sql/gnc-transaction-sql.cpp b/libgnucash/backend/sql/gnc-transaction-sql.cpp index c04fc4cdd4..e600c923e0 100644 --- a/libgnucash/backend/sql/gnc-transaction-sql.cpp +++ b/libgnucash/backend/sql/gnc-transaction-sql.cpp @@ -792,27 +792,27 @@ GncSqlColumnTableEntryImpl::load (const GncSqlBackend* sql_be, g_return_if_fail (sql_be != NULL); g_return_if_fail (pObject != NULL); - try + auto val = row.get_string_at_col (m_col_name); + if (!val) + return; + + GncGUID guid; + Transaction *tx = nullptr; + if (string_to_guid (val->c_str(), &guid)) + tx = xaccTransLookup (&guid, sql_be->book()); + + // If the transaction is not found, try loading it + std::string tpkey(tx_col_table[0]->name()); + if (tx == nullptr) { - auto val = row.get_string_at_col (m_col_name); - GncGUID guid; - Transaction *tx = nullptr; - if (string_to_guid (val.c_str(), &guid)) - tx = xaccTransLookup (&guid, sql_be->book()); - - // If the transaction is not found, try loading it - std::string tpkey(tx_col_table[0]->name()); - if (tx == nullptr) - { - std::string sql = tpkey + " = '" + val + "'"; - query_transactions ((GncSqlBackend*)sql_be, sql); - tx = xaccTransLookup (&guid, sql_be->book()); - } - - if (tx != nullptr) - set_parameter (pObject, tx, get_setter(obj_name), m_gobj_param_name); + std::string sql = tpkey + " = '" + *val + "'"; + query_transactions ((GncSqlBackend*)sql_be, sql); + tx = xaccTransLookup (&guid, sql_be->book()); } - catch (std::invalid_argument&) {} + + if (tx != nullptr) + set_parameter (pObject, tx, get_setter(obj_name), m_gobj_param_name); + } template<> void diff --git a/libgnucash/backend/sql/test/utest-gnc-backend-sql.cpp b/libgnucash/backend/sql/test/utest-gnc-backend-sql.cpp index 2368a3d0b8..a288cc17b6 100644 --- a/libgnucash/backend/sql/test/utest-gnc-backend-sql.cpp +++ b/libgnucash/backend/sql/test/utest-gnc-backend-sql.cpp @@ -62,15 +62,15 @@ protected: virtual GncSqlRow& operator++() { return m_inst->m_row; } virtual GncSqlRow& operator++(int) { return ++(*this); }; virtual GncSqlResult* operator*() { return m_inst; } - virtual int64_t get_int_at_col (const char* col) const + virtual std::optional get_int_at_col (const char* col) const { return 1LL; } - virtual double get_float_at_col (const char* col) const + virtual std::optional get_float_at_col (const char* col) const { return 1.0; } - virtual double get_double_at_col (const char* col) const + virtual std::optional get_double_at_col (const char* col) const { return 1.0; } - virtual std::string get_string_at_col (const char* col)const + virtual std::optional get_string_at_col (const char* col)const { return std::string{"foo"}; } - virtual time64 get_time64_at_col (const char* col) const + virtual std::optional get_time64_at_col (const char* col) const { return 1466270857LL; } virtual bool is_col_null(const char* col) const noexcept { return false; }