HDK
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
UT_SQLORM.h
Go to the documentation of this file.
1 /*
2  * PROPRIETARY INFORMATION. This software is proprietary to
3  * Side Effects Software Inc., and is not to be reproduced,
4  * transmitted, or disclosed in any way without written permission.
5  *
6  * NAME: UT_SQLORM.h
7  *
8  * COMMENTS:
9  *
10  *
11  */
12 
13 #ifndef __UT_SQLORM_H__
14 #define __UT_SQLORM_H__
15 
16 #include "UT_API.h"
17 
18 #include "UT_Array.h"
19 #include "UT_Debug.h"
20 #include "UT_Function.h"
21 #include "UT_Lock.h"
22 #include "UT_NonCopyable.h"
23 #include "UT_ORMField.h"
24 #include "UT_Optional.h"
25 #include "UT_SGuid.h"
26 #include "UT_SQL.h"
27 #include "UT_SharedPtr.h"
28 #include "UT_StringHolder.h"
29 #include "UT_ThreadSpecificValue.h"
30 #include "UT_UniquePtr.h"
31 #include "UT_WorkBuffer.h"
32 #include "UT_ORMMigrationResult.h"
33 
34 #include <variant>
35 #include <utility>
36 
37 class UT_SqlOrm;
38 class UT_IORMOperation;
39 class UT_IStream;
40 class UT_OStream;
42 
43 namespace UT
44 {
45 /// Error codes to describe ORM errors.
46 enum class SqlOrmError
47 {
48  UT_ORM_OK,
57 };
58 
60 
61 inline UT_ErrorCode
63 {
64  return UT_ErrorCode{static_cast<int>(e), GetOrmErrorCategory()};
65 }
66 } // namespace UT
67 
68 namespace std
69 {
70  template <> struct is_error_code_enum<UT::SqlOrmError> : true_type
71  {
72  };
73 }
74 
75 template <typename T>
77 {
78  friend class UT_ORMModelMeta;
79 public:
80  UT_ORMModel() = default;
81  virtual ~UT_ORMModel() = default;
82  UT_ORMModel(const UT_ORMModel&) = default;
83  UT_ORMModel& operator=(const UT_ORMModel&) = default;
84 
85  bool save(
86  UT_ErrorCode& ec,
87  bool force_insert = false,
88  bool force_update = false)
89  {
90  auto&& meta_info = T::metaInfo();
91  return meta_info.save(*this, ec, force_insert, force_update);
92  }
93  void remove(UT_ErrorCode& ec)
94  {
95  auto&& meta_info = T::metaInfo();
96  return meta_info.remove(*this, ec);
97  }
98  bool update(UT_ErrorCode& ec)
99  {
100  auto&& meta_info = T::metaInfo();
101  return meta_info.update(*this, ec);
102  }
103 
104  template <typename PK>
105  static UT_Optional<T> fetch(const PK& pk, UT_ErrorCode& ec)
106  {
107  auto&& meta_info = T::metaInfo();
108  return meta_info.template fetch<T>(pk, ec);
109  }
110 
112  {
113  auto&& meta_info = T::metaInfo();
114  return meta_info.template fetchAll<T>(ec);
115  }
116 
117  bool hasSavedBefore() const { return myHasSavedBefore; }
118 
119 protected:
120  bool myHasSavedBefore = false;
121 };
122 
124 {
125 public:
126  UT_ORMModelMeta(const UT_StringHolder& name, UT_SqlOrm* orm = nullptr) :
127  myName(name),
128  myORM(orm)
129  {}
130  virtual ~UT_ORMModelMeta() = default;
132 
133  /// Must always be called PRIOR to running the application
134  void migrate(UT_SqlOrm& orm, UT_ORMMigrationBuilder& builder) const;
135 
136  /// Call to build out all of your meta information.
137  void build()
138  {
139  if (!myHasBuilt)
140  {
141  myHasBuilt = true;
142 
143  // Let the child build the meta info
144  doBuild();
145 
146  // Now that we have the meta info go through it to set cache info.
147  // This is to save on lookups and common queries like inserting.
148  configureInternals_();
149  }
150  }
151  virtual void doMigrate(UT_ORMMigrationBuilder& builder) const = 0;
152 
153  template <typename Cls, typename FieldT>
155  const UT_StringHolder& name,
156  FieldT Cls::*field,
157  unsigned props = UT_ORMColumn::Empty)
158  {
159  myColumns.emplace_back(
161  name, field, props | UT_ORMColumn::AutoIncrement));
162  }
163  template <typename Cls, typename FieldT>
164  void addField(
165  const UT_StringHolder& name,
166  FieldT Cls::*field,
167  unsigned props = UT_ORMColumn::Empty)
168  {
169  myColumns.emplace_back(
171  name, field, props));
172  }
173  template <typename Cls, typename ForeignModel>
174  void addField(
175  const UT_StringHolder& name,
177  unsigned props = UT_ORMColumn::Empty,
178  UT_ORMColumn::OnDelete ondelete_type
179  = UT_ORMColumn::OnDelete::DoNothing)
180  {
181  using adapter_t = UT_ORMForeignKeyFieldAdapter<
183  myColumns.emplace_back(
184  adapter_t::createColumn(name, field, props, ondelete_type));
185  }
186 
187  const UT_StringHolder& tableName() const { return myName; }
189  {
190  for (auto&& col : myColumns)
191  {
192  if (col.isPrimaryKey())
193  {
194  return &col;
195  break;
196  }
197  }
198  return nullptr;
199  }
200  const UT_Array<UT_ORMFieldColumn>& columns() const { return myColumns; }
201 
202  template <typename T>
203  bool save(
204  T& obj,
205  UT_ErrorCode& ec,
206  bool force_insert = false,
207  bool force_update = false) const;
208  template <typename T>
209  void remove(T& obj, UT_ErrorCode& ec) const;
210  template <typename T>
211  bool update(T& obj, UT_ErrorCode& ec) const;
212  template <typename T, typename PK>
213  UT_Optional<T> fetch(const PK& pk, UT_ErrorCode& ec) const;
214  template <typename T>
215  UT_Array<T> fetchAll(UT_ErrorCode& ec) const;
216 
217 protected:
218  virtual void doBuild() = 0;
219 
220  template <typename T>
221  bool loadObject_(T& obj, UT_SqlStatement& stmt, UT_ErrorCode& ec) const
222  {
223  for (int i = 0; i < myColumns.size(); i++)
224  {
225  const UT_ORMFieldColumn& column = myColumns[i];
226  if (UT_IORMFieldAdapter* adapter = column.adapter(); adapter)
227  {
228  adapter->load(&obj, stmt, i, ec);
229  if (ec)
230  return false;
231  }
232  else
233  {
235  UT_ASSERT(!"Type not handled");
236  }
237  }
238 
239  // If the object is being loaded from the db then at some point it
240  // was saved.
241  obj.myHasSavedBefore = true;
242  return true;
243  }
244 
245  /// Configure any cacheable information that is used often.
246  void configureInternals_();
247 
248  bool myHasBuilt = false;
249  bool myIsUsingAutoIncPK = false;
252  UT_SqlOrm* myORM = nullptr;
253 };
254 
256 {
257 public:
258  explicit UT_SqlOrm(const UT_StringHolder& db_path)
259  {
260  configure(db_path);
261  }
262  UT_SqlOrm() = default;
263  virtual ~UT_SqlOrm() = default;
265 
266  void configure(const UT_StringHolder& db_path)
267  {
268  myDBPath = db_path;
269  }
270 
272  {
273  SYSconst_cast(this)->ensureConnection_();
274  return UT_SqlStatement(myDBLocal.local());
275  }
276 
278  {
279  ensureConnection_(ec);
280  return UT_SqlTransaction(myDBLocal.local(), ec);
281  }
282 
283  void migrate(UT_ORMModelMeta* meta, UT_ORMMigrationResult& result);
284  void migrate(UT_ORMMigrationResult& result);
285 
286  void close(UT_ErrorCode* ec = nullptr);
287 
288  template <typename T>
290  {
291  auto&& meta_info = T::metaInfo();
292  for (auto&& m : myRegisteredMetas)
293  {
294  if (m == &meta_info)
295  return;
296  }
297 
298  myRegisteredMetas.emplace_back(&meta_info);
299  }
300 
301 protected:
302  friend class UT_SqlOrmTable;
303 
304  void doMigrate_(
305  UT_ORMMigrationBuilder& builder,
307 
308  static UT_UniquePtr<UT_IORMOperation> createOperation(
309  const UT_StringRef& type);
310 
311  void ensureConnection_(UT_ErrorCode* ec = nullptr);
312 
315 
317 
319 };
320 
321 template <typename T>
322 bool
324  T& obj,
325  UT_ErrorCode& ec,
326  bool force_insert,
327  bool force_update) const
328 {
329  // If this model uses a auto increment pk and the model instance has already
330  // been saved (ie. record exists and pk value is valid) then it only
331  // makes sense to use update.
332  if (obj.hasSavedBefore())
333  {
334  if (myIsUsingAutoIncPK)
335  force_update = true;
336  }
337  else if (myIsUsingAutoIncPK)
338  force_insert = true;
339 
340  if (force_update)
341  {
342  return update(obj, ec);
343  }
344 
345  UT_WorkBuffer sql_query;
346  if (force_insert)
347  {
348  sql_query = "INSERT INTO ";
349  }
350  else
351  {
352  sql_query = "INSERT OR REPLACE INTO ";
353  }
354 
357 
358  for (auto&& col : myColumns)
359  {
360  if (col.isAutoIncrement())
361  outputs.emplace_back(&col);
362  else
363  // When the value is auto incremented we need to capture the value
364  // as the current value is likely not correct to the DB.
365  values.emplace_back(&col);
366  }
367 
368  sql_query.append(tableName());
369  sql_query.append(" (");
370  for (exint i = 0; i < values.size(); i++)
371  {
372  const UT_ORMFieldColumn* col = values[i];
373  if (!col->isAutoIncrement())
374  {
375  if (i != 0)
376  sql_query.append(',');
377  sql_query.append(col->name());
378  }
379  }
380  sql_query.append(')');
381 
382 
383  sql_query.append(" VALUES (");
384  for (exint i = 0; i < values.size(); i++)
385  {
386  const UT_ORMFieldColumn* col = values[i];
387  if (!col->isAutoIncrement())
388  {
389  if (i != 0)
390  sql_query.append(',');
391  sql_query.append('?');
392  }
393  }
394  sql_query.append(')');
395 
396  if (outputs.size() > 0)
397  {
398  sql_query.append(" RETURNING ");
399  // Add any outputs we might need to capture
400  for (exint i = 0, c = outputs.size(); i < c; i++)
401  {
402  if (i != 0)
403  sql_query.append(',');
404  sql_query.appendFormat("{}", outputs[i]->name());
405  }
406  }
407  sql_query.append(';');
408 
409  UT_ASSERT(myORM);
410  UT_SqlStatement stmt(myORM->cursor());
411 
412  if (stmt.getError())
413  {
414  ec = stmt.getError();
415  return false;
416  }
417 
418  if (!stmt.prepare(sql_query, ec))
419  return false;
420  for (int i = 0; i < values.size(); i++)
421  {
422  const UT_ORMFieldColumn* col = values[i];
423  if (!col->isAutoIncrement())
424  {
425  if (UT_IORMFieldAdapter* adapter = col->adapter(); adapter)
426  adapter->bind(&obj, stmt, i+1, ec);
427  else
428  {
429  // TODO: error
430  }
431  if (ec)
432  return false;
433  }
434  }
435 
436  stmt.step();
437 
438  int changes = stmt.changes();
439 
440  // If the insert was a success load any outputs into the columns.
441  if (changes > 0 && outputs.size() > 0)
442  {
443  for (exint i = 0, c= outputs.size(); i < c; i++)
444  {
445  const UT_ORMFieldColumn* output = outputs[i];
446  if (UT_IORMFieldAdapter* adapter = output->adapter(); adapter)
447  {
448  adapter->load(&obj, stmt, i, ec);
449  }
450  }
451 
452  stmt.step();
453  }
454 
455  if (stmt.getError())
456  ec = stmt.getError();
457 
458  return changes > 0;
459 }
460 
461 template <typename T>
462 void
464 {
466 
467  UT_WorkBuffer sql_query;
468  sql_query = "DELETE FROM ";
469  sql_query.append(tableName());
470  sql_query.append(" WHERE ");
471  sql_query.append(column->name());
472  sql_query.append("=?");
473 
474  UT_SqlStatement stmt(myORM->cursor());
475  if (stmt.getError())
476  {
477  ec = stmt.getError();
478  return;
479  }
480 
481  if (!stmt.prepare(sql_query, ec))
482  return;
483  if (UT_IORMFieldAdapter* adapter = column->adapter(); adapter)
484  adapter->bind(&obj, stmt, 1, ec);
485  else
486  {
487  // TODO: error
488  }
489  stmt.run();
490  if (stmt.getError())
491  {
492  ec = stmt.getError();
493  return;
494  }
495 
496  // Object has been removed from the db let the c++ object know in the
497  // event it has to do some extra work
498  for (auto&& meta_column : myColumns)
499  {
500  UT_IORMFieldAdapter* adapter = column->adapter();
501  adapter->onDelete(&obj, meta_column.onDelete(), ec);
502 
503  if (ec)
504  return;
505  }
506 }
507 
508 template <typename T>
509 bool
511 {
512  UT_WorkBuffer sql_query;
513  sql_query = "UPDATE ";
514  sql_query.append(tableName());
515  sql_query.append(" SET ");
516 
517  for (exint i = 0; i < myColumns.size(); i++)
518  {
519  if (myColumns[i].isPrimaryKey() || myColumns[i].isUnique())
520  continue;
521 
522  if (i != 0)
523  sql_query.append(',');
524  sql_query.append(myColumns[i].name());
525  sql_query.append("=?");
526  }
527  sql_query.append(" WHERE ");
528 
529  exint count = 0;
530  for (exint i = 0; i < myColumns.size(); i++)
531  {
532  if (myColumns[i].isUnique() || myColumns[i].isPrimaryKey())
533  {
534  if (count > 0)
535  {
536  sql_query.append(" AND ");
537  }
538  sql_query.append(myColumns[i].name());
539  sql_query.append("=?");
540  count++;
541  }
542  }
543  sql_query.append(";");
544 
545  UT_SqlStatement stmt(myORM->cursor());
546 
547  if (stmt.getError())
548  {
549  ec = stmt.getError();
550  return false;
551  }
552 
553  if (!stmt.prepare(sql_query, ec))
554  return false;
555 
556  // Bind the values
557  int index = 0;
558  for (int i = 0; i < myColumns.size(); i++)
559  {
560  if (myColumns[i].isPrimaryKey() || myColumns[i].isUnique())
561  continue;
562  index++;
563  if (UT_IORMFieldAdapter* adapter = myColumns[i].adapter(); adapter)
564  adapter->bind(&obj, stmt, index, ec);
565  else
566  {
567  // TODO: error
568  }
569  if (ec)
570  return false;
571  }
572  // Bind the primary key conditions
573  for (int i = 0; i < myColumns.size(); i++)
574  {
575  if (myColumns[i].isPrimaryKey() || myColumns[i].isUnique())
576  {
577  index++;
578  if (UT_IORMFieldAdapter* adapter = myColumns[i].adapter(); adapter)
579  adapter->bind(&obj, stmt, index, ec);
580  else
581  {
582  // TODO: error
583  }
584  if (ec)
585  return false;
586  }
587  }
588 
589  stmt.run();
590  if (stmt.getError())
591  ec = stmt.getError();
592 
593  if (stmt.changes() > 0)
594  obj.myHasSavedBefore = true;
595 
596  return stmt.changes() > 0;
597 }
598 
599 template <typename T, typename PK>
601 UT_ORMModelMeta::fetch(const PK& pk, UT_ErrorCode& ec) const
602 {
603  UT_WorkBuffer sql_query;
604  sql_query = "SELECT ";
605  for (exint i = 0; i < myColumns.size(); i++)
606  {
607  if (i != 0)
608  sql_query.append(',');
609  sql_query.append(' ');
610  sql_query.append(myColumns[i].name());
611  }
612  sql_query.append(" FROM ");
613  sql_query.append(tableName());
614  sql_query.append(" WHERE ");
615  sql_query.append(primaryKey()->name());
616  sql_query.append("=?");
617  sql_query.append(';');
618 
619  UT_SqlStatement stmt(myORM->cursor());
620 
621  if (stmt.getError())
622  {
623  ec = stmt.getError();
624  return std::nullopt;
625  }
626 
627  if (!stmt.prepare(sql_query, ec))
628  return std::nullopt;
629 
630  stmt.bindAll(pk);
631 
632  stmt.step();
633 
634  if (stmt.getError())
635  {
636  ec = stmt.getError();
637  return std::nullopt;
638  }
639 
640  if (!stmt.hasRow())
641  return std::nullopt;
642 
643  T item;
644  if (loadObject_(item, stmt, ec))
645  return item;
646 
647  return std::nullopt;
648 }
649 
650 template <typename T>
653 {
654  UT_WorkBuffer sql_query;
655  sql_query.append("SELECT");
656  for (exint i = 0; i < myColumns.size(); i++)
657  {
658  if (i != 0)
659  sql_query.append(',');
660  sql_query.append(' ');
661  sql_query.append(myColumns[i].name());
662  }
663  sql_query.append(" FROM ");
664  sql_query.append(tableName());
665  sql_query.append(';');
666 
667  UT_SqlStatement stmt(myORM->cursor());
668  if (stmt.getError())
669  {
670  ec = stmt.getError();
671  return UT_Array<T>{};
672  }
673 
674  if (!stmt.prepare(sql_query, ec))
675  return UT_Array<T>{};
676  UT_Array<T> items;
677  while (stmt.step() && !stmt.getError())
678  {
679  T item;
680  if (loadObject_(item, stmt, ec))
681  items.emplace_back(std::move(item));
682  else
683  return UT_Array<T>{};
684  }
685 
686  return items;
687 }
688 
689 #define UT_DECLARE_MODEL() \
690 public: \
691  class Meta; \
692  friend class UT_ORMModelMeta; \
693  static const Meta& metaInfo();
694 
695 #define UT_DEFINE_MODEL(_model_) \
696  const _model_::Meta& _model_::metaInfo() \
697  { \
698  static Meta _instance; \
699  return _instance; \
700  }
701 #endif // __UT_SQLORM_H__
UT_ORMModel & operator=(const UT_ORMModel &)=default
const UT_StringHolder & name() const
Definition: UT_ORMColumn.h:83
void addAutoField(const UT_StringHolder &name, FieldT Cls::*field, unsigned props=UT_ORMColumn::Empty)
Definition: UT_SQLORM.h:154
const UT_ErrorCode & getError() const
Definition: UT_SQL.h:615
UT_Array< const UT_ORMModelMeta * > myRegisteredMetas
Definition: UT_SQLORM.h:318
UT_Optional< T > fetch(const PK &pk, UT_ErrorCode &ec) const
Definition: UT_SQLORM.h:601
void remove(T &obj, UT_ErrorCode &ec) const
Definition: UT_SQLORM.h:463
static UT_Optional< T > fetch(const PK &pk, UT_ErrorCode &ec)
Definition: UT_SQLORM.h:105
UT_ORMModelMeta(const UT_StringHolder &name, UT_SqlOrm *orm=nullptr)
Definition: UT_SQLORM.h:126
SYS_FORCE_INLINE T * SYSconst_cast(const T *foo)
Definition: SYS_Types.h:136
UT_Array< T > fetchAll(UT_ErrorCode &ec) const
Definition: UT_SQLORM.h:652
const UT_ORMFieldColumn * primaryKey() const
Definition: UT_SQLORM.h:188
SqlOrmError
Error codes to describe ORM errors.
Definition: UT_SQLORM.h:46
int64 exint
Definition: SYS_Types.h:125
bool hasSavedBefore() const
Definition: UT_SQLORM.h:117
virtual void onDelete(void *obj, UT_ORMColumn::OnDelete ondelete, UT_ErrorCode &ec)
Definition: UT_ORMField.h:55
bool save(T &obj, UT_ErrorCode &ec, bool force_insert=false, bool force_update=false) const
Definition: UT_SQLORM.h:323
#define UT_API
Definition: UT_API.h:14
**But if you need a result
Definition: thread.h:613
void close() override
std::error_category UT_ErrorCategory
Definition: UT_ErrorCode.h:22
const UT_Array< UT_ORMFieldColumn > & columns() const
Definition: UT_SQLORM.h:200
exint size() const
Definition: UT_Array.h:646
std::optional< T > UT_Optional
Definition: UT_Optional.h:26
std::unique_ptr< T, Deleter > UT_UniquePtr
A smart pointer for unique ownership of dynamically allocated objects.
Definition: UT_UniquePtr.h:39
void build()
Call to build out all of your meta information.
Definition: UT_SQLORM.h:137
size_t appendFormat(const char *fmt, const Args &...args)
UT_IORMFieldAdapter * adapter()
Definition: UT_ORMField.h:78
exint emplace_back(S &&...s)
Definition: UT_ArrayImpl.h:769
bool isAutoIncrement() const
Definition: UT_ORMColumn.h:123
UT_API const UT_ErrorCategory & GetOrmErrorCategory()
UT_StringHolder myDBPath
Definition: UT_SQLORM.h:313
bool update(T &obj, UT_ErrorCode &ec) const
Definition: UT_SQLORM.h:510
#define UT_NON_COPYABLE(CLASS)
Define deleted copy constructor and assignment operator inside a class.
GLuint const GLchar * name
Definition: glcorearb.h:786
UT_ErrorCode make_error_code(UT::OrmMigError e)
void configure(const UT_StringHolder &db_path)
Definition: UT_SQLORM.h:266
UT_ORMModel()=default
bool update(UT_ErrorCode &ec)
Definition: UT_SQLORM.h:98
bool save(UT_ErrorCode &ec, bool force_insert=false, bool force_update=false)
Definition: UT_SQLORM.h:85
std::error_code UT_ErrorCode
Definition: UT_ErrorCode.h:20
auto UTmakeErrorCode(EnumT e) -> decltype(make_error_code(e))
Make a UT_ErrorCode based on the provided namespaced enum class.
Definition: UT_ErrorCode.h:29
void registerModelMeta()
Definition: UT_SQLORM.h:289
UT_Array< UT_ORMFieldColumn > myColumns
Definition: UT_SQLORM.h:251
UT_SqlStatement cursor() const
Definition: UT_SQLORM.h:271
const UT_StringHolder & tableName() const
Definition: UT_SQLORM.h:187
GLenum GLsizei GLsizei GLint * values
Definition: glcorearb.h:1602
GLuint index
Definition: glcorearb.h:786
UT_SqlOrm(const UT_StringHolder &db_path)
Definition: UT_SQLORM.h:258
SYS_FORCE_INLINE void append(char character)
UT_ThreadSpecificValue< UT_SqlDatabase > myDBLocal
Definition: UT_SQLORM.h:314
void addField(const UT_StringHolder &name, UT_ORMForeignKeyField< ForeignModel > Cls::*field, unsigned props=UT_ORMColumn::Empty, UT_ORMColumn::OnDelete ondelete_type=UT_ORMColumn::OnDelete::DoNothing)
Definition: UT_SQLORM.h:174
UT_StringHolder myName
Definition: UT_SQLORM.h:250
static UT_Array< T > fetchAll(UT_ErrorCode &ec)
Definition: UT_SQLORM.h:111
SYS_AtomicInt32 myHasConfiguredInternals
Definition: UT_SQLORM.h:316
#define UT_ASSERT(ZZ)
Definition: UT_Assert.h:156
bool loadObject_(T &obj, UT_SqlStatement &stmt, UT_ErrorCode &ec) const
Definition: UT_SQLORM.h:221
bool myHasSavedBefore
Definition: UT_SQLORM.h:120
type
Definition: core.h:1059
GLenum GLenum GLsizei void GLsizei void * column
Definition: glad.h:5135
virtual ~UT_ORMModel()=default
UT_SqlOrm * myORM
Definition: UT_SQLORM.h:252
void addField(const UT_StringHolder &name, FieldT Cls::*field, unsigned props=UT_ORMColumn::Empty)
Definition: UT_SQLORM.h:164
GLint GLsizei count
Definition: glcorearb.h:405
bool myIsUsingAutoIncPK
Definition: UT_SQLORM.h:249
UT_SqlTransaction transaction(UT_ErrorCode *ec=nullptr)
Definition: UT_SQLORM.h:277
GLenum GLuint GLsizei const GLenum * props
Definition: glcorearb.h:2525