Fix static analysis issues
[platform/core/base/bundle.git] / tizen-database / database.hpp
index 32140b0..2ed94fa 100644 (file)
@@ -47,7 +47,7 @@ void for_(F func) {
   for_(func, std::make_index_sequence<N>());
 }
 
-using DbType = std::optional<std::variant<int, double, std::string,
+using DbType = std::optional<std::variant<int64_t, double, std::string,
     std::vector<unsigned char>>>;
 
 class DbException : public std::runtime_error {
@@ -79,12 +79,29 @@ class DbException : public std::runtime_error {
 class AutoDbType {
  public:
   AutoDbType() = default;
-  explicit AutoDbType(DbType db_type) : db_type_(db_type) {}
+  explicit AutoDbType(DbType db_type, int real_type)
+      : db_type_(db_type), real_type_(real_type) {}
+
+  int GetType() const {
+    return real_type_;
+  }
 
   explicit operator int () {
     if (!db_type_)
       throw DbException("invalid type conversion from nullopt to int");
-    return std::get<int>(*db_type_);
+    if (real_type_ != SQLITE_INTEGER)
+      throw DbException("invalid type conversion to int");
+
+    return std::get<int64_t>(*db_type_);
+  }
+
+  explicit operator int64_t () {
+    if (!db_type_)
+      throw DbException("invalid type conversion from nullopt to int64_t");
+    if (real_type_ != SQLITE_INTEGER)
+      throw DbException("invalid type conversion to int64_t");
+
+    return std::get<int64_t>(*db_type_);
   }
 
   explicit operator std::string () {
@@ -93,6 +110,9 @@ class AutoDbType {
           "invalid type conversion from nullopt to string");
     }
 
+    if (real_type_ != SQLITE_TEXT)
+      throw DbException("invalid type conversion to string");
+
     return std::get<std::string>(*db_type_);
   }
 
@@ -102,6 +122,9 @@ class AutoDbType {
           "invalid type conversion from nullopt to double");
     }
 
+    if (real_type_ != SQLITE_FLOAT)
+      throw DbException("invalid type conversion to double");
+
     return std::get<double>(*db_type_);
   }
 
@@ -111,35 +134,60 @@ class AutoDbType {
           "invalid type conversion from nullopt to std::vector<unsigned char>");
     }
 
+    if (real_type_ != SQLITE_BLOB)
+      throw DbException("invalid type conversion to std::vector<unsigned char>");
+
     return std::get<std::vector<unsigned char>>(*db_type_);
   }
 
   operator std::optional<int> () {
     if (!db_type_)
       return std::nullopt;
-    return std::get<int>(*db_type_);
+    if (real_type_ != SQLITE_INTEGER)
+      throw DbException("invalid type conversion to int");
+
+    return std::get<int64_t>(*db_type_);
+  }
+
+  operator std::optional<int64_t> () {
+    if (!db_type_)
+      return std::nullopt;
+    if (real_type_ != SQLITE_INTEGER)
+      throw DbException("invalid type conversion to int64_t");
+
+    return std::get<int64_t>(*db_type_);
   }
 
   operator std::optional<std::string> () {
     if (!db_type_)
       return std::nullopt;
+    if (real_type_ != SQLITE_TEXT)
+      throw DbException("invalid type conversion to string");
+
     return std::get<std::string>(*db_type_);
   }
 
   operator std::optional<double> () {
     if (!db_type_)
       return std::nullopt;
+    if (real_type_ != SQLITE_FLOAT)
+      throw DbException("invalid type conversion to double");
+
     return std::get<double>(*db_type_);
   }
 
   operator std::optional<std::vector<unsigned char>> () {
     if (!db_type_)
       return std::nullopt;
+    if (real_type_ != SQLITE_BLOB)
+      throw DbException("invalid type conversion to std::vector<unsigned char>");
+
     return std::get<std::vector<unsigned char>>(*db_type_);
   }
 
  private:
   DbType db_type_;
+  int real_type_ = SQLITE_NULL;
 };
 
 using _ = AutoDbType;
@@ -197,6 +245,19 @@ class Database {
    public:
     Sql(std::string query) : query_(std::move(query)) {}
 
+    Sql& Bind(const char* val) {
+      if (val == nullptr) {
+        bindings_.push_back(DbType(std::nullopt));
+      } else {
+        std::string str = val;
+        if (empty_string_as_null_ && str.empty())
+          bindings_.push_back(DbType(std::nullopt));
+        else
+          bindings_.push_back(DbType(std::move(str)));
+      }
+      return *this;
+    }
+
     Sql& Bind(std::string val) {
       if (empty_string_as_null_ && val.empty())
         bindings_.push_back(DbType(std::nullopt));
@@ -206,6 +267,11 @@ class Database {
     }
 
     Sql& Bind(int val) {
+      bindings_.push_back(DbType(static_cast<int64_t>(val)));
+      return *this;
+    }
+
+    Sql& Bind(int64_t val) {
       bindings_.push_back(DbType(val));
       return *this;
     }
@@ -228,6 +294,19 @@ class Database {
       return *this;
     }
 
+    Sql& Bind(int pos, const char* val) {
+      if (val == nullptr) {
+        binding_map_[pos] = DbType(std::nullopt);
+      } else {
+        std::string str = val;
+        if (empty_string_as_null_ && str.empty())
+          binding_map_[pos] = DbType(std::nullopt);
+        else
+          binding_map_[pos] = DbType(std::move(str));
+      }
+      return *this;
+    }
+
     Sql& Bind(int pos, std::string val) {
       if (empty_string_as_null_ && val.empty())
         binding_map_[pos] = DbType(std::nullopt);
@@ -237,6 +316,11 @@ class Database {
     }
 
     Sql& Bind(int pos, int val) {
+      binding_map_[pos] = DbType(static_cast<int64_t>(val));
+      return *this;
+    }
+
+    Sql& Bind(int pos, int64_t val) {
       binding_map_[pos] = DbType(val);
       return *this;
     }
@@ -259,6 +343,19 @@ class Database {
       return *this;
     }
 
+    Sql& Bind(std::string name, const char* val) {
+      if (val == nullptr) {
+        binding_name_map_[std::move(name)] = DbType(std::nullopt);
+      } else {
+        std::string str = val;
+        if (empty_string_as_null_ && str.empty())
+          binding_name_map_[std::move(name)] = DbType(std::nullopt);
+        else
+          binding_name_map_[std::move(name)] = DbType(std::move(str));
+      }
+      return *this;
+    }
+
     Sql& Bind(std::string name, std::string val) {
       if (empty_string_as_null_ && val.empty())
         binding_name_map_[std::move(name)] = DbType(std::nullopt);
@@ -268,6 +365,11 @@ class Database {
     }
 
     Sql& Bind(std::string name, int val) {
+      binding_name_map_[std::move(name)] = DbType(static_cast<int64_t>(val));
+      return *this;
+    }
+
+    Sql& Bind(std::string name, int64_t val) {
       binding_name_map_[std::move(name)] = DbType(val);
       return *this;
     }
@@ -348,6 +450,8 @@ class Database {
     Result(Result&& r) noexcept {
       stmt_ = r.stmt_;
       r.stmt_ = nullptr;
+      query_ = std::move(r.query_);
+      is_done_ = r.is_done_;
     }
 
     Result& operator = (Result&& r) noexcept {
@@ -356,6 +460,8 @@ class Database {
           sqlite3_finalize(stmt_);
         stmt_ = r.stmt_;
         r.stmt_ = nullptr;
+        query_ = std::move(r.query_);
+        is_done_ = r.is_done_;
       }
 
       return *this;
@@ -365,6 +471,16 @@ class Database {
      public:
       explicit Record(const sqlite3_stmt* stmt) : stmt_(stmt) {}
 
+      std::optional<std::string> GetString(int pos) const {
+        sqlite3_stmt* stmt = const_cast<sqlite3_stmt*>(stmt_);
+        const char* text = reinterpret_cast<const char*>(
+            sqlite3_column_text(stmt, pos));
+        if (!text)
+          return std::nullopt;
+
+        return text;
+      }
+
       AutoDbType Get(int pos) const {
         sqlite3_stmt* stmt = const_cast<sqlite3_stmt*>(stmt_);
         int type = sqlite3_column_type(stmt, pos);
@@ -374,7 +490,7 @@ class Database {
           dbt = DbType(reinterpret_cast<const char*>(
               sqlite3_column_text(stmt, pos)));
         } else if (type == SQLITE_INTEGER) {
-          dbt = DbType(sqlite3_column_int(stmt, pos));
+          dbt = DbType(static_cast<int64_t>(sqlite3_column_int64(stmt, pos)));
         } else if (type == SQLITE_FLOAT) {
           dbt = DbType(sqlite3_column_double(stmt, pos));
         } else if (type == SQLITE_BLOB) {
@@ -393,7 +509,7 @@ class Database {
           throw DbException("invalid column type", type);
         }
 
-        return AutoDbType(dbt);
+        return AutoDbType(dbt, type);
       }
 
       template <typename ...Types>
@@ -432,6 +548,8 @@ class Database {
     };
 
     Iterator begin() const {
+      if (is_done_)
+        return Iterator(nullptr);
       return Iterator(stmt_);
     }
 
@@ -439,19 +557,19 @@ class Database {
       return Iterator(nullptr);
     }
 
-    explicit operator bool() {
+    operator bool() const {
       if (stmt_ == nullptr)
         return false;
       return true;
     }
 
-    explicit operator int() {
+    explicit operator int() const {
       if (db_ == nullptr)
         return SQLITE_ERROR;
       return sqlite3_errcode(db_);
     }
 
-    operator const char*() {
+    explicit operator const char*() const {
       if (db_ == nullptr)
         return "";
       return sqlite3_errmsg(db_);
@@ -518,14 +636,23 @@ class Database {
       return query_;
     }
 
+    void SetDone(bool is_done) {
+      is_done_ = is_done;
+    }
+
+    size_t GetColumnCount() const {
+      return sqlite3_column_count(stmt_);
+    }
+
    private:
     friend class Database;
-    Result(sqlite3_stmt* stmt, sqlite3* db, std::string query)
-        : stmt_(stmt), db_(db), query_(std::move(query)) {}
+    Result(sqlite3_stmt* stmt, sqlite3* db, std::string query, bool is_done)
+        : stmt_(stmt), db_(db), query_(std::move(query)), is_done_(is_done) {}
 
     sqlite3_stmt* stmt_ = nullptr;
     sqlite3* db_ = nullptr;
     std::string query_;
+    bool is_done_ = false;
   };
 
   Database(std::string db, int flags) {
@@ -567,7 +694,7 @@ class Database {
     db.db_ = nullptr;
   }
 
-  explicit operator bool() {
+  explicit operator bool() const {
     if (db_ == nullptr)
       return false;
     return true;
@@ -588,6 +715,18 @@ class Database {
     return TransactionGuard(db_);
   }
 
+  Result Prepare(const Sql& sql) const {
+    if (!db_)
+      throw DbException("Not opened");
+
+    sqlite3_stmt* stmt = nullptr;
+    int r = sqlite3_prepare_v2(db_, sql.GetQuery().c_str(),
+        -1, &stmt, nullptr);
+    if (r != SQLITE_OK)
+      return { nullptr, nullptr, "", true };
+    return { stmt, db_, sql.GetQuery(), false };
+  }
+
   Result Exec(const Sql& sql) const {
     if (!db_)
       throw DbException("Not opened");
@@ -596,7 +735,7 @@ class Database {
     int r = sqlite3_prepare_v2(db_, sql.GetQuery().c_str(),
         -1, &stmt, nullptr);
     if (r != SQLITE_OK) {
-      return { nullptr, nullptr, "" };
+      return { nullptr, nullptr, "", true };
     }
 
     std::unique_ptr<sqlite3_stmt, decltype(sqlite3_finalize)*> stmt_auto(stmt,
@@ -619,13 +758,14 @@ class Database {
 
     r = sqlite3_step(stmt);
     if (r != SQLITE_ROW && r != SQLITE_DONE) {
-      return { nullptr, db_, "" };
+      return { nullptr, db_, "", true };
     }
 
-    return { stmt_auto.release(), db_, sql.GetQuery() };
+    return { stmt_auto.release(), db_, sql.GetQuery(),
+        r == SQLITE_DONE ? true : false };
   }
 
-  bool Exec(const Sql& sql, const Result& previous_stmt) {
+  bool Exec(const Sql& sql, Result& previous_stmt) const {
     if (sql.GetQuery() != previous_stmt.GetQuery())
       throw DbException("Query is different");
 
@@ -657,6 +797,7 @@ class Database {
     if (r != SQLITE_ROW && r != SQLITE_DONE)
       return false;
 
+    previous_stmt.SetDone(r == SQLITE_DONE ? true : false);
     return true;
   }
 
@@ -665,11 +806,18 @@ class Database {
     int ret = sqlite3_exec(db_, sql.GetQuery().c_str(), nullptr, nullptr,
         &errmsg);
     if (ret != SQLITE_OK) {
-       std::unique_ptr<char, decltype(free)*> errmsg_auto(errmsg, free);
-       throw DbException(errmsg);
+      std::unique_ptr<char, decltype(sqlite3_free)*> errmsg_auto(
+          errmsg, sqlite3_free);
+      throw DbException(errmsg);
     }
   }
 
+  sqlite3* GetRaw() const {
+    if (!db_)
+      throw DbException("Not opened");
+    return db_;
+  }
+
  private:
   void Bind(int pos, const DbType& type, sqlite3_stmt* stmt) const {
     int r;
@@ -685,8 +833,8 @@ class Database {
     if (const std::string* pstr = std::get_if<std::string>(&(*type))) {
       r = sqlite3_bind_text(stmt, pos, (*pstr).c_str(), -1,
           SQLITE_TRANSIENT);
-    } else if (const int* pint = std::get_if<int>(&(*type))) {
-      r = sqlite3_bind_int(stmt, pos, (*pint));
+    } else if (const int64_t* pint = std::get_if<int64_t>(&(*type))) {
+      r = sqlite3_bind_int64(stmt, pos, (*pint));
     } else if (const double* pdouble = std::get_if<double>(&(*type))) {
       r = sqlite3_bind_double(stmt, pos, (*pdouble));
     } else if (const std::vector<unsigned char>* pvector =