GnuCash  5.6-150-g038405b370+
gnc-dbisqlresult.cpp
1 /********************************************************************
2  * gnc-dbisqlresult.cpp: Encapsulate libdbi dbi_result *
3  * *
4  * Copyright 2016 John Ralls <jralls@ceridwen.us> *
5  * *
6  * This program is free software; you can redistribute it and/or *
7  * modify it under the terms of the GNU General Public License as *
8  * published by the Free Software Foundation; either version 2 of *
9  * the License, or (at your option) any later version. *
10  * *
11  * This program is distributed in the hope that it will be useful, *
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of *
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
14  * GNU General Public License for more details. *
15  * *
16  * You should have received a copy of the GNU General Public License*
17  * along with this program; if not, contact: *
18  * *
19  * Free Software Foundation Voice: +1-617-542-5942 *
20  * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 *
21  * Boston, MA 02110-1301, USA gnu@gnu.org *
22 \********************************************************************/
23 
24 #include <guid.hpp>
25 #include <config.h>
26 #include <gnc-locale-utils.h>
27 #include <dbi/dbi.h>
28 /* For direct access to dbi data structs, sadly needed for datetime */
29 #include <dbi/dbi-dev.h>
30 #include <cmath>
31 #include <gnc-datetime.hpp>
32 #include "gnc-dbisqlresult.hpp"
33 #include "gnc-dbisqlconnection.hpp"
34 
35 static QofLogModule log_module = G_LOG_DOMAIN;
36 
37 #if LIBDBI_VERSION >= 900
38 #define HAVE_LIBDBI_TO_LONGLONG 1
39 #else
40 #define HAVE_LIBDBI_TO_LONGLONG 0
41 #endif
42 
43 GncDbiSqlResult::~GncDbiSqlResult()
44 {
45  int status = dbi_result_free (m_dbi_result);
46 
47  if (status == 0)
48  return;
49 
50  PERR ("Error %d in dbi_result_free() result.", m_conn->dberror() );
52 }
53 
54 int
55 GncDbiSqlResult::dberror() const noexcept
56 {
57  return m_conn->dberror();
58 }
59 
60 GncSqlRow&
61 GncDbiSqlResult::begin()
62 {
63 
64  if (m_dbi_result == nullptr ||
65  dbi_result_get_numrows(m_dbi_result) == 0)
66  return m_sentinel;
67  int status = dbi_result_first_row(m_dbi_result);
68  if (status)
69  return m_row;
70  int error = dberror(); //
71 
72  if (error != DBI_ERROR_BADIDX) //otherwise just an empty result set
73  {
74  PERR ("Error %d in dbi_result_first_row()", dberror());
76  }
77  return m_sentinel;
78 }
79 
80 uint64_t
81 GncDbiSqlResult::size() const noexcept
82 {
83  return dbi_result_get_numrows(m_dbi_result);
84 }
85 /* --------------------------------------------------------- */
86 
87 GncSqlRow&
88 GncDbiSqlResult::IteratorImpl::operator++()
89 {
90  int status = dbi_result_next_row (m_inst->m_dbi_result);
91  if (status)
92  return m_inst->m_row;
93  int error = m_inst->dberror();
94  if (error == DBI_ERROR_BADIDX || error == 0) //ran off the end of the results
95  return m_inst->m_sentinel;
96  PERR("Error %d incrementing results iterator.", error);
97  qof_backend_set_error (m_inst->m_conn->qbe(), ERR_BACKEND_SERVER_ERR);
98  return m_inst->m_sentinel;
99 }
100 
101 std::optional<int64_t>
102 GncDbiSqlResult::IteratorImpl::get_int_at_col(const char* col) const
103 {
104  auto type = dbi_result_get_field_type (m_inst->m_dbi_result, col);
105  if(type != DBI_TYPE_INTEGER)
106  return std::nullopt;
107  return std::optional<int64_t>{dbi_result_get_longlong (m_inst->m_dbi_result, col)};
108 }
109 
110 std::optional<double>
111 GncDbiSqlResult::IteratorImpl::get_float_at_col(const char* col) const
112 {
113  constexpr double float_precision = 1000000.0;
114  auto type = dbi_result_get_field_type (m_inst->m_dbi_result, col);
115  auto attrs = dbi_result_get_field_attribs (m_inst->m_dbi_result, col);
116  if(type != DBI_TYPE_DECIMAL ||
117  (attrs & DBI_DECIMAL_SIZEMASK) != DBI_DECIMAL_SIZE4)
118  return std::nullopt;
119  auto locale = gnc_push_locale (LC_NUMERIC, "C");
120  auto interim = dbi_result_get_float(m_inst->m_dbi_result, col);
121  gnc_pop_locale (LC_NUMERIC, locale);
122  double retval = static_cast<double>(round(interim * float_precision)) / float_precision;
123  return std::optional<double>{retval};
124 }
125 
126 std::optional<double>
127 GncDbiSqlResult::IteratorImpl::get_double_at_col(const char* col) const
128 {
129  auto type = dbi_result_get_field_type (m_inst->m_dbi_result, col);
130  auto attrs = dbi_result_get_field_attribs (m_inst->m_dbi_result, col);
131  if(type != DBI_TYPE_DECIMAL ||
132  (attrs & DBI_DECIMAL_SIZEMASK) != DBI_DECIMAL_SIZE8)
133  return std::nullopt;
134  auto locale = gnc_push_locale (LC_NUMERIC, "C");
135  auto retval = dbi_result_get_double(m_inst->m_dbi_result, col);
136  gnc_pop_locale (LC_NUMERIC, locale);
137  return std::optional<double>{retval};
138 }
139 
140 std::optional<std::string>
141 GncDbiSqlResult::IteratorImpl::get_string_at_col(const char* col) const
142 {
143  auto type = dbi_result_get_field_type (m_inst->m_dbi_result, col);
144  dbi_result_get_field_attribs (m_inst->m_dbi_result, col);
145  if(type != DBI_TYPE_STRING)
146  return std::nullopt;
147  auto strval = dbi_result_get_string(m_inst->m_dbi_result, col);
148  return std::optional<std::string>{strval ? strval : ""};
149 }
150 
151 std::optional<time64>
152 GncDbiSqlResult::IteratorImpl::get_time64_at_col (const char* col) const
153 {
154  auto result = (dbi_result_t*) (m_inst->m_dbi_result);
155  auto type = dbi_result_get_field_type (result, col);
156  dbi_result_get_field_attribs (result, col);
157  if (type != DBI_TYPE_DATETIME)
158  return std::nullopt;
159 #if HAVE_LIBDBI_TO_LONGLONG
160  /* A less evil hack than the one required by libdbi-0.8, but
161  * still necessary to work around the same bug.
162  */
163  auto timeval = dbi_result_get_as_longlong(result, col);
164 #else
165  /* A seriously evil hack to work around libdbi bug #15
166  * https://sourceforge.net/p/libdbi/bugs/15/. When libdbi
167  * v0.9 is widely available this can be replaced with
168  * dbi_result_get_as_longlong.
169  * Note: 0.9 is available in Debian Jessie and Fedora 21.
170  */
171  auto row = dbi_result_get_currow (result);
172  auto idx = dbi_result_get_field_idx (result, col) - 1;
173  time64 timeval = result->rows[row]->field_values[idx].d_datetime;
174 #endif //HAVE_LIBDBI_TO_LONGLONG
175  if (timeval < MINTIME || timeval > MAXTIME)
176  timeval = 0;
177  return std::optional<time64>(timeval);
178 }
179 
180 
181 /* --------------------------------------------------------- */
182 
#define G_LOG_DOMAIN
Functions providing the SX List as a plugin page.
void qof_backend_set_error(QofBackend *qof_be, QofBackendError err)
Set the error on the specified QofBackend.
#define PERR(format, args...)
Log a serious error.
Definition: qoflog.h:244
error in response from server
Definition: qofbackend.h:71
Row of SQL Query results.
int dberror() const noexcept override
Get the connection error value.
gint64 time64
Most systems that are currently maintained, including Microsoft Windows, BSD-derived Unixes and Linux...
Definition: gnc-date.h:87