Branch data Line data Source code
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 : 0 : GncDbiSqlResult::~GncDbiSqlResult()
44 : : {
45 : 0 : int status = dbi_result_free (m_dbi_result);
46 : :
47 : 0 : if (status == 0)
48 : 0 : return;
49 : :
50 : 0 : PERR ("Error %d in dbi_result_free() result.", m_conn->dberror() );
51 : 0 : qof_backend_set_error (m_conn->qbe(), ERR_BACKEND_SERVER_ERR);
52 : 0 : }
53 : :
54 : : int
55 : 0 : GncDbiSqlResult::dberror() const noexcept
56 : : {
57 : 0 : return m_conn->dberror();
58 : : }
59 : :
60 : : GncSqlRow&
61 : 0 : GncDbiSqlResult::begin()
62 : : {
63 : :
64 : 0 : if (m_dbi_result == nullptr ||
65 : 0 : dbi_result_get_numrows(m_dbi_result) == 0)
66 : 0 : return m_sentinel;
67 : 0 : int status = dbi_result_first_row(m_dbi_result);
68 : 0 : if (status)
69 : 0 : return m_row;
70 : 0 : int error = dberror(); //
71 : :
72 : 0 : if (error != DBI_ERROR_BADIDX) //otherwise just an empty result set
73 : : {
74 : 0 : PERR ("Error %d in dbi_result_first_row()", dberror());
75 : 0 : qof_backend_set_error (m_conn->qbe(), ERR_BACKEND_SERVER_ERR);
76 : : }
77 : 0 : return m_sentinel;
78 : : }
79 : :
80 : : uint64_t
81 : 0 : GncDbiSqlResult::size() const noexcept
82 : : {
83 : 0 : return dbi_result_get_numrows(m_dbi_result);
84 : : }
85 : : /* --------------------------------------------------------- */
86 : :
87 : : GncSqlRow&
88 : 0 : GncDbiSqlResult::IteratorImpl::operator++()
89 : : {
90 : 0 : int status = dbi_result_next_row (m_inst->m_dbi_result);
91 : 0 : if (status)
92 : 0 : return m_inst->m_row;
93 : 0 : int error = m_inst->dberror();
94 : 0 : if (error == DBI_ERROR_BADIDX || error == 0) //ran off the end of the results
95 : 0 : return m_inst->m_sentinel;
96 : 0 : PERR("Error %d incrementing results iterator.", error);
97 : 0 : qof_backend_set_error (m_inst->m_conn->qbe(), ERR_BACKEND_SERVER_ERR);
98 : 0 : return m_inst->m_sentinel;
99 : : }
100 : :
101 : : std::optional<int64_t>
102 : 0 : GncDbiSqlResult::IteratorImpl::get_int_at_col(const char* col) const
103 : : {
104 : 0 : auto type = dbi_result_get_field_type (m_inst->m_dbi_result, col);
105 : 0 : if(type != DBI_TYPE_INTEGER)
106 : 0 : return std::nullopt;
107 : 0 : return std::optional<int64_t>{dbi_result_get_longlong (m_inst->m_dbi_result, col)};
108 : : }
109 : :
110 : : std::optional<double>
111 : 0 : GncDbiSqlResult::IteratorImpl::get_float_at_col(const char* col) const
112 : : {
113 : 0 : constexpr double float_precision = 1000000.0;
114 : 0 : auto type = dbi_result_get_field_type (m_inst->m_dbi_result, col);
115 : 0 : auto attrs = dbi_result_get_field_attribs (m_inst->m_dbi_result, col);
116 : 0 : if(type != DBI_TYPE_DECIMAL ||
117 : 0 : (attrs & DBI_DECIMAL_SIZEMASK) != DBI_DECIMAL_SIZE4)
118 : 0 : return std::nullopt;
119 : 0 : auto locale = gnc_push_locale (LC_NUMERIC, "C");
120 : 0 : auto interim = dbi_result_get_float(m_inst->m_dbi_result, col);
121 : 0 : gnc_pop_locale (LC_NUMERIC, locale);
122 : 0 : double retval = static_cast<double>(round(interim * float_precision)) / float_precision;
123 : 0 : return std::optional<double>{retval};
124 : 0 : }
125 : :
126 : : std::optional<double>
127 : 0 : GncDbiSqlResult::IteratorImpl::get_double_at_col(const char* col) const
128 : : {
129 : 0 : auto type = dbi_result_get_field_type (m_inst->m_dbi_result, col);
130 : 0 : auto attrs = dbi_result_get_field_attribs (m_inst->m_dbi_result, col);
131 : 0 : if(type != DBI_TYPE_DECIMAL ||
132 : 0 : (attrs & DBI_DECIMAL_SIZEMASK) != DBI_DECIMAL_SIZE8)
133 : 0 : return std::nullopt;
134 : 0 : auto locale = gnc_push_locale (LC_NUMERIC, "C");
135 : 0 : auto retval = dbi_result_get_double(m_inst->m_dbi_result, col);
136 : 0 : gnc_pop_locale (LC_NUMERIC, locale);
137 : 0 : return std::optional<double>{retval};
138 : 0 : }
139 : :
140 : : std::optional<std::string>
141 : 0 : GncDbiSqlResult::IteratorImpl::get_string_at_col(const char* col) const
142 : : {
143 : 0 : auto type = dbi_result_get_field_type (m_inst->m_dbi_result, col);
144 : 0 : dbi_result_get_field_attribs (m_inst->m_dbi_result, col);
145 : 0 : if(type != DBI_TYPE_STRING)
146 : 0 : return std::nullopt;
147 : 0 : auto strval = dbi_result_get_string(m_inst->m_dbi_result, col);
148 : 0 : return std::optional<std::string>{strval ? strval : ""};
149 : : }
150 : :
151 : : std::optional<time64>
152 : 0 : GncDbiSqlResult::IteratorImpl::get_time64_at_col (const char* col) const
153 : : {
154 : 0 : auto result = (dbi_result_t*) (m_inst->m_dbi_result);
155 : 0 : auto type = dbi_result_get_field_type (result, col);
156 : 0 : dbi_result_get_field_attribs (result, col);
157 : 0 : if (type != DBI_TYPE_DATETIME)
158 : 0 : 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 : 0 : 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 : 0 : if (timeval < MINTIME || timeval > MAXTIME)
176 : 0 : timeval = 0;
177 : 0 : return std::optional<time64>(timeval);
178 : : }
179 : :
180 : :
181 : : /* --------------------------------------------------------- */
182 : :
|