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 {
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 () {
"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_);
}
"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_);
}
"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;
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));
}
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;
}
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);
}
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;
}
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);
}
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;
}
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 {
sqlite3_finalize(stmt_);
stmt_ = r.stmt_;
r.stmt_ = nullptr;
+ query_ = std::move(r.query_);
+ is_done_ = r.is_done_;
}
return *this;
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);
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) {
throw DbException("invalid column type", type);
}
- return AutoDbType(dbt);
+ return AutoDbType(dbt, type);
}
template <typename ...Types>
};
Iterator begin() const {
+ if (is_done_)
+ return Iterator(nullptr);
return Iterator(stmt_);
}
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_);
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) {
db.db_ = nullptr;
}
- explicit operator bool() {
+ explicit operator bool() const {
if (db_ == nullptr)
return false;
return true;
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");
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,
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");
if (r != SQLITE_ROW && r != SQLITE_DONE)
return false;
+ previous_stmt.SetDone(r == SQLITE_DONE ? true : false);
return true;
}
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;
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 =