GnuCash  4.12-404-geb24099a91
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 extern "C"
28 {
29 #include <qof.h>
30 }
31 #include <memory>
32 #include <vector>
33 #include <iostream>
34 #include <iomanip>
35 
36 #include "gnc-sql-result.hpp"
37 
38 struct GncSqlColumnInfo;
39 using ColVec = std::vector<GncSqlColumnInfo>;
40 using PairVec = std::vector<std::pair<std::string, std::string>>;
41 using InstanceVec = std::vector<QofInstance*>;
42 using uint_t = unsigned int;
43 class GncSqlBackend;
44 
48 typedef enum
49 {
50  BCT_STRING,
51  BCT_INT,
52  BCT_INT64,
53  BCT_DATE,
54  BCT_DOUBLE,
55  BCT_DATETIME
56 } GncSqlBasicColumnType;
57 
58 enum ColumnFlags : int
59 {
60  COL_NO_FLAG = 0,
61  COL_PKEY = 0x01,
62  COL_NNUL = 0x02,
63  COL_UNIQUE = 0x04,
64  COL_AUTOINC = 0x08
65 };
66 
67 // Type for conversion of db row to object.
68 enum GncSqlObjectType
69 {
70  CT_STRING,
71  CT_GUID,
72  CT_INT,
73  CT_INT64,
74  CT_TIME,
75  CT_GDATE,
76  CT_NUMERIC,
77  CT_DOUBLE,
78  CT_BOOLEAN,
79  CT_ACCOUNTREF,
80  CT_BUDGETREF,
81  CT_COMMODITYREF,
82  CT_LOTREF,
83  CT_TXREF,
84  CT_ADDRESS,
85  CT_BILLTERMREF,
86  CT_INVOICEREF,
87  CT_ORDERREF,
88  CT_OWNERREF,
89  CT_TAXTABLEREF
90 };
91 
92 static inline std::string
93 quote_string(const std::string& str)
94 {
95  if (str == "NULL" || str == "null") return "NULL";
96  /* FIXME: This is here because transactions.num has a NULL
97  * constraint, which is dumb; it's often empty.
98  */
99  if (str.empty()) return "''";
100  std::string retval;
101  retval.reserve(str.length() + 2);
102  retval.insert(0, 1, '\'');
103  for (auto c = str.begin(); c != str.end(); ++c)
104  {
105  if (*c == '\'')
106  retval += *c;
107  retval += *c;
108  }
109  retval += '\'';
110  return retval;
111 }
112 
130 {
131 public:
132  GncSqlColumnTableEntry (const char* name, const GncSqlObjectType type,
133  unsigned int s,
134  int f, const char* gobj_name = nullptr,
135  const char* qof_name = nullptr,
136  QofAccessFunc get = nullptr,
137  QofSetterFunc set = nullptr) :
138  m_col_name{name}, m_col_type{type}, m_size{s},
139  m_flags{static_cast<ColumnFlags>(f)},
140  m_gobj_param_name{gobj_name}, m_qof_param_name{qof_name}, m_getter{get},
141  m_setter{set} {}
142  virtual ~GncSqlColumnTableEntry() = default;
143 
147  virtual void load(const GncSqlBackend* sql_be, GncSqlRow& row,
148  QofIdTypeConst obj_name, void* pObject) const noexcept = 0;
153  virtual void add_to_table(ColVec& vec) const noexcept = 0;
159  virtual void add_to_query(QofIdTypeConst obj_name,
160  void* pObject, PairVec& vec) const noexcept = 0;
165  QofAccessFunc get_getter(QofIdTypeConst obj_name) const noexcept;
170  QofSetterFunc get_setter(QofIdTypeConst obj_name) const noexcept;
175  const char* name() const noexcept { return m_col_name; }
179  bool is_autoincr() const noexcept { return m_flags & COL_AUTOINC; }
180  /* On the other hand, our implementation class and GncSqlColumnInfo need to
181  * be able to read our member variables.
182  */
183  template<GncSqlObjectType Otype> friend class GncSqlColumnTableEntryImpl;
184  friend struct GncSqlColumnInfo;
185  template<typename T> void load_from_guid_ref(GncSqlRow& row,
186  QofIdTypeConst obj_name,
187  void* pObject, T get_ref)
188  const noexcept
189  {
190  g_return_if_fail (pObject != NULL);
191 
192  try
193  {
194  GncGUID guid;
195  auto val = row.get_string_at_col (m_col_name);
196  if (string_to_guid (val.c_str(), &guid))
197  {
198  auto target = get_ref(&guid);
199  if (target != nullptr)
200  set_parameter (pObject, target, get_setter(obj_name),
201  m_gobj_param_name);
202  }
203  }
204  catch (std::invalid_argument&) {}
205  }
206 
207 
208 protected:
209  template <typename T> T
210  get_row_value_from_object(QofIdTypeConst obj_name, const void* pObject) const;
211  template <typename T> void
212  add_value_to_vec(QofIdTypeConst obj_name,
213  const void* pObject, PairVec& vec) const;
223  const void* pObject,
224  PairVec& vec) const noexcept;
231  void add_objectref_guid_to_table (ColVec& vec) const noexcept;
232 private:
233  const char* m_col_name = nullptr;
234  const GncSqlObjectType m_col_type;
235  unsigned int m_size;
236  ColumnFlags m_flags;
237  const char* m_gobj_param_name = nullptr;
238  const char* m_qof_param_name = nullptr;
239  QofAccessFunc m_getter;
240  QofSetterFunc m_setter;
241  template <typename T> T get_row_value_from_object(QofIdTypeConst obj_name,
242  const void* pObject,
243  std::true_type) const;
244  template <typename T> T get_row_value_from_object(QofIdTypeConst obj_name,
245  const void* pObject,
246  std::false_type) const;
247  template <typename T> void add_value_to_vec(QofIdTypeConst obj_name,
248  const void* pObject,
249  PairVec& vec, std::true_type) const;
250  template <typename T> void add_value_to_vec(QofIdTypeConst obj_name,
251  const void* pObject,
252  PairVec& vec, std::false_type) const;
253 
254 };
255 
256 template <GncSqlObjectType Type>
258 {
259 public:
260  GncSqlColumnTableEntryImpl (const char* name, const GncSqlObjectType type,
261  unsigned int s,
262  int f, const char* gobj_name = nullptr,
263  const char* qof_name = nullptr,
264  QofAccessFunc get = nullptr,
265  QofSetterFunc set = nullptr) :
266  GncSqlColumnTableEntry (name, type, s, f, gobj_name,qof_name, get, set)
267  {}
268 
269  void load(const GncSqlBackend* sql_be, GncSqlRow& row, QofIdTypeConst obj_name,
270  void* pObject) const noexcept override;
271  void add_to_table(ColVec& vec) const noexcept override;
272  void add_to_query(QofIdTypeConst obj_name, void* pObject, PairVec& vec)
273  const noexcept override;
274 };
275 
276 using GncSqlColumnTableEntryPtr = std::shared_ptr<GncSqlColumnTableEntry>;
277 using EntryVec = std::vector<GncSqlColumnTableEntryPtr>;
278 
279 template <GncSqlObjectType Type>
280 std::shared_ptr<GncSqlColumnTableEntryImpl<Type>>
281 gnc_sql_make_table_entry(const char* name, unsigned int s, int f)
282 {
283  return std::make_shared<GncSqlColumnTableEntryImpl<Type>>(name, Type, s, f);
284 }
285 
286 template <GncSqlObjectType Type>
287 std::shared_ptr<GncSqlColumnTableEntryImpl<Type>>
288 gnc_sql_make_table_entry(const char* name, unsigned int s, int f,
289  const char* param)
290 {
291  return std::make_shared<GncSqlColumnTableEntryImpl<Type>>(name, Type, s,
292  f, param);
293 }
294 
295 class is_qof : public std::true_type {};
296 
297 template <GncSqlObjectType Type>
298 std::shared_ptr<GncSqlColumnTableEntryImpl<Type>>
299 gnc_sql_make_table_entry(const char* name, unsigned int s, int f,
300  const char* param, bool qofp)
301 {
302  return std::make_shared<GncSqlColumnTableEntryImpl<Type>>(name, Type, s,
303  f, nullptr,
304  param);
305 }
306 
307 template <GncSqlObjectType Type>
308 std::shared_ptr<GncSqlColumnTableEntryImpl<Type>>
309 gnc_sql_make_table_entry(const char* name, unsigned int s, int f,
310  QofAccessFunc get, QofSetterFunc set)
311 {
312  return std::make_shared<GncSqlColumnTableEntryImpl<Type>>(
313  name, Type, s, f, nullptr, nullptr, get, set);
314 }
315 
316 
317 template <typename T> T
318 GncSqlColumnTableEntry::get_row_value_from_object(QofIdTypeConst obj_name,
319  const void* pObject) const
320 {
321  return get_row_value_from_object<T>(obj_name, pObject,
322  std::is_pointer<T>());
323 }
324 
325 template <typename T> T
326 GncSqlColumnTableEntry::get_row_value_from_object(QofIdTypeConst obj_name,
327  const void* pObject,
328  std::true_type) const
329 {
330  g_return_val_if_fail(obj_name != nullptr && pObject != nullptr, nullptr);
331  T result = nullptr;
332  if (m_gobj_param_name != nullptr)
333  g_object_get(const_cast<void*>(pObject), m_gobj_param_name,
334  &result, nullptr);
335  else
336  {
337  QofAccessFunc getter = get_getter(obj_name);
338  if (getter != nullptr)
339  result = reinterpret_cast<T>((getter)(const_cast<void*>(pObject),
340  nullptr));
341  }
342  return result;
343 }
344 
345 template <typename T> T
346 GncSqlColumnTableEntry::get_row_value_from_object(QofIdTypeConst obj_name,
347  const void* pObject,
348  std::false_type) const
349 {
350  g_return_val_if_fail(obj_name != nullptr && pObject != nullptr,
351  static_cast<T>(0));
352  T result = static_cast<T>(0);
353  if (m_gobj_param_name != nullptr)
354  g_object_get(const_cast<void*>(pObject), m_gobj_param_name,
355  &result, nullptr);
356  else
357  {
358  QofAccessFunc getter = get_getter(obj_name);
359  if (getter != nullptr)
360  result = reinterpret_cast<T>((getter)(const_cast<void*>(pObject),
361  nullptr));
362  }
363  return result;
364 }
365 
366 template <typename T> void
367 GncSqlColumnTableEntry::add_value_to_vec(QofIdTypeConst obj_name,
368  const void* pObject,
369  PairVec& vec) const
370 {
371  add_value_to_vec<T>(obj_name, pObject, vec, std::is_pointer<T>());
372 }
373 
374 template <typename T> void
375 GncSqlColumnTableEntry::add_value_to_vec(QofIdTypeConst obj_name,
376  const void* pObject,
377  PairVec& vec, std::true_type) const
378 {
379  T s = get_row_value_from_object<T>(obj_name, pObject);
380 
381  if (s != nullptr)
382  {
383  std::ostringstream stream;
384  stream << *s;
385  vec.emplace_back(std::make_pair(std::string{m_col_name}, stream.str()));
386  return;
387  }
388 }
389 
390 template <> inline void
391 GncSqlColumnTableEntry::add_value_to_vec<double*>(QofIdTypeConst obj_name,
392  const void* pObject,
393  PairVec& vec, std::true_type) const
394 {
395  double* s = get_row_value_from_object<double*>(obj_name, pObject);
396 
397  if (s != nullptr)
398  {
399  std::ostringstream stream;
400  stream << std::setprecision(12) << std::fixed << *s;
401  vec.emplace_back(std::make_pair(std::string{m_col_name}, stream.str()));
402  return;
403  }
404 }
405 
406 template <typename T> void
407 GncSqlColumnTableEntry::add_value_to_vec(QofIdTypeConst obj_name,
408  const void* pObject,
409  PairVec& vec, std::false_type) const
410 {
411  T s = get_row_value_from_object<T>(obj_name, pObject);
412 
413  std::ostringstream stream;
414  stream << s;
415  vec.emplace_back(std::make_pair(std::string{m_col_name}, stream.str()));
416  return;
417 }
418 
419 template <> inline void
420 GncSqlColumnTableEntry::add_value_to_vec<double>(QofIdTypeConst obj_name,
421  const void* pObject,
422  PairVec& vec, std::false_type) const
423 {
424  double s = *get_row_value_from_object<double*>(obj_name, pObject);
425 
426  std::ostringstream stream;
427  stream << std::setprecision(12) << std::fixed << s;
428  vec.emplace_back(std::make_pair(std::string{m_col_name}, stream.str()));
429  return;
430 }
431 
441 void gnc_sql_load_object (const GncSqlBackend* sql_be, GncSqlRow& row,
442  QofIdTypeConst obj_name, gpointer pObject,
443  const EntryVec& table);
450 const GncGUID*
451 gnc_sql_load_guid (const GncSqlBackend* sql_be, GncSqlRow& row);
452 
460 uint_t gnc_sql_append_guids_to_sql (std::stringstream& sql,
461  const InstanceVec& instances);
462 
467 {
468  GncSqlColumnInfo (std::string&& name, GncSqlBasicColumnType type,
469  unsigned int size = 0, bool unicode = false,
470  bool autoinc = false, bool primary = false,
471  bool not_null = false) :
472  m_name{name}, m_type{type}, m_size{size}, m_unicode{unicode},
473  m_autoinc{autoinc}, m_primary_key{primary}, m_not_null{not_null}
474  {}
475  GncSqlColumnInfo(const GncSqlColumnTableEntry& e, GncSqlBasicColumnType t,
476  unsigned int size = 0, bool unicode = true) :
477  m_name{e.m_col_name}, m_type{t}, m_size{size}, m_unicode{unicode},
478  m_autoinc(e.m_flags & COL_AUTOINC),
479  m_primary_key(e.m_flags & COL_PKEY),
480  m_not_null(e.m_flags & COL_NNUL) {}
481  std::string m_name;
482  GncSqlBasicColumnType m_type;
483  unsigned int m_size;
484  bool m_unicode;
485  bool m_autoinc;
487  bool m_not_null;
488 };
489 
490 inline bool operator==(const GncSqlColumnInfo& l,
491  const GncSqlColumnInfo& r)
492 {
493  return l.m_name == r.m_name && l.m_type == r.m_type;
494 }
495 
496 inline bool operator!=(const GncSqlColumnInfo& l,
497  const GncSqlColumnInfo& r)
498 {
499  return !(l == r);
500 }
501 
511 template <typename T, typename P, typename F>
512 void set_parameter(T object, P item, F& setter)
513 {
514  (*setter)(object, item);
515 }
516 
517 template <typename T, typename P>
518 void set_parameter(T object, P item, QofSetterFunc setter, std::true_type)
519 {
520  (*setter)(object, (void*)item);
521 }
522 
523 template <typename T, typename P>
524 void set_parameter(T object, P item, QofSetterFunc setter, std::false_type)
525 {
526  (*setter)(object, (void*)(&item));
527 }
528 
529 template <typename T, typename P>
530 void set_parameter(T object, P item, QofSetterFunc setter)
531 {
532  set_parameter(object, item, setter, std::is_pointer<P>());
533 }
534 
543 template <typename T, typename P>
544 void set_parameter(T object, P item, const char* property)
545 {
546  // Properly use qof_begin_edit and qof_commit_edit{_part2}
547  // here. This is needed to reset the infant state of objects
548  // when loading them initially from sql. Failing to do so
549  // could prevent future editing of these objects
550  // Example of this is https://bugs.gnucash.org/show_bug.cgi?id=795944
551  qof_begin_edit(QOF_INSTANCE(object));
552  g_object_set(object, property, item, nullptr);
553  if (!qof_commit_edit(QOF_INSTANCE(object))) return;
554  // FIXME I can't use object specific callbacks in generic code
555  // so for now these will silently fail. As the GObject based method
556  // of setting qof objects should go away eventually I won't bother
557  // finding a proper solution for this.
558  qof_commit_edit_part2(QOF_INSTANCE(object), nullptr, nullptr, nullptr);
559 };
560 
567 template <typename T, typename P, typename F>
568 void set_parameter(T object, P item, F setter, const char* property)
569 {
570  if (property)
571  set_parameter(object, item, property);
572  else
573  set_parameter(object, item, setter);
574 }
575 
576 #endif //__GNC_SQL_COLUMN_TABLE_ENTRY_HPP__
information required to create a column in a table.
QofSetterFunc get_setter(QofIdTypeConst obj_name) const noexcept
Retrieve the setter function depending on whether it&#39;s an auto-increment field, a QofClass getter...
bool m_not_null
Column forbids NULL values.
virtual void add_to_table(ColVec &vec) const noexcept=0
Add a GncSqlColumnInfo structure for the column type to a ColVec.
const gchar * QofIdTypeConst
QofIdTypeConst declaration.
Definition: qofid.h:87
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.
bool m_autoinc
Column is autoinc (int type)
gboolean string_to_guid(const gchar *string, GncGUID *guid)
Given a string, replace the given guid with the parsed one unless the given value is null...
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&#39;s value&#39;s string representation to a PairVec; used ...
void add_to_query(QofIdTypeConst obj_name, void *pObject, PairVec &vec) const noexcept override
Add a pair of the table column heading and object&#39;s value&#39;s string representation to a PairVec; used ...
GncSqlBasicColumnType m_type
Column basic type.
gboolean qof_commit_edit(QofInstance *inst)
commit_edit helpers
void(* QofSetterFunc)(gpointer, gpointer)
The QofSetterFunc defines an function pointer for parameter setters.
Definition: qofclass.h:184
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.
gboolean qof_begin_edit(QofInstance *inst)
begin_edit
const char * name() const noexcept
Retrieve the field name so that we don&#39;t need to make create_single_col_select_statement and friend...
bool m_unicode
Column is unicode (string types)
Row of SQL Query results.
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
gpointer(* QofAccessFunc)(gpointer object, const QofParam *param)
The QofAccessFunc defines an arbitrary function pointer for access functions.
Definition: qofclass.h:177
bool m_primary_key
Column is the primary key.
bool is_autoincr() const noexcept
Report if the entry is an auto-increment field.
unsigned int m_size
Column size (string types)
void add_to_table(ColVec &vec) const noexcept override
Add a GncSqlColumnInfo structure for the column type to a ColVec.
void add_objectref_guid_to_table(ColVec &vec) const noexcept
Adds a column info structure for an object reference GncGUID to a ColVec.
std::string m_name
Column name.
Contains all of the information required to copy information between an object and the database for a...
The type used to store guids in C.
Definition: guid.h:75
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.
Main SQL backend structure.
QofAccessFunc get_getter(QofIdTypeConst obj_name) const noexcept
Retrieve the getter function depending on whether it&#39;s an auto-increment field, a QofClass getter...