diff --git a/include/sqlgen/Connection.hpp b/include/sqlgen/Connection.hpp index 26f0ced..205efd4 100644 --- a/include/sqlgen/Connection.hpp +++ b/include/sqlgen/Connection.hpp @@ -17,6 +17,8 @@ namespace sqlgen { /// Abstract base class to be implemented by the different /// database connections. struct Connection { + virtual ~Connection() = default; + /// Commits a statement, virtual Result commit() = 0; diff --git a/include/sqlgen/sqlite/Connection.hpp b/include/sqlgen/sqlite/Connection.hpp index 5b2d846..0f6fa32 100644 --- a/include/sqlgen/sqlite/Connection.hpp +++ b/include/sqlgen/sqlite/Connection.hpp @@ -18,9 +18,11 @@ namespace sqlgen::sqlite { class Connection : public sqlgen::Connection { using ConnPtr = std::unique_ptr; + using StmtPtr = std::unique_ptr; public: - Connection(const std::string& _fname) : conn_(make_conn(_fname)) {} + Connection(const std::string& _fname) + : stmt_(StmtPtr(nullptr, &sqlite3_finalize)), conn_(make_conn(_fname)) {} Connection(const Connection& _other) = delete; @@ -41,11 +43,9 @@ class Connection : public sqlgen::Connection { std::string to_sql(const dynamic::Statement& _stmt) noexcept final; - Result start_write(const dynamic::Insert& _stmt) final { - return error("TODO"); - } + Result start_write(const dynamic::Insert& _stmt) final; - Result end_write() final { return error("TODO"); } + Result end_write() final; Result write( const std::vector>>& _data) final { @@ -73,6 +73,10 @@ class Connection : public sqlgen::Connection { std::string type_to_sql(const dynamic::Type& _type) noexcept; private: + /// A prepared statement - needed for the write operations. Note that we have + /// declared it before conn_, meaning it will be destroyed first. + StmtPtr stmt_; + /// The underlying sqlite3 connection. ConnPtr conn_; }; diff --git a/src/sqlgen/sqlite/Connection.cpp b/src/sqlgen/sqlite/Connection.cpp index c12b855..516f10c 100644 --- a/src/sqlgen/sqlite/Connection.cpp +++ b/src/sqlgen/sqlite/Connection.cpp @@ -123,6 +123,43 @@ std::string Connection::to_sql(const dynamic::Statement& _stmt) noexcept { }); } +Result Connection::start_write(const dynamic::Insert& _stmt) { + if (stmt_) { + return error( + "A write operation has already been launched. You need to call " + ".end_write() before you can start another."); + } + + const auto sql = to_sql(_stmt); + + sqlite3_stmt* p_stmt = nullptr; + + sqlite3_prepare(conn_.get(), /* Database handle */ + sql.c_str(), /* SQL statement, UTF-8 encoded */ + sql.size(), /* Maximum length of zSql in bytes. */ + &p_stmt, /* OUT: Statement handle */ + nullptr /* OUT: Pointer to unused portion of zSql */ + ); + + if (!p_stmt) { + return error(sqlite3_errmsg(conn_.get())); + } + + stmt_ = StmtPtr(p_stmt, &sqlite3_finalize); + + return Nothing{}; +} + +Result Connection::end_write() { + if (!stmt_) { + return error( + " You need to call .start_write(...) before you can call " + ".end_write()."); + } + stmt_ = nullptr; + return Nothing{}; +} + std::string Connection::type_to_sql(const dynamic::Type& _type) noexcept { return _type.visit([](const auto _t) -> std::string { using T = std::remove_cvref_t;