gnucash stable: Multiple changes pushed

John Ralls jralls at code.gnucash.org
Thu Aug 10 17:06:45 EDT 2023


Updated	 via  https://github.com/Gnucash/gnucash/commit/73dec63d (commit)
	 via  https://github.com/Gnucash/gnucash/commit/5781f344 (commit)
	from  https://github.com/Gnucash/gnucash/commit/beec4204 (commit)



commit 73dec63d1d63cccd1423c21c39626c4a4e5cc90d
Author: John Ralls <jralls at ceridwen.us>
Date:   Thu Aug 10 13:53:21 2023 -0700

    Eliminate a frequent exception in guid_from_string
    
    By not trying to construct a GUID from an empty string.

diff --git a/libgnucash/engine/guid.cpp b/libgnucash/engine/guid.cpp
index aaf58c53ad..55aade3cfd 100644
--- a/libgnucash/engine/guid.cpp
+++ b/libgnucash/engine/guid.cpp
@@ -186,7 +186,7 @@ guid_to_string_buff (const GncGUID * guid, gchar *str)
 gboolean
 string_to_guid (const char * str, GncGUID * guid)
 {
-    if (!guid || !str) return false;
+    if (!guid || !str || !*str) return false;
 
     try
     {
@@ -194,6 +194,7 @@ string_to_guid (const char * str, GncGUID * guid)
     }
     catch (...)
     {
+        PINFO("Failed to construct a GUID from %s", str);
         return false;
     }
     return true;

commit 5781f3445bd8e10c27ba173f259d9b72d0aef299
Author: John Ralls <jralls at ceridwen.us>
Date:   Thu Aug 10 13:36:39 2023 -0700

    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.

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 <dbi/dbi-dev.h>
 #include <cmath>
 #include <gnc-datetime.hpp>
+#include <sys/_types/_timeval.h>
 #include "gnc-dbisqlresult.hpp"
 #include "gnc-dbisqlconnection.hpp"
 
@@ -98,16 +99,16 @@ GncDbiSqlResult::IteratorImpl::operator++()
     return m_inst->m_sentinel;
 }
 
-int64_t
+std::optional<int64_t>
 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<int64_t>{dbi_result_get_longlong (m_inst->m_dbi_result, col)};
 }
 
-double
+std::optional<double>
 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<double>(round(interim * float_precision)) / float_precision;
-    return retval;
+    return std::optional<double>{retval};
 }
 
-double
+std::optional<double>
 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<double>{retval};
 }
 
-std::string
+std::optional<std::string>
 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<std::string>{strval ? strval : ""};
 }
-time64
+
+std::optional<time64>
 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<time64>(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 <optional>
+
 #include "gnc-backend-dbi.h"
 #include <gnc-sql-result.hpp>
 
@@ -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<int64_t> get_int_at_col (const char* col) const;
+        virtual std::optional<double> get_float_at_col (const char* col) const;
+        virtual std::optional<double> get_double_at_col (const char* col) const;
+        virtual std::optional<std::string> get_string_at_col (const char* col)const;
+        virtual std::optional<time64> 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<CT_ADDRESS>::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<AddressSetterFunc>(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<CT_OWNERREF>::load (const GncSqlBackend* sql_be,
     auto buf = std::string{m_col_name} + "_type";
     try
     {
-        type = static_cast<decltype(type)>(row.get_int_at_col (buf.c_str()));
+        type = static_cast<decltype(type)>(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<CT_OWNERREF>::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<unsigned int>(*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<CT_STRING>::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<CT_INT>::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<IntSetterFunc>(get_setter(obj_name)), m_gobj_param_name);
+    if (val)
+        set_parameter(pObject, *val,
+                      reinterpret_cast<IntSetterFunc>(get_setter(obj_name)),
+                      m_gobj_param_name);
 }
 
 template<> void
@@ -208,9 +207,10 @@ GncSqlColumnTableEntryImpl<CT_BOOLEAN>::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<int>(val),
-                  reinterpret_cast<BooleanSetterFunc>(get_setter(obj_name)),
-                  m_gobj_param_name);
+    if (val)
+        set_parameter(pObject, static_cast<int>(*val),
+                      reinterpret_cast<BooleanSetterFunc>(get_setter(obj_name)),
+                      m_gobj_param_name);
 }
 
 template<> void
@@ -242,9 +242,10 @@ GncSqlColumnTableEntryImpl<CT_INT64>::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<Int64SetterFunc>(get_setter(obj_name)),
-                  m_gobj_param_name);
+    if (val)
+        set_parameter(pObject, *val,
+                      reinterpret_cast<Int64SetterFunc>(get_setter(obj_name)),
+                      m_gobj_param_name);
 }
 
 template<> void
@@ -273,29 +274,15 @@ GncSqlColumnTableEntryImpl<CT_DOUBLE>::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<double>(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<decltype(val)>(*int_val);
+    else if (auto float_val{row.get_float_at_col(m_col_name)})
+        val = static_cast<decltype(val)>(*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<CT_GUID>::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<CT_TIME>::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<time64>(time);
-        }
-        catch (const std::invalid_argument& err)
-        {
-            if (strcmp(err.what(), "Column empty.") != 0)
+        if (!strval->empty())
+            try
+            {
+                GncDateTime time(*strval);
+                t = static_cast<time64>(time);
+            }
+            catch (const std::invalid_argument& err)
             {
-                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());
+                      "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<CT_GDATE>::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<GDateYear>(stoi (strval->substr (0,4)));
+        auto month = static_cast<GDateMonth>(stoi (strval->substr (4,2)));
+        auto day = static_cast<GDateDay>(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<GDateMonth>(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<GDateYear>(stoi (str.substr (0,4)));
-            auto month = static_cast<GDateMonth>(stoi (str.substr (4,2)));
-            auto day = static_cast<GDateDay>(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<CT_NUMERIC>::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);
-        n = gnc_numeric_create (num, denom);
-        g_free (buf);
-    }
-    catch (std::invalid_argument&)
+
+    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)
     {
-        return;
+        auto n = gnc_numeric_create (*num, *denom);
+        set_parameter(pObject, n,
+                      reinterpret_cast<NumericSetterFunc>(get_setter(obj_name)),
+                      m_gobj_param_name);
     }
-    set_parameter(pObject, n,
-                  reinterpret_cast<NumericSetterFunc>(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,34 +183,36 @@ public:
                                                  QofIdTypeConst obj_name,
                                                  void* pObject, T get_ref)
         const noexcept
+    {
+        static QofLogModule log_module = G_LOG_DOMAIN;
+        g_return_if_fail (pObject != NULL);
+
+        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
         {
-            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);
-            }
+            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:
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 <qof.h>
 
 #include <cstdint>
+#include <optional>
 #include <string>
 #include <vector>
 
@@ -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<int64_t> get_int_at_col (const char* col) const = 0;
+        virtual std::optional<double> get_float_at_col (const char* col) const = 0;
+        virtual std::optional<double> get_double_at_col (const char* col) const = 0;
+        virtual std::optional<std::string> get_string_at_col (const char* col) const = 0;
+        virtual std::optional<time64> 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<int64_t> 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<double> 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<double> 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<std::string> 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<time64> 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<CT_TXREF>::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);
-        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());
-        }
+    auto val = row.get_string_at_col (m_col_name);
+    if (!val)
+        return;
 
-        if (tx != nullptr)
-            set_parameter (pObject, tx, get_setter(obj_name), m_gobj_param_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());
     }
-    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<int64_t> get_int_at_col (const char* col) const
             { return 1LL; }
-            virtual double get_float_at_col (const char* col) const
+            virtual std::optional<double> get_float_at_col (const char* col) const
             { return 1.0; }
-            virtual double get_double_at_col (const char* col) const
+            virtual std::optional<double> 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<std::string> 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<time64> get_time64_at_col (const char* col) const
             { return 1466270857LL; }
             virtual bool is_col_null(const char* col) const noexcept
             { return false; }



Summary of changes:
 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 +-
 .../backend/sql/gnc-sql-column-table-entry.cpp     | 174 +++++++++------------
 .../backend/sql/gnc-sql-column-table-entry.hpp     |  54 ++++---
 libgnucash/backend/sql/gnc-sql-result.hpp          |  21 +--
 libgnucash/backend/sql/gnc-transaction-sql.cpp     |  38 ++---
 .../backend/sql/test/utest-gnc-backend-sql.cpp     |  10 +-
 libgnucash/engine/guid.cpp                         |   3 +-
 12 files changed, 183 insertions(+), 217 deletions(-)



More information about the gnucash-changes mailing list