Databáze SQLite - nadstavba v C++
3.2.2020
Pokud píšeme aplikaci v C++ (tedy nemusíme se "omezovat" na čisté neobjektové C), můžeme si některé častěji používané funkce SQLite zabalit do tříd/objektů jazyka C++. Napsal jsem jednoduché zabalení do 2 tříd: database a command. Třída database zabaluje základnífunkce nad ukazatelem sqlite3*. Třída command zabaluje funkce nad "prepared statement", tj. ukazatelem sqlite3_stmt*
Následující zdrojový kód naleznete také na mém GitHubu a můžete jej použít a upravit podle svých potřeb.
#pragma once
#ifndef RCSQLITE3_H
#define RCSQLITE3_H
#include <sqlite3.h>
#include <stdexcept>
#include <string.h>
#include <assert.h>
namespace rc
{
namespace sqlite
{
inline void __attribute__((noreturn)) fatal_exit() noexcept
{
std::terminate();
}
class database
{
protected:
sqlite3* _pointer = nullptr;
public:
~database() noexcept
{
release();
}
void release() noexcept
{
if (_pointer)
{
if (sqlite3_close(_pointer) != SQLITE_OK)
rc::sqlite::fatal_exit();
_pointer = nullptr;
}
}
__attribute__((always_inline)) operator sqlite3*() const noexcept
{
return _pointer;
}
__attribute__((always_inline)) sqlite3* handle() const noexcept
{
return _pointer;
}
__attribute__((always_inline)) bool opened() noexcept
{
return (_pointer != nullptr);
}
void open(const char* file_path)
{
release();
int sqlres = sqlite3_open(file_path, &_pointer);
if (sqlres != SQLITE_OK)
{
release();
throw std::runtime_error(sqlite3_errstr(sqlres));
}
}
void create(const char* file_path)
{
release();
int ires = sqlite3_open(file_path, &_pointer);
if (ires != SQLITE_OK)
{
release();
throw std::runtime_error(sqlite3_errstr(ires));
}
}
void execute_command(const char* sz_sql)
{
assert(_pointer != nullptr);
int sqlres = sqlite3_exec(_pointer, sz_sql, nullptr, nullptr, nullptr);
if (sqlres != SQLITE_OK)
throw std::runtime_error(sqlite3_errstr(sqlres));
}
void begin_transaction()
{
execute_command("BEGIN TRANSACTION;");
}
void end_transaction()
{
execute_command("END TRANSACTION;");
}
void check_integrity()
{
execute_command("PRAGMA integrity_check;");
}
}; // class database
class command
{
private:
sqlite3_stmt* _stmt = nullptr;
database* _database = nullptr;
public:
command(const char* sz_sql, database* db)
{
create(sz_sql, db);
}
command(const std::string& str_sql, database* db)
{
create(str_sql.c_str(), db);
}
~command() noexcept
{
release();
}
void release() noexcept
{
if (_stmt != nullptr)
{
int vysl = sqlite3_reset(_stmt);
if (vysl != SQLITE_OK)
rc::sqlite::fatal_exit();
vysl = sqlite3_finalize(_stmt);
if (vysl != SQLITE_OK)
rc::sqlite::fatal_exit();
_stmt = nullptr;
}
}
// Pokud není další záznam příkazu, vrátí false
// Při jiné chybě SQLite vyvolá kritické ukončení
bool next_line() noexcept
{
assert(_stmt != nullptr);
int sqlres = sqlite3_step(_stmt);
if (SQLITE_ERROR == sqlres)
rc::sqlite::fatal_exit();
return (SQLITE_ROW == sqlres);
}
// Musí být ověřeno předchozí úspěšné provedení funkce next_line,
// a nesmí být před tímto _smtp uvolněn
std::string get_text(int column_index) noexcept
{
assert(_stmt != nullptr);
const unsigned char* pt = sqlite3_column_text(_stmt, column_index);
if (nullptr == pt)
return std::string("");
else
{
size_t length = static_cast(sqlite3_column_bytes(_stmt, column_index));
return std::string((const char*)pt, length);
}
}
sqlite3_int64 get_int64(int column_index) noexcept
{
assert(_stmt != nullptr);
return sqlite3_column_int64(_stmt, column_index);
}
const void* get_blob(int column_index) noexcept
{
assert(_stmt != nullptr);
return sqlite3_column_blob(_stmt, column_index);
}
size_t get_blob_size(int column_index) noexcept
{
assert(_stmt != nullptr);
return static_cast(sqlite3_column_bytes(_stmt, column_index));
}
void set_param_text(int param_number, const char* text)
{
assert(_stmt != nullptr);
int sqlres = sqlite3_bind_text(_stmt, param_number, text,
static_cast(strlen(text)), SQLITE_STATIC);
if (sqlres != SQLITE_OK)
{
release();
throw std::logic_error(sqlite3_errstr(sqlres));
}
}
void set_param_int64(int param_number, sqlite3_int64 i64_value)
{
assert(_stmt != nullptr);
int sqlres = sqlite3_bind_int64(_stmt, param_number, i64_value);
if (sqlres != SQLITE_OK)
{
release();
throw std::logic_error(sqlite3_errstr(sqlres));
}
}
void set_param_blob(int param_number, const void* p_value, size_t bytes_count)
{
assert(_stmt != nullptr);
int sqlres = sqlite3_bind_blob64(_stmt, param_number, p_value,
static_cast(bytes_count), nullptr);
if (sqlres != SQLITE_OK)
{
release();
throw std::logic_error(sqlite3_errstr(sqlres));
}
}
// on error throw exception
void execute()
{
assert(_stmt != nullptr);
int sqlres = sqlite3_step(_stmt);
if ((sqlres != SQLITE_OK) && (sqlres != SQLITE_DONE))
throw std::runtime_error(sqlite3_errstr(sqlres));
}
private:
void create(const char* sz_sql, rc::sqlite::database* db)
{
assert(db!= nullptr);
if (nullptr == db)
rc::sqlite::fatal_exit();
_database = db;
int sqlres = sqlite3_prepare_v2(*_database, sz_sql,
static_cast(strlen(sz_sql)), &_stmt, nullptr);
if (SQLITE_OK != sqlres)
{
release();
throw std::runtime_error(sqlite3_errstr(sqlres));
}
}
}; // class command
} // namespace sqlite
} // namespace rc
#endif // RCSQLITE3_H