GnuCash  5.6-150-g038405b370+
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 
35 struct GncSqlColumnInfo;
36 using ColVec = std::vector<GncSqlColumnInfo>;
37 using PairVec = std::vector<std::pair<std::string, std::string>>;
38 using InstanceVec = std::vector<QofInstance*>;
39 using uint_t = unsigned int;
40 class GncSqlBackend;
41 
45 typedef enum
46 {
47  BCT_STRING,
48  BCT_INT,
49  BCT_INT64,
50  BCT_DATE,
51  BCT_DOUBLE,
52  BCT_DATETIME
53 } GncSqlBasicColumnType;
54 
55 enum 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.
65 enum 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 
89 static inline std::string
90 quote_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 {
128 public:
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 
218 protected:
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;
242 private:
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 
266 template <GncSqlObjectType Type>
268 {
269 public:
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 
286 using GncSqlColumnTableEntryPtr = std::shared_ptr<GncSqlColumnTableEntry>;
287 using EntryVec = std::vector<GncSqlColumnTableEntryPtr>;
288 
289 template <GncSqlObjectType Type>
290 std::shared_ptr<GncSqlColumnTableEntryImpl<Type>>
291 gnc_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 
296 template <GncSqlObjectType Type>
297 std::shared_ptr<GncSqlColumnTableEntryImpl<Type>>
298 gnc_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 
305 class is_qof : public std::true_type {};
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  const char* param, bool qofp)
311 {
312  return std::make_shared<GncSqlColumnTableEntryImpl<Type>>(name, Type, s,
313  f, nullptr,
314  param);
315 }
316 
317 template <GncSqlObjectType Type>
318 std::shared_ptr<GncSqlColumnTableEntryImpl<Type>>
319 gnc_sql_make_table_entry(const char* name, unsigned int s, int f,
320  QofAccessFunc get, QofSetterFunc set)
321 {
322  return std::make_shared<GncSqlColumnTableEntryImpl<Type>>(
323  name, Type, s, f, nullptr, nullptr, get, set);
324 }
325 
326 
327 template <typename T> T
328 GncSqlColumnTableEntry::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 
335 template <typename T> T
336 GncSqlColumnTableEntry::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 
355 template <typename T> T
356 GncSqlColumnTableEntry::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 
378 template <typename T> void
379 GncSqlColumnTableEntry::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 
386 template <typename T> void
387 GncSqlColumnTableEntry::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 
402 template <> inline void
403 GncSqlColumnTableEntry::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 
418 template <typename T> void
419 GncSqlColumnTableEntry::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 
431 template <> inline void
432 GncSqlColumnTableEntry::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 
453 void gnc_sql_load_object (const GncSqlBackend* sql_be, GncSqlRow& row,
454  QofIdTypeConst obj_name, gpointer pObject,
455  const EntryVec& table);
462 const GncGUID*
463 gnc_sql_load_guid (const GncSqlBackend* sql_be, GncSqlRow& row);
464 
472 uint_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;
496  bool m_unicode;
497  bool m_autoinc;
499  bool m_not_null;
500 };
501 
502 inline 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 
508 inline bool operator!=(const GncSqlColumnInfo& l,
509  const GncSqlColumnInfo& r)
510 {
511  return !(l == r);
512 }
513 
523 template <typename T, typename P, typename F>
524 void set_parameter(T object, P item, F& setter)
525 {
526  (*setter)(object, item);
527 }
528 
529 template <typename T, typename P>
530 void set_parameter(T object, P item, QofSetterFunc setter, std::true_type)
531 {
532  (*setter)(object, (void*)item);
533 }
534 
535 template <typename T, typename P>
536 void set_parameter(T object, P item, QofSetterFunc setter, std::false_type)
537 {
538  (*setter)(object, (void*)(&item));
539 }
540 
541 template <typename T, typename P>
542 void set_parameter(T object, P item, QofSetterFunc setter)
543 {
544  set_parameter(object, item, setter, std::is_pointer<P>());
545 }
546 
555 template <typename T, typename P>
556 void 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 
579 template <typename T, typename P, typename F>
580 void 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__
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.
#define G_LOG_DOMAIN
Functions providing the SX List as a plugin page.
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:82
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)
#define DEBUG(format, args...)
Print a debugging message.
Definition: qoflog.h:264
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:185
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:178
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...