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