HDK
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
UT_SQL.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_SQL.h
7  *
8  * COMMENTS:
9  * C++ Wrapper around sqlite
10  */
11 
12 #ifndef __UT_SQL_H__
13 #define __UT_SQL_H__
14 
15 #include "UT_API.h"
16 
17 #include "UT_Error.h"
18 #include "UT_ErrorCode.h"
19 #include "UT_StringHolder.h"
20 #include "UT_IntrusivePtr.h"
21 
22 #include <SYS/SYS_Compiler.h>
23 #include <SYS/SYS_Inline.h>
24 
25 #include <map>
26 
27 class UT_SqlDatabase;
28 
29 class sqlite3;
30 class sqlite3_stmt;
31 
32 #define NO_DISCARD SYS_NO_DISCARD_RESULT
33 
34 // NB: An actual namespace is used here because std::error_code does an ADL
35 // lookup when creating std::error_code with just the enum class. This allows us
36 // to do `if (ec == UT::SqlError::UT_SQL_HAS_STEPPED)` instead of
37 // `if (ec == UTmakeErrorCode(UT_SqlError::UT_SQL_HAS_STEPPED))`
38 namespace UT
39 {
40 /// Error codes to describe errors as a result of UT_SQL
41 enum class SqlError
42 {
43  UT_SQL_OK = 0,
46 };
47 
50 
51 inline UT_ErrorCode
53 {
54  return UT_ErrorCode{static_cast<int>(e), GetSqlErrorCategory()};
55 }
56 } // namespace UT
57 
58 namespace std
59 {
60  template <> struct is_error_code_enum<UT::SqlError> : true_type
61  {
62  };
63 }
64 
66 {
67 public:
68  UT_SqlStatementHandleId(const char* source_file, exint source_line) :
69  mySourceFile(source_file), mySourceLine(source_line)
70  {}
71 
72  bool operator<(const UT_SqlStatementHandleId& id) const
73  {
74  if (mySourceLine != id.mySourceLine)
75  return mySourceLine < id.mySourceLine;
76  return SYSstrcmp(mySourceFile, id.mySourceFile) < 0;
77  }
78 private:
79  const char* mySourceFile;
80  exint mySourceLine;
81 };
82 
83 #define UT_SQL_ID UT_SqlStatementHandleId(__FILE__, __LINE__)
84 
86  : public UT_IntrusiveRefCounter<UT_SqlStatementHandle>
87 {
88 public:
90  const UT_SqlDatabase& db,
91  const UT_StringRef& sql,
92  UT_ErrorCode& ec);
94 
97 
98  sqlite3_stmt *stmt() const { return myCtx; }
99  const UT_SqlDatabase &db() const { return myDB; }
100 
101  bool isValid() const { return myCtx != nullptr; }
102 
103  int columnCount() const { return myColumnCount; }
104 
105 private:
106  void finalize();
107 
108  const UT_SqlDatabase &myDB;
109  sqlite3_stmt *myCtx;
110  int myColumnCount;
111 };
112 
114 {
115 public:
117  const UT_SqlDatabase& db,
118  const UT_StringRef& sql);
120  ~UT_SqlStatement();
121 
122  UT_SqlStatement(const UT_SqlStatement &) = delete;
123  UT_SqlStatement &operator=(const UT_SqlStatement &) = delete;
124  UT_SqlStatement(UT_SqlStatement &&stmt) = delete;
125  UT_SqlStatement &operator=(UT_SqlStatement &&stmt) = delete;
126 
127  enum DataType
128  {
129  kUnknown = -1,
134  kNull
135  };
136 
137  struct null_tag_t {};
138 
139  void reset(bool clear_bindings = false);
140 
141  NO_DISCARD int columnAsInt(int idx) const;
142  NO_DISCARD bool columnAsBool(int idx) const;
143  NO_DISCARD int64 columnAsInt64(int idx) const;
144  NO_DISCARD UT_StringHolder columnAsStr(int idx) const;
145  NO_DISCARD double columnAsDouble(int idx) const;
146  NO_DISCARD UT_IntArray columnAsIntArray(int idx) const;
147  NO_DISCARD UT_Int64Array columnAsInt64Array(int idx) const;
148  // Stores size of blob in bytes in 'size'.
149  NO_DISCARD const void *columnAsBlob(int idx, int &size) const;
150  NO_DISCARD null_tag_t columnAsNull(int idx) const;
151  NO_DISCARD UT_StringHolder columnName(int idx) const;
152  NO_DISCARD int columnCount() const;
153  NO_DISCARD DataType columnType(int idx) const;
154  NO_DISCARD int columnBytes(int idx) const;
155 
156  // The original text of the sql statement. Do not keep a pointer to it.
157  const char* sql() const;
158 
159  // Add more binds as needed
160  // This binds a NULL
161  bool bind(int idx, null_tag_t);
162  bool bind(int idx, const UT_StringRef &value);
163  bool bind(int idx, const char *value);
164  bool bind(int idx, int value);
165  bool bind(int idx, int64 value);
166  bool bind(int idx, bool value);
167  bool bind(int idx, double value);
168  // The following methods bind arrays as strings of the form [#, #, ..., #]
169  bool bind(int idx, const UT_IntArray &value);
170  bool bind(int idx, const UT_Int64Array &value);
171  // For binding blobs. `size` is required in bytes. Note that this
172  // function does not destroy `value`.
173  bool bind(int idx, const void *value, int size);
174 
175  // TODO: Try redesigning this to work with the bind that accepts a blob.
176  template <typename... Args>
177  bool bindAll(Args &&... args)
178  { return bindHelper(1, std::forward<Args>(args)...); }
179 
180  bool bindNull(int idx)
181  { return bind(idx, null_tag_t()); }
182 
183  NO_DISCARD bool isValid() const;
184 
185  // Called when there is nothing to grab after (i.e. INSERT).
186  bool run();
187  // Called when there are rows to iterate over (i.e. SELECT).
188  bool step();
189 
190  template <typename T>
191  T get(int idx) const
192  {
193  static_assert(
194  !std::is_same_v<T, void> && std::is_same_v<T, void>,
195  "Type not handled.");
196  return T();
197  }
198 
199  // Methods on this class will always test myError before doing their
200  // work. If there is already an error, they will do nothing.
201  const UT_ErrorCode &getError() const
202  { return myError; }
203 
204 protected:
205  // Verify that we have a stepped
206  bool verifyHasStepped() const;
207  // Verify that the index is a valid index.
208  bool verifyIndex(int idx) const;
209  bool verifyColumn(int idx) const;
210 
211 private:
212  friend class UT_SqlDatabase;
213 
214  int step_();
215 
216  bool bindHelper(int)
217  { return true; }
218  template <typename T, typename... Args>
219  bool bindHelper(int idx, T value, Args &&... args)
220  {
221  if (!bind(idx, value))
222  return false;
223  return bindHelper(idx + 1, std::forward<Args>(args)...);
224  }
225 
227 
228  mutable UT_ErrorCode myError;
229  bool myHasStepped;
230 };
231 
232 template <>
233 inline double
234 UT_SqlStatement::get<double>(int idx) const
235 {
236  return columnAsDouble(idx);
237 }
238 
239 template <>
241 UT_SqlStatement::get<UT_SqlStatement::null_tag_t>(int idx) const
242 {
243  return columnAsNull(idx);
244 }
245 
246 template <>
247 inline UT_StringHolder
248 UT_SqlStatement::get<UT_StringHolder>(int idx) const
249 {
250  return columnAsStr(idx);
251 }
252 
253 template <>
254 inline int
255 UT_SqlStatement::get<int>(int idx) const
256 {
257  return columnAsInt(idx);
258 }
259 
260 template <>
261 inline int64
262 UT_SqlStatement::get<int64>(int idx) const
263 {
264  return columnAsInt64(idx);
265 }
266 
267 template <>
268 inline bool
269 UT_SqlStatement::get<bool>(int idx) const
270 {
271  return columnAsBool(idx);
272 }
273 
274 template <>
275 inline const void *
276 UT_SqlStatement::get<const void *>(int idx) const
277 {
278  int size;
279  return columnAsBlob(idx, size);
280 }
281 
283 {
284 public:
285  /// Open an sqlite object with the provided filename.
286  /// - See setBusyTimeout() for details about busy_timeout.
287  /// - no_raise will avoid raising an exception if an error occurred
288  // while opening the database.
290  int busy_timeout = 0,
291  UT_ErrorCode *ec = nullptr);
292  UT_SqlDatabase();
293  ~UT_SqlDatabase();
294 
295  UT_SqlDatabase(const UT_SqlDatabase &) = delete;
296  UT_SqlDatabase &operator=(const UT_SqlDatabase &) = delete;
297 
298  bool close(UT_ErrorCode *ec = nullptr);
299  bool open(const UT_StringHolder& filename, UT_ErrorCode *ec = nullptr);
300 
302  { return myCtx != nullptr && myFilename.isstring(); }
303 
304  NO_DISCARD bool isReadOnly(const char *db = "main",
305  UT_ErrorCode *ec = nullptr) const;
306 
307  /// Helper function to run an sql statement with provided typed args.
308  template <typename... Args>
309  bool run(UT_ErrorCode *ec, const UT_StringRef &sql, Args &&... args)
310  {
311  UT_SqlStatement stmt(*this, sql);
312  if (sizeof...(Args) > 0)
313  stmt.bindAll(ec, std::forward<Args>(args)...);
314  stmt.step();
315  if (ec)
316  *ec = stmt.getError();
317  return !(bool)stmt.getError();
318  }
319  /// Returns the number of rows modified, inserted or deleted
320  int exec(const UT_StringRef &sql, UT_ErrorCode *ec = nullptr) const;
321 
322  /// Check the database's "data_version"
323  /// (see https://www.sqlite.org/pragma.html#pragma_data_version)
324  NO_DISCARD int dataVersion(UT_ErrorCode *ec = nullptr) const;
325 
326  /// Get/set the database's "user_version"
327  /// (see https://www.sqlite.org/pragma.html#pragma_user_version)
328  NO_DISCARD int userVersion(UT_ErrorCode *ec = nullptr) const;
329  void setUserVersion(int version, UT_ErrorCode *ec = nullptr) const;
330 
331  /// Check if the specified table exists.
332  NO_DISCARD bool tableExists(const UT_StringRef& name,
333  UT_ErrorCode *ec = nullptr) const;
334  NO_DISCARD bool indexExists(const UT_StringRef& name,
335  UT_ErrorCode *ec = nullptr) const;
336  NO_DISCARD bool viewExists(const UT_StringRef& name,
337  UT_ErrorCode *ec = nullptr) const;
338  NO_DISCARD bool columnExists(
339  const UT_StringRef& table_name,
340  const UT_StringRef& column_name,
341  UT_ErrorCode *ec = nullptr) const;
342  NO_DISCARD UT_StringHolder errorMessage() const;
343  NO_DISCARD int errorCode() const;
344  NO_DISCARD int extendedErrorCode() const;
345 
346  NO_DISCARD UT_StringHolder getSchema(UT_ErrorCode *ec = nullptr) const;
347 
348  /// This sets a busy handler that sleeps for a specified amount of time
349  /// when a table is locked. The handler will sleep multiple times until at
350  /// least milliseconds of sleeping have accumulated. After the timeout the
351  /// handler returns 0 which causes the step() to return kSQLITE_BUSY.
352  bool setBusyTimeout(int timeout_ms, UT_ErrorCode *ec = nullptr);
353 
354  /// Copy the contents of this database into the provided destination
355  /// database. Return true on success and false otherwise.
356  /// On failure, the error code is set in the destination database.
357  bool copyTo(UT_SqlDatabase &destination, UT_ErrorCode *ec = nullptr) const;
358 
359  /// Get a sql statement that is retrieved from the cache. If the statement
360  /// is not already cached then its compiled and then cached if the compiled
361  /// statement is valid.
363  const UT_SqlStatementHandleId &id,
364  const UT_StringRef& sql,
365  UT_ErrorCode *ec = nullptr) const;
366 
367  /// Find an sql handle based on its id. The sql statement must have already
368  /// been added from cachedStatement(). This method is typically used when
369  /// a statement has already been compiled and added to the cache but needs
370  /// to be dynamically looked up some time later.
371  UT_IntrusivePtr<UT_SqlStatementHandle> findCachedStatement(
372  const UT_SqlStatementHandleId& id) const;
373 
374  /// These are primarily used by UT_SqlTransaction (but can be used by any
375  /// client code) and provide an abstraction that resembles nested transations.
376  /// This is inspired by (and follows the same behaviour as):
377  /// https://developer.android.com/reference/android/database/sqlite/SQLiteDatabase#beginTransaction()
378  bool startTransaction(UT_ErrorCode *ec = nullptr);
379  bool endTransaction(bool commit, UT_ErrorCode *ec = nullptr);
380 
381 protected:
382  bool schemaItemExists(
383  const UT_StringRef& type,
384  const UT_StringRef& name,
385  UT_ErrorCode *ec) const;
386 
387 private:
388  bool verifyOpen(
389  const UT_StringHolder &filename,
390  UT_ErrorCode *ec);
391  friend class UT_SqlStatementHandle;
392 
393  mutable std::map<UT_SqlStatementHandleId,
394  UT_IntrusivePtr<UT_SqlStatementHandle>> myCachedStatements;
395  UT_StringHolder myFilename;
396  sqlite3 *myCtx;
397  SYS_AtomicCounter myActiveTransactionDepth;
398  bool myShouldCommitTransaction;
399 };
400 
401 // Transactions are a way to group SQL statements.
402 // This class ensures we properly setup and execute the group (or rollback if needed)
403 //
404 // NOTE: If commit() is not explicitly called before the object is destroyed
405 // (e.g., goes out of scope), the transaction will be aborted (rolled back)
406 //
408 {
409 public:
410  UT_SqlTransaction(UT_SqlDatabase &db, UT_ErrorCode *ec = nullptr);
412 
413  void commit(UT_ErrorCode *ec = nullptr);
414 
415 private:
416  bool myCommited;
417  UT_SqlDatabase &myDB;
418 };
419 
420 #endif // __UT_SQL_H__
421 
GT_API const UT_StringHolder filename
UT_SqlStatementHandleId(const char *source_file, exint source_line)
Definition: UT_SQL.h:68
const UT_ErrorCode & getError() const
Definition: UT_SQL.h:201
#define NO_DISCARD
Definition: UT_SQL.h:32
sqlite3_stmt * stmt() const
Definition: UT_SQL.h:98
UT_ErrorCode make_error_code(UT::SqlError e)
Definition: UT_SQL.h:52
int64 exint
Definition: SYS_Types.h:125
NO_DISCARD SYS_FORCE_INLINE bool isValid() const
Definition: UT_SQL.h:301
#define UT_API
Definition: UT_API.h:14
void close() override
std::error_category UT_ErrorCategory
Definition: UT_ErrorCode.h:22
A reference counter base class for use with UT_IntrusivePtr.
GLintptr GLsizeiptr GLboolean commit
Definition: glcorearb.h:3363
bool run(UT_ErrorCode *ec, const UT_StringRef &sql, Args &&...args)
Helper function to run an sql statement with provided typed args.
Definition: UT_SQL.h:309
UT_IntrusiveRefCounter & operator=(const UT_IntrusiveRefCounter &) noexcept
Assignment operator: Does not modify counter.
UT_API const UT_ErrorCategory & GetSqlErrorCategory()
GLboolean reset
Definition: glad.h:5138
int columnCount() const
Definition: UT_SQL.h:103
int open(float queuesize) override
bool isValid() const
Definition: UT_SQL.h:101
bool operator<(const UT_SqlStatementHandleId &id) const
Definition: UT_SQL.h:72
const UT_SqlDatabase & db() const
Definition: UT_SQL.h:99
#define SYS_FORCE_INLINE
Definition: SYS_Inline.h:45
bool bindAll(Args &&...args)
Definition: UT_SQL.h:177
UT_API const UT_ErrorCategory & GetSqliteErrorCategory()
long long int64
Definition: SYS_Types.h:116
int SYSstrcmp(const char *a, const char *b)
Definition: SYS_String.h:237
bool bindNull(int idx)
Definition: UT_SQL.h:180
GLuint const GLchar * name
Definition: glcorearb.h:786
GT_API const UT_StringHolder version
std::error_code UT_ErrorCode
Definition: UT_ErrorCode.h:20
GLsizeiptr size
Definition: glcorearb.h:664
SqlError
Error codes to describe errors as a result of UT_SQL.
Definition: UT_SQL.h:41
**If you just want to fire and args
Definition: thread.h:609
Definition: core.h:1131
type
Definition: core.h:1059