mirror of
https://github.com/rbock/sqlpp11.git
synced 2026-01-04 12:10:43 -06:00
Make sqlite3 connector header-only
This commit is contained in:
@@ -28,8 +28,11 @@
|
||||
#define SQLPP_SQLITE3_BIND_RESULT_H
|
||||
|
||||
#include <memory>
|
||||
#include <iostream>
|
||||
#include <sqlpp11/chrono.h>
|
||||
#include <sqlpp11/exception.h>
|
||||
#include <sqlpp11/sqlite3/export.h>
|
||||
#include <sqlpp11/sqlite3/prepared_statement_handle.h>
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(push)
|
||||
@@ -42,8 +45,33 @@ namespace sqlpp
|
||||
{
|
||||
namespace detail
|
||||
{
|
||||
struct prepared_statement_handle_t;
|
||||
}
|
||||
const auto date_digits = std::vector<char>{1, 1, 1, 1, 0, 1, 1, 0, 1, 1}; // 2015-10-28
|
||||
const auto time_digits = std::vector<char>{0, 1, 1, 0, 1, 1, 0, 1, 1}; // T23:00:12
|
||||
const auto ms_digits = std::vector<char>{0, 1, 1, 1}; // .123
|
||||
|
||||
auto check_digits(const char* text, const std::vector<char>& digitFlags) -> bool
|
||||
{
|
||||
for (const auto digitFlag : digitFlags)
|
||||
{
|
||||
if (digitFlag)
|
||||
{
|
||||
if (not std::isdigit(*text))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (std::isdigit(*text) or *text == '\0')
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
++text;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
} // namespace detail
|
||||
|
||||
class SQLPP11_SQLITE3_EXPORT bind_result_t
|
||||
{
|
||||
@@ -51,7 +79,12 @@ namespace sqlpp
|
||||
|
||||
public:
|
||||
bind_result_t() = default;
|
||||
bind_result_t(const std::shared_ptr<detail::prepared_statement_handle_t>& handle);
|
||||
bind_result_t(const std::shared_ptr<detail::prepared_statement_handle_t>& handle) : _handle(handle)
|
||||
{
|
||||
if (_handle and _handle->debug)
|
||||
std::cerr << "Sqlite3 debug: Constructing bind result, using handle at " << _handle.get() << std::endl;
|
||||
}
|
||||
|
||||
bind_result_t(const bind_result_t&) = delete;
|
||||
bind_result_t(bind_result_t&& rhs) = default;
|
||||
bind_result_t& operator=(const bind_result_t&) = delete;
|
||||
@@ -87,17 +120,172 @@ namespace sqlpp
|
||||
}
|
||||
}
|
||||
|
||||
void _bind_boolean_result(size_t index, signed char* value, bool* is_null);
|
||||
void _bind_floating_point_result(size_t index, double* value, bool* is_null);
|
||||
void _bind_integral_result(size_t index, int64_t* value, bool* is_null);
|
||||
void _bind_unsigned_integral_result(size_t index, uint64_t* value, bool* is_null);
|
||||
void _bind_text_result(size_t index, const char** text, size_t* len);
|
||||
void _bind_blob_result(size_t index, const uint8_t** text, size_t* len);
|
||||
void _bind_date_result(size_t index, ::sqlpp::chrono::day_point* value, bool* is_null);
|
||||
void _bind_date_time_result(size_t index, ::sqlpp::chrono::microsecond_point* value, bool* is_null);
|
||||
void _bind_boolean_result(size_t index, signed char* value, bool* is_null)
|
||||
{
|
||||
if (_handle->debug)
|
||||
std::cerr << "Sqlite3 debug: binding boolean result " << *value << " at index: " << index << std::endl;
|
||||
|
||||
*value = static_cast<signed char>(sqlite3_column_int(_handle->sqlite_statement, static_cast<int>(index)));
|
||||
*is_null = sqlite3_column_type(_handle->sqlite_statement, static_cast<int>(index)) == SQLITE_NULL;
|
||||
}
|
||||
|
||||
void _bind_floating_point_result(size_t index, double* value, bool* is_null)
|
||||
{
|
||||
if (_handle->debug)
|
||||
std::cerr << "Sqlite3 debug: binding floating_point result " << *value << " at index: " << index << std::endl;
|
||||
|
||||
switch (sqlite3_column_type(_handle->sqlite_statement, static_cast<int>(index)))
|
||||
{
|
||||
case (SQLITE3_TEXT):
|
||||
*value = atof(
|
||||
reinterpret_cast<const char*>(sqlite3_column_text(_handle->sqlite_statement, static_cast<int>(index))));
|
||||
break;
|
||||
default:
|
||||
*value = sqlite3_column_double(_handle->sqlite_statement, static_cast<int>(index));
|
||||
}
|
||||
*is_null = sqlite3_column_type(_handle->sqlite_statement, static_cast<int>(index)) == SQLITE_NULL;
|
||||
}
|
||||
|
||||
void _bind_integral_result(size_t index, int64_t* value, bool* is_null)
|
||||
{
|
||||
if (_handle->debug)
|
||||
std::cerr << "Sqlite3 debug: binding integral result " << *value << " at index: " << index << std::endl;
|
||||
|
||||
*value = sqlite3_column_int64(_handle->sqlite_statement, static_cast<int>(index));
|
||||
*is_null = sqlite3_column_type(_handle->sqlite_statement, static_cast<int>(index)) == SQLITE_NULL;
|
||||
}
|
||||
|
||||
void _bind_unsigned_integral_result(size_t index, uint64_t* value, bool* is_null)
|
||||
{
|
||||
if (_handle->debug)
|
||||
std::cerr << "Sqlite3 debug: binding unsigned integral result " << *value << " at index: " << index
|
||||
<< std::endl;
|
||||
|
||||
*value = static_cast<uint64_t>(sqlite3_column_int64(_handle->sqlite_statement, static_cast<int>(index)));
|
||||
*is_null = sqlite3_column_type(_handle->sqlite_statement, static_cast<int>(index)) == SQLITE_NULL;
|
||||
}
|
||||
|
||||
void _bind_text_result(size_t index, const char** value, size_t* len)
|
||||
{
|
||||
if (_handle->debug)
|
||||
std::cerr << "Sqlite3 debug: binding text result at index: " << index << std::endl;
|
||||
|
||||
*value = (reinterpret_cast<const char*>(sqlite3_column_text(_handle->sqlite_statement, static_cast<int>(index))));
|
||||
*len = sqlite3_column_bytes(_handle->sqlite_statement, static_cast<int>(index));
|
||||
}
|
||||
|
||||
void _bind_blob_result(size_t index, const uint8_t** value, size_t* len)
|
||||
{
|
||||
if (_handle->debug)
|
||||
std::cerr << "Sqlite3 debug: binding text result at index: " << index << std::endl;
|
||||
|
||||
*value =
|
||||
(reinterpret_cast<const uint8_t*>(sqlite3_column_blob(_handle->sqlite_statement, static_cast<int>(index))));
|
||||
*len = sqlite3_column_bytes(_handle->sqlite_statement, static_cast<int>(index));
|
||||
}
|
||||
|
||||
|
||||
void _bind_date_result(size_t index, ::sqlpp::chrono::day_point* value, bool* is_null)
|
||||
{
|
||||
if (_handle->debug)
|
||||
std::cerr << "Sqlite3 debug: binding date result at index: " << index << std::endl;
|
||||
|
||||
*is_null = sqlite3_column_type(_handle->sqlite_statement, static_cast<int>(index)) == SQLITE_NULL;
|
||||
if (*is_null)
|
||||
{
|
||||
*value = {};
|
||||
return;
|
||||
}
|
||||
|
||||
const auto date_string =
|
||||
reinterpret_cast<const char*>(sqlite3_column_text(_handle->sqlite_statement, static_cast<int>(index)));
|
||||
if (_handle->debug)
|
||||
std::cerr << "Sqlite3 debug: date string: " << date_string << std::endl;
|
||||
|
||||
if (detail::check_digits(date_string, detail::date_digits))
|
||||
{
|
||||
const auto ymd = ::date::year(std::atoi(date_string)) / atoi(date_string + 5) / atoi(date_string + 8);
|
||||
*value = ::sqlpp::chrono::day_point(ymd);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (_handle->debug)
|
||||
std::cerr << "Sqlite3 debug: invalid date result: " << date_string << std::endl;
|
||||
*value = {};
|
||||
}
|
||||
}
|
||||
|
||||
void _bind_date_time_result(size_t index, ::sqlpp::chrono::microsecond_point* value, bool* is_null)
|
||||
{
|
||||
if (_handle->debug)
|
||||
std::cerr << "Sqlite3 debug: binding date result at index: " << index << std::endl;
|
||||
|
||||
*is_null = sqlite3_column_type(_handle->sqlite_statement, static_cast<int>(index)) == SQLITE_NULL;
|
||||
if (*is_null)
|
||||
{
|
||||
*value = {};
|
||||
return;
|
||||
}
|
||||
|
||||
const auto date_time_string =
|
||||
reinterpret_cast<const char*>(sqlite3_column_text(_handle->sqlite_statement, static_cast<int>(index)));
|
||||
if (_handle->debug)
|
||||
std::cerr << "Sqlite3 debug: date_time string: " << date_time_string << std::endl;
|
||||
|
||||
if (detail::check_digits(date_time_string, detail::date_digits))
|
||||
{
|
||||
const auto ymd =
|
||||
::date::year(std::atoi(date_time_string)) / atoi(date_time_string + 5) / atoi(date_time_string + 8);
|
||||
*value = ::sqlpp::chrono::day_point(ymd);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (_handle->debug)
|
||||
std::cerr << "Sqlite3 debug: invalid date_time result: " << date_time_string << std::endl;
|
||||
*value = {};
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
const auto time_string = date_time_string + 10;
|
||||
if (detail::check_digits(time_string, detail::time_digits))
|
||||
{
|
||||
*value += ::std::chrono::hours(std::atoi(time_string + 1)) + std::chrono::minutes(std::atoi(time_string + 4)) +
|
||||
std::chrono::seconds(std::atoi(time_string + 7));
|
||||
}
|
||||
else
|
||||
{
|
||||
return;
|
||||
}
|
||||
const auto ms_string = time_string + 9;
|
||||
if (detail::check_digits(ms_string, detail::ms_digits) and ms_string[4] == '\0')
|
||||
{
|
||||
*value += ::std::chrono::milliseconds(std::atoi(ms_string + 1));
|
||||
}
|
||||
else
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
bool next_impl();
|
||||
bool next_impl()
|
||||
{
|
||||
if (_handle->debug)
|
||||
std::cerr << "Sqlite3 debug: Accessing next row of handle at " << _handle.get() << std::endl;
|
||||
|
||||
auto rc = sqlite3_step(_handle->sqlite_statement);
|
||||
|
||||
switch (rc)
|
||||
{
|
||||
case SQLITE_ROW:
|
||||
return true;
|
||||
case SQLITE_DONE:
|
||||
return false;
|
||||
default:
|
||||
throw sqlpp::exception("Sqlite3 error: Unexpected return value for sqlite3_step()");
|
||||
}
|
||||
}
|
||||
};
|
||||
} // namespace sqlite3
|
||||
} // namespace sqlpp
|
||||
|
||||
@@ -33,17 +33,22 @@
|
||||
#include <sqlite3.h>
|
||||
#endif
|
||||
#include <sqlpp11/connection.h>
|
||||
#include <sqlpp11/exception.h>
|
||||
#include <sqlpp11/schema.h>
|
||||
#include <sqlpp11/serialize.h>
|
||||
#include <sqlpp11/sqlite3/bind_result.h>
|
||||
#include <sqlpp11/sqlite3/connection_config.h>
|
||||
#include <sqlpp11/sqlite3/prepared_statement.h>
|
||||
#include <sqlpp11/sqlite3/export.h>
|
||||
#include <sqlpp11/transaction.h>
|
||||
#include <sqlpp11/type_traits.h>
|
||||
#include <sqlpp11/sqlite3/export.h>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
|
||||
#ifdef SQLPP_DYNAMIC_LOADING
|
||||
#include <sqlpp11/sqlite3/dynamic_libsqlite3.h>
|
||||
#endif
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable:4251)
|
||||
@@ -53,9 +58,97 @@ namespace sqlpp
|
||||
{
|
||||
namespace sqlite3
|
||||
{
|
||||
#ifdef SQLPP_DYNAMIC_LOADING
|
||||
using namespace dynamic;
|
||||
#endif
|
||||
|
||||
namespace detail
|
||||
{
|
||||
struct connection_handle;
|
||||
struct connection_handle
|
||||
{
|
||||
connection_config config;
|
||||
::sqlite3* sqlite;
|
||||
|
||||
connection_handle(connection_config conf) : config(conf), sqlite(nullptr)
|
||||
{
|
||||
#ifdef SQLPP_DYNAMIC_LOADING
|
||||
init_sqlite("");
|
||||
#endif
|
||||
|
||||
auto rc = sqlite3_open_v2(conf.path_to_database.c_str(), &sqlite, conf.flags,
|
||||
conf.vfs.empty() ? nullptr : conf.vfs.c_str());
|
||||
if (rc != SQLITE_OK)
|
||||
{
|
||||
const std::string msg = sqlite3_errmsg(sqlite);
|
||||
sqlite3_close(sqlite);
|
||||
throw sqlpp::exception("Sqlite3 error: Can't open database: " + msg);
|
||||
}
|
||||
#ifdef SQLITE_HAS_CODEC
|
||||
if (conf.password.size() > 0)
|
||||
{
|
||||
int ret = sqlite3_key(sqlite, conf.password.data(), conf.password.size());
|
||||
if (ret != SQLITE_OK)
|
||||
{
|
||||
const std::string msg = sqlite3_errmsg(sqlite);
|
||||
sqlite3_close(sqlite);
|
||||
throw sqlpp::exception("Sqlite3 error: Can't set password to database: " + msg);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
connection_handle()
|
||||
{
|
||||
auto rc = sqlite3_close(sqlite);
|
||||
if (rc != SQLITE_OK)
|
||||
{
|
||||
std::cerr << "Sqlite3 error: Can't close database: " << sqlite3_errmsg(sqlite) << std::endl;
|
||||
}
|
||||
}
|
||||
connection_handle(const connection_handle&) = delete;
|
||||
connection_handle(connection_handle&&) = delete;
|
||||
connection_handle& operator=(const connection_handle&) = delete;
|
||||
connection_handle& operator=(connection_handle&&) = delete;
|
||||
};
|
||||
|
||||
detail::prepared_statement_handle_t prepare_statement(detail::connection_handle& handle,
|
||||
const std::string& statement)
|
||||
{
|
||||
if (handle.config.debug)
|
||||
std::cerr << "Sqlite3 debug: Preparing: '" << statement << "'" << std::endl;
|
||||
|
||||
detail::prepared_statement_handle_t result(nullptr, handle.config.debug);
|
||||
|
||||
auto rc = sqlite3_prepare_v2(handle.sqlite, statement.c_str(), static_cast<int>(statement.size()),
|
||||
&result.sqlite_statement, nullptr);
|
||||
|
||||
if (rc != SQLITE_OK)
|
||||
{
|
||||
throw sqlpp::exception("Sqlite3 error: Could not prepare statement: " +
|
||||
std::string(sqlite3_errmsg(handle.sqlite)) + " (statement was >>" +
|
||||
(rc == SQLITE_TOOBIG ? statement.substr(0, 128) + "..." : statement) +
|
||||
"<<\n");
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void execute_statement(detail::connection_handle& handle, detail::prepared_statement_handle_t& prepared)
|
||||
{
|
||||
auto rc = sqlite3_step(prepared.sqlite_statement);
|
||||
switch (rc)
|
||||
{
|
||||
case SQLITE_OK:
|
||||
case SQLITE_ROW: // might occur if execute is called with a select
|
||||
case SQLITE_DONE:
|
||||
return;
|
||||
default:
|
||||
if(handle.config.debug)
|
||||
std::cerr << "Sqlite3 debug: sqlite3_step return code: " << rc << std::endl;
|
||||
throw sqlpp::exception("Sqlite3 error: Could not execute statement: " +
|
||||
std::string(sqlite3_errmsg(handle.sqlite)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class connection;
|
||||
@@ -107,18 +200,81 @@ namespace sqlpp
|
||||
transaction_status_type _transaction_status = transaction_status_type::none;
|
||||
|
||||
// direct execution
|
||||
bind_result_t select_impl(const std::string& statement);
|
||||
size_t insert_impl(const std::string& statement);
|
||||
size_t update_impl(const std::string& statement);
|
||||
size_t remove_impl(const std::string& statement);
|
||||
bind_result_t select_impl(const std::string& statement)
|
||||
{
|
||||
std::unique_ptr<detail::prepared_statement_handle_t> prepared(
|
||||
new detail::prepared_statement_handle_t(prepare_statement(*_handle, statement)));
|
||||
if (!prepared)
|
||||
{
|
||||
throw sqlpp::exception("Sqlite3 error: Could not store result set");
|
||||
}
|
||||
|
||||
return {std::move(prepared)};
|
||||
}
|
||||
|
||||
size_t insert_impl(const std::string& statement)
|
||||
{
|
||||
auto prepared = prepare_statement(*_handle, statement);
|
||||
execute_statement(*_handle, prepared);
|
||||
|
||||
return sqlite3_last_insert_rowid(_handle->sqlite);
|
||||
}
|
||||
|
||||
size_t update_impl(const std::string& statement)
|
||||
{
|
||||
auto prepared = prepare_statement(*_handle, statement);
|
||||
execute_statement(*_handle, prepared);
|
||||
return sqlite3_changes(_handle->sqlite);
|
||||
}
|
||||
|
||||
size_t remove_impl(const std::string& statement)
|
||||
{
|
||||
auto prepared = prepare_statement(*_handle, statement);
|
||||
execute_statement(*_handle, prepared);
|
||||
return sqlite3_changes(_handle->sqlite);
|
||||
}
|
||||
|
||||
|
||||
// prepared execution
|
||||
prepared_statement_t prepare_impl(const std::string& statement);
|
||||
bind_result_t run_prepared_select_impl(prepared_statement_t& prepared_statement);
|
||||
size_t run_prepared_execute_impl(prepared_statement_t& prepared_statement);
|
||||
size_t run_prepared_insert_impl(prepared_statement_t& prepared_statement);
|
||||
size_t run_prepared_update_impl(prepared_statement_t& prepared_statement);
|
||||
size_t run_prepared_remove_impl(prepared_statement_t& prepared_statement);
|
||||
prepared_statement_t prepare_impl(const std::string& statement)
|
||||
{
|
||||
return {std::unique_ptr<detail::prepared_statement_handle_t>(
|
||||
new detail::prepared_statement_handle_t(prepare_statement(*_handle, statement)))};
|
||||
}
|
||||
|
||||
bind_result_t run_prepared_select_impl(prepared_statement_t& prepared_statement)
|
||||
{
|
||||
return {prepared_statement._handle};
|
||||
}
|
||||
|
||||
size_t run_prepared_insert_impl(prepared_statement_t& prepared_statement)
|
||||
{
|
||||
execute_statement(*_handle, *prepared_statement._handle.get());
|
||||
|
||||
return sqlite3_last_insert_rowid(_handle->sqlite);
|
||||
}
|
||||
|
||||
size_t run_prepared_update_impl(prepared_statement_t& prepared_statement)
|
||||
{
|
||||
execute_statement(*_handle, *prepared_statement._handle.get());
|
||||
|
||||
return sqlite3_changes(_handle->sqlite);
|
||||
}
|
||||
|
||||
size_t run_prepared_remove_impl(prepared_statement_t& prepared_statement)
|
||||
{
|
||||
execute_statement(*_handle, *prepared_statement._handle.get());
|
||||
|
||||
return sqlite3_changes(_handle->sqlite);
|
||||
}
|
||||
|
||||
size_t run_prepared_execute_impl(prepared_statement_t& prepared_statement)
|
||||
{
|
||||
execute_statement(*_handle, *prepared_statement._handle.get());
|
||||
|
||||
return sqlite3_changes(_handle->sqlite);
|
||||
}
|
||||
|
||||
|
||||
public:
|
||||
using _prepared_statement_t = prepared_statement_t;
|
||||
@@ -143,10 +299,15 @@ namespace sqlpp
|
||||
return ::sqlpp::serialize(t, context);
|
||||
}
|
||||
|
||||
connection(connection_config config);
|
||||
connection(connection&&) noexcept;
|
||||
connection& operator=(connection&&) noexcept;
|
||||
~connection();
|
||||
connection(connection_config config) : _handle(new detail::connection_handle(std::move(config)))
|
||||
{
|
||||
}
|
||||
|
||||
connection(connection&&) noexcept = default;
|
||||
connection& operator=(connection&&) noexcept = default;
|
||||
|
||||
~connection() = default;
|
||||
|
||||
connection(const connection&) = delete;
|
||||
connection& operator=(const connection&) = delete;
|
||||
|
||||
@@ -251,7 +412,12 @@ namespace sqlpp
|
||||
}
|
||||
|
||||
//! execute arbitrary command (e.g. create a table)
|
||||
size_t execute(const std::string& command);
|
||||
size_t execute(const std::string& statement)
|
||||
{
|
||||
auto prepared = prepare_statement(*_handle, statement);
|
||||
execute_statement(*_handle, prepared);
|
||||
return sqlite3_changes(_handle->sqlite);
|
||||
}
|
||||
|
||||
template <
|
||||
typename Execute,
|
||||
@@ -280,7 +446,20 @@ namespace sqlpp
|
||||
}
|
||||
|
||||
//! escape given string (does not quote, though)
|
||||
std::string escape(const std::string& s) const;
|
||||
std::string escape(const std::string& s) const
|
||||
{
|
||||
std::string t;
|
||||
t.reserve(s.size());
|
||||
|
||||
for (const char c : s)
|
||||
{
|
||||
if (c == '\'')
|
||||
t.push_back(c);
|
||||
t.push_back(c);
|
||||
}
|
||||
|
||||
return t;
|
||||
}
|
||||
|
||||
//! call run on the argument
|
||||
template <typename T>
|
||||
@@ -317,30 +496,98 @@ namespace sqlpp
|
||||
}
|
||||
|
||||
//! set the transaction isolation level for this connection
|
||||
void set_default_isolation_level(isolation_level level);
|
||||
void set_default_isolation_level(isolation_level level)
|
||||
{
|
||||
if (level == sqlpp::isolation_level::read_uncommitted)
|
||||
{
|
||||
execute("pragma read_uncommitted = true");
|
||||
} else {
|
||||
execute("pragma read_uncommitted = false");
|
||||
}
|
||||
}
|
||||
|
||||
//! get the currently active transaction isolation level
|
||||
isolation_level get_default_isolation_level();
|
||||
sqlpp::isolation_level get_default_isolation_level()
|
||||
{
|
||||
auto stmt = prepare_statement(*_handle, "pragma read_uncommitted");
|
||||
execute_statement(*_handle, stmt);
|
||||
|
||||
int level = sqlite3_column_int(stmt.sqlite_statement, 0);
|
||||
|
||||
return level == 0 ? sqlpp::isolation_level::serializable :
|
||||
sqlpp::isolation_level::read_uncommitted;
|
||||
}
|
||||
|
||||
//! start transaction
|
||||
void start_transaction();
|
||||
void start_transaction()
|
||||
{
|
||||
if (_transaction_status == transaction_status_type::active)
|
||||
{
|
||||
throw sqlpp::exception("Sqlite3 error: Cannot have more than one open transaction per connection");
|
||||
}
|
||||
|
||||
_transaction_status = transaction_status_type::maybe;
|
||||
auto prepared = prepare_statement(*_handle, "BEGIN");
|
||||
execute_statement(*_handle, prepared);
|
||||
_transaction_status = transaction_status_type::active;
|
||||
}
|
||||
|
||||
//! commit transaction (or throw if the transaction has been finished already)
|
||||
void commit_transaction();
|
||||
void commit_transaction()
|
||||
{
|
||||
if (_transaction_status == transaction_status_type::none)
|
||||
{
|
||||
throw sqlpp::exception("Sqlite3 error: Cannot commit a finished or failed transaction");
|
||||
}
|
||||
_transaction_status = transaction_status_type::maybe;
|
||||
auto prepared = prepare_statement(*_handle, "COMMIT");
|
||||
execute_statement(*_handle, prepared);
|
||||
_transaction_status = transaction_status_type::none;
|
||||
}
|
||||
|
||||
//! rollback transaction with or without reporting the rollback (or throw if the transaction has been finished
|
||||
// already)
|
||||
void rollback_transaction(bool report);
|
||||
void rollback_transaction(bool report)
|
||||
{
|
||||
if (_transaction_status == transaction_status_type::none)
|
||||
{
|
||||
throw sqlpp::exception("Sqlite3 error: Cannot rollback a finished or failed transaction");
|
||||
}
|
||||
if (report)
|
||||
{
|
||||
std::cerr << "Sqlite3 warning: Rolling back unfinished transaction" << std::endl;
|
||||
}
|
||||
_transaction_status = transaction_status_type::maybe;
|
||||
auto prepared = prepare_statement(*_handle, "ROLLBACK");
|
||||
execute_statement(*_handle, prepared);
|
||||
_transaction_status = transaction_status_type::none;
|
||||
}
|
||||
|
||||
//! report a rollback failure (will be called by transactions in case of a rollback failure in the destructor)
|
||||
void report_rollback_failure(const std::string message) noexcept;
|
||||
void report_rollback_failure(const std::string message) noexcept
|
||||
{
|
||||
std::cerr << "Sqlite3 message:" << message << std::endl;
|
||||
}
|
||||
|
||||
//! get the last inserted id
|
||||
uint64_t last_insert_id() noexcept;
|
||||
uint64_t last_insert_id() noexcept
|
||||
{
|
||||
return sqlite3_last_insert_rowid(_handle->sqlite);
|
||||
}
|
||||
|
||||
::sqlite3* native_handle();
|
||||
::sqlite3* native_handle()
|
||||
{
|
||||
return _handle->sqlite;
|
||||
}
|
||||
|
||||
auto attach(const connection_config&, const std::string name) -> schema_t;
|
||||
schema_t attach(const connection_config& config, const std::string name)
|
||||
{
|
||||
auto prepared =
|
||||
prepare_statement(*_handle, "ATTACH '" + escape(config.path_to_database) + "' AS " + escape(name));
|
||||
execute_statement(*_handle, prepared);
|
||||
|
||||
return {name};
|
||||
}
|
||||
};
|
||||
|
||||
inline std::string serializer_t::escape(std::string arg)
|
||||
|
||||
@@ -28,10 +28,16 @@
|
||||
#define SQLPP_SQLITE3_PREPARED_STATEMENT_H
|
||||
|
||||
#include <memory>
|
||||
#include <sqlpp11/chrono.h>
|
||||
#include <sqlpp11/sqlite3/export.h>
|
||||
#include <ciso646>
|
||||
#include <cmath>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <date/date.h>
|
||||
|
||||
#include <sqlpp11/chrono.h>
|
||||
#include <sqlpp11/sqlite3/export.h>
|
||||
|
||||
#include <sqlpp11/sqlite3/prepared_statement_handle.h>
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(push)
|
||||
@@ -42,12 +48,28 @@ namespace sqlpp
|
||||
{
|
||||
namespace sqlite3
|
||||
{
|
||||
class connection;
|
||||
|
||||
namespace detail
|
||||
{
|
||||
struct prepared_statement_handle_t;
|
||||
}
|
||||
void check_bind_result(int result, const char* const type)
|
||||
{
|
||||
switch (result)
|
||||
{
|
||||
case SQLITE_OK:
|
||||
return;
|
||||
case SQLITE_RANGE:
|
||||
throw sqlpp::exception("Sqlite3 error: " + std::string(type) + " bind value out of range");
|
||||
case SQLITE_NOMEM:
|
||||
throw sqlpp::exception("Sqlite3 error: " + std::string(type) + " bind out of memory");
|
||||
case SQLITE_TOOBIG:
|
||||
throw sqlpp::exception("Sqlite3 error: " + std::string(type) + " bind too big");
|
||||
default:
|
||||
throw sqlpp::exception("Sqlite3 error: " + std::string(type) +
|
||||
" bind returned unexpected value: " + std::to_string(result));
|
||||
}
|
||||
}
|
||||
} // namespace detail
|
||||
|
||||
class connection;
|
||||
|
||||
class SQLPP11_SQLITE3_EXPORT prepared_statement_t
|
||||
{
|
||||
@@ -56,7 +78,12 @@ namespace sqlpp
|
||||
|
||||
public:
|
||||
prepared_statement_t() = default;
|
||||
prepared_statement_t(std::shared_ptr<detail::prepared_statement_handle_t>&& handle);
|
||||
prepared_statement_t(std::shared_ptr<detail::prepared_statement_handle_t>&& handle)
|
||||
: _handle(std::move(handle))
|
||||
{
|
||||
if (_handle and _handle->debug)
|
||||
std::cerr << "Sqlite3 debug: Constructing prepared_statement, using handle at " << _handle.get() << std::endl;
|
||||
}
|
||||
prepared_statement_t(const prepared_statement_t&) = delete;
|
||||
prepared_statement_t(prepared_statement_t&& rhs) = default;
|
||||
prepared_statement_t& operator=(const prepared_statement_t&) = delete;
|
||||
@@ -68,15 +95,158 @@ namespace sqlpp
|
||||
return _handle == rhs._handle;
|
||||
}
|
||||
|
||||
void _reset();
|
||||
void _bind_boolean_parameter(size_t index, const signed char* value, bool is_null);
|
||||
void _bind_floating_point_parameter(size_t index, const double* value, bool is_null);
|
||||
void _bind_integral_parameter(size_t index, const int64_t* value, bool is_null);
|
||||
void _bind_unsigned_integral_parameter(size_t index, const uint64_t* value, bool is_null);
|
||||
void _bind_text_parameter(size_t index, const std::string* value, bool is_null);
|
||||
void _bind_date_parameter(size_t index, const ::sqlpp::chrono::day_point* value, bool is_null);
|
||||
void _bind_date_time_parameter(size_t index, const ::sqlpp::chrono::microsecond_point* value, bool is_null);
|
||||
void _bind_blob_parameter(size_t index, const std::vector<uint8_t>* value, bool is_null);
|
||||
void _reset()
|
||||
{
|
||||
if (_handle->debug)
|
||||
std::cerr << "Sqlite3 debug: resetting prepared statement" << std::endl;
|
||||
sqlite3_reset(_handle->sqlite_statement);
|
||||
}
|
||||
|
||||
void _bind_boolean_parameter(size_t index, const signed char* value, bool is_null)
|
||||
{
|
||||
if (_handle->debug)
|
||||
std::cerr << "Sqlite3 debug: binding boolean parameter " << (*value ? "true" : "false")
|
||||
<< " at index: " << index << ", being " << (is_null ? "" : "not ") << "null" << std::endl;
|
||||
|
||||
int result;
|
||||
if (not is_null)
|
||||
result = sqlite3_bind_int(_handle->sqlite_statement, static_cast<int>(index + 1), *value);
|
||||
else
|
||||
result = sqlite3_bind_null(_handle->sqlite_statement, static_cast<int>(index + 1));
|
||||
detail::check_bind_result(result, "boolean");
|
||||
}
|
||||
|
||||
void _bind_floating_point_parameter(size_t index, const double* value, bool is_null)
|
||||
{
|
||||
if (_handle->debug)
|
||||
std::cerr << "Sqlite3 debug: binding floating_point parameter " << *value << " at index: " << index
|
||||
<< ", being " << (is_null ? "" : "not ") << "null" << std::endl;
|
||||
|
||||
int result;
|
||||
if (not is_null)
|
||||
{
|
||||
if (std::isnan(*value))
|
||||
result = sqlite3_bind_text(_handle->sqlite_statement, static_cast<int>(index + 1), "NaN", 3, SQLITE_STATIC);
|
||||
else if (std::isinf(*value))
|
||||
{
|
||||
if (*value > std::numeric_limits<double>::max())
|
||||
result = sqlite3_bind_text(_handle->sqlite_statement, static_cast<int>(index + 1), "Inf", 3, SQLITE_STATIC);
|
||||
else
|
||||
result =
|
||||
sqlite3_bind_text(_handle->sqlite_statement, static_cast<int>(index + 1), "-Inf", 4, SQLITE_STATIC);
|
||||
}
|
||||
else
|
||||
result = sqlite3_bind_double(_handle->sqlite_statement, static_cast<int>(index + 1), *value);
|
||||
}
|
||||
else
|
||||
result = sqlite3_bind_null(_handle->sqlite_statement, static_cast<int>(index + 1));
|
||||
detail::check_bind_result(result, "floating_point");
|
||||
}
|
||||
|
||||
void _bind_integral_parameter(size_t index, const int64_t* value, bool is_null)
|
||||
{
|
||||
if (_handle->debug)
|
||||
std::cerr << "Sqlite3 debug: binding integral parameter " << *value << " at index: " << index << ", being "
|
||||
<< (is_null ? "" : "not ") << "null" << std::endl;
|
||||
|
||||
int result;
|
||||
if (not is_null)
|
||||
result = sqlite3_bind_int64(_handle->sqlite_statement, static_cast<int>(index + 1), *value);
|
||||
else
|
||||
result = sqlite3_bind_null(_handle->sqlite_statement, static_cast<int>(index + 1));
|
||||
detail::check_bind_result(result, "integral");
|
||||
}
|
||||
|
||||
void _bind_unsigned_integral_parameter(size_t index, const uint64_t* value, bool is_null)
|
||||
{
|
||||
if (_handle->debug)
|
||||
std::cerr << "Sqlite3 debug: binding unsigned integral parameter " << *value << " at index: " << index
|
||||
<< ", being " << (is_null ? "" : "not ") << "null" << std::endl;
|
||||
|
||||
int result;
|
||||
if (not is_null)
|
||||
result =
|
||||
sqlite3_bind_int64(_handle->sqlite_statement, static_cast<int>(index + 1), static_cast<int64_t>(*value));
|
||||
else
|
||||
result = sqlite3_bind_null(_handle->sqlite_statement, static_cast<int>(index + 1));
|
||||
detail::check_bind_result(result, "integral");
|
||||
}
|
||||
|
||||
void _bind_text_parameter(size_t index, const std::string* value, bool is_null)
|
||||
{
|
||||
if (_handle->debug)
|
||||
std::cerr << "Sqlite3 debug: binding text parameter " << *value << " at index: " << index << ", being "
|
||||
<< (is_null ? "" : "not ") << "null" << std::endl;
|
||||
|
||||
int result;
|
||||
if (not is_null)
|
||||
result = sqlite3_bind_text(_handle->sqlite_statement, static_cast<int>(index + 1), value->data(),
|
||||
static_cast<int>(value->size()), SQLITE_STATIC);
|
||||
else
|
||||
result = sqlite3_bind_null(_handle->sqlite_statement, static_cast<int>(index + 1));
|
||||
detail::check_bind_result(result, "text");
|
||||
}
|
||||
|
||||
void _bind_date_parameter(size_t index, const ::sqlpp::chrono::day_point* value, bool is_null)
|
||||
{
|
||||
if (_handle->debug)
|
||||
std::cerr << "Sqlite3 debug: binding date parameter "
|
||||
<< " at index: " << index << ", being " << (is_null ? "" : "not ") << "null" << std::endl;
|
||||
|
||||
int result;
|
||||
if (not is_null)
|
||||
{
|
||||
std::ostringstream os;
|
||||
const auto ymd = ::date::year_month_day{*value};
|
||||
os << ymd;
|
||||
const auto text = os.str();
|
||||
result = sqlite3_bind_text(_handle->sqlite_statement, static_cast<int>(index + 1), text.data(),
|
||||
static_cast<int>(text.size()), SQLITE_TRANSIENT);
|
||||
}
|
||||
else
|
||||
result = sqlite3_bind_null(_handle->sqlite_statement, static_cast<int>(index + 1));
|
||||
detail::check_bind_result(result, "date");
|
||||
}
|
||||
|
||||
void _bind_date_time_parameter(size_t index,
|
||||
const ::sqlpp::chrono::microsecond_point* value,
|
||||
bool is_null)
|
||||
{
|
||||
if (_handle->debug)
|
||||
std::cerr << "Sqlite3 debug: binding date_time parameter "
|
||||
<< " at index: " << index << ", being " << (is_null ? "" : "not ") << "null" << std::endl;
|
||||
|
||||
int result;
|
||||
if (not is_null)
|
||||
{
|
||||
const auto dp = ::sqlpp::chrono::floor<::date::days>(*value);
|
||||
const auto time = ::date::make_time(::sqlpp::chrono::floor<::std::chrono::milliseconds>(*value - dp));
|
||||
const auto ymd = ::date::year_month_day{dp};
|
||||
std::ostringstream os; // gcc-4.9 does not support auto os = std::ostringstream{};
|
||||
os << ymd << ' ' << time;
|
||||
const auto text = os.str();
|
||||
result = sqlite3_bind_text(_handle->sqlite_statement, static_cast<int>(index + 1), text.data(),
|
||||
static_cast<int>(text.size()), SQLITE_TRANSIENT);
|
||||
}
|
||||
else
|
||||
result = sqlite3_bind_null(_handle->sqlite_statement, static_cast<int>(index + 1));
|
||||
detail::check_bind_result(result, "date");
|
||||
}
|
||||
|
||||
void _bind_blob_parameter(size_t index, const std::vector<uint8_t>* value, bool is_null)
|
||||
{
|
||||
if (_handle->debug)
|
||||
std::cerr << "Sqlite3 debug: binding vector parameter size of " << value->size() << " at index: " << index
|
||||
<< ", being " << (is_null ? "" : "not ") << "null" << std::endl;
|
||||
|
||||
int result;
|
||||
if (not is_null)
|
||||
result = sqlite3_bind_blob(_handle->sqlite_statement, static_cast<int>(index + 1), value->data(),
|
||||
static_cast<int>(value->size()), SQLITE_STATIC);
|
||||
else
|
||||
result = sqlite3_bind_null(_handle->sqlite_statement, static_cast<int>(index + 1));
|
||||
detail::check_bind_result(result, "blob");
|
||||
}
|
||||
};
|
||||
} // namespace sqlite3
|
||||
} // namespace sqlpp
|
||||
|
||||
101
include/sqlpp11/sqlite3/prepared_statement_handle.h
Normal file
101
include/sqlpp11/sqlite3/prepared_statement_handle.h
Normal file
@@ -0,0 +1,101 @@
|
||||
/*
|
||||
* Copyright (c) 2013 - 2015, Roland Bock
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification,
|
||||
* are permitted provided that the following conditions are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* Redistributions in binary form must reproduce the above copyright notice, this
|
||||
* list of conditions and the following disclaimer in the documentation and/or
|
||||
* other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
|
||||
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef SQLPP_SQLITE3_PREPARED_STATEMENT_HANDLE_H
|
||||
#define SQLPP_SQLITE3_PREPARED_STATEMENT_HANDLE_H
|
||||
|
||||
#include <memory>
|
||||
#include <sqlpp11/chrono.h>
|
||||
#include <sqlpp11/sqlite3/export.h>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable : 4251)
|
||||
#endif
|
||||
|
||||
namespace sqlpp
|
||||
{
|
||||
namespace sqlite3
|
||||
{
|
||||
class connection;
|
||||
|
||||
namespace detail
|
||||
{
|
||||
struct prepared_statement_handle_t
|
||||
{
|
||||
sqlite3_stmt* sqlite_statement;
|
||||
bool debug;
|
||||
|
||||
prepared_statement_handle_t(sqlite3_stmt* statement, bool debug_) : sqlite_statement(statement), debug(debug_)
|
||||
{
|
||||
}
|
||||
|
||||
prepared_statement_handle_t(const prepared_statement_handle_t&) = delete;
|
||||
prepared_statement_handle_t(prepared_statement_handle_t&& rhs)
|
||||
{
|
||||
sqlite_statement = rhs.sqlite_statement;
|
||||
rhs.sqlite_statement = nullptr;
|
||||
|
||||
debug = rhs.debug;
|
||||
}
|
||||
prepared_statement_handle_t& operator=(const prepared_statement_handle_t&) = delete;
|
||||
prepared_statement_handle_t& operator=(prepared_statement_handle_t&& rhs)
|
||||
{
|
||||
if (sqlite_statement != rhs.sqlite_statement)
|
||||
{
|
||||
sqlite_statement = rhs.sqlite_statement;
|
||||
rhs.sqlite_statement = nullptr;
|
||||
}
|
||||
debug = rhs.debug;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
~prepared_statement_handle_t()
|
||||
{
|
||||
if (sqlite_statement)
|
||||
{
|
||||
sqlite3_finalize(sqlite_statement);
|
||||
}
|
||||
}
|
||||
|
||||
bool operator!() const
|
||||
{
|
||||
return !sqlite_statement;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
} // namespace sqlite3
|
||||
} // namespace sqlpp
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(pop)
|
||||
#endif
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user