SlHelpers
SQLConn.h
1 // SPDX-License-Identifier: GPL-2.0-only
2 
3 #pragma once
4 
5 #include <filesystem>
6 #include <optional>
7 #include <string>
8 #include <variant>
9 #include <vector>
10 
11 #include "../helpers/Enum.h"
12 #include "../helpers/LastError.h"
13 #include "SQLiteSmart.h"
14 
15 namespace SlSqlite {
16 
20 enum class OpenFlags : unsigned {
21  NONE = 0,
22  CREATE = 1 << 0,
23  NO_FOREIGN_KEY = 1 << 1,
24  ERROR_ON_UNIQUE_CONSTRAINT = 1 << 2,
25  READ_ONLY = 1 << 3,
26 };
27 
28 } // namespace
29 
30 ENABLE_BITMASK_OPERATORS(SlSqlite::OpenFlags);
31 
32 namespace SlSqlite {
33 
37 enum struct TransactionType {
38  DEFERRED,
39  IMMEDIATE,
40  EXCLUSIVE,
41 };
42 
43 class Select;
44 class SQLConn;
45 
50 public:
51  AutoTransaction() = delete;
53  AutoTransaction(const SQLConn &conn,
54  TransactionType type = TransactionType::DEFERRED);
55  ~AutoTransaction() { end(); }
56 
57  AutoTransaction(const AutoTransaction &) = delete;
58  AutoTransaction &operator=(const AutoTransaction &) = delete;
59 
61  AutoTransaction(AutoTransaction &&other) noexcept : m_conn(other.m_conn) {
62  other.m_conn = nullptr;
63  }
66  if (this != &other) {
67  end();
68  m_conn = other.m_conn;
69  other.m_conn = nullptr;
70  }
71 
72  return *this;
73  }
74 
76  void end();
77 
79  bool operator!() const { return !m_conn; }
81  operator bool() const { return m_conn; }
82 private:
83  const SQLConn *m_conn;
84 };
85 
89 class SQLConn {
90  friend class Select;
91 public:
92  virtual ~SQLConn() = default;
93 
95  SQLConn(SQLConn &&) = default;
97  SQLConn &operator=(SQLConn &&) = default;
98 
105  bool open(const std::filesystem::path &dbFile, OpenFlags flags = OpenFlags::NONE)
106  {
107  return openDB(dbFile, flags) && createDB() && prepDB();
108  }
109 
116  bool openDB(const std::filesystem::path &dbFile,
117  OpenFlags flags = OpenFlags::NONE) noexcept;
118 
125  virtual bool createDB() { return true; }
126 
133  virtual bool prepDB() { return true; }
134 
140  bool exec(const std::string &sql) const noexcept { return exec(sql, "exec failed"); }
141 
148  bool attach(const std::filesystem::path &dbFile,
149  std::string_view dbName) const noexcept;
150 
156  bool begin(TransactionType type = TransactionType::DEFERRED) const noexcept;
157 
162  bool end() const noexcept { return exec("END;", "db END failed"); }
163 
169  AutoTransaction beginAuto(TransactionType type = TransactionType::DEFERRED) const noexcept {
170  return AutoTransaction(*this, type);
171  }
172 
174  std::string lastError() const { return m_lastError.lastError(); }
176  int lastErrorCode() const { return m_lastError.get<0>(); }
178  int lastErrorCodeExt() const { return m_lastError.get<1>(); }
179 
180 protected:
181  SQLConn() : m_flags(OpenFlags::NONE) {}
182 
184  using BindVal = std::variant<std::monostate, int, unsigned, std::string, std::string_view>;
186  using Binding = std::vector<std::pair<std::string, BindVal>>;
188  using Column = std::variant<std::monostate, int, std::string>;
190  using Row = std::vector<Column>;
192  using SelectResult = std::vector<Row>;
193 
195  enum TableFlags : unsigned {
196  TABLE_TEMPORARY = 1u << 0,
197  };
198 
200  struct TableEntry {
202  std::string name;
204  std::vector<std::string> columns;
206  unsigned flags = 0u;
207  };
208 
210  using Tables = std::vector<TableEntry>;
212  using Indices = std::vector<std::pair<std::string, std::string>>;
214  using Triggers = Indices;
216  using Views = Indices;
218  using Statements = std::vector<std::pair<std::reference_wrapper<SQLStmtHolder>, std::string>>;
219 
221  bool createTables(const Tables &tables) const noexcept;
223  bool createIndices(const Indices &indices) const noexcept;
225  bool createTriggers(const Triggers &triggers) const noexcept;
227  bool createViews(const Views &views) const noexcept;
228 
230  bool prepareStatement(std::string_view sql, SQLStmtHolder &stmt) const noexcept;
232  bool prepareStatements(const Statements &stmts) const noexcept;
233 
235  bool bind(const SQLStmtHolder &ins, const std::string &key,
236  const BindVal &val, bool transient = false) const noexcept;
238  bool bind(const SQLStmtHolder &ins, const Binding &binding,
239  bool transient = false) const noexcept;
241  bool step(const SQLStmtHolder &ins, uint64_t *affected = nullptr,
242  bool *uniqueError = nullptr) const noexcept;
244  bool insert(const SQLStmtHolder &ins, const Binding &binding = {},
245  uint64_t *affected = nullptr) const noexcept;
246 
248  std::optional<SQLConn::SelectResult>
249  select(const SQLStmtHolder &sel, const Binding &binding) const noexcept;
250 
252  static BindVal valueOrNull(bool cond, BindVal val) {
253  return cond ? std::move(val) : std::monostate();
254  }
255 
258 
260  LastError &setError(int ret, std::string_view error, bool errmsg = false) const;
261 
265  OpenFlags m_flags;
268 private:
269  bool exec(const std::string &SQL, std::string_view errorMsg,
270  bool includeSQL = false) const noexcept;
271 
272  static constexpr bool isUniqueConstraint(int sqlExtError) noexcept;
273  static int busyHandler(void *, int count);
274  void dumpBinding(const Binding &binding) const noexcept;
275 };
276 
277 inline AutoTransaction::AutoTransaction(const SQLConn &conn, TransactionType type)
278  : m_conn(conn.begin(type) ? &conn : nullptr)
279 {
280 }
281 
282 inline void AutoTransaction::end()
283 {
284  if (m_conn) {
285  m_conn->end();
286  m_conn = nullptr;
287  }
288 }
289 
290 }
bool prepareStatement(std::string_view sql, SQLStmtHolder &stmt) const noexcept
Prepate one sql string into stmt.
Begin a transaction in the constructor and end in the destructor.
Definition: SQLConn.h:49
OpenFlags m_flags
OpenFlags.
Definition: SQLConn.h:265
bool createTables(const Tables &tables) const noexcept
Create tables in the DB as specified in tables.
std::string lastError() const
Return the last error string if some.
Definition: SQLConn.h:174
Indices Triggers
Triggers to be created by createTriggers()
Definition: SQLConn.h:214
virtual bool createDB()
Creates tables, views, triggers and such.
Definition: SQLConn.h:125
void end()
End the transaction manually.
Definition: SQLConn.h:282
SQLite3 connection (the core class)
Definition: SQLConn.h:89
TableFlags
Flags for TableEntry::flags.
Definition: SQLConn.h:195
Wrapper around std::unique_ptr for easier re-definition of free.
Definition: Unique.h:41
static BindVal valueOrNull(bool cond, BindVal val)
A helper to build a null BindVal if cond does not hold, val otherwise.
Definition: SQLConn.h:252
std::vector< Row > SelectResult
Complete SELECT result.
Definition: SQLConn.h:192
bool end() const noexcept
End a transaction.
Definition: SQLConn.h:162
AutoTransaction(AutoTransaction &&other) noexcept
Move constructor.
Definition: SQLConn.h:61
std::tuple_element_t< idx, Tuple > & get() noexcept
Get n-th error member.
Definition: LastError.h:22
bool exec(const std::string &sql) const noexcept
Execute sql.
Definition: SQLConn.h:140
int lastErrorCode() const
Return the last error number.
Definition: SQLConn.h:176
bool open(const std::filesystem::path &dbFile, OpenFlags flags=OpenFlags::NONE)
Open a database connection (openDB() + createDB() + prepDB())
Definition: SQLConn.h:105
LastError m_lastError
The last error + error code + extended error code.
Definition: SQLConn.h:267
Definition: SQLConn.h:15
std::vector< std::pair< std::string, BindVal > > Binding
Bind name -> bind value.
Definition: SQLConn.h:186
bool createViews(const Views &views) const noexcept
Create views in the DB as specified in views.
bool prepareStatements(const Statements &stmts) const noexcept
Prepate all statements as specified in stmts.
bool createTriggers(const Triggers &triggers) const noexcept
Create triggers in the DB as specified in triggers.
AutoTransaction beginAuto(TransactionType type=TransactionType::DEFERRED) const noexcept
Begin a transaction which is automatically ended when the returned object dies.
Definition: SQLConn.h:169
LastError & setError(int ret, std::string_view error, bool errmsg=false) const
Set last error to ret and error.
A table to be created by createTables()
Definition: SQLConn.h:200
virtual bool prepDB()
Prepares statements.
Definition: SQLConn.h:133
int lastErrorCodeExt() const
Return the last extended error number.
Definition: SQLConn.h:178
bool attach(const std::filesystem::path &dbFile, std::string_view dbName) const noexcept
Attach another database using ATTACH.
std::optional< SQLConn::SelectResult > select(const SQLStmtHolder &sel, const Binding &binding) const noexcept
Perform one SELECT (sel), using the passed binding.
Indices Views
Views to be created by createViews()
Definition: SQLConn.h:216
std::vector< std::pair< std::reference_wrapper< SQLStmtHolder >, std::string > > Statements
Statements to be prepared by prepareStatements()
Definition: SQLConn.h:218
std::variant< std::monostate, int, unsigned, std::string, std::string_view > BindVal
Bind value (SQL&#39;s null, number, string)
Definition: SQLConn.h:184
std::vector< std::string > columns
List of columns.
Definition: SQLConn.h:204
bool bind(const SQLStmtHolder &ins, const std::string &key, const BindVal &val, bool transient=false) const noexcept
Bind one value val into key of the statement ins.
std::string name
Name of the table.
Definition: SQLConn.h:202
std::vector< TableEntry > Tables
Tables to be created by createTables()
Definition: SQLConn.h:210
std::vector< Column > Row
One row returned by SELECT (ie. list of Columns)
Definition: SQLConn.h:190
bool begin(TransactionType type=TransactionType::DEFERRED) const noexcept
Begin a transaction.
bool operator!() const
Test whether AutoTransaction is valid.
Definition: SQLConn.h:79
unsigned flags
See TableFlags.
Definition: SQLConn.h:206
bool insert(const SQLStmtHolder &ins, const Binding &binding={}, uint64_t *affected=nullptr) const noexcept
Bind, step, and reset the statement ins, using the passed binding.
std::vector< std::pair< std::string, std::string > > Indices
Indices to be created by createIndices()
Definition: SQLConn.h:212
bool createIndices(const Indices &indices) const noexcept
Create indices in the DB as specified in indices.
AutoTransaction & operator=(AutoTransaction &&other) noexcept
Move assignment.
Definition: SQLConn.h:65
SQLHolder sqlHolder
The DB connection.
Definition: SQLConn.h:263
bool openDB(const std::filesystem::path &dbFile, OpenFlags flags=OpenFlags::NONE) noexcept
Just open a database file.
std::variant< std::monostate, int, std::string > Column
One column returned by SELECT.
Definition: SQLConn.h:188
std::string lastError() const noexcept
Obtain the stored string.
Definition: LastError.h:112
bool step(const SQLStmtHolder &ins, uint64_t *affected=nullptr, bool *uniqueError=nullptr) const noexcept
Do one step of the statement ins.
SQLConn & operator=(SQLConn &&)=default
Move assignment.