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  result = reinterpret_cast<T>((getter)(const_cast<void*>(pObject),
371  nullptr));
372  }
373  return result;
374 }
375 
376 template <typename T> void
377 GncSqlColumnTableEntry::add_value_to_vec(QofIdTypeConst obj_name,
378  const void* pObject,
379  PairVec& vec) const
380 {
381  add_value_to_vec<T>(obj_name, pObject, vec, std::is_pointer<T>());
382 }
383 
384 template <typename T> void
385 GncSqlColumnTableEntry::add_value_to_vec(QofIdTypeConst obj_name,
386  const void* pObject,
387  PairVec& vec, std::true_type) const
388 {
389  T s = get_row_value_from_object<T>(obj_name, pObject);
390 
391  if (s != nullptr)
392  {
393  std::ostringstream stream;
394  stream << *s;
395  vec.emplace_back(std::make_pair(std::string{m_col_name}, stream.str()));
396  return;
397  }
398 }
399 
400 template <> inline void
401 GncSqlColumnTableEntry::add_value_to_vec<double*>(QofIdTypeConst obj_name,
402  const void* pObject,
403  PairVec& vec, std::true_type) const
404 {
405  double* s = get_row_value_from_object<double*>(obj_name, pObject);
406 
407  if (s != nullptr)
408  {
409  std::ostringstream stream;
410  stream << std::setprecision(12) << std::fixed << *s;
411  vec.emplace_back(std::make_pair(std::string{m_col_name}, stream.str()));
412  return;
413  }
414 }
415 
416 template <typename T> void
417 GncSqlColumnTableEntry::add_value_to_vec(QofIdTypeConst obj_name,
418  const void* pObject,
419  PairVec& vec, std::false_type) const
420 {
421  T s = get_row_value_from_object<T>(obj_name, pObject);
422 
423  std::ostringstream stream;
424  stream << s;
425  vec.emplace_back(std::make_pair(std::string{m_col_name}, stream.str()));
426  return;
427 }
428 
429 template <> inline void
430 GncSqlColumnTableEntry::add_value_to_vec<double>(QofIdTypeConst obj_name,
431  const void* pObject,
432  PairVec& vec, std::false_type) const
433 {
434  double s = *get_row_value_from_object<double*>(obj_name, pObject);
435 
436  std::ostringstream stream;
437  stream << std::setprecision(12) << std::fixed << s;
438  vec.emplace_back(std::make_pair(std::string{m_col_name}, stream.str()));
439  return;
440 }
441 
451 void gnc_sql_load_object (const GncSqlBackend* sql_be, GncSqlRow& row,
452  QofIdTypeConst obj_name, gpointer pObject,
453  const EntryVec& table);
460 const GncGUID*
461 gnc_sql_load_guid (const GncSqlBackend* sql_be, GncSqlRow& row);
462 
470 uint_t gnc_sql_append_guids_to_sql (std::stringstream& sql,
471  const InstanceVec& instances);
472 
477 {
478  GncSqlColumnInfo (std::string&& name, GncSqlBasicColumnType type,
479  unsigned int size = 0, bool unicode = false,
480  bool autoinc = false, bool primary = false,
481  bool not_null = false) :
482  m_name{name}, m_type{type}, m_size{size}, m_unicode{unicode},
483  m_autoinc{autoinc}, m_primary_key{primary}, m_not_null{not_null}
484  {}
485  GncSqlColumnInfo(const GncSqlColumnTableEntry& e, GncSqlBasicColumnType t,
486  unsigned int size = 0, bool unicode = true) :
487  m_name{e.m_col_name}, m_type{t}, m_size{size}, m_unicode{unicode},
488  m_autoinc(e.m_flags & COL_AUTOINC),
489  m_primary_key(e.m_flags & COL_PKEY),
490  m_not_null(e.m_flags & COL_NNUL) {}
491  std::string m_name;
492  GncSqlBasicColumnType m_type;
493  unsigned int m_size;
494  bool m_unicode;
495  bool m_autoinc;
497  bool m_not_null;
498 };
499 
500 inline bool operator==(const GncSqlColumnInfo& l,
501  const GncSqlColumnInfo& r)
502 {
503  return l.m_name == r.m_name && l.m_type == r.m_type;
504 }
505 
506 inline bool operator!=(const GncSqlColumnInfo& l,
507  const GncSqlColumnInfo& r)
508 {
509  return !(l == r);
510 }
511 
521 template <typename T, typename P, typename F>
522 void set_parameter(T object, P item, F& setter)
523 {
524  (*setter)(object, item);
525 }
526 
527 template <typename T, typename P>
528 void set_parameter(T object, P item, QofSetterFunc setter, std::true_type)
529 {
530  (*setter)(object, (void*)item);
531 }
532 
533 template <typename T, typename P>
534 void set_parameter(T object, P item, QofSetterFunc setter, std::false_type)
535 {
536  (*setter)(object, (void*)(&item));
537 }
538 
539 template <typename T, typename P>
540 void set_parameter(T object, P item, QofSetterFunc setter)
541 {
542  set_parameter(object, item, setter, std::is_pointer<P>());
543 }
544 
553 template <typename T, typename P>
554 void set_parameter(T object, P item, const char* property)
555 {
556  // Properly use qof_begin_edit and qof_commit_edit{_part2}
557  // here. This is needed to reset the infant state of objects
558  // when loading them initially from sql. Failing to do so
559  // could prevent future editing of these objects
560  // Example of this is https://bugs.gnucash.org/show_bug.cgi?id=795944
561  qof_begin_edit(QOF_INSTANCE(object));
562  g_object_set(object, property, item, nullptr);
563  if (!qof_commit_edit(QOF_INSTANCE(object))) return;
564  // FIXME I can't use object specific callbacks in generic code
565  // so for now these will silently fail. As the GObject based method
566  // of setting qof objects should go away eventually I won't bother
567  // finding a proper solution for this.
568  qof_commit_edit_part2(QOF_INSTANCE(object), nullptr, nullptr, nullptr);
569 };
570 
577 template <typename T, typename P, typename F>
578 void set_parameter(T object, P item, F setter, const char* property)
579 {
580  if (property)
581  set_parameter(object, item, property);
582  else
583  set_parameter(object, item, setter);
584 }
585 
586 #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...