Add class 'DbException' 46/285146/4
authorjh9216.park <jh9216.park@samsung.com>
Wed, 7 Dec 2022 01:01:05 +0000 (20:01 -0500)
committerjh9216.park <jh9216.park@samsung.com>
Wed, 7 Dec 2022 02:15:13 +0000 (21:15 -0500)
- It is a std::runtime_error but has some additional methods
- The class 'Database' will use it to throw exceptions

Change-Id: Icf01850e907e3336545f4c4220d6cccb0a3560f2
Signed-off-by: jh9216.park <jh9216.park@samsung.com>
tizen-database/database.hpp

index 516ae1a..a75c644 100644 (file)
@@ -50,6 +50,32 @@ void for_(F func) {
 using DbType = std::optional<std::variant<int, double, std::string,
     std::vector<unsigned char>>>;
 
+class DbException : public std::runtime_error {
+ public:
+  explicit DbException(const std::string& msg, int code = SQLITE_ERROR)
+      : std::runtime_error(msg), code_(code) {
+  }
+
+  DbException(const std::string& msg, int code, const std::string& file,
+      int line) : std::runtime_error(msg), code_(code) {
+    msg_ = msg + " at " + file + ":" + std::to_string(line);
+  }
+
+  int code() const {
+    return code_;
+  }
+
+  const char* msg() const {
+    if (msg_.empty())
+      return what();
+    return msg_.c_str();
+  }
+
+ private:
+  int code_;
+  std::string msg_;
+};
+
 class AutoDbType {
  public:
   AutoDbType() = default;
@@ -57,13 +83,13 @@ class AutoDbType {
 
   explicit operator int () {
     if (!db_type_)
-      throw std::runtime_error("invalid type conversion from nullopt to int");
+      throw DbException("invalid type conversion from nullopt to int");
     return std::get<int>(*db_type_);
   }
 
   explicit operator std::string () {
     if (!db_type_) {
-      throw std::runtime_error(
+      throw DbException(
           "invalid type conversion from nullopt to string");
     }
 
@@ -72,7 +98,7 @@ class AutoDbType {
 
   explicit operator double () {
     if (!db_type_) {
-      throw std::runtime_error(
+      throw DbException(
           "invalid type conversion from nullopt to double");
     }
 
@@ -81,7 +107,7 @@ class AutoDbType {
 
   explicit operator std::vector<unsigned char> () {
     if (!db_type_) {
-      throw std::runtime_error(
+      throw DbException(
           "invalid type conversion from nullopt to std::vector<unsigned char>");
     }
 
@@ -142,9 +168,9 @@ class Database {
     }
 
     explicit TransactionGuard(sqlite3* db) : db_(db) {
-      if (sqlite3_exec(db, "BEGIN DEFERRED", nullptr, nullptr, nullptr)
-          != SQLITE_OK) {
-        throw std::runtime_error("begin transaction failed");
+      int r = sqlite3_exec(db, "BEGIN DEFERRED", nullptr, nullptr, nullptr);
+      if (r != SQLITE_OK) {
+        throw DbException("begin transaction failed", r);
       }
     }
 
@@ -357,14 +383,14 @@ class Database {
           int len = sqlite3_column_bytes(stmt, pos);
 
           if (!val || len < 0) {
-            throw std::runtime_error("invalid blob");;
+            throw DbException("invalid blob");;
           } else {
             dbt = DbType(std::vector<unsigned char>(val, val + len));
           }
         } else if (type == SQLITE_NULL) {
           dbt = DbType(std::nullopt);
         } else {
-          throw std::runtime_error("invalid column type");
+          throw DbException("invalid column type", type);
         }
 
         return AutoDbType(dbt);
@@ -505,13 +531,13 @@ class Database {
   Database(std::string db, int flags) {
     int r = sqlite3_open_v2(db.c_str(), &db_, flags, nullptr);
     if (r != SQLITE_OK)
-      throw std::runtime_error("open failed");
+      throw DbException("open failed", r);
   }
 
   Database(std::string db, int flags, std::function<bool(int)> busy_handler) {
     int r = sqlite3_open_v2(db.c_str(), &db_, flags, nullptr);
     if (r != SQLITE_OK)
-      throw std::runtime_error("sqlite3_open_v2() failed");
+      throw DbException("sqlite3_open_v2() failed", r);
 
     busy_handler_ = std::move(busy_handler);
     r = sqlite3_busy_handler(db_, [](void* data, int count) {
@@ -523,7 +549,7 @@ class Database {
 
     if (r != SQLITE_OK) {
       sqlite3_close_v2(db_);
-      throw std::runtime_error("sqlite3_busy_handler() failed");
+      throw DbException("sqlite3_busy_handler() failed", r);
     }
   }
 
@@ -558,13 +584,13 @@ class Database {
     return *this;
   }
 
-  TransactionGuard CreateTransactionGuard() {
+  TransactionGuard CreateTransactionGuard() const {
     return TransactionGuard(db_);
   }
 
   Result Exec(const Sql& sql) const {
     if (!db_)
-      throw std::runtime_error("Not opened");
+      throw DbException("Not opened");
 
     sqlite3_stmt* stmt = nullptr;
     int r = sqlite3_prepare_v2(db_, sql.GetQuery().c_str(),
@@ -587,7 +613,7 @@ class Database {
     for (const auto& i : sql.GetBindingNameMap()) {
       int pos = sqlite3_bind_parameter_index(stmt, i.first.c_str());
       if (pos == 0)
-        throw std::runtime_error("Invalid binding");
+        throw DbException("Invalid binding");
       Bind(pos, i.second, stmt);
     }
 
@@ -601,7 +627,7 @@ class Database {
 
   bool Exec(const Sql& sql, const Result& previous_stmt) {
     if (sql.GetQuery() != previous_stmt.GetQuery())
-      throw std::runtime_error("Query is different");
+      throw DbException("Query is different");
 
     sqlite3_stmt* stmt = previous_stmt.GetRaw();
     if (!stmt)
@@ -623,7 +649,7 @@ class Database {
     for (const auto& i : sql.GetBindingNameMap()) {
       int pos = sqlite3_bind_parameter_index(stmt, i.first.c_str());
       if (pos == 0)
-        throw std::runtime_error("Invalid binding");
+        throw DbException("Invalid binding");
       Bind(pos, i.second, stmt);
     }
 
@@ -640,7 +666,7 @@ class Database {
     if (!type) {
       r = sqlite3_bind_null(stmt, pos);
       if (r != SQLITE_OK) {
-        throw std::runtime_error("Invalid binding");
+        throw DbException("Invalid binding", r);
       }
 
       return;
@@ -662,7 +688,7 @@ class Database {
     }
 
     if (r != SQLITE_OK) {
-      throw std::runtime_error("Invalid binding");
+      throw DbException("Invalid binding");
     }
   }