gnucash/libgnucash/backend/sql/gnc-sql-result.hpp
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

114 lines
4.8 KiB
C++

/***********************************************************************\
* gnc-sql-result.hpp: Encapsulate the results of a SQL query. *
* *
* Copyright 2016 John Ralls <jralls@ceridwen.us> *
* *
* 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 *
\***********************************************************************/
#ifndef __GNC_SQL_RESULT_HPP__
#define __GNC_SQL_RESULT_HPP__
#include <qof.h>
#include <cstdint>
#include <optional>
#include <string>
#include <vector>
class GncSqlRow;
/**
* Pure virtual class to iterate over a query result set.
*/
class GncSqlResult
{
public:
virtual ~GncSqlResult() = default;
virtual uint64_t size() const noexcept = 0;
virtual GncSqlRow& begin() = 0;
virtual GncSqlRow& end() = 0;
friend GncSqlRow;
protected:
class IteratorImpl {
public:
virtual ~IteratorImpl() = default;
virtual GncSqlRow& operator++() = 0;
virtual GncSqlResult* operator*() = 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;
};
};
/**
* Row of SQL Query results.
*
* This is a "pointer" class of a pimpl pattern, the implementation being
* GncSqlResult::IteratorImpl. It's designed to present a std::forward_iterator
* like interface for use with range-for while allowing for wrapping a C API.
*
* Important Implementation Note: Operator++() as written requires that the
* sentinel GncSqlRow returned by GncSqlResult::end() has m_impl = nullptr in
* order to terminate the loop condition. This is a bit of a hack and might be a
* problem with a different SQL interface library from libdbi.
*
* Note that GncSqlResult::begin and GncSqlRow::operator++() return
* GncSqlRow&. This is necessary for operator++() to be called: Using operator
* ++() on a pointer performs pointer arithmetic rather than calling the
* pointed-to-class's operator++() and C++'s range-for uses operator++()
* directly on whatever begin() gives it.
*/
class GncSqlRow
{
public:
GncSqlRow (GncSqlResult::IteratorImpl* iter) : m_iter{iter} {}
~GncSqlRow() { }
GncSqlRow& operator++();
GncSqlRow& operator*() { return *this; }
friend bool operator!=(const GncSqlRow&, const GncSqlRow&);
std::optional<int64_t> get_int_at_col (const char* col) const {
return m_iter->get_int_at_col (col); }
std::optional<double> get_float_at_col (const char* col) const {
return m_iter->get_float_at_col (col); }
std::optional<double> get_double_at_col (const char* col) const {
return m_iter->get_double_at_col (col); }
std::optional<std::string> get_string_at_col (const char* col) const {
return m_iter->get_string_at_col (col); }
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); }
private:
GncSqlResult::IteratorImpl* m_iter;
};
inline bool operator!=(const GncSqlRow& lr, const GncSqlRow& rr) {
return lr.m_iter != rr.m_iter;
}
inline bool operator==(const GncSqlRow& lr, const GncSqlRow& rr) {
return !(lr != rr);
}
#endif //__GNC_SQL_RESULT_HPP__