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 :
|