GnuCash c935c2f+
Loading...
Searching...
No Matches
gnc-sql-column-table-entry.hpp
1/***********************************************************************\
2 * gnc-sql-column-table-entry.hpp: Column Specification for SQL Table. *
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#ifndef __GNC_SQL_COLUMN_TABLE_ENTRY_HPP__
25#define __GNC_SQL_COLUMN_TABLE_ENTRY_HPP__
26
27#include <qof.h>
28#include <memory>
29#include <vector>
30#include <iostream>
31#include <iomanip>
32
33#include "gnc-sql-result.hpp"
34
35struct GncSqlColumnInfo;
36using ColVec = std::vector<GncSqlColumnInfo>;
37using PairVec = std::vector<std::pair<std::string, std::string>>;
38using InstanceVec = std::vector<QofInstance*>;
39using uint_t = unsigned int;
40class GncSqlBackend;
41
45typedef enum
46{
47 BCT_STRING,
48 BCT_INT,
49 BCT_INT64,
50 BCT_DATE,
51 BCT_DOUBLE,
52 BCT_DATETIME
53} GncSqlBasicColumnType;
54
55enum ColumnFlags : int
56{
57 COL_NO_FLAG = 0,
58 COL_PKEY = 0x01,
59 COL_NNUL = 0x02,
60 COL_UNIQUE = 0x04,
61 COL_AUTOINC = 0x08
62};
63
64// Type for conversion of db row to object.
65enum GncSqlObjectType
66{
67 CT_STRING,
68 CT_GUID,
69 CT_INT,
70 CT_INT64,
71 CT_TIME,
72 CT_GDATE,
73 CT_NUMERIC,
74 CT_DOUBLE,
75 CT_BOOLEAN,
76 CT_ACCOUNTREF,
77 CT_BUDGETREF,
78 CT_COMMODITYREF,
79 CT_LOTREF,
80 CT_TXREF,
81 CT_ADDRESS,
82 CT_BILLTERMREF,
83 CT_INVOICEREF,
84 CT_ORDERREF,
85 CT_OWNERREF,
86 CT_TAXTABLEREF
87};
88
89static inline std::string
90quote_string(const std::string& str)
91{
92 if (str == "NULL" || str == "null") return "NULL";
93 /* FIXME: This is here because transactions.num has a NULL
94 * constraint, which is dumb; it's often empty.
95 */
96 if (str.empty()) return "''";
97 std::string retval;
98 retval.reserve(str.length() + 2);
99 retval.insert(0, 1, '\'');
100 for (auto c = str.begin(); c != str.end(); ++c)
101 {
102 if (*c == '\'')
103 retval += *c;
104 retval += *c;
105 }
106 retval += '\'';
107 return retval;
108}
109
127{
128public:
129 GncSqlColumnTableEntry (const char* name, const GncSqlObjectType type,
130 unsigned int s,
131 int f, const char* gobj_name = nullptr,
132 const char* qof_name = nullptr,
133 QofAccessFunc get = nullptr,
134 QofSetterFunc set = nullptr) :
135 m_col_name{name}, m_col_type{type}, m_size{s},
136 m_flags{static_cast<ColumnFlags>(f)},
137 m_gobj_param_name{gobj_name}, m_qof_param_name{qof_name}, m_getter{get},
138 m_setter{set} {}
139 virtual ~GncSqlColumnTableEntry() = default;
140
144 virtual void load(const GncSqlBackend* sql_be, GncSqlRow& row,
145 QofIdTypeConst obj_name, void* pObject) const noexcept = 0;
150 virtual void add_to_table(ColVec& vec) const noexcept = 0;
156 virtual void add_to_query(QofIdTypeConst obj_name,
157 void* pObject, PairVec& vec) const noexcept = 0;
162 QofAccessFunc get_getter(QofIdTypeConst obj_name) const noexcept;
167 QofSetterFunc get_setter(QofIdTypeConst obj_name) const noexcept;
172 const char* name() const noexcept { return m_col_name; }
176 bool is_autoincr() const noexcept { return m_flags & COL_AUTOINC; }
177 /* On the other hand, our implementation class and GncSqlColumnInfo need to
178 * be able to read our member variables.
179 */
180 template<GncSqlObjectType Otype> friend class GncSqlColumnTableEntryImpl;
181 friend struct GncSqlColumnInfo;
182 template<typename T> void load_from_guid_ref(GncSqlRow& row,
183 QofIdTypeConst obj_name,
184 void* pObject, T get_ref)
185 const noexcept
186 {
187 static QofLogModule log_module = G_LOG_DOMAIN;
188 g_return_if_fail (pObject != NULL);
189
190 GncGUID guid;
191 auto val = row.get_string_at_col (m_col_name);
192 if (!val)
193 {
194 DEBUG("set parameter: No string in column %s.", m_col_name);
195 return;
196 }
197
198 if (string_to_guid (val->c_str(), &guid))
199 {
200 auto target = get_ref(&guid);
201 if (target != nullptr)
202 set_parameter (pObject, target, get_setter(obj_name),
203 m_gobj_param_name);
204 else
205 DEBUG("GUID %s returned null %s reference.",
206 val->c_str(), m_gobj_param_name);
207 }
208 else
209 {
210 if (val->empty())
211 DEBUG("Can't load empty guid string for column %s", m_col_name);
212 else
213 DEBUG("Invalid GUID %s for column %s", val->c_str(), m_col_name);
214 }
215 }
216
217
218protected:
219 template <typename T> T
220 get_row_value_from_object(QofIdTypeConst obj_name, const void* pObject) const;
221 template <typename T> void
222 add_value_to_vec(QofIdTypeConst obj_name,
223 const void* pObject, PairVec& vec) const;
233 const void* pObject,
234 PairVec& vec) const noexcept;
241 void add_objectref_guid_to_table (ColVec& vec) const noexcept;
242private:
243 const char* m_col_name = nullptr;
244 const GncSqlObjectType m_col_type;
245 unsigned int m_size;
246 ColumnFlags m_flags;
247 const char* m_gobj_param_name = nullptr;
248 const char* m_qof_param_name = nullptr;
249 QofAccessFunc m_getter;
250 QofSetterFunc m_setter;
251 template <typename T> T get_row_value_from_object(QofIdTypeConst obj_name,
252 const void* pObject,
253 std::true_type) const;
254 template <typename T> T get_row_value_from_object(QofIdTypeConst obj_name,
255 const void* pObject,
256 std::false_type) const;
257 template <typename T> void add_value_to_vec(QofIdTypeConst obj_name,
258 const void* pObject,
259 PairVec& vec, std::true_type) const;
260 template <typename T> void add_value_to_vec(QofIdTypeConst obj_name,
261 const void* pObject,
262 PairVec& vec, std::false_type) const;
263
264};
265
266template <GncSqlObjectType Type>
268{
269public:
270 GncSqlColumnTableEntryImpl (const char* name, const GncSqlObjectType type,
271 unsigned int s,
272 int f, const char* gobj_name = nullptr,
273 const char* qof_name = nullptr,
274 QofAccessFunc get = nullptr,
275 QofSetterFunc set = nullptr) :
276 GncSqlColumnTableEntry (name, type, s, f, gobj_name,qof_name, get, set)
277 {}
278
279 void load(const GncSqlBackend* sql_be, GncSqlRow& row, QofIdTypeConst obj_name,
280 void* pObject) const noexcept override;
281 void add_to_table(ColVec& vec) const noexcept override;
282 void add_to_query(QofIdTypeConst obj_name, void* pObject, PairVec& vec)
283 const noexcept override;
284};
285
286using GncSqlColumnTableEntryPtr = std::shared_ptr<GncSqlColumnTableEntry>;
287using EntryVec = std::vector<GncSqlColumnTableEntryPtr>;
288
289template <GncSqlObjectType Type>
290std::shared_ptr<GncSqlColumnTableEntryImpl<Type>>
291gnc_sql_make_table_entry(const char* name, unsigned int s, int f)
292{
293 return std::make_shared<GncSqlColumnTableEntryImpl<Type>>(name, Type, s, f);
294}
295
296template <GncSqlObjectType Type>
297std::shared_ptr<GncSqlColumnTableEntryImpl<Type>>
298gnc_sql_make_table_entry(const char* name, unsigned int s, int f,
299 const char* param)
300{
301 return std::make_shared<GncSqlColumnTableEntryImpl<Type>>(name, Type, s,
302 f, param);
303}
304
305class is_qof : public std::true_type {};
306
307template <GncSqlObjectType Type>
308std::shared_ptr<GncSqlColumnTableEntryImpl<Type>>
309gnc_sql_make_table_entry(const char* name, unsigned int s, int f,
310 const char* param, bool qofp)
311{
312 return std::make_shared<GncSqlColumnTableEntryImpl<Type>>(name, Type, s,
313 f, nullptr,
314 param);
315}
316
317template <GncSqlObjectType Type>
318std::shared_ptr<GncSqlColumnTableEntryImpl<Type>>
319gnc_sql_make_table_entry(const char* name, unsigned int s, int f,
321{
322 return std::make_shared<GncSqlColumnTableEntryImpl<Type>>(
323 name, Type, s, f, nullptr, nullptr, get, set);
324}
325
326
327template <typename T> T
328GncSqlColumnTableEntry::get_row_value_from_object(QofIdTypeConst obj_name,
329 const void* pObject) const
330{
331 return get_row_value_from_object<T>(obj_name, pObject,
332 std::is_pointer<T>());
333}
334
335template <typename T> T
336GncSqlColumnTableEntry::get_row_value_from_object(QofIdTypeConst obj_name,
337 const void* pObject,
338 std::true_type) const
339{
340 g_return_val_if_fail(obj_name != nullptr && pObject != nullptr, nullptr);
341 T result = nullptr;
342 if (m_gobj_param_name != nullptr)
343 g_object_get(const_cast<void*>(pObject), m_gobj_param_name,
344 &result, nullptr);
345 else
346 {
347 QofAccessFunc getter = get_getter(obj_name);
348 if (getter != nullptr)
349 result = reinterpret_cast<T>((getter)(const_cast<void*>(pObject),
350 nullptr));
351 }
352 return result;
353}
354
355template <typename T> T
356GncSqlColumnTableEntry::get_row_value_from_object(QofIdTypeConst obj_name,
357 const void* pObject,
358 std::false_type) const
359{
360 g_return_val_if_fail(obj_name != nullptr && pObject != nullptr,
361 static_cast<T>(0));
362 T result = static_cast<T>(0);
363 if (m_gobj_param_name != nullptr)
364 g_object_get(const_cast<void*>(pObject), m_gobj_param_name,
365 &result, nullptr);
366 else
367 {
368 QofAccessFunc getter = get_getter(obj_name);
369 if (getter != nullptr)
370 {
371 result = static_cast<T>(reinterpret_cast<intptr_t>((getter)(const_cast<void*>(pObject),
372 nullptr)));
373 }
374 }
375 return result;
376}
377
378template <typename T> void
379GncSqlColumnTableEntry::add_value_to_vec(QofIdTypeConst obj_name,
380 const void* pObject,
381 PairVec& vec) const
382{
383 add_value_to_vec<T>(obj_name, pObject, vec, std::is_pointer<T>());
384}
385
386template <typename T> void
387GncSqlColumnTableEntry::add_value_to_vec(QofIdTypeConst obj_name,
388 const void* pObject,
389 PairVec& vec, std::true_type) const
390{
391 T s = get_row_value_from_object<T>(obj_name, pObject);
392
393 if (s != nullptr)
394 {
395 std::ostringstream stream;
396 stream << *s;
397 vec.emplace_back(std::make_pair(std::string{m_col_name}, stream.str()));
398 return;
399 }
400}
401
402template <> inline void
403GncSqlColumnTableEntry::add_value_to_vec<double*>(QofIdTypeConst obj_name,
404 const void* pObject,
405 PairVec& vec, std::true_type) const
406{
407 double* s = get_row_value_from_object<double*>(obj_name, pObject);
408
409 if (s != nullptr)
410 {
411 std::ostringstream stream;
412 stream << std::setprecision(12) << std::fixed << *s;
413 vec.emplace_back(std::make_pair(std::string{m_col_name}, stream.str()));
414 return;
415 }
416}
417
418template <typename T> void
419GncSqlColumnTableEntry::add_value_to_vec(QofIdTypeConst obj_name,
420 const void* pObject,
421 PairVec& vec, std::false_type) const
422{
423 T s = get_row_value_from_object<T>(obj_name, pObject);
424
425 std::ostringstream stream;
426 stream << s;
427 vec.emplace_back(std::make_pair(std::string{m_col_name}, stream.str()));
428 return;
429}
430
431template <> inline void
432GncSqlColumnTableEntry::add_value_to_vec<double>(QofIdTypeConst obj_name,
433 const void* pObject,
434 PairVec& vec, std::false_type) const
435{
436 double s = *get_row_value_from_object<double*>(obj_name, pObject);
437
438 std::ostringstream stream;
439 stream << std::setprecision(12) << std::fixed << s;
440 vec.emplace_back(std::make_pair(std::string{m_col_name}, stream.str()));
441 return;
442}
443
453void gnc_sql_load_object (const GncSqlBackend* sql_be, GncSqlRow& row,
454 QofIdTypeConst obj_name, gpointer pObject,
455 const EntryVec& table);
462const GncGUID*
463gnc_sql_load_guid (const GncSqlBackend* sql_be, GncSqlRow& row);
464
472uint_t gnc_sql_append_guids_to_sql (std::stringstream& sql,
473 const InstanceVec& instances);
474
479{
480 GncSqlColumnInfo (std::string&& name, GncSqlBasicColumnType type,
481 unsigned int size = 0, bool unicode = false,
482 bool autoinc = false, bool primary = false,
483 bool not_null = false) :
484 m_name{name}, m_type{type}, m_size{size}, m_unicode{unicode},
485 m_autoinc{autoinc}, m_primary_key{primary}, m_not_null{not_null}
486 {}
487 GncSqlColumnInfo(const GncSqlColumnTableEntry& e, GncSqlBasicColumnType t,
488 unsigned int size = 0, bool unicode = true) :
489 m_name{e.m_col_name}, m_type{t}, m_size{size}, m_unicode{unicode},
490 m_autoinc(e.m_flags & COL_AUTOINC),
491 m_primary_key(e.m_flags & COL_PKEY),
492 m_not_null(e.m_flags & COL_NNUL) {}
493 std::string m_name;
494 GncSqlBasicColumnType m_type;
495 unsigned int m_size;
500};
501
502inline bool operator==(const GncSqlColumnInfo& l,
503 const GncSqlColumnInfo& r)
504{
505 return l.m_name == r.m_name && l.m_type == r.m_type;
506}
507
508inline bool operator!=(const GncSqlColumnInfo& l,
509 const GncSqlColumnInfo& r)
510{
511 return !(l == r);
512}
513
523template <typename T, typename P, typename F>
524void set_parameter(T object, P item, F& setter)
525{
526 (*setter)(object, item);
527}
528
529template <typename T, typename P>
530void set_parameter(T object, P item, QofSetterFunc setter, std::true_type)
531{
532 (*setter)(object, (void*)item);
533}
534
535template <typename T, typename P>
536void set_parameter(T object, P item, QofSetterFunc setter, std::false_type)
537{
538 (*setter)(object, (void*)(&item));
539}
540
541template <typename T, typename P>
542void set_parameter(T object, P item, QofSetterFunc setter)
543{
544 set_parameter(object, item, setter, std::is_pointer<P>());
545}
546
555template <typename T, typename P>
556void set_parameter(T object, P item, const char* property)
557{
558 // Properly use qof_begin_edit and qof_commit_edit{_part2}
559 // here. This is needed to reset the infant state of objects
560 // when loading them initially from sql. Failing to do so
561 // could prevent future editing of these objects
562 // Example of this is https://bugs.gnucash.org/show_bug.cgi?id=795944
563 qof_begin_edit(QOF_INSTANCE(object));
564 g_object_set(object, property, item, nullptr);
565 if (!qof_commit_edit(QOF_INSTANCE(object))) return;
566 // FIXME I can't use object specific callbacks in generic code
567 // so for now these will silently fail. As the GObject based method
568 // of setting qof objects should go away eventually I won't bother
569 // finding a proper solution for this.
570 qof_commit_edit_part2(QOF_INSTANCE(object), nullptr, nullptr, nullptr);
571};
572
579template <typename T, typename P, typename F>
580void set_parameter(T object, P item, F setter, const char* property)
581{
582 if (property)
583 set_parameter(object, item, property);
584 else
585 set_parameter(object, item, setter);
586}
587
588#endif //__GNC_SQL_COLUMN_TABLE_ENTRY_HPP__
Main SQL backend structure.
void add_to_query(QofIdTypeConst obj_name, void *pObject, PairVec &vec) const noexcept override
Add a pair of the table column heading and object's value's string representation to a PairVec; used ...
void add_to_table(ColVec &vec) const noexcept override
Add a GncSqlColumnInfo structure for the column type to a ColVec.
void load(const GncSqlBackend *sql_be, GncSqlRow &row, QofIdTypeConst obj_name, void *pObject) const noexcept override
Load a value into an object from the database row.
Contains all of the information required to copy information between an object and the database for a...
bool is_autoincr() const noexcept
Report if the entry is an auto-increment field.
QofSetterFunc get_setter(QofIdTypeConst obj_name) const noexcept
Retrieve the setter function depending on whether it's an auto-increment field, a QofClass getter,...
void add_objectref_guid_to_query(QofIdTypeConst obj_name, const void *pObject, PairVec &vec) const noexcept
Adds a name/guid std::pair to a PairVec for creating a query.
virtual void add_to_table(ColVec &vec) const noexcept=0
Add a GncSqlColumnInfo structure for the column type to a ColVec.
virtual void add_to_query(QofIdTypeConst obj_name, void *pObject, PairVec &vec) const noexcept=0
Add a pair of the table column heading and object's value's string representation to a PairVec; used ...
const char * name() const noexcept
Retrieve the field name so that we don't need to make create_single_col_select_statement and friend.
virtual void load(const GncSqlBackend *sql_be, GncSqlRow &row, QofIdTypeConst obj_name, void *pObject) const noexcept=0
Load a value into an object from the database row.
void add_objectref_guid_to_table(ColVec &vec) const noexcept
Adds a column info structure for an object reference GncGUID to a ColVec.
QofAccessFunc get_getter(QofIdTypeConst obj_name) const noexcept
Retrieve the getter function depending on whether it's an auto-increment field, a QofClass getter,...
Row of SQL Query results.
gpointer(* QofAccessFunc)(gpointer object, const QofParam *param)
The QofAccessFunc defines an arbitrary function pointer for access functions.
Definition qofclass.h:123
void(* QofSetterFunc)(gpointer, gpointer)
The QofSetterFunc defines an function pointer for parameter setters.
Definition qofclass.h:130
const gchar * QofIdTypeConst
QofIdTypeConst declaration.
Definition qofid.h:82
#define DEBUG(format, args...)
Print a debugging message.
Definition qoflog.h:264
gboolean qof_commit_edit_part2(QofInstance *inst, void(*on_error)(QofInstance *, QofBackendError), void(*on_done)(QofInstance *), void(*on_free)(QofInstance *))
part2 – deal with the backend
gboolean qof_commit_edit(QofInstance *inst)
commit_edit helpers
gboolean qof_begin_edit(QofInstance *inst)
begin_edit
The type used to store guids in C.
Definition guid.h:75
information required to create a column in a table.
GncSqlBasicColumnType m_type
Column basic type.
unsigned int m_size
Column size (string types)
bool m_not_null
Column forbids NULL values.
bool m_unicode
Column is unicode (string types)
bool m_autoinc
Column is autoinc (int type)
bool m_primary_key
Column is the primary key.
std::string m_name
Column name.