blob: 0b6e3250bd7fceeedf74148ab354e9204c082500 [file] [log] [blame]
/**
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef NIFI_MINIFI_CPP_SQLITECONNECTION_H
#define NIFI_MINIFI_CPP_SQLITECONNECTION_H
#include <sqlite3.h>
namespace org {
namespace apache {
namespace nifi {
namespace minifi {
namespace sqlite {
class SQLiteConnection;
/**
* RAII wrapper for a sqlite3 prepared statement
*/
class SQLiteStatement {
public:
SQLiteStatement(sqlite3 *db, const std::string &sql)
: logger_(logging::LoggerFactory<SQLiteConnection>::getLogger()) {
if (sqlite3_prepare_v3(db, sql.c_str(), sql.size(), 0, &stmt_, nullptr)) {
std::stringstream err_msg;
err_msg << "Failed to create prepared statement: ";
err_msg << sql;
err_msg << " because ";
err_msg << sqlite3_errmsg(db);
throw std::runtime_error(err_msg.str());
}
if (!stmt_) {
std::stringstream err_msg;
err_msg << "Failed to create prepared statement: ";
err_msg << sql;
err_msg << " because statement was NULL";
throw std::runtime_error(err_msg.str());
}
db_ = db;
}
~SQLiteStatement() {
sqlite3_finalize(stmt_);
}
void bind_text(int pos, const std::string &text) {
if (sqlite3_bind_text(stmt_, pos, text.c_str(), text.size(), SQLITE_TRANSIENT)) {
std::stringstream err_msg;
err_msg << "Failed to bind text parameter"
<< pos
<< ": "
<< text
<< " because "
<< sqlite3_errmsg(db_);
throw std::runtime_error(err_msg.str());
}
}
void bind_int64(int pos, uint64_t val) {
if (sqlite3_bind_int64(stmt_, pos, val)) {
std::stringstream err_msg;
err_msg << "Failed to bind int64 parameter"
<< pos
<< ": "
<< val
<< " because "
<< sqlite3_errmsg(db_);
throw std::runtime_error(err_msg.str());
}
}
void bind_double(int pos, double val) {
if (sqlite3_bind_double(stmt_, pos, val)) {
std::stringstream err_msg;
err_msg << "Failed to bind double parameter"
<< pos
<< ": "
<< val
<< " because "
<< sqlite3_errmsg(db_);
throw std::runtime_error(err_msg.str());
}
}
void bind_null(int pos) {
if (sqlite3_bind_null(stmt_, pos)) {
std::stringstream err_msg;
err_msg << "Failed to bind NULL parameter"
<< pos
<< " because "
<< sqlite3_errmsg(db_);
throw std::runtime_error(err_msg.str());
}
}
void step() {
int rc = sqlite3_step(stmt_);
if (rc == SQLITE_BUSY) {
reset_flags();
is_ok_ = false;
is_busy_ = true;
} else if (rc == SQLITE_DONE) {
reset_flags();
is_done_ = true;
} else if (rc == SQLITE_ROW) {
reset_flags();
is_row_ = true;
} else {
is_ok_ = false;
is_error_ = true;
std::stringstream err_msg;
err_msg << "Failed to step statement because "
<< sqlite3_errmsg(db_);
throw std::runtime_error(err_msg.str());
}
}
bool is_ok() {
return is_ok_;
}
bool is_done() {
return is_done_;
}
bool is_row() {
return is_row_;
}
bool is_error() {
return is_error_;
}
bool is_busy() {
return is_busy_;
}
std::string column_text(int col) {
return std::string(reinterpret_cast<const char *>(sqlite3_column_text(stmt_, col)));
}
uint64_t column_int64(int col) {
return sqlite3_column_int64(stmt_, col);
}
double column_double(int col) {
return sqlite3_column_double(stmt_, col);
}
bool column_is_int(int col) {
return SQLITE_INTEGER == sqlite3_column_type(stmt_, col);
}
bool column_is_float(int col) {
return SQLITE_FLOAT == sqlite3_column_type(stmt_, col);
}
bool column_is_text(int col) {
return SQLITE_TEXT == sqlite3_column_type(stmt_, col);
}
bool column_is_blob(int col) {
return SQLITE_BLOB == sqlite3_column_type(stmt_, col);
}
bool column_is_null(int col) {
return SQLITE_NULL == sqlite3_column_type(stmt_, col);
}
std::string column_name(int col) {
return std::string(sqlite3_column_name(stmt_, col));
}
int column_count() {
return sqlite3_column_count(stmt_);
}
void reset() {
sqlite3_reset(stmt_);
}
private:
std::shared_ptr<logging::Logger> logger_;
sqlite3_stmt *stmt_;
sqlite3 *db_ = nullptr;
bool is_ok_ = true;
bool is_busy_ = false;
bool is_done_ = false;
bool is_error_ = false;
bool is_row_ = false;
void reset_flags() {
is_ok_ = true;
is_busy_ = false;
is_done_ = false;
is_error_ = false;
is_row_ = false;
}
};
/**
* RAII wrapper for a sqlite3 connection
*/
class SQLiteConnection {
public:
SQLiteConnection(const std::string &filename)
: logger_(logging::LoggerFactory<SQLiteConnection>::getLogger()),
filename_(filename) {
logger_->log_info("Opening SQLite database: %s", filename_);
if (sqlite3_open(filename_.c_str(), &db_)) {
std::stringstream err_msg("Failed to open database: ");
err_msg << filename_;
err_msg << " because ";
err_msg << sqlite3_errmsg(db_);
throw std::runtime_error(err_msg.str());
}
}
SQLiteConnection(SQLiteConnection &&other)
: logger_(std::move(other.logger_)),
filename_(std::move(other.filename_)),
db_(other.db_) {
other.db_ = nullptr;
}
~SQLiteConnection() {
logger_->log_info("Closing SQLite database: %s", filename_);
sqlite3_close(db_);
}
SQLiteStatement prepare(const std::string sql) {
return SQLiteStatement(db_, sql);
}
std::string errormsg() {
return sqlite3_errmsg(db_);
}
private:
std::shared_ptr<logging::Logger> logger_;
std::string filename_;
sqlite3 *db_ = nullptr;
};
} /* namespace sqlite */
} /* namespace minifi */
} /* namespace nifi */
} /* namespace apache */
} /* namespace org */
#endif //NIFI_MINIFI_CPP_SQLITECONNECTION_H