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  * UT_SqlOrm provides support for ORM (object-relational mapping) backed by
11  * your sql backend of choice (currently only sqlite3 is supported). ORM maps
12  * a C++ object (or event a python object) to a database table. There are many
13  * benefits to using the ORM system over hand rolling the sql.
14  * 1. The developer can waste less time on sql and spend more time on the
15  * taks they are actually working on while the ORM system handles all of
16  * the sql work under the hood.
17  * 2. Provides an easy way to cache sql statements for more performant code
18  * 3. Better multi-thread support as this is all handled internally
19  * 4. Easily handle foreign relationships without the developer having to
20  * do anything past specifying the relationships between models.
21  *
22  * An important feature of the ORM system is its migration abilities. The ORM
23  * system handles the migration of the different models internally when
24  * `orm.migrate` is called (it can be called multiple times pior to first use).
25  * The migration is a critical step to the ORM system and great care must be
26  * taken when defining the migrations.
27  * - Whenever a column is added or dropped a new migration must be made.
28  * NOTE: It cannot be made combined with an existing migration as it would
29  * cause your new migration to not run.
30  * - Each model must define its own migration to ensure each model migrates
31  * independently of each other.
32  * - Once a migration has been committed it CANNOT be changed as this would
33  * cause undefined behaviour to the database. If a change needs to be made
34  * just add a new migration step for that model.
35  *
36  * For performing custom searches checkout UT_ORMQuerySet for building
37  * custom queries besides what are found from UT_ORMModelMeta.
38  *
39  * Basic Usage:
40  * class User : public UT_ORMModel<User>
41  * {
42  * UT_DECLARE_MODEL();
43  *
44  * public:
45  * class Meta : public UT_ORMModelMeta
46  * {
47  * public:
48  * Meta() : UT_ORModelMeta("user")
49  * {}
50  * void doBuild() override
51  * {
52  * // These can change at any time. If a change is made there MUST
53  * // a corresponding migration entry.
54  * addField("username", &User::myUsername, UT_ORMColumn::PrimaryKey);
55  * addField("password", &User::myPassword);
56  * addField("email", &User::myEmail);
57  * }
58  * void doMigrate(UT_ORMMigrationBuilder& builder) const override
59  * {
60  * static UT_StringLit theMigName = "user_mig";
61  * // 001 initial
62  * {
63  * UT_ORMMigration& migration = builder.addMigration(theMigName.asHolder(), "user_001_initial");
64  *
65  * UT_ORMCreateTableOperation& create_table = migration.createModel("user");
66  * create_table.addField("username", UT_ORMColumn::Type::String, UT_ORMColumn::PrimaryKey);
67  * create_table.addField("password", UT_ORMColumn::Type::String);
68  * }
69  * // 002 add email field
70  * {
71  * UT_ORMMigration& migration = builder.addMigration(theMigName.asHolder(), "user_002_add_email");
72  *
73  * UT_ORMColumn email_field("email", UT_ORMColumn::Type::String);
74  * migration.addField(modelName(), std::move(email_field));
75  * }
76  * }
77  * };
78  *
79  * private:
80  * UT_StringHolder myUsername;
81  * UT_StringHolder myPassword;
82  * UT_StringHolder myEmail;
83  * };
84  * // Define in the source file!!!
85  * UT_DEFINE_MODEL(User);
86  *
87  * int main()
88  * {
89  * UT_SqlOrm orm;
90  * // Make sure to register the model so that the ORM system knows about
91  * // its type
92  * orm.registerModelMeta<User>();
93  *
94  * // Run the migration where all the different model meta will run their
95  * // specific migration steps (create tables, add/drop columns, etc).
96  *
97  * // This create a new table called `user` with columns `username`,
98  * // `password`, and `email`.
99  * UT_ORMMigrationResult result;
100  * orm.migrate(result);
101  * if (result.error())
102  * return 1;
103  *
104  * User user;
105  * user.myUsername = "someuser";
106  * user.myPassword = "somepassword";
107  * user.myEmail = "user@email.com";
108  * // Based on whether this specific object has already been saved we will
109  * // first either try to update the object or insert it.
110  * UT_ErrorCode ec;
111  * user.save(ec);
112  * if (ec)
113  * return 1;
114  *
115  * if (UT_Optional<User> opt_user = User::filter(
116  * {UT::orm::FilterArg("username", User::metaInfo().primaryKey(), &user)});
117  * opt_user)
118  * return opt_user.value() == user;
119  *
120  * return 1;
121  * }
122  *
123  */
124 
125 #ifndef __UT_SQLORM_H__
126 #define __UT_SQLORM_H__
127 
128 #include "UT_API.h"
129 
130 #include "UT_Array.h"
131 #include "UT_ArrayStringSet.h"
132 #include "UT_Debug.h"
133 #include "UT_Function.h"
134 #include "UT_Lock.h"
135 #include "UT_NonCopyable.h"
136 #include "UT_ORMError.h"
137 #include "UT_ORMField.h"
138 #include "UT_ORMMigrationResult.h"
139 #include "UT_ORMModelMeta.h"
140 #include "UT_ORMQuerySet.h"
141 #include "UT_Optional.h"
142 #include "UT_Options.h"
143 #include "UT_SGuid.h"
144 #include "UT_SQL.h"
145 #include "UT_SharedPtr.h"
146 #include "UT_StringHolder.h"
147 #include "UT_ThreadSpecificValue.h"
148 #include "UT_UniquePtr.h"
149 #include "UT_WorkBuffer.h"
150 
151 #include <functional>
152 #include <variant>
153 #include <utility>
154 
155 class UT_SqlOrm;
156 class UT_IORMOperation;
157 class UT_IStream;
158 class UT_OStream;
160 class UT_ORMMigration;
161 
163 {
164 public:
165  bool isAdding_() const { return myOrmAdding; }
166 
167 protected:
168  friend class UT_ORMModelMeta;
169  bool myOrmAdding = true;
170 };
171 
172 template <typename T>
174 {
175  friend class UT_ORMModelMeta;
176 public:
177  UT_ORMModel(const UT_ORMModelMeta& meta = T::metaInfo()) :
179  {}
180  virtual ~UT_ORMModel() = default;
181  UT_ORMModel(const UT_ORMModel&) = default;
182  UT_ORMModel& operator=(const UT_ORMModel&) = default;
183 
184  bool save(
185  UT_ErrorCode& ec,
186  bool force_insert = false,
187  bool force_update = false)
188  {
189  return myModelMeta.get().save(*this, ec, force_insert, force_update);
190  }
191  void remove(UT_ErrorCode& ec)
192  {
193  return myModelMeta.get().remove(*this, ec);
194  }
195 
196  const UT_ORMModelMeta& meta() const { return myModelMeta.get(); }
197 
199  const std::initializer_list<UT::orm::FilterArg>& args,
200  UT_ErrorCode& ec,
201  const UT_ORMModelMeta& meta = T::metaInfo())
202  {
203  return meta.template fetch<T>(args, ec);
204  }
205 
206  template <typename PK>
207  static UT_Optional<T> fetch(const PK& pk, UT_ErrorCode& ec,
208  const UT_ORMModelMeta& meta = T::metaInfo())
209  {
210  return meta.template fetch<T>(pk, ec);
211  }
212 
214  UT_ErrorCode& ec,
215  const UT_ORMModelMeta& meta = T::metaInfo())
216  {
217  return meta.template fetchAll<T>(ec);
218  }
219 
221  std::initializer_list<UT::orm::FilterArg>&& args,
222  UT_ErrorCode& ec,
223  const UT_ORMModelMeta& meta = T::metaInfo())
224  {
225  return meta.template filter<T>(
226  std::forward<std::initializer_list<UT::orm::FilterArg>>(args),
227  ec);
228  }
229 
230 #if 0
231  template <typename C>
232  static UT_ORMQuerySetResult findByForeignKey(
233  const UT_StringRef& fk_name,
234  const C& obj,
235  UT_ORMModelMeta& meta = T::metaInfo())
236  {
237  return meta.template findByForeignKey<T, C>(fk_name, obj);
238  }
239 #endif
240 
241  static std::pair<T, bool> getOrCreate(
242  const std::initializer_list<UT::orm::FilterArg>& args,
243  UT_ErrorCode& ec,
244  const UT_ORMModelMeta& meta = T::metaInfo())
245  {
246  return meta.template getOrCreate<T>(args, ec);
247  }
248 
250  const std::initializer_list<UT::orm::FilterArg>& args,
251  UT_ErrorCode& ec,
252  const UT_ORMModelMeta& meta = T::metaInfo())
253  {
254  return meta.template create<T>(args, ec);
255  }
256 
257  template <typename ArrayT>
258  static UT_Array<T> bulkFetch(const ArrayT& items, UT_ErrorCode& ec,
260  const UT_ORMModelMeta& meta = T::metaInfo())
261  {
262  return meta.template bulkFetch<T, ArrayT>(items, ec, col_name);
263  }
264 
265  static void bulkSave(
266  const UT_Array<T>& items,
267  UT_ErrorCode& ec,
268  const UT_ORMModelMeta& meta = T::metaInfo())
269  {
270  return meta.template bulkSave<T>(items, ec);
271  }
272 
273  template <typename... Args>
274  static T fromDB(Args&&... args)
275  {
276  auto&& meta_info = T::metaInfo();
277  return fromDB(meta_info, std::forward<Args>(args)...);
278  }
279 
280  template <typename... Args>
281  static T fromDB(const UT_ORMModelMeta& meta, Args&&... args)
282  {
283  return meta.template fromDB<T, Args...>(
284  std::forward<Args>(args)...);
285  }
286 
287  static bool exists(
288  const std::initializer_list<UT::orm::FilterArg>& args,
289  UT_ErrorCode& ec,
290  const UT_ORMModelMeta& meta = T::metaInfo())
291  {
292  return meta.template exists<T>(args, ec);
293  }
294 
295 protected:
296  std::reference_wrapper<const UT_ORMModelMeta> myModelMeta;
297 };
298 
300 {
301 public:
302  using error_callback_t
304 
305  explicit UT_SqlOrm(const UT_StringHolder& db_path);
306  UT_SqlOrm();
307  virtual ~UT_SqlOrm() = default;
309 
310  void configure(
311  const UT_StringHolder& db_path,
313 
315  {
316  return UT_SqlStatement(database());
317  }
318  const UT_SqlDatabase& database() const
319  {
320  return SYSconst_cast(this)->getDatabase_();
321  }
323  {
324  return getDatabase_();
325  }
326 
329  bool savepoint = true)
330  {
331  return UT_AutoSqlTransaction(
332  database(),
333  std::forward<UT_AutoSqlTransaction::commit_callback_t>(
334  callback),
335  savepoint);
336  }
337 
338  void migrate(UT_ORMMigrationResult& result, bool collect_sql = false);
339 
340  void close(UT_ErrorCode* ec = nullptr);
341 
342  template <typename T>
344  {
345  auto&& meta_info = T::metaInfo();
346  for (auto&& m : myRegisteredMetas)
347  {
348  if (m->tableName() == meta_info.tableName())
349  return;
350  }
351 
352  // The pointer is a singleton so register it without the deleter since
353  // the singleton will manage that part
355  SYSconst_cast(&meta_info),
357 
358  ptr->resetInternals();
359  myBuildStack.emplace_back(ptr);
360  myRegisteredMetas.emplace_back(ptr);
361  }
362 
364  const UT_SharedPtr<UT_ORMModelMeta>& meta)
365  {
366  for (auto&& m : myRegisteredMetas)
367  {
368  if (m->tableName() == meta->tableName())
369  return m.get();
370  }
371 
372  meta->resetInternals();
373  myBuildStack.emplace_back(meta);
374  myRegisteredMetas.emplace_back(meta);
375  return meta.get();
376  }
377 
379  {
380  for (auto&& m : myRegisteredMetas)
381  {
382  if (UT_StringView(m->tableName()) == name)
383  return m;
384  }
385  return nullptr;
386  }
387 
389  {
390  return myRegisteredMetas;
391  }
392 
393 protected:
394  friend class UT_SqlOrmTable;
395 
396  static UT_UniquePtr<UT_IORMOperation> createOperation(
397  const UT_StringRef& type);
398 
399  UT_SqlDatabase& getDatabase_();
400 
401  void error_(const UT_StringHolder& error, const UT_ErrorCode& ec);
402 
403  UT_Array<std::pair<UT_SharedPtr<UT_ORMMigration>, bool>> sortMigrations_(
404  const UT_Array<UT_SharedPtr<UT_ORMMigration>>& migrations,
405  const UT_StringSet& applied_migrations,
406  UT_ErrorCode& ec);
407 
408  void recordMigration_(const UT_ORMMigration& mig);
409  void collectAppliedMigrations_(
410  UT_StringSet& applied_migrations,
412  UT_ErrorCode& ec);
413  void createMigrationTableIfNotExist_(
415  UT_ErrorCode& ec);
416 
420 
423 
424  // Meta objects waiting to be built
426 
428 };
429 
430 #define UT_DECLARE_MODEL() \
431 public: \
432  class Meta; \
433  friend class UT_ORMModelMeta; \
434  static const Meta& metaInfo();
435 
436 #define UT_DEFINE_MODEL(_model_) \
437  const _model_::Meta& _model_::metaInfo() \
438  { \
439  static Meta _instance; \
440  return _instance; \
441  }
442 
443 #endif // __UT_SQLORM_H__
GLuint GLsizei const GLchar * message
Definition: glcorearb.h:2543
UT_ORMModel & operator=(const UT_ORMModel &)=default
UT_Array< UT_SharedPtr< UT_ORMModelMeta > > myRegisteredMetas
Definition: UT_SQLORM.h:427
std::reference_wrapper< const UT_ORMModelMeta > myModelMeta
Definition: UT_SQLORM.h:296
static T fromDB(Args &&...args)
Definition: UT_SQLORM.h:274
static UT_Optional< T > create(const std::initializer_list< UT::orm::FilterArg > &args, UT_ErrorCode &ec, const UT_ORMModelMeta &meta=T::metaInfo())
Definition: UT_SQLORM.h:249
static T fromDB(const UT_ORMModelMeta &meta, Args &&...args)
Definition: UT_SQLORM.h:281
static UT_Optional< T > filter(std::initializer_list< UT::orm::FilterArg > &&args, UT_ErrorCode &ec, const UT_ORMModelMeta &meta=T::metaInfo())
Definition: UT_SQLORM.h:220
const UT_Array< UT_SharedPtr< UT_ORMModelMeta > > & metas() const
Definition: UT_SQLORM.h:388
SYS_FORCE_INLINE T * SYSconst_cast(const T *foo)
Definition: SYS_Types.h:136
bool isAdding_() const
Definition: UT_SQLORM.h:165
static UT_Optional< T > fetch(const PK &pk, UT_ErrorCode &ec, const UT_ORMModelMeta &meta=T::metaInfo())
Definition: UT_SQLORM.h:207
#define UT_API
Definition: UT_API.h:14
UT_ORMModel(const UT_ORMModelMeta &meta=T::metaInfo())
Definition: UT_SQLORM.h:177
**But if you need a result
Definition: thread.h:622
UT_ThreadSpecificValue< UT_SqlDatabase > myDB
Definition: UT_SQLORM.h:418
void close() override
UT_AutoSqlTransaction transaction(UT_AutoSqlTransaction::commit_callback_t &&callback, bool savepoint=true)
Definition: UT_SQLORM.h:327
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
< returns > If no error
Definition: snippets.dox:2
UT_SharedPtr< UT_ORMModelMeta > lookupMeta(const UT_StringView &name)
Definition: UT_SQLORM.h:378
A utility class to do read-only operations on a subset of an existing string.
Definition: UT_StringView.h:40
static std::pair< T, bool > getOrCreate(const std::initializer_list< UT::orm::FilterArg > &args, UT_ErrorCode &ec, const UT_ORMModelMeta &meta=T::metaInfo())
Definition: UT_SQLORM.h:241
GLint GLint GLsizei GLint GLenum GLenum type
Definition: glcorearb.h:108
std::shared_ptr< T > UT_SharedPtr
Wrapper around std::shared_ptr.
Definition: UT_SharedPtr.h:36
const UT_ORMModelMeta & meta() const
Definition: UT_SQLORM.h:196
static UT_Array< T > fetch(const std::initializer_list< UT::orm::FilterArg > &args, UT_ErrorCode &ec, const UT_ORMModelMeta &meta=T::metaInfo())
Definition: UT_SQLORM.h:198
static const UT_StringHolder theEmptyString
static bool exists(const std::initializer_list< UT::orm::FilterArg > &args, UT_ErrorCode &ec, const UT_ORMModelMeta &meta=T::metaInfo())
Definition: UT_SQLORM.h:287
#define UT_NON_COPYABLE(CLASS)
Define deleted copy constructor and assignment operator inside a class.
GLuint const GLchar * name
Definition: glcorearb.h:786
std::function< T > UT_Function
Definition: UT_Function.h:37
UT_Function< void(const UT_StringHolder &, const UT_ErrorCode &)> error_callback_t
Definition: UT_SQLORM.h:303
UT_SqlDatabase & database()
Definition: UT_SQLORM.h:322
bool save(UT_ErrorCode &ec, bool force_insert=false, bool force_update=false)
Definition: UT_SQLORM.h:184
std::error_code UT_ErrorCode
Definition: UT_ErrorCode.h:20
void registerModelMeta()
Definition: UT_SQLORM.h:343
UT_SqlStatement cursor() const
Definition: UT_SQLORM.h:314
error_callback_t myErrorCallback
Definition: UT_SQLORM.h:419
A map of string to various well defined value types.
Definition: UT_Options.h:84
UT_Options myOptions
Definition: UT_SQLORM.h:417
UT_Array< UT_SharedPtr< UT_ORMModelMeta > > myBuildStack
Definition: UT_SQLORM.h:425
const UT_SqlDatabase & database() const
Definition: UT_SQLORM.h:318
auto ptr(T p) -> const void *
Definition: format.h:4331
**If you just want to fire and args
Definition: thread.h:618
UT_ORMModelMeta * registerModelMeta(const UT_SharedPtr< UT_ORMModelMeta > &meta)
Definition: UT_SQLORM.h:363
UT_Lock myInternalLock
Definition: UT_SQLORM.h:421
SYS_AtomicInt32 myHasConfiguredInternals
Definition: UT_SQLORM.h:422
static UT_Array< T > bulkFetch(const ArrayT &items, UT_ErrorCode &ec, const UT_StringRef &col_name=UT_StringHolder::theEmptyString, const UT_ORMModelMeta &meta=T::metaInfo())
Definition: UT_SQLORM.h:258
UT_Function< bool()> commit_callback_t
Definition: UT_SQL.h:1539
static void bulkSave(const UT_Array< T > &items, UT_ErrorCode &ec, const UT_ORMModelMeta &meta=T::metaInfo())
Definition: UT_SQLORM.h:265
static UT_Array< T > fetchAll(UT_ErrorCode &ec, const UT_ORMModelMeta &meta=T::metaInfo())
Definition: UT_SQLORM.h:213
virtual ~UT_ORMModel()=default
GLint GLint GLint GLint GLint GLint GLint GLbitfield GLenum filter
Definition: glcorearb.h:1297