1 /********************************************************************
2 * gnc-sql-column-table-entry.cpp: Implement GncSqlColumnTableEntry *
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 extern "C"
25 {
26 #include <config.h>
27 #include <qof.h>
28 }
29 #include <sstream>
30 #include <iomanip>
31 #include <gnc-datetime.hpp>
32 #include "gnc-sql-backend.hpp"
33 #include "gnc-sql-object-backend.hpp"
34 #include "gnc-sql-column-table-entry.hpp"
35 #include "gnc-sql-result.hpp"
36
37 static QofLogModule log_module = G_LOG_DOMAIN;
38
39 /* ================================================================= */
40 static gpointer
get_autoinc_id(void * object,const QofParam * param)41 get_autoinc_id (void* object, const QofParam* param)
42 {
43 // Just need a 0 to force a new autoinc value
44 return (gpointer)0;
45 }
46
47 static void
set_autoinc_id(void * object,void * item)48 set_autoinc_id (void* object, void* item)
49 {
50 // Nowhere to put the ID
51 }
52
53
54 QofAccessFunc
get_getter(QofIdTypeConst obj_name) const55 GncSqlColumnTableEntry::get_getter (QofIdTypeConst obj_name) const noexcept
56 {
57 QofAccessFunc getter;
58
59 g_return_val_if_fail (obj_name != NULL, NULL);
60
61 if (m_flags & COL_AUTOINC)
62 {
63 getter = get_autoinc_id;
64 }
65 else if (m_qof_param_name != NULL)
66 {
67 getter = qof_class_get_parameter_getter (obj_name, m_qof_param_name);
68 }
69 else
70 {
71 getter = m_getter;
72 }
73
74 return getter;
75 }
76
77 QofSetterFunc
get_setter(QofIdTypeConst obj_name) const78 GncSqlColumnTableEntry::get_setter(QofIdTypeConst obj_name) const noexcept
79 {
80 QofSetterFunc setter = nullptr;
81 if (m_flags & COL_AUTOINC)
82 {
83 setter = set_autoinc_id;
84 }
85 else if (m_qof_param_name != nullptr)
86 {
87 g_assert (obj_name != NULL);
88 setter = qof_class_get_parameter_setter (obj_name, m_qof_param_name);
89 }
90 else
91 {
92 setter = m_setter;
93 }
94 return setter;
95 }
96
97 void
add_objectref_guid_to_query(QofIdTypeConst obj_name,const void * pObject,PairVec & vec) const98 GncSqlColumnTableEntry::add_objectref_guid_to_query (QofIdTypeConst obj_name,
99 const void* pObject,
100 PairVec& vec) const noexcept
101 {
102 auto inst = get_row_value_from_object<QofInstance*>(obj_name, pObject);
103 if (inst == nullptr) return;
104 auto guid = qof_instance_get_guid (inst);
105 if (guid != nullptr)
106 vec.emplace_back (std::make_pair (std::string{m_col_name},
107 quote_string(guid_to_string(guid))));
108 }
109
110 void
add_objectref_guid_to_table(ColVec & vec) const111 GncSqlColumnTableEntry::add_objectref_guid_to_table (ColVec& vec) const noexcept
112 {
113 GncSqlColumnInfo info{*this, BCT_STRING, GUID_ENCODING_LENGTH, FALSE};
114 vec.emplace_back(std::move(info));
115 }
116
117
118 /* ----------------------------------------------------------------- */
119 template<> void
load(const GncSqlBackend * sql_be,GncSqlRow & row,QofIdTypeConst obj_name,gpointer pObject) const120 GncSqlColumnTableEntryImpl<CT_STRING>::load (const GncSqlBackend* sql_be,
121 GncSqlRow& row,
122 QofIdTypeConst obj_name,
123 gpointer pObject) const noexcept
124 {
125 g_return_if_fail (pObject != NULL);
126 g_return_if_fail (m_gobj_param_name != NULL || get_setter(obj_name) != NULL);
127
128 try
129 {
130 auto s = row.get_string_at_col (m_col_name);
131 set_parameter(pObject, s.c_str(), get_setter(obj_name), m_gobj_param_name);
132 }
133 catch (std::invalid_argument&) {}
134 }
135
136 template<> void
add_to_table(ColVec & vec) const137 GncSqlColumnTableEntryImpl<CT_STRING>::add_to_table(ColVec& vec) const noexcept
138 {
139 GncSqlColumnInfo info{*this, BCT_STRING, m_size, TRUE};
140 vec.emplace_back(std::move(info));
141 }
142
143 /* char is unusual in that we get a pointer but don't deref it to pass
144 * it to operator<<().
145 */
146 template<> void
add_to_query(QofIdTypeConst obj_name,const gpointer pObject,PairVec & vec) const147 GncSqlColumnTableEntryImpl<CT_STRING>::add_to_query(QofIdTypeConst obj_name,
148 const gpointer pObject,
149 PairVec& vec) const noexcept
150 {
151 auto s = get_row_value_from_object<char*>(obj_name, pObject);
152
153 if (s != nullptr)
154 {
155 std::ostringstream stream;
156 stream << s;
157 vec.emplace_back (std::make_pair (std::string{m_col_name},
158 quote_string(stream.str())));
159 return;
160 }
161 }
162
163 /* ----------------------------------------------------------------- */
164 typedef gint (*IntAccessFunc) (const gpointer);
165 typedef void (*IntSetterFunc) (const gpointer, gint);
166
167 template<> void
load(const GncSqlBackend * sql_be,GncSqlRow & row,QofIdTypeConst obj_name,gpointer pObject) const168 GncSqlColumnTableEntryImpl<CT_INT>::load (const GncSqlBackend* sql_be,
169 GncSqlRow& row,
170 QofIdTypeConst obj_name,
171 gpointer pObject) const noexcept
172 {
173
174 g_return_if_fail (pObject != NULL);
175 g_return_if_fail (m_gobj_param_name != NULL || get_setter(obj_name) != NULL);
176
177 auto val = row.get_int_at_col(m_col_name);
178 set_parameter(pObject, val,
179 reinterpret_cast<IntSetterFunc>(get_setter(obj_name)), m_gobj_param_name);
180 }
181
182 template<> void
add_to_table(ColVec & vec) const183 GncSqlColumnTableEntryImpl<CT_INT>::add_to_table(ColVec& vec) const noexcept
184 {
185 GncSqlColumnInfo info{*this, BCT_INT, 0, FALSE};
186 vec.emplace_back(std::move(info));
187 }
188
189 template<> void
add_to_query(QofIdTypeConst obj_name,const gpointer pObject,PairVec & vec) const190 GncSqlColumnTableEntryImpl<CT_INT>::add_to_query(QofIdTypeConst obj_name,
191 const gpointer pObject,
192 PairVec& vec) const noexcept
193 {
194 add_value_to_vec<int>(obj_name, pObject, vec);
195 }
196
197 /* ----------------------------------------------------------------- */
198 typedef gboolean (*BooleanAccessFunc) (const gpointer);
199 typedef void (*BooleanSetterFunc) (const gpointer, gboolean);
200
201 template<> void
load(const GncSqlBackend * sql_be,GncSqlRow & row,QofIdTypeConst obj_name,gpointer pObject) const202 GncSqlColumnTableEntryImpl<CT_BOOLEAN>::load (const GncSqlBackend* sql_be,
203 GncSqlRow& row,
204 QofIdTypeConst obj_name,
205 gpointer pObject)
206 const noexcept
207 {
208 g_return_if_fail (pObject != NULL);
209 g_return_if_fail (m_gobj_param_name != NULL || get_setter(obj_name) != NULL);
210
211 auto val = row.get_int_at_col (m_col_name);
212 set_parameter(pObject, static_cast<int>(val),
213 reinterpret_cast<BooleanSetterFunc>(get_setter(obj_name)),
214 m_gobj_param_name);
215 }
216
217 template<> void
add_to_table(ColVec & vec) const218 GncSqlColumnTableEntryImpl<CT_BOOLEAN>::add_to_table(ColVec& vec) const noexcept
219 {
220 GncSqlColumnInfo info{*this, BCT_INT, 0, FALSE};
221 vec.emplace_back(std::move(info));
222 }
223
224 template<> void
add_to_query(QofIdTypeConst obj_name,const gpointer pObject,PairVec & vec) const225 GncSqlColumnTableEntryImpl<CT_BOOLEAN>::add_to_query(QofIdTypeConst obj_name,
226 const gpointer pObject,
227 PairVec& vec) const noexcept
228 {
229 add_value_to_vec<int>(obj_name, pObject, vec);
230 }
231
232 /* ----------------------------------------------------------------- */
233 typedef gint64 (*Int64AccessFunc) (const gpointer);
234 typedef void (*Int64SetterFunc) (const gpointer, gint64);
235
236 template<> void
load(const GncSqlBackend * sql_be,GncSqlRow & row,QofIdTypeConst obj_name,gpointer pObject) const237 GncSqlColumnTableEntryImpl<CT_INT64>::load (const GncSqlBackend* sql_be,
238 GncSqlRow& row,
239 QofIdTypeConst obj_name,
240 gpointer pObject)
241 const noexcept
242 {
243 g_return_if_fail (m_gobj_param_name != nullptr || get_setter(obj_name) != nullptr);
244
245 auto val = row.get_int_at_col (m_col_name);
246 set_parameter(pObject, val,
247 reinterpret_cast<Int64SetterFunc>(get_setter(obj_name)),
248 m_gobj_param_name);
249 }
250
251 template<> void
add_to_table(ColVec & vec) const252 GncSqlColumnTableEntryImpl<CT_INT64>::add_to_table(ColVec& vec) const noexcept
253 {
254
255 GncSqlColumnInfo info{*this, BCT_INT64, 0, FALSE};
256 vec.emplace_back(std::move(info));
257 }
258
259 template<> void
add_to_query(QofIdTypeConst obj_name,const gpointer pObject,PairVec & vec) const260 GncSqlColumnTableEntryImpl<CT_INT64>::add_to_query(QofIdTypeConst obj_name,
261 const gpointer pObject,
262 PairVec& vec) const noexcept
263 {
264 add_value_to_vec<int64_t>(obj_name, pObject, vec);
265 }
266 /* ----------------------------------------------------------------- */
267
268 template<> void
load(const GncSqlBackend * sql_be,GncSqlRow & row,QofIdTypeConst obj_name,gpointer pObject) const269 GncSqlColumnTableEntryImpl<CT_DOUBLE>::load (const GncSqlBackend* sql_be,
270 GncSqlRow& row,
271 QofIdTypeConst obj_name,
272 gpointer pObject)
273 const noexcept
274 {
275 g_return_if_fail (pObject != NULL);
276 g_return_if_fail (m_gobj_param_name != nullptr || get_setter(obj_name) != nullptr);
277 double val;
278 try
279 {
280 val = static_cast<double>(row.get_int_at_col(m_col_name));
281 }
282 catch (std::invalid_argument&)
283 {
284 try
285 {
286 val = row.get_float_at_col(m_col_name);
287 }
288 catch (std::invalid_argument&)
289 {
290 try
291 {
292 val = row.get_double_at_col(m_col_name);
293 }
294 catch (std::invalid_argument&)
295 {
296 val = 0.0;
297 }
298 }
299 }
300 set_parameter(pObject, val, get_setter(obj_name), m_gobj_param_name);
301 }
302
303 template<> void
add_to_table(ColVec & vec) const304 GncSqlColumnTableEntryImpl<CT_DOUBLE>::add_to_table(ColVec& vec) const noexcept
305 {
306 GncSqlColumnInfo info{*this, BCT_DOUBLE, 0, FALSE};
307 vec.emplace_back(std::move(info));
308 }
309
310 template<> void
add_to_query(QofIdTypeConst obj_name,const gpointer pObject,PairVec & vec) const311 GncSqlColumnTableEntryImpl<CT_DOUBLE>::add_to_query(QofIdTypeConst obj_name,
312 const gpointer pObject,
313 PairVec& vec) const noexcept
314 {
315 add_value_to_vec<double*>(obj_name, pObject, vec);
316 }
317
318 /* ----------------------------------------------------------------- */
319
320 template<> void
load(const GncSqlBackend * sql_be,GncSqlRow & row,QofIdTypeConst obj_name,gpointer pObject) const321 GncSqlColumnTableEntryImpl<CT_GUID>::load (const GncSqlBackend* sql_be,
322 GncSqlRow& row,
323 QofIdTypeConst obj_name,
324 gpointer pObject)
325 const noexcept
326 {
327
328 GncGUID guid;
329 const GncGUID* pGuid;
330
331 g_return_if_fail (pObject != NULL);
332 g_return_if_fail (m_gobj_param_name != nullptr || get_setter(obj_name) != nullptr);
333
334 std::string str;
335 try
336 {
337 str = row.get_string_at_col(m_col_name);
338 }
339 catch (std::invalid_argument&)
340 {
341 return;
342 }
343 if (string_to_guid (str.c_str(), &guid))
344 set_parameter(pObject, &guid, get_setter(obj_name), m_gobj_param_name);
345 }
346
347 template<> void
add_to_table(ColVec & vec) const348 GncSqlColumnTableEntryImpl<CT_GUID>::add_to_table(ColVec& vec) const noexcept
349 {
350 GncSqlColumnInfo info{*this, BCT_STRING, GUID_ENCODING_LENGTH, FALSE};
351 vec.emplace_back(std::move(info));
352 }
353
354 template<> void
add_to_query(QofIdTypeConst obj_name,const gpointer pObject,PairVec & vec) const355 GncSqlColumnTableEntryImpl<CT_GUID>::add_to_query(QofIdTypeConst obj_name,
356 const gpointer pObject,
357 PairVec& vec) const noexcept
358 {
359 auto s = get_row_value_from_object<GncGUID*>(obj_name, pObject);
360
361 if (s != nullptr)
362 {
363
364 vec.emplace_back (std::make_pair (std::string{m_col_name},
365 quote_string(guid_to_string(s))));
366 return;
367 }
368 }
369 /* ----------------------------------------------------------------- */
370 typedef time64 (*Time64AccessFunc) (const gpointer);
371 typedef void (*Time64SetterFunc) (const gpointer, time64);
372 constexpr int TIME_COL_SIZE = 4 + 3 + 3 + 3 + 3 + 3;
373
374 template<> void
load(const GncSqlBackend * sql_be,GncSqlRow & row,QofIdTypeConst obj_name,gpointer pObject) const375 GncSqlColumnTableEntryImpl<CT_TIME>::load (const GncSqlBackend* sql_be,
376 GncSqlRow& row,
377 QofIdTypeConst obj_name,
378 gpointer pObject)
379 const noexcept
380 {
381 time64 t{0};
382 g_return_if_fail (m_gobj_param_name != nullptr || get_setter(obj_name) != nullptr);
383 try
384 {
385 t = row.get_time64_at_col (m_col_name);
386 }
387 catch (std::invalid_argument&)
388 {
389 try
390 {
391 auto val = row.get_string_at_col(m_col_name);
392 GncDateTime time(val);
393 t = static_cast<time64>(time);
394 }
395 catch (const std::invalid_argument& err)
396 {
397 if (strcmp(err.what(), "Column empty.") != 0)
398 {
399 auto val = row.get_string_at_col (m_col_name);
400 PWARN("An invalid date %s was found in your database."
401 "It has been set to 1 January 1970.", val.c_str());
402 }
403 }
404 }
405 if (m_gobj_param_name != nullptr)
406 {
407 Time64 t64{t};
408 set_parameter(pObject, &t64, m_gobj_param_name);
409 }
410 else
411 {
412 set_parameter(pObject, t,
413 reinterpret_cast<Time64SetterFunc>(get_setter(obj_name)),
414 nullptr);
415 }
416 }
417
418 template<> void
add_to_table(ColVec & vec) const419 GncSqlColumnTableEntryImpl<CT_TIME>::add_to_table(ColVec& vec) const noexcept
420 {
421
422 GncSqlColumnInfo info{*this, BCT_DATETIME, TIME_COL_SIZE, FALSE};
423 vec.emplace_back(std::move(info));
424 }
425
426 template<> void
add_to_query(QofIdTypeConst obj_name,const gpointer pObject,PairVec & vec) const427 GncSqlColumnTableEntryImpl<CT_TIME>::add_to_query(QofIdTypeConst obj_name,
428 const gpointer pObject,
429 PairVec& vec) const noexcept
430 {
431 /* We still can't use get_row_value_from_object because while g_value could
432 * contentedly store a time64 in an int64, KVP wouldn't be able to tell them
433 * apart, so we have the struct Time64 hack, see engine/gnc-date.c.
434 */
435 time64 t64;
436 if (m_gobj_param_name != nullptr)
437 {
438 Time64* t;
439 g_object_get (pObject, m_gobj_param_name, &t, nullptr);
440 t64 = t->t;
441 }
442 else
443 {
444 auto getter = (Time64AccessFunc)get_getter (obj_name);
445 g_return_if_fail(getter != nullptr);
446 t64 = (*getter)(pObject);
447 }
448 if (t64 > MINTIME && t64 < MAXTIME)
449 {
450 GncDateTime time(t64);
451 std::string timestr("'");
452 timestr += time.format_iso8601() + "'";
453 vec.emplace_back (std::make_pair (std::string{m_col_name}, timestr));
454 }
455 else
456 {
457 vec.emplace_back (std::make_pair (std::string{m_col_name},
458 "NULL"));
459 }
460 }
461
462 /* ----------------------------------------------------------------- */
463 #define DATE_COL_SIZE 8
464
465 template<> void
load(const GncSqlBackend * sql_be,GncSqlRow & row,QofIdTypeConst obj_name,gpointer pObject) const466 GncSqlColumnTableEntryImpl<CT_GDATE>::load (const GncSqlBackend* sql_be,
467 GncSqlRow& row,
468 QofIdTypeConst obj_name,
469 gpointer pObject) const noexcept
470 {
471 g_return_if_fail (pObject != NULL);
472 g_return_if_fail (m_gobj_param_name != nullptr || get_setter(obj_name) != nullptr);
473 if (row.is_col_null(m_col_name))
474 return;
475 GDate date;
476 g_date_clear (&date, 1);
477 try
478 {
479 /* time64_to_gdate applies the tz, and gdates are saved
480 * as ymd, so we don't want that.
481 */
482 auto time = row.get_time64_at_col(m_col_name);
483 auto tm = gnc_gmtime(&time);
484 g_date_set_dmy(&date, tm->tm_mday,
485 static_cast<GDateMonth>(tm->tm_mon + 1),
486 tm->tm_year + 1900);
487 free(tm);
488 }
489 catch (std::invalid_argument&)
490 {
491 try
492 {
493 std::string str = row.get_string_at_col(m_col_name);
494 if (str.empty()) return;
495 auto year = static_cast<GDateYear>(stoi (str.substr (0,4)));
496 auto month = static_cast<GDateMonth>(stoi (str.substr (4,2)));
497 auto day = static_cast<GDateDay>(stoi (str.substr (6,2)));
498
499 if (year != 0 || month != 0 || day != (GDateDay)0)
500 g_date_set_dmy(&date, day, month, year);
501
502 }
503 catch (std::invalid_argument&)
504 {
505 return;
506 }
507 }
508 set_parameter(pObject, &date, get_setter(obj_name), m_gobj_param_name);
509 }
510
511 template<> void
add_to_table(ColVec & vec) const512 GncSqlColumnTableEntryImpl<CT_GDATE>::add_to_table(ColVec& vec) const noexcept
513 {
514 GncSqlColumnInfo info{*this, BCT_DATE, DATE_COL_SIZE, FALSE};
515 vec.emplace_back(std::move(info));
516 }
517
518 template<> void
add_to_query(QofIdTypeConst obj_name,const gpointer pObject,PairVec & vec) const519 GncSqlColumnTableEntryImpl<CT_GDATE>::add_to_query(QofIdTypeConst obj_name,
520 const gpointer pObject,
521 PairVec& vec) const noexcept
522 {
523 GDate *date = get_row_value_from_object<GDate*>(obj_name, pObject);
524
525 if (date && g_date_valid (date))
526 {
527 std::ostringstream buf;
528 buf << std::setfill ('0') << std::setw (4) << g_date_get_year (date) <<
529 std::setw (2) << g_date_get_month (date) <<
530 std::setw (2) << static_cast<int>(g_date_get_day (date));
531 vec.emplace_back (std::make_pair (std::string{m_col_name},
532 quote_string(buf.str())));
533 return;
534 }
535 }
536
537 /* ----------------------------------------------------------------- */
538 typedef gnc_numeric (*NumericGetterFunc) (const gpointer);
539 typedef void (*NumericSetterFunc) (gpointer, gnc_numeric);
540
541 static const EntryVec numeric_col_table =
542 {
543 gnc_sql_make_table_entry<CT_INT64>("num", 0, COL_NNUL, "guid"),
544 gnc_sql_make_table_entry<CT_INT64>("denom", 0, COL_NNUL, "guid")
545 };
546
547 template <>
set_parameter(gpointer object,gnc_numeric item,const char * property)548 void set_parameter<gpointer, gnc_numeric>(gpointer object,
549 gnc_numeric item,
550 const char* property)
551 {
552 qof_instance_increase_editlevel(object);
553 g_object_set(object, property, &item, nullptr);
554 qof_instance_decrease_editlevel(object);
555 };
556
557 template<> void
load(const GncSqlBackend * sql_be,GncSqlRow & row,QofIdTypeConst obj_name,gpointer pObject) const558 GncSqlColumnTableEntryImpl<CT_NUMERIC>::load (const GncSqlBackend* sql_be,
559 GncSqlRow& row,
560 QofIdTypeConst obj_name,
561 gpointer pObject) const noexcept
562 {
563
564
565 g_return_if_fail (pObject != NULL);
566 g_return_if_fail (m_gobj_param_name != nullptr || get_setter(obj_name) != nullptr);
567 gnc_numeric n;
568 try
569 {
570 auto buf = g_strdup_printf ("%s_num", m_col_name);
571 auto num = row.get_int_at_col (buf);
572 g_free (buf);
573 buf = g_strdup_printf ("%s_denom", m_col_name);
574 auto denom = row.get_int_at_col (buf);
575 n = gnc_numeric_create (num, denom);
576 g_free (buf);
577 }
578 catch (std::invalid_argument&)
579 {
580 return;
581 }
582 set_parameter(pObject, n,
583 reinterpret_cast<NumericSetterFunc>(get_setter(obj_name)),
584 m_gobj_param_name);
585 }
586
587 template<> void
add_to_table(ColVec & vec) const588 GncSqlColumnTableEntryImpl<CT_NUMERIC>::add_to_table(ColVec& vec) const noexcept
589 {
590
591 for (auto const& subtable_row : numeric_col_table)
592 {
593 gchar* buf = g_strdup_printf("%s_%s", m_col_name,
594 subtable_row->m_col_name);
595 GncSqlColumnInfo info(buf, BCT_INT64, 0, false, false,
596 m_flags & COL_PKEY, m_flags & COL_NNUL);
597 g_free (buf);
598 vec.emplace_back(std::move(info));
599 }
600 }
601
602 template<> void
add_to_query(QofIdTypeConst obj_name,const gpointer pObject,PairVec & vec) const603 GncSqlColumnTableEntryImpl<CT_NUMERIC>::add_to_query(QofIdTypeConst obj_name,
604 const gpointer pObject,
605 PairVec& vec) const noexcept
606 {
607 /* We can't use get_row_value_from_object for the same reason as time64. */
608 NumericGetterFunc getter;
609 gnc_numeric n;
610
611 g_return_if_fail (obj_name != NULL);
612 g_return_if_fail (pObject != NULL);
613
614 if (m_gobj_param_name != nullptr)
615 {
616 gnc_numeric* s;
617 g_object_get (pObject, m_gobj_param_name, &s, NULL);
618 n = *s;
619 }
620 else
621 {
622 getter = reinterpret_cast<NumericGetterFunc>(get_getter (obj_name));
623 if (getter != NULL)
624 {
625 n = (*getter) (pObject);
626 }
627 else
628 {
629 n = gnc_numeric_zero ();
630 }
631 }
632
633 std::ostringstream buf;
634 std::string num_col{m_col_name};
635 std::string denom_col{m_col_name};
636 num_col += "_num";
637 denom_col += "_denom";
638 buf << gnc_numeric_num (n);
639 vec.emplace_back (std::make_pair (num_col, buf.str ()));
640 buf.str ("");
641 buf << gnc_numeric_denom (n);
642 vec.emplace_back (denom_col, buf.str ());
643 }
644
645 static void
_retrieve_guid_(gpointer pObject,gpointer pValue)646 _retrieve_guid_ (gpointer pObject, gpointer pValue)
647 {
648 GncGUID* pGuid = (GncGUID*)pObject;
649 GncGUID* guid = (GncGUID*)pValue;
650
651 g_return_if_fail (pObject != NULL);
652 g_return_if_fail (pValue != NULL);
653
654 memcpy (pGuid, guid, sizeof (GncGUID));
655 }
656
657 // Table to retrieve just the guid
658 static EntryVec guid_table
659 {
660 gnc_sql_make_table_entry<CT_GUID>("guid", 0, 0, nullptr, _retrieve_guid_)
661 };
662
663 const GncGUID*
gnc_sql_load_guid(const GncSqlBackend * sql_be,GncSqlRow & row)664 gnc_sql_load_guid (const GncSqlBackend* sql_be, GncSqlRow& row)
665 {
666 static GncGUID guid;
667
668 g_return_val_if_fail (sql_be != NULL, NULL);
669
670 gnc_sql_load_object (sql_be, row, NULL, &guid, guid_table);
671
672 return &guid;
673 }
674
675 void
gnc_sql_load_object(const GncSqlBackend * sql_be,GncSqlRow & row,QofIdTypeConst obj_name,gpointer pObject,const EntryVec & table)676 gnc_sql_load_object (const GncSqlBackend* sql_be, GncSqlRow& row,
677 QofIdTypeConst obj_name, gpointer pObject,
678 const EntryVec& table)
679 {
680 QofSetterFunc setter;
681
682 g_return_if_fail (sql_be != NULL);
683 g_return_if_fail (pObject != NULL);
684
685 for (auto const& table_row : table)
686 {
687 table_row->load (sql_be, row, obj_name, pObject);
688 }
689 }
690
691 uint_t
gnc_sql_append_guids_to_sql(std::stringstream & sql,const InstanceVec & instances)692 gnc_sql_append_guids_to_sql (std::stringstream& sql,
693 const InstanceVec& instances)
694 {
695 char guid_buf[GUID_ENCODING_LENGTH + 1];
696
697 for (auto inst : instances)
698 {
699 (void)guid_to_string_buff (qof_instance_get_guid (inst), guid_buf);
700
701 if (inst != *(instances.begin()))
702 {
703 sql << ",";
704 }
705 sql << "'" << guid_buf << "'";
706 }
707
708 return instances.size();
709 }
710
711 /* This is necessary for 64-bit builds because g++ complains
712 * that reinterpret_casting a void* (64 bits) to an int (32 bits)
713 * loses precision, so we have to explicitly dispose of the precision.
714 * FIXME: We shouldn't be storing ints in ptrs in the first place.
715 */
716 #ifdef __LP64__
717 template <> int
get_row_value_from_object(QofIdTypeConst obj_name,const void * pObject,std::false_type) const718 GncSqlColumnTableEntry::get_row_value_from_object<int>(QofIdTypeConst obj_name,
719 const void* pObject,
720 std::false_type) const
721 {
722 g_return_val_if_fail(obj_name != nullptr && pObject != nullptr, 0);
723 int result = 0;
724 if (m_gobj_param_name != nullptr)
725 g_object_get(const_cast<void*>(pObject), m_gobj_param_name, &result,
726 nullptr);
727 else
728 {
729 QofAccessFunc getter = get_getter(obj_name);
730 if (getter != nullptr)
731 {
732 auto value = ((getter)(const_cast<void*>(pObject), nullptr));
733 result = reinterpret_cast<uint64_t>(value) &
734 UINT64_C(0x00000000FFFFFFFF);
735 }
736 }
737 return result;
738 }
739 #endif
740