Add a method to reuse prepared statements 68/285068/2
authorjh9216.park <jh9216.park@samsung.com>
Tue, 6 Dec 2022 00:26:58 +0000 (19:26 -0500)
committerjh9216.park <jh9216.park@samsung.com>
Tue, 6 Dec 2022 00:39:49 +0000 (19:39 -0500)
Change-Id: I2915cc004ff9a25129c95d9abcda0c75743ab14e
Signed-off-by: jh9216.park <jh9216.park@samsung.com>
tests/tizen-database_unittests/src/test_database.cc
tizen-database/database.hpp

index d67f2e4..176ef7c 100644 (file)
@@ -212,6 +212,33 @@ TEST_F(DatabaseTest, test_insert_name_and_data_with_null) {
   EXPECT_TRUE(static_cast<bool>(r));
 }
 
+TEST_F(DatabaseTest, test_reuse_result) {
+  tizen_base::Database db(TEST_DB, SQLITE_OPEN_READWRITE);
+  auto q = tizen_base::Database::Sql(Q_INSERT)
+      .Bind("gogo")
+      .Bind(1234)
+      .Bind(9.216)
+      .Bind(std::vector<unsigned char> {'9', '2', '1', '6' });
+  auto r = db.Exec(q);
+  EXPECT_TRUE(static_cast<bool>(r));
+
+  q.Reset()
+      .Bind("gugu")
+      .Bind(5678)
+      .Bind(9.216)
+      .Bind(std::nullopt);
+  bool ret = db.Exec(q, r);
+  EXPECT_TRUE(ret);
+
+  q.Reset()
+      .Bind("gg")
+      .Bind(7777)
+      .Bind(9.216)
+      .Bind(std::nullopt);
+  ret = db.Exec(q, r);
+  EXPECT_TRUE(ret);
+}
+
 TEST_F(DatabaseTest, test_select) {
   SetDefault();
   using tizen_base::_;
index 492af5f..8157659 100644 (file)
@@ -262,6 +262,13 @@ class Database {
       return query_;
     }
 
+    Sql& Reset() {
+      bindings_.clear();
+      binding_map_.clear();
+      binding_name_map_.clear();
+      return *this;
+    }
+
    private:
     std::string query_;
     std::vector<DbType> bindings_;
@@ -445,12 +452,22 @@ class Database {
       return std::nullopt;
     }
 
+    sqlite3_stmt* GetRaw() const {
+      return stmt_;
+    }
+
+    const std::string& GetQuery() const {
+      return query_;
+    }
+
    private:
     friend class Database;
-    Result(sqlite3_stmt* stmt, sqlite3* db) : stmt_(stmt), db_(db) {}
+    Result(sqlite3_stmt* stmt, sqlite3* db, std::string query)
+        : stmt_(stmt), db_(db), query_(std::move(query)) {}
 
     sqlite3_stmt* stmt_ = nullptr;
     sqlite3* db_ = nullptr;
+    std::string query_;
   };
 
   Database(std::string db, int flags) {
@@ -521,7 +538,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, "" };
     }
 
     std::unique_ptr<sqlite3_stmt, decltype(sqlite3_finalize)*> stmt_auto(stmt,
@@ -544,10 +561,45 @@ class Database {
 
     r = sqlite3_step(stmt);
     if (r != SQLITE_ROW && r != SQLITE_DONE) {
-      return { nullptr, db_ };
+      return { nullptr, db_, "" };
     }
 
-    return { stmt_auto.release(), db_ };
+    return { stmt_auto.release(), db_, sql.GetQuery() };
+  }
+
+  bool Exec(const Sql& sql, const Result& previous_stmt) {
+    if (sql.GetQuery() != previous_stmt.GetQuery())
+      throw std::runtime_error("Query is different");
+
+    sqlite3_stmt* stmt = previous_stmt.GetRaw();
+    if (!stmt)
+      return false;
+
+    int r = sqlite3_reset(stmt);
+    if (r != SQLITE_ROW && r != SQLITE_DONE && r != SQLITE_OK)
+      return false;
+
+    int pos = 1;
+    for (const auto& i : sql.GetBindings()) {
+      Bind(pos++, i, stmt);
+    }
+
+    for (const auto& i : sql.GetBindingMap()) {
+      Bind(i.first, i.second, stmt);
+    }
+
+    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");
+      Bind(pos, i.second, stmt);
+    }
+
+    r = sqlite3_step(stmt);
+    if (r != SQLITE_ROW && r != SQLITE_DONE)
+      return false;
+
+    return true;
   }
 
  private: