Money Manager EX
An easy to use, money management application built with wxWidgets
DB_Table_Translink_V1.h
Go to the documentation of this file.
1 // -*- C++ -*-
2 //=============================================================================
18 //=============================================================================
19 #pragma once
20 
21 #include "DB_Table.h"
22 
24 {
25  struct Data;
27 
29  struct Data_Set : public std::vector<Self::Data>
30  {
32  wxString to_json() const
33  {
34  StringBuffer json_buffer;
35  PrettyWriter<StringBuffer> json_writer(json_buffer);
36 
37  json_writer.StartArray();
38  for (const auto & item: *this)
39  {
40  json_writer.StartObject();
41  item.as_json(json_writer);
42  json_writer.EndObject();
43  }
44  json_writer.EndArray();
45 
46  return json_buffer.GetString();
47  }
48  };
49 
51  typedef std::vector<Self::Data*> Cache;
52  typedef std::map<int64, Self::Data*> Index_By_Id;
53  Cache cache_;
54  Index_By_Id index_by_id_;
55  Data* fake_; // in case the entity not found
56 
59  {
60  delete this->fake_;
61  destroy_cache();
62  }
63 
66  {
67  std::for_each(cache_.begin(), cache_.end(), std::mem_fn(&Data::destroy));
68  cache_.clear();
69  index_by_id_.clear(); // no memory release since it just stores pointer and the according objects are in cache
70  }
71 
73  bool ensure(wxSQLite3Database* db)
74  {
75  if (!exists(db))
76  {
77  try
78  {
79  db->ExecuteUpdate("CREATE TABLE TRANSLINK_V1 (TRANSLINKID integer NOT NULL primary key, CHECKINGACCOUNTID integer NOT NULL, LINKTYPE TEXT NOT NULL /* Asset, Stock */, LINKRECORDID integer NOT NULL)");
80  this->ensure_data(db);
81  }
82  catch(const wxSQLite3Exception &e)
83  {
84  wxLogError("TRANSLINK_V1: Exception %s", e.GetMessage().utf8_str());
85  return false;
86  }
87  }
88 
89  this->ensure_index(db);
90 
91  return true;
92  }
93 
94  bool ensure_index(wxSQLite3Database* db)
95  {
96  try
97  {
98  db->ExecuteUpdate("CREATE INDEX IF NOT EXISTS IDX_CHECKINGACCOUNT ON TRANSLINK_V1 (CHECKINGACCOUNTID)");
99  db->ExecuteUpdate("CREATE INDEX IF NOT EXISTS IDX_LINKRECORD ON TRANSLINK_V1 (LINKTYPE, LINKRECORDID)");
100  }
101  catch(const wxSQLite3Exception &e)
102  {
103  wxLogError("TRANSLINK_V1: Exception %s", e.GetMessage().utf8_str());
104  return false;
105  }
106 
107  return true;
108  }
109 
110  void ensure_data(wxSQLite3Database* db)
111  {
112  db->Begin();
113  db->Commit();
114  }
115 
116  struct TRANSLINKID : public DB_Column<int64>
117  {
118  static wxString name() { return "TRANSLINKID"; }
119  explicit TRANSLINKID(const int64 &v, OP op = EQUAL): DB_Column<int64>(v, op) {}
120  };
121 
122  struct CHECKINGACCOUNTID : public DB_Column<int64>
123  {
124  static wxString name() { return "CHECKINGACCOUNTID"; }
125  explicit CHECKINGACCOUNTID(const int64 &v, OP op = EQUAL): DB_Column<int64>(v, op) {}
126  };
127 
128  struct LINKTYPE : public DB_Column<wxString>
129  {
130  static wxString name() { return "LINKTYPE"; }
131  explicit LINKTYPE(const wxString &v, OP op = EQUAL): DB_Column<wxString>(v, op) {}
132  };
133 
134  struct LINKRECORDID : public DB_Column<int64>
135  {
136  static wxString name() { return "LINKRECORDID"; }
137  explicit LINKRECORDID(const int64 &v, OP op = EQUAL): DB_Column<int64>(v, op) {}
138  };
139 
141  enum COLUMN
142  {
147  };
148 
150  static wxString column_to_name(const COLUMN col)
151  {
152  switch(col)
153  {
154  case COL_TRANSLINKID: return "TRANSLINKID";
155  case COL_CHECKINGACCOUNTID: return "CHECKINGACCOUNTID";
156  case COL_LINKTYPE: return "LINKTYPE";
157  case COL_LINKRECORDID: return "LINKRECORDID";
158  default: break;
159  }
160 
161  return "UNKNOWN";
162  }
163 
165  static COLUMN name_to_column(const wxString& name)
166  {
167  if ("TRANSLINKID" == name) return COL_TRANSLINKID;
168  else if ("CHECKINGACCOUNTID" == name) return COL_CHECKINGACCOUNTID;
169  else if ("LINKTYPE" == name) return COL_LINKTYPE;
170  else if ("LINKRECORDID" == name) return COL_LINKRECORDID;
171 
172  return COLUMN(-1);
173  }
174 
176  struct Data
177  {
178  friend struct DB_Table_TRANSLINK_V1;
180  Self* table_;
181 
182  int64 TRANSLINKID;// primary key
184  wxString LINKTYPE;
186 
187  int64 id() const
188  {
189  return TRANSLINKID;
190  }
191 
192  void id(const int64 id)
193  {
194  TRANSLINKID = id;
195  }
196 
197  auto operator < (const Data& other) const
198  {
199  return this->id() < other.id();
200  }
201 
202  auto operator < (const Data* other) const
203  {
204  return this->id() < other->id();
205  }
206 
207  bool equals(const Data* r) const
208  {
209  if(TRANSLINKID != r->TRANSLINKID) return false;
210  if(CHECKINGACCOUNTID != r->CHECKINGACCOUNTID) return false;
211  if(!LINKTYPE.IsSameAs(r->LINKTYPE)) return false;
212  if(LINKRECORDID != r->LINKRECORDID) return false;
213  return true;
214  }
215 
216  explicit Data(Self* table = nullptr )
217  {
218  table_ = table;
219 
220  TRANSLINKID = -1;
221  CHECKINGACCOUNTID = -1;
222  LINKRECORDID = -1;
223  }
224 
225  explicit Data(wxSQLite3ResultSet& q, Self* table = nullptr )
226  {
227  table_ = table;
228 
229  TRANSLINKID = q.GetInt64(0); // TRANSLINKID
230  CHECKINGACCOUNTID = q.GetInt64(1); // CHECKINGACCOUNTID
231  LINKTYPE = q.GetString(2); // LINKTYPE
232  LINKRECORDID = q.GetInt64(3); // LINKRECORDID
233  }
234 
235  Data(const Data& other) = default;
236 
237  Data& operator=(const Data& other)
238  {
239  if (this == &other) return *this;
240 
241  TRANSLINKID = other.TRANSLINKID;
242  CHECKINGACCOUNTID = other.CHECKINGACCOUNTID;
243  LINKTYPE = other.LINKTYPE;
244  LINKRECORDID = other.LINKRECORDID;
245  return *this;
246  }
247 
248  template<typename C>
249  bool match(const C &) const
250  {
251  return false;
252  }
253 
254  bool match(const Self::TRANSLINKID &in) const
255  {
256  return this->TRANSLINKID == in.v_;
257  }
258 
259  bool match(const Self::CHECKINGACCOUNTID &in) const
260  {
261  return this->CHECKINGACCOUNTID == in.v_;
262  }
263 
264  bool match(const Self::LINKTYPE &in) const
265  {
266  return this->LINKTYPE.CmpNoCase(in.v_) == 0;
267  }
268 
269  bool match(const Self::LINKRECORDID &in) const
270  {
271  return this->LINKRECORDID == in.v_;
272  }
273 
274  // Return the data record as a json string
275  wxString to_json() const
276  {
277  StringBuffer json_buffer;
278  PrettyWriter<StringBuffer> json_writer(json_buffer);
279 
280  json_writer.StartObject();
281  this->as_json(json_writer);
282  json_writer.EndObject();
283 
284  return json_buffer.GetString();
285  }
286 
287  // Add the field data as json key:value pairs
288  void as_json(PrettyWriter<StringBuffer>& json_writer) const
289  {
290  json_writer.Key("TRANSLINKID");
291  json_writer.Int64(this->TRANSLINKID.GetValue());
292  json_writer.Key("CHECKINGACCOUNTID");
293  json_writer.Int64(this->CHECKINGACCOUNTID.GetValue());
294  json_writer.Key("LINKTYPE");
295  json_writer.String(this->LINKTYPE.utf8_str());
296  json_writer.Key("LINKRECORDID");
297  json_writer.Int64(this->LINKRECORDID.GetValue());
298  }
299 
300  row_t to_row_t() const
301  {
302  row_t row;
303  row(L"TRANSLINKID") = TRANSLINKID.GetValue();
304  row(L"CHECKINGACCOUNTID") = CHECKINGACCOUNTID.GetValue();
305  row(L"LINKTYPE") = LINKTYPE;
306  row(L"LINKRECORDID") = LINKRECORDID.GetValue();
307  return row;
308  }
309 
310  void to_template(html_template& t) const
311  {
312  t(L"TRANSLINKID") = TRANSLINKID.GetValue();
313  t(L"CHECKINGACCOUNTID") = CHECKINGACCOUNTID.GetValue();
314  t(L"LINKTYPE") = LINKTYPE;
315  t(L"LINKRECORDID") = LINKRECORDID.GetValue();
316  }
317 
319  bool save(wxSQLite3Database* db, bool force_insert = false)
320  {
321  if (db && db->IsReadOnly()) return false;
322  if (!table_ || !db)
323  {
324  wxLogError("can not save TRANSLINK_V1");
325  return false;
326  }
327 
328  return table_->save(this, db, force_insert);
329  }
330 
332  bool remove(wxSQLite3Database* db)
333  {
334  if (!table_ || !db)
335  {
336  wxLogError("can not remove TRANSLINK_V1");
337  return false;
338  }
339 
340  return table_->remove(this, db);
341  }
342 
343  void destroy()
344  {
345  delete this;
346  }
347  };
348 
349  enum
350  {
352  };
353 
354  size_t num_columns() const { return NUM_COLUMNS; }
355 
357  wxString name() const { return "TRANSLINK_V1"; }
358 
359  DB_Table_TRANSLINK_V1() : fake_(new Data())
360  {
361  query_ = "SELECT TRANSLINKID, CHECKINGACCOUNTID, LINKTYPE, LINKRECORDID FROM TRANSLINK_V1 ";
362  }
363 
366  {
367  Self::Data* entity = new Self::Data(this);
368  cache_.push_back(entity);
369  return entity;
370  }
371 
373  Self::Data* clone(const Data* e)
374  {
375  Self::Data* entity = create();
376  *entity = *e;
377  entity->id(-1);
378  return entity;
379  }
380 
386  bool save(Self::Data* entity, wxSQLite3Database* db, bool force_insert = false)
387  {
388  wxString sql = wxEmptyString;
389  if (entity->id() <= 0 || force_insert) // new & insert
390  {
391  sql = "INSERT INTO TRANSLINK_V1(CHECKINGACCOUNTID, LINKTYPE, LINKRECORDID, TRANSLINKID) VALUES(?, ?, ?, ?)";
392  }
393  else
394  {
395  sql = "UPDATE TRANSLINK_V1 SET CHECKINGACCOUNTID = ?, LINKTYPE = ?, LINKRECORDID = ? WHERE TRANSLINKID = ?";
396  }
397 
398  try
399  {
400  wxSQLite3Statement stmt = db->PrepareStatement(sql);
401 
402  stmt.Bind(1, entity->CHECKINGACCOUNTID);
403  stmt.Bind(2, entity->LINKTYPE);
404  stmt.Bind(3, entity->LINKRECORDID);
405  stmt.Bind(4, entity->id() > 0 ? entity->TRANSLINKID : newId());
406 
407  stmt.ExecuteUpdate();
408  stmt.Finalize();
409 
410  if (entity->id() > 0) // existent
411  {
412  for(Cache::iterator it = cache_.begin(); it != cache_.end(); ++ it)
413  {
414  Self::Data* e = *it;
415  if (e->id() == entity->id())
416  *e = *entity; // in-place update
417  }
418  }
419  }
420  catch(const wxSQLite3Exception &e)
421  {
422  wxLogError("TRANSLINK_V1: Exception %s, %s", e.GetMessage().utf8_str(), entity->to_json());
423  return false;
424  }
425 
426  if (entity->id() <= 0)
427  {
428  entity->id(db->GetLastRowId());
429  index_by_id_.insert(std::make_pair(entity->id(), entity));
430  }
431  return true;
432  }
433 
435  bool remove(const int64 id, wxSQLite3Database* db)
436  {
437  if (id <= 0) return false;
438  try
439  {
440  wxString sql = "DELETE FROM TRANSLINK_V1 WHERE TRANSLINKID = ?";
441  wxSQLite3Statement stmt = db->PrepareStatement(sql);
442  stmt.Bind(1, id);
443  stmt.ExecuteUpdate();
444  stmt.Finalize();
445 
446  Cache c;
447  for(Cache::iterator it = cache_.begin(); it != cache_.end(); ++ it)
448  {
449  Self::Data* entity = *it;
450  if (entity->id() == id)
451  {
452  index_by_id_.erase(entity->id());
453  delete entity;
454  }
455  else
456  {
457  c.push_back(entity);
458  }
459  }
460  cache_.clear();
461  cache_.swap(c);
462  }
463  catch(const wxSQLite3Exception &e)
464  {
465  wxLogError("TRANSLINK_V1: Exception %s", e.GetMessage().utf8_str());
466  return false;
467  }
468 
469  return true;
470  }
471 
473  bool remove(Self::Data* entity, wxSQLite3Database* db)
474  {
475  if (remove(entity->id(), db))
476  {
477  entity->id(-1);
478  return true;
479  }
480 
481  return false;
482  }
483 
484  template<typename... Args>
485  Self::Data* get_one(const Args& ... args)
486  {
487  for (auto& [_, item] : index_by_id_)
488  {
489  if (item->id() > 0 && match(item, args...))
490  {
491  ++ hit_;
492  return item;
493  }
494  }
495 
496  ++ miss_;
497 
498  return 0;
499  }
500 
505  Self::Data* get(const int64 id, wxSQLite3Database* db)
506  {
507  if (id <= 0)
508  {
509  ++ skip_;
510  return nullptr;
511  }
512 
513  if (auto it = index_by_id_.find(id); it != index_by_id_.end())
514  {
515  ++ hit_;
516  return it->second;
517  }
518 
519  ++ miss_;
520  Self::Data* entity = nullptr;
521  wxString where = wxString::Format(" WHERE %s = ?", PRIMARY::name().utf8_str());
522  try
523  {
524  wxSQLite3Statement stmt = db->PrepareStatement(this->query() + where);
525  stmt.Bind(1, id);
526 
527  wxSQLite3ResultSet q = stmt.ExecuteQuery();
528  if(q.NextRow())
529  {
530  entity = new Self::Data(q, this);
531  cache_.push_back(entity);
532  index_by_id_.insert(std::make_pair(id, entity));
533  }
534  stmt.Finalize();
535  }
536  catch(const wxSQLite3Exception &e)
537  {
538  wxLogError("%s: Exception %s", this->name().utf8_str(), e.GetMessage().utf8_str());
539  }
540 
541  if (!entity)
542  {
543  entity = this->fake_;
544  // wxLogError("%s: %d not found", this->name().utf8_str(), id);
545  }
546 
547  return entity;
548  }
552  Self::Data* get_record(const int64 id, wxSQLite3Database* db)
553  {
554  if (id <= 0)
555  {
556  ++ skip_;
557  return nullptr;
558  }
559 
560  Self::Data* entity = nullptr;
561  wxString where = wxString::Format(" WHERE %s = ?", PRIMARY::name().utf8_str());
562  try
563  {
564  wxSQLite3Statement stmt = db->PrepareStatement(this->query() + where);
565  stmt.Bind(1, id);
566 
567  wxSQLite3ResultSet q = stmt.ExecuteQuery();
568  if(q.NextRow())
569  {
570  entity = new Self::Data(q, this);
571  }
572  stmt.Finalize();
573  }
574  catch(const wxSQLite3Exception &e)
575  {
576  wxLogError("%s: Exception %s", this->name().utf8_str(), e.GetMessage().utf8_str());
577  }
578 
579  if (!entity)
580  {
581  entity = this->fake_;
582  // wxLogError("%s: %d not found", this->name().utf8_str(), id);
583  }
584 
585  return entity;
586  }
587 
592  const Data_Set all(wxSQLite3Database* db, const COLUMN col = COLUMN(0), const bool asc = true)
593  {
594  Data_Set result;
595  try
596  {
597  wxSQLite3ResultSet q = db->ExecuteQuery(col == COLUMN(0) ? this->query() : this->query() + " ORDER BY " + column_to_name(col) + " COLLATE NOCASE " + (asc ? " ASC " : " DESC "));
598 
599  while(q.NextRow())
600  {
601  Self::Data entity(q, this);
602  result.push_back(std::move(entity));
603  }
604 
605  q.Finalize();
606  }
607  catch(const wxSQLite3Exception &e)
608  {
609  wxLogError("%s: Exception %s", this->name().utf8_str(), e.GetMessage().utf8_str());
610  }
611 
612  return result;
613  }
614 };
615 
bool exists(wxSQLite3Database *db) const
Definition: DB_Table.h:65
bool match(const DATA *data, const Arg1 &arg1)
Definition: DB_Table.h:170
Copyright: (c) 2013 - 2025 Guan Lisheng (guanlisheng@gmail.com) Copyright: (c) 2017 - 2018 Stefano Gi...
V v_
Definition: DB_Table.h:47
size_t hit_
Definition: DB_Table.h:60
OP
Definition: DB_Table.h:42
static int64 newId()
Definition: DB_Table.h:75
id
Definition: constants.h:125
size_t skip_
Definition: DB_Table.h:60
Definition: DB_Table.h:42
Definition: DB_Table.h:45
virtual wxString query() const
Definition: DB_Table.h:61
size_t miss_
Definition: DB_Table.h:60
Definition: DB_Table.h:55
wxLongLong int64
Definition: customfieldlistdialog.h:26
wxString query_
Definition: DB_Table.h:58