namespace {
-const char *DB_CMD_OBJECT_SELECT = "SELECT * FROM [join_name_object_tables];";
-
-const char *DB_CMD_NAME_SELECT_BY_IDX = "SELECT name, label FROM NAMES WHERE idx = ?001;";
+constexpr CKM::DB::CommandBuffer OBJECT_SELECT_CMD_BUF{CKM::DB::DB_CMD_OBJECT_SELECT_ID,
+ "SELECT * FROM [join_name_object_tables];"};
+constexpr CKM::DB::CommandBuffer NAME_SELECT_BY_IDX_CMD_BUF{CKM::DB::DB_CMD_NAME_SELECT_BY_IDX_ID,
+ "SELECT name, label FROM NAMES WHERE idx = ?001;"};
} // anonymous namespace
DB::RowVector CKMLogicExt::getRows(uid_t user)
{
DB::Crypto& database = m_userDataMap[user].database;
- DB::SqlConnection::DataCommandUniquePtr selectCommand =
- database.m_connection->PrepareDataCommand(DB_CMD_OBJECT_SELECT);
+ auto selectCommand = database.m_connection->PrepareDataCommand(OBJECT_SELECT_CMD_BUF);
DB::RowVector rows;
while (selectCommand->Step())
rows.push_back(database.getRow(selectCommand));
+
return rows;
}
NameEntry CKMLogicExt::getNameByIdx(uid_t user, int idx)
{
- DB::SqlConnection::DataCommandUniquePtr selectCommand =
- m_userDataMap[user].database.m_connection->PrepareDataCommand(DB_CMD_NAME_SELECT_BY_IDX);
+ auto selectCommand =
+ m_userDataMap[user].database.m_connection->PrepareDataCommand(NAME_SELECT_BY_IDX_CMD_BUF);
selectCommand->BindInteger(1, idx);
if (!selectCommand->Step())
#ifndef CKM_SQL_CONNECTION_H
#define CKM_SQL_CONNECTION_H
+#include <map>
#include <memory>
+#include <utility>
#include <stdint.h>
#include <boost/optional.hpp>
#include <sqlite3.h>
-
#include <dpl/availability.h>
#include <dpl/exception.h>
#include <dpl/log/log.h>
namespace CKM {
namespace DB {
+
+enum DBCommandID {
+ DB_CMD_SCHEMA_SET_ID,
+ DB_CMD_SCHEMA_GET_ID,
+ DB_CMD_NAME_INSERT_ID,
+ DB_CMD_NAME_COUNT_ROWS_ID,
+ DB_CMD_NAME_DELETE_ID,
+ DB_CMD_NAME_DELETE_BY_OWNER_ID,
+ DB_CMD_OBJECT_INSERT_ID,
+ DB_CMD_OBJECT_UPDATE_ID,
+ DB_CMD_OBJECT_SELECT_BY_NAME_AND_OWNER_ID,
+ DB_CMD_KEY_INSERT_ID,
+ DB_CMD_KEY_SELECT_ID,
+ DB_CMD_KEY_DELETE_ID,
+ DB_CMD_PERMISSION_SET_ID,
+ DB_CMD_PERMISSION_SELECT_ID,
+ DB_CMD_PERMISSION_DELETE_ID,
+ DB_CMD_INFO_SELECT_BY_TYPE_AND_PERMISSION_ID,
+ DB_CMD_SELECT_TABLE_ID,
+ DB_CMD_OBJECT_SELECT_ID,
+ DB_CMD_NAME_SELECT_BY_IDX_ID,
+ DB_CMD_TABLE_EXISTS_ID
+};
+
+struct CommandBuffer {
+ DBCommandID id;
+ const char* buffer;
+};
+
/**
* SQL connection class
*/
void CheckBindResult(int result);
void CheckColumnIndex(SqlConnection::ColumnIndex column);
- DataCommand(SqlConnection *connection, const char *buffer);
-
friend class SqlConnection;
public:
+ DataCommand(SqlConnection *connection, const char *buffer);
NONCOPYABLE(DataCommand);
virtual ~DataCommand();
*/
bool Step();
+ /**
+ * Reset prepared statement
+ *
+ * This method is used to reset the prepared statement to it's initial state.
+ * Due to sqlite documentation unfinalized SQL statements typically hold a read
+ * transaction open, so the execution of VACUUM (or different write operations)
+ * command might fail if there are unfinalized SQL statements on the same connection.
+ *
+ */
+ void Reset();
+
/**
* Checks whether column value is null
*
boost::optional<RawBuffer> GetColumnOptionalBlob(ColumnIndex column);
};
+ class DataCommandHandler {
+ public:
+ explicit DataCommandHandler(DataCommand &dataCommand) : m_dataCommand(dataCommand) {}
+
+ NONCOPYABLE(DataCommandHandler);
+ ~DataCommandHandler();
+
+ void BindNull(ArgumentIndex pos) { m_dataCommand.BindNull(pos); }
+
+ void BindInteger(ArgumentIndex pos, int val) { m_dataCommand.BindInteger(pos, val); }
+
+ void BindInt8(ArgumentIndex pos, int8_t val) { m_dataCommand.BindInt8(pos, val); }
+
+ void BindInt16(ArgumentIndex pos, int8_t val) { m_dataCommand.BindInt16(pos, val); }
+
+ void BindInt32(ArgumentIndex pos, int8_t val) { m_dataCommand.BindInt32(pos, val); }
+
+ void BindInt64(ArgumentIndex pos, int8_t val) { m_dataCommand.BindInt64(pos, val); }
+
+ void BindFloat(ArgumentIndex pos, float val) { m_dataCommand.BindFloat(pos, val); }
+
+ void BindDouble(ArgumentIndex pos, double val) { m_dataCommand.BindDouble(pos, val); }
+
+ void BindString(ArgumentIndex pos, const char *val) { m_dataCommand.BindString(pos, val); }
+
+ void BindBlob(ArgumentIndex pos, const RawBuffer &val) { m_dataCommand.BindBlob(pos, val); }
+
+ void BindInteger(ArgumentIndex pos, const boost::optional<int> &val)
+ {
+ m_dataCommand.BindInteger(pos, val);
+ }
+
+ void BindInt8(ArgumentIndex pos, const boost::optional<int8_t> &val)
+ {
+ m_dataCommand.BindInt8(pos, val);
+ }
+
+ void BindInt16(ArgumentIndex pos, const boost::optional<int16_t> &val)
+ {
+ m_dataCommand.BindInt16(pos, val);
+ }
+
+ void BindInt32(ArgumentIndex pos, const boost::optional<int32_t> &val)
+ {
+ m_dataCommand.BindInt32(pos, val);
+ }
+
+ void BindInt64(ArgumentIndex pos, const boost::optional<int64_t> &val)
+ {
+ m_dataCommand.BindInt64(pos, val);
+ }
+
+ void BindFloat(ArgumentIndex pos, const boost::optional<float> &val)
+ {
+ m_dataCommand.BindFloat(pos, val);
+ }
+
+ void BindDouble(ArgumentIndex pos, const boost::optional<double> &val)
+ {
+ m_dataCommand.BindDouble(pos, val);
+ }
+
+ void BindBlob(ArgumentIndex pos, const boost::optional<RawBuffer> &val)
+ {
+ m_dataCommand.BindBlob(pos, val);
+ }
+
+ bool Step() { return m_dataCommand.Step(); }
+
+ bool IsColumnNull(ColumnIndex col) { return m_dataCommand.IsColumnNull(col); }
+
+ int GetColumnInteger(ColumnIndex col) { return m_dataCommand.GetColumnInteger(col); }
+
+ int8_t GetColumnInt8(ColumnIndex col) { return m_dataCommand.GetColumnInt8(col); }
+
+ int16_t GetColumnInt16(ColumnIndex col) { return m_dataCommand.GetColumnInt16(col); }
+
+ int32_t GetColumnInt32(ColumnIndex col) { return m_dataCommand.GetColumnInt32(col); }
+
+ int64_t GetColumnInt64(ColumnIndex col) { return m_dataCommand.GetColumnInt64(col); }
+
+ float GetColumnFloat(ColumnIndex col) { return m_dataCommand.GetColumnFloat(col); }
+
+ double GetColumnDouble(ColumnIndex col) { return m_dataCommand.GetColumnDouble(col); }
+
+ std::string GetColumnString(ColumnIndex col) { return m_dataCommand.GetColumnString(col); }
+
+ RawBuffer GetColumnBlob(ColumnIndex col) { return m_dataCommand.GetColumnBlob(col); }
+
+ boost::optional<int> GetColumnOptionalInteger(ColumnIndex col)
+ {
+ return m_dataCommand.GetColumnOptionalInteger(col);
+ }
+
+ boost::optional<int8_t> GetColumnOptionalInt8(ColumnIndex col)
+ {
+ return m_dataCommand.GetColumnOptionalInt8(col);
+ }
+
+ boost::optional<int16_t> GetColumnOptionalInt16(ColumnIndex col)
+ {
+ return m_dataCommand.GetColumnOptionalInt16(col);
+ }
+
+ boost::optional<int32_t> GetColumnOptionalInt32(ColumnIndex col)
+ {
+ return m_dataCommand.GetColumnOptionalInt32(col);
+ }
+
+ boost::optional<int64_t> GetColumnOptionalInt64(ColumnIndex col)
+ {
+ return m_dataCommand.GetColumnOptionalInt64(col);
+ }
+
+ boost::optional<float> GetColumnOptionalFloat(ColumnIndex col)
+ {
+ return m_dataCommand.GetColumnOptionalFloat(col);
+ }
+
+ boost::optional<double> GetColumnOptionalDouble(ColumnIndex col)
+ {
+ return m_dataCommand.GetColumnOptionalDouble(col);
+ }
+
+ boost::optional<RawBuffer> GetColumnOptionalBlob(ColumnIndex col)
+ {
+ return m_dataCommand.GetColumnOptionalBlob(col);
+ }
+
+ private:
+ DataCommand &m_dataCommand;
+ };
+
// Move on copy constructor. No copy semantics
- typedef std::unique_ptr<DataCommand> DataCommandUniquePtr;
+ typedef std::unique_ptr<DataCommandHandler> DataCommandHandlerUniquePtr;
// Open flags
class Flag {
protected:
sqlite3 *m_connection;
-
- // Options
-
- // Stored data procedures
- int m_dataCommandsCount;
+ std::unordered_map<DBCommandID, DataCommand> m_preparedStatements;
// Synchronization object
std::unique_ptr<SynchronizationObject> m_synchronizationObject;
* @param format SQL statement
* @return Data command representing stored procedure
*/
- DataCommandUniquePtr PrepareDataCommand(const char *format, ...);
-
+ DataCommandHandlerUniquePtr PrepareDataCommand(const CommandBuffer &buff, ...);
/**
* Check whether given table exists
*
*/
RowID GetLastInsertRowID() const;
+ /**
+ * Clear prepared SQL statements map
+ *
+ */
+ void ClearMap();
+
private:
void ExecCommandHelper(Output *out, const char *format, va_list args);
};
#include <cstdarg>
#include <memory>
#include <noncopyable.h>
-
+#include <utility>
namespace {
const int MAX_RETRY = 10;
namespace CKM {
namespace DB {
namespace { // anonymous
+
+constexpr CommandBuffer TABLE_EXISTS_CMD_BUF{DBCommandID::DB_CMD_TABLE_EXISTS_ID,
+ "select tbl_name from sqlite_master where name=?;"};
+
class ScopedNotifyAll {
private:
SqlConnection::SynchronizationObject *m_synchronizationObject;
// Notify all after potentially synchronized database connection access
ScopedNotifyAll notifyAll(connection->m_synchronizationObject.get());
+ int ret;
for (int i = 0; i < MAX_RETRY; i++) {
- int ret = sqlite3_prepare_v2(connection->m_connection,
+ ret = sqlite3_prepare_v2(connection->m_connection,
buffer, strlen(buffer),
&m_stmt, NULL);
if (ret == SQLITE_OK) {
LogPedantic("Prepared data command: " << buffer);
-
- // Increment stored data command count
- ++m_masterConnection->m_dataCommandsCount;
return;
} else if (ret == SQLITE_BUSY) {
LogPedantic("Collision occurred while preparing SQL command");
{
LogPedantic("SQL data command finalizing");
- if (sqlite3_finalize(m_stmt) != SQLITE_OK)
- LogError("Failed to finalize data command");
-
- // Decrement stored data command count
- --m_masterConnection->m_dataCommandsCount;
+ int ret = sqlite3_finalize(m_stmt);
+ if (ret != SQLITE_OK) {
+ const char *error = sqlite3_errmsg(
+ m_masterConnection->m_connection);
+ LogError("SQL Finalize data command failed, Error: " << error);
+ }
}
void SqlConnection::DataCommand::CheckBindResult(int result)
ThrowMsg(Exception::InternalError, "sqlite permanently busy");
}
+void SqlConnection::DataCommand::Reset()
+{
+ for (int i = 0; i < MAX_RETRY; i++) {
+ int ret = sqlite3_reset(m_stmt);
+
+ // sqlite3_reset() returns SQLITE_OK if sqlite3_step() has never before
+ // been called/has not been called since previous reset or appriopriate
+ // error code indicated in sqlite3_step.
+ // Also it could return it's own error when the process of resetting
+ // prepared statement caused a new error.
+ if (ret == SQLITE_OK || ret == SQLITE_DONE || ret == SQLITE_ROW)
+ return;
+ else if (ret == SQLITE_BUSY) {
+ LogPedantic("Collision occurred while executing SQL reset command");
+
+ // Synchronize if synchronization object is available
+ if (m_masterConnection->m_synchronizationObject) {
+ LogPedantic("Performing synchronization");
+
+ m_masterConnection->
+ m_synchronizationObject->Synchronize();
+
+ continue;
+ }
+
+ // No synchronization object defined. Fail.
+ }
+ const char *error = sqlite3_errmsg(m_masterConnection->m_connection);
+
+ // This error occurs most often due to an error in DataCommand::Step()
+ // but can also be a DataCommand::Reset() error.
+ LogError("SQL reset data command failed");
+ LogError(" Error: " << error);
+ ThrowMsg(Exception::InternalError, error);
+ }
+
+ LogError("sqlite in the state of possible infinite loop");
+ ThrowMsg(Exception::InternalError, "sqlite permanently busy");
+}
+
void SqlConnection::DataCommand::CheckColumnIndex(
SqlConnection::ColumnIndex column)
{
return boost::optional<RawBuffer>(temp);
}
//LCOV_EXCL_STOP
+SqlConnection::DataCommandHandler::~DataCommandHandler() {
+ LogPedantic("SQL DataCommandHandler finalizing");
+ try{
+ m_dataCommand.Reset();
+ } catch (const Exception::InternalError &) {
+ LogError("Couldn't execute reset statement - Internal Error");
+ } catch (...) {
+ LogError("Couldn't execute reset statement - unknown exception");
+ }
+}
void SqlConnection::Connect(const std::string &address,
Flag::Option flag)
LogPedantic("Disconnecting from DB...");
- // All stored data commands must be deleted before disconnect
- assert(m_dataCommandsCount == 0);
-
int result;
+ m_preparedStatements.clear();
result = sqlite3_close(m_connection);
if (result != SQLITE_OK) {
return false;
}
- DataCommandUniquePtr command =
- PrepareDataCommand("select tbl_name from sqlite_master where name=?;");
+ DataCommandHandlerUniquePtr command = PrepareDataCommand(TABLE_EXISTS_CMD_BUF);
command->BindString(1, tableName);
LogPedantic("No matching records in table");
return false;
}
-
return command->GetColumnString(0) == tableName;
}
Flag::Option option,
SynchronizationObject *synchronizationObject) :
m_connection(NULL),
- m_dataCommandsCount(0),
m_synchronizationObject(synchronizationObject),
m_isKeySet(false)
{
va_end(args);
}
-SqlConnection::DataCommandUniquePtr SqlConnection::PrepareDataCommand(
- const char *format,
+SqlConnection::DataCommandHandlerUniquePtr SqlConnection::PrepareDataCommand(
+ const CommandBuffer &buff,
...)
{
if (m_connection == NULL) {
LogError("Cannot execute data command. Not connected to DB!");
- return DataCommandUniquePtr();
+ return DataCommandHandlerUniquePtr();
}
+ auto findStatement = m_preparedStatements.find(buff.id);
+ if (findStatement != m_preparedStatements.end())
+ return DataCommandHandlerUniquePtr(new DataCommandHandler(findStatement->second));
+
char *rawBuffer;
va_list args;
- va_start(args, format);
+ va_start(args, buff);
- if (vasprintf(&rawBuffer, format, args) == -1)
+ if (vasprintf(&rawBuffer, buff.buffer, args) == -1)
rawBuffer = NULL;
va_end(args);
if (!buffer) {
LogError("Failed to allocate statement string");
- return DataCommandUniquePtr();
+ return DataCommandHandlerUniquePtr();
}
LogPedantic("Executing SQL data command: " << buffer.get());
- return DataCommandUniquePtr(new DataCommand(this, buffer.get()));
+ return DataCommandHandlerUniquePtr(new DataCommandHandler(m_preparedStatements.emplace(
+ std::piecewise_construct,
+ std::forward_as_tuple(buff.id),
+ std::forward_as_tuple(this, buffer.get()))
+ .first->second));
+}
+
+void SqlConnection::ClearMap()
+{
+ m_preparedStatements.clear();
}
//LCOV_EXCL_START
const char *SCRIPT_CREATE_SCHEMA = "create_schema";
const char *SCRIPT_DROP_ALL_ITEMS = "drop_all";
const char *SCRIPT_MIGRATE = "migrate_";
+const char *DB_SCHEMA_VERSION_FIELD = "schema_version";
+
+} //anonymous namespace
+
+namespace CKM {
+namespace DB {
+namespace {
// common substitutions:
// 100 - idx
// 103 - value
// 104 - permissionLabel
// 105 - permissionMask
-const char *DB_CMD_SCHEMA_SET =
- "REPLACE INTO SCHEMA_INFO(name, value) "
- " VALUES(?101, ?103);";
-
-const char *DB_CMD_SCHEMA_GET =
- "SELECT * FROM SCHEMA_INFO WHERE name=?101;";
-
-const char *DB_SCHEMA_VERSION_FIELD = "schema_version";
-
-
-const char *DB_CMD_NAME_INSERT =
- "INSERT INTO NAMES("
- " name, label) "
- " VALUES(?101, ?102);";
-
-const char *DB_CMD_NAME_COUNT_ROWS =
- "SELECT COUNT(idx) FROM NAMES WHERE name=?101 AND label=?102;";
-
-const char *DB_CMD_NAME_DELETE =
- "DELETE FROM NAMES WHERE name=?101 AND label=?102;";
-
-const char *DB_CMD_NAME_DELETE_BY_OWNER =
- "DELETE FROM NAMES WHERE label=?102;";
-
-
-const char *DB_CMD_OBJECT_INSERT =
- "INSERT INTO OBJECTS("
- " exportable, dataType,"
- " algorithmType, encryptionScheme,"
- " iv, dataSize, data, tag, idx, backendId) "
- " VALUES(?001, ?002, ?003, ?004, ?005, "
- " ?006, ?007, ?008,"
- " (SELECT idx FROM NAMES WHERE name=?101 and label=?102),"
- " ?009"
- " );";
-
-const char *DB_CMD_OBJECT_UPDATE =
- "UPDATE OR FAIL OBJECTS SET"
- " algorithmType = ?003,"
- " encryptionScheme = ?004,"
- " iv = ?005,"
- " dataSize = ?006,"
- " data = ?007,"
- " tag = ?008"
- " WHERE idx IN (SELECT idx FROM NAMES WHERE name=?101 and label=?102)"
- " AND dataType = ?002;";
-
-const char *DB_CMD_OBJECT_SELECT_BY_NAME_AND_OWNER =
- "SELECT * FROM [join_name_object_tables] "
- " WHERE (dataType BETWEEN ?001 AND ?002) "
- " AND name=?101 and label=?102;";
-
-
-const char *DB_CMD_KEY_INSERT =
- "INSERT INTO KEYS(label, key) VALUES (?, ?);";
-const char *DB_CMD_KEY_SELECT =
- "SELECT key FROM KEYS WHERE label=?;";
-const char *DB_CMD_KEY_DELETE =
- "DELETE FROM KEYS WHERE label=?";
-
-
-const char *DB_CMD_PERMISSION_SET = // SQLite does not support updating views
- "REPLACE INTO PERMISSIONS(permissionLabel, permissionMask, idx) "
- " VALUES (?104, ?105, (SELECT idx FROM NAMES WHERE name=?101 and label=?102));";
-
-const char *DB_CMD_PERMISSION_SELECT =
- "SELECT permissionMask FROM [join_name_permission_tables] "
- " WHERE permissionLabel=?104 "
- " AND name=?101 and label=?102;";
-
-const char *DB_CMD_PERMISSION_DELETE = // SQLite does not support updating views
- "DELETE FROM PERMISSIONS WHERE permissionLabel=?104 AND "
- " idx=(SELECT idx FROM NAMES WHERE name=?101 and label=?102);";
-
-
+constexpr CommandBuffer SCHEMA_SET_CMD_BUF{DB_CMD_SCHEMA_SET_ID,
+ "REPLACE INTO SCHEMA_INFO(name, value) "
+ " VALUES(?101, ?103);"};
+constexpr CommandBuffer SCHEMA_GET_CMD_BUF{DB_CMD_SCHEMA_GET_ID,
+ "SELECT * FROM SCHEMA_INFO WHERE name=?101;"};
+constexpr CommandBuffer NAME_INSERT_CMD_BUF{DB_CMD_NAME_INSERT_ID,
+ "INSERT INTO NAMES("
+ " name, label) "
+ " VALUES(?101, ?102);"};
+constexpr CommandBuffer NAME_COUNT_ROWS_CMD_BUF{DB_CMD_NAME_COUNT_ROWS_ID,
+ "SELECT COUNT(idx) FROM NAMES WHERE name=?101 AND label=?102;"};
+constexpr CommandBuffer NAME_DELETE_CMD_BUF{DB_CMD_NAME_DELETE_ID,
+ "DELETE FROM NAMES WHERE name=?101 AND label=?102;"};
+constexpr CommandBuffer NAME_DELETE_BY_OWNER_CMD_BUF{DB_CMD_NAME_DELETE_BY_OWNER_ID,
+ "DELETE FROM NAMES WHERE label=?102;"};
+constexpr CommandBuffer OBJECT_INSERT_CMD_BUF{DB_CMD_OBJECT_INSERT_ID,
+ "INSERT INTO OBJECTS("
+ " exportable, dataType,"
+ " algorithmType, encryptionScheme,"
+ " iv, dataSize, data, tag, idx, backendId) "
+ " VALUES(?001, ?002, ?003, ?004, ?005, "
+ " ?006, ?007, ?008,"
+ " (SELECT idx FROM NAMES WHERE name=?101 and label=?102),"
+ " ?009"
+ " );"};
+constexpr CommandBuffer OBJECT_UPDATE_CMD_BUF{DB_CMD_OBJECT_UPDATE_ID,
+ "UPDATE OR FAIL OBJECTS SET"
+ " algorithmType = ?003,"
+ " encryptionScheme = ?004,"
+ " iv = ?005,"
+ " dataSize = ?006,"
+ " data = ?007,"
+ " tag = ?008"
+ " WHERE idx IN (SELECT idx FROM NAMES WHERE name=?101 and label=?102)"
+ " AND dataType = ?002;"};
+constexpr CommandBuffer OBJECT_SELECT_BY_NAME_AND_OWNER_CMD_BUF{
+ DB_CMD_OBJECT_SELECT_BY_NAME_AND_OWNER_ID,
+ "SELECT * FROM [join_name_object_tables] "
+ " WHERE (dataType BETWEEN ?001 AND ?002) "
+ " AND name=?101 and label=?102;"};
+constexpr CommandBuffer KEY_INSERT_CMD_BUF{DB_CMD_KEY_INSERT_ID,
+ "INSERT INTO KEYS(label, key) VALUES (?, ?);"};
+constexpr CommandBuffer KEY_SELECT_CMD_BUF{DB_CMD_KEY_SELECT_ID,
+ "SELECT key FROM KEYS WHERE label=?;"};
+constexpr CommandBuffer KEY_DELETE_CMD_BUF{DB_CMD_KEY_DELETE_ID,
+ "DELETE FROM KEYS WHERE label=?"};
+// SQLite does not support updating views
+constexpr CommandBuffer PERMISSION_SET_CMD_BUF{DB_CMD_PERMISSION_SET_ID,
+ "REPLACE INTO PERMISSIONS(permissionLabel, permissionMask, idx) "
+ " VALUES (?104, ?105, (SELECT idx FROM NAMES WHERE name=?101 and label=?102));"};
+constexpr CommandBuffer PERMISSION_SELECT_CMD_BUF{DB_CMD_PERMISSION_SELECT_ID,
+ "SELECT permissionMask FROM [join_name_permission_tables] "
+ " WHERE permissionLabel=?104 "
+ " AND name=?101 and label=?102;"};
+// SQLite does not support updating views
+constexpr CommandBuffer PERMISSION_DELETE_CMD_BUF{DB_CMD_PERMISSION_DELETE_ID,
+ "DELETE FROM PERMISSIONS WHERE permissionLabel=?104 AND "
+ " idx=(SELECT idx FROM NAMES WHERE name=?101 and label=?102);"};
/*
* GROUP BY is necessary because of the following case:
* -There are several permissions to L1, N1 (label, name) from other accessors. When listing
* objects accessible by L1 the query will produce one result (L1, N1) for each allowed
* accessor but GROUP BY will reduce them to one so L1 will have (L1, N1) on its list only once
*/
-const char *DB_CMD_INFO_SELECT_BY_TYPE_AND_PERMISSION =
- "SELECT label, name, dataType, backendId FROM [join_all_tables] "
- " WHERE dataType>=?001 AND dataType<=?002 "
- " AND permissionLabel=?104 AND permissionMask&?004!=0 GROUP BY idx;";
-}
-
-namespace CKM {
-namespace DB {
+constexpr CommandBuffer INFO_SELECT_BY_TYPE_AND_PERMISSION_CMD_BUF{
+ DB_CMD_INFO_SELECT_BY_TYPE_AND_PERMISSION_ID,
+ "SELECT label, name, dataType, backendId FROM [join_all_tables] "
+ " WHERE dataType>=?001 AND dataType<=?002 "
+ " AND permissionLabel=?104 AND permissionMask&?004!=0 GROUP BY idx;"};
-namespace {
auto openSqlConnection(const std::string &path, const RawBuffer &rawPass)
{
auto conn = std::make_unique<SqlConnection>(path, SqlConnection::Flag::Option::CRW);
}
Row Crypto::getRow(
- const SqlConnection::DataCommandUniquePtr &selectCommand) const
+ const SqlConnection::DataCommandHandlerUniquePtr &selectCommand) const
{
Row row;
row.name = selectCommand->GetColumnString(0);
DataType typeRangeStop)
{
try {
- SqlConnection::DataCommandUniquePtr selectCommand =
- m_connection->PrepareDataCommand(DB_CMD_OBJECT_SELECT_BY_NAME_AND_OWNER);
+ auto selectCommand =
+ m_connection->PrepareDataCommand(OBJECT_SELECT_BY_NAME_AND_OWNER_CMD_BUF);
selectCommand->BindInteger(1, typeRangeStart);
selectCommand->BindInteger(2, typeRangeStop);
RowVector &output)
{
try {
- SqlConnection::DataCommandUniquePtr selectCommand =
- m_connection->PrepareDataCommand(DB_CMD_OBJECT_SELECT_BY_NAME_AND_OWNER);
+ auto selectCommand =
+ m_connection->PrepareDataCommand(OBJECT_SELECT_BY_NAME_AND_OWNER_CMD_BUF);
selectCommand->BindInteger(1, typeRangeStart);
selectCommand->BindInteger(2, typeRangeStop);
{
try {
Transaction transaction(this);
- SqlConnection::DataCommandUniquePtr selectCommand =
- m_connection->PrepareDataCommand(DB_CMD_INFO_SELECT_BY_TYPE_AND_PERMISSION);
+ auto selectCommand =
+ m_connection->PrepareDataCommand(INFO_SELECT_BY_TYPE_AND_PERMISSION_CMD_BUF);
selectCommand->BindInteger(1, typeRangeStart);
selectCommand->BindInteger(2, typeRangeStop);
selectCommand->BindString(104, accessor.c_str());
const RawBuffer &key)
{
try {
- SqlConnection::DataCommandUniquePtr insertCommand =
- m_connection->PrepareDataCommand(DB_CMD_KEY_INSERT);
+ auto insertCommand = m_connection->PrepareDataCommand(KEY_INSERT_CMD_BUF);
insertCommand->BindString(1, owner.c_str());
insertCommand->BindBlob(2, key);
insertCommand->Step();
Crypto::RawBufferOptional Crypto::getKey(const ClientId &owner)
{
try {
- SqlConnection::DataCommandUniquePtr selectCommand =
- m_connection->PrepareDataCommand(DB_CMD_KEY_SELECT);
+ auto selectCommand = m_connection->PrepareDataCommand(KEY_SELECT_CMD_BUF);
selectCommand->BindString(1, owner.c_str());
- if (selectCommand->Step())
- return RawBufferOptional(selectCommand->GetColumnBlob(0));
- else
+ if (selectCommand->Step()) {
+ RawBuffer ret = selectCommand->GetColumnBlob(0);
+ return RawBufferOptional(ret);
+ } else
return RawBufferOptional();
+
} catch (const SqlConnection::Exception::InvalidColumn &) {
LogError("Select statement invalid column error");
} catch (const SqlConnection::Exception::SyntaxError &) {
try {
Transaction transaction(this);
- SqlConnection::DataCommandUniquePtr deleteCommand =
- m_connection->PrepareDataCommand(DB_CMD_KEY_DELETE);
+ auto deleteCommand = m_connection->PrepareDataCommand(KEY_DELETE_CMD_BUF);
deleteCommand->BindString(1, owner.c_str());
deleteCommand->Step();
-
NameTable nameTable(m_connection.get());
nameTable.deleteAllRows(owner);
void Crypto::SchemaInfo::setVersionInfo()
{
- SqlConnection::DataCommandUniquePtr insertContextCommand =
- m_connection->PrepareDataCommand(DB_CMD_SCHEMA_SET);
+ auto insertContextCommand = m_connection->PrepareDataCommand(SCHEMA_SET_CMD_BUF);
insertContextCommand->BindString(101, DB_SCHEMA_VERSION_FIELD);
insertContextCommand->BindString(103,
std::to_string(DB_VERSION_CURRENT).c_str());
bool Crypto::SchemaInfo::getVersionInfo(int &version)
{
- SqlConnection::DataCommandUniquePtr selectCommand =
- m_connection->PrepareDataCommand(DB_CMD_SCHEMA_GET);
+ auto selectCommand = m_connection->PrepareDataCommand(SCHEMA_GET_CMD_BUF);
selectCommand->BindString(101, DB_SCHEMA_VERSION_FIELD);
if (selectCommand->Step()) {
{
if (permissionMask == Permission::NONE) {
// clear permissions
- SqlConnection::DataCommandUniquePtr deletePermissionCommand =
- m_connection->PrepareDataCommand(DB_CMD_PERMISSION_DELETE);
+ auto deletePermissionCommand = m_connection->PrepareDataCommand(PERMISSION_DELETE_CMD_BUF);
deletePermissionCommand->BindString(104, accessor.c_str());
deletePermissionCommand->BindString(101, name.c_str());
deletePermissionCommand->BindString(102, owner.c_str());
deletePermissionCommand->Step();
} else {
// add new permissions
- SqlConnection::DataCommandUniquePtr setPermissionCommand =
- m_connection->PrepareDataCommand(DB_CMD_PERMISSION_SET);
+ auto setPermissionCommand = m_connection->PrepareDataCommand(PERMISSION_SET_CMD_BUF);
setPermissionCommand->BindString(104, accessor.c_str());
setPermissionCommand->BindInteger(105, static_cast<int>(permissionMask));
setPermissionCommand->BindString(101, name.c_str());
const ClientId &owner,
const ClientId &accessor) const
{
- SqlConnection::DataCommandUniquePtr selectCommand =
- m_connection->PrepareDataCommand(DB_CMD_PERMISSION_SELECT);
+ auto selectCommand = m_connection->PrepareDataCommand(PERMISSION_SELECT_CMD_BUF);
selectCommand->BindString(104, accessor.c_str());
// name table reference
if (selectCommand->Step()) {
// there is entry for the <name, owner> pair
- return PermissionMaskOptional(PermissionMask(selectCommand->GetColumnInteger(
- 0)));
+ int ret = selectCommand->GetColumnInteger(0);
+ return PermissionMaskOptional(PermissionMask(ret));
}
return PermissionMaskOptional();
const ClientId &owner)
{
// insert NAMES item
- SqlConnection::DataCommandUniquePtr insertNameCommand =
- m_connection->PrepareDataCommand(DB_CMD_NAME_INSERT);
+ auto insertNameCommand = m_connection->PrepareDataCommand(NAME_INSERT_CMD_BUF);
insertNameCommand->BindString(101, name.c_str());
insertNameCommand->BindString(102, owner.c_str());
insertNameCommand->Step();
const Name &name,
const ClientId &ownerOwner)
{
- SqlConnection::DataCommandUniquePtr deleteCommand =
- m_connection->PrepareDataCommand(DB_CMD_NAME_DELETE);
+ auto deleteCommand = m_connection->PrepareDataCommand(NAME_DELETE_CMD_BUF);
deleteCommand->BindString(101, name.c_str());
deleteCommand->BindString(102, ownerOwner.c_str());
void Crypto::NameTable::deleteAllRows(const ClientId &owner)
{
- SqlConnection::DataCommandUniquePtr deleteData =
- m_connection->PrepareDataCommand(DB_CMD_NAME_DELETE_BY_OWNER);
+ auto deleteData = m_connection->PrepareDataCommand(NAME_DELETE_BY_OWNER_CMD_BUF);
deleteData->BindString(102, owner.c_str());
// Step() result code does not provide information whether
bool Crypto::NameTable::isPresent(const Name &name,
const ClientId &owner) const
{
- SqlConnection::DataCommandUniquePtr checkCmd =
- m_connection->PrepareDataCommand(DB_CMD_NAME_COUNT_ROWS);
+ auto checkCmd = m_connection->PrepareDataCommand(NAME_COUNT_ROWS_CMD_BUF);
checkCmd->BindString(101, name.c_str());
checkCmd->BindString(102, owner.c_str());
void Crypto::ObjectTable::addRow(const Row &row)
{
- SqlConnection::DataCommandUniquePtr insertObjectCommand =
- m_connection->PrepareDataCommand(DB_CMD_OBJECT_INSERT);
+ auto insertObjectCommand = m_connection->PrepareDataCommand(OBJECT_INSERT_CMD_BUF);
insertObjectCommand->BindInteger(1, row.exportable);
insertObjectCommand->BindInteger(2, row.dataType);
insertObjectCommand->BindInteger(3, static_cast<int>(row.algorithmType));
void Crypto::ObjectTable::updateRow(const Row &row)
{
- SqlConnection::DataCommandUniquePtr updateObjectCommand =
- m_connection->PrepareDataCommand(DB_CMD_OBJECT_UPDATE);
+ auto updateObjectCommand = m_connection->PrepareDataCommand(OBJECT_UPDATE_CMD_BUF);
updateObjectCommand->BindInteger(2, row.dataType);
updateObjectCommand->BindInteger(3, static_cast<int>(row.algorithmType));
updateObjectCommand->BindInteger(4, row.encryptionScheme);
std::unique_ptr<SqlConnection> m_connection;
Row getRow(
- const SqlConnection::DataCommandUniquePtr &selectCommand) const;
+ const SqlConnection::DataCommandHandlerUniquePtr &selectCommand) const;
private:
bool m_inUserTransaction;
#include <test_common.h>
using CKM::DB::SqlConnection;
+using CKM::DB::CommandBuffer;
#pragma GCC diagnostic push
#pragma GCC diagnostic warning "-Wdeprecated-declarations"
const char *INSERT_TABLE = "INSERT INTO t1(a,b) VALUES ("
" 'one for the money',"
" 'two for the show');";
-const char *SELECT_TABLE = "SELECT * FROM t1";
-
-const char *SELECT_COMMAND = "SELECT COUNT(idx) FROM NAMES WHERE name=?101 AND label=?102;";
-const char *INSERT_COMMAND = "INSERT INTO NAMES("
- "name, label) "
- "VALUES(?101, ?102);";
-const char *DELETE_COMMAND = "DELETE FROM NAMES WHERE name=?101 AND label=?102;";
-constexpr int TIMES = 100;
CKM::RawBuffer RAW_PASSWORD = createDefaultPass();
+constexpr CommandBuffer SELECT_TABLE_BUF{CKM::DB::DB_CMD_SELECT_TABLE_ID,
+ "SELECT * FROM t1"};
+
+constexpr CommandBuffer SELECT_TEST_BUF{CKM::DB::DB_CMD_NAME_COUNT_ROWS_ID,
+ "SELECT COUNT(idx) FROM NAMES WHERE name=?101 AND label=?102;"};
+constexpr CommandBuffer INSERT_TEST_BUF{CKM::DB::DB_CMD_NAME_INSERT_ID,
+ "INSERT INTO NAMES("
+ " name, label) "
+ " VALUES(?101, ?102);"};
+constexpr CommandBuffer DELETE_TEST_BUF{CKM::DB::DB_CMD_NAME_DELETE_ID,
+ "DELETE FROM NAMES WHERE name=?101 AND label=?102;"};
+constexpr int TIMES = 100;
+
class MockCrypto : public CKM::DB::Crypto {
public:
MockCrypto(const std::string &path, const CKM::RawBuffer &rawPass)
:Crypto("", path, rawPass) {}
- void countTimePreparedStatements(const char * statement){
+ void countTimePreparedStatements(const CommandBuffer &statement)
+ {
auto begin = std::chrono::steady_clock::now();
for (int i = 0; i < TIMES; ++i) {
BOOST_REQUIRE_NO_THROW(m_connection->PrepareDataCommand(statement));
}
auto end = std::chrono::steady_clock::now();
- std::cout << "Average " << statement << "\npreparation time: "
+ std::cout << "Average " << statement.buffer << "\npreparation time: "
<< std::chrono::duration_cast<std::chrono::microseconds>(end - begin).count() / TIMES
<< "[µs]\n";
}
- void countTimePreparedStatementsSteps(const char * statement)
+ void countTimePreparedStatementsSteps(const CommandBuffer &statement)
{
auto begin = std::chrono::steady_clock::now();
for (int i = 0; i < TIMES; ++i) {
- SqlConnection::DataCommandUniquePtr command;
+ SqlConnection::DataCommandHandlerUniquePtr command;
std::string str = "name";
str += i;
BOOST_REQUIRE_NO_THROW(command = m_connection->PrepareDataCommand(statement));
BOOST_REQUIRE_NO_THROW(command->Step());
}
auto end = std::chrono::steady_clock::now();
- std::cout << "Average " << statement << "\npreparation&execution time: "
+ std::cout << "Average " << statement.buffer << "\npreparation&execution time: "
<< std::chrono::duration_cast<std::chrono::microseconds>(end - begin).count() / TIMES
<< "[µs]\n";
}
+ void countTimePreparedStatementsReset(const CommandBuffer &statement)
+ {
+ auto count = std::chrono::steady_clock::duration::zero();
+ for (int i = 0; i < TIMES; ++i) {
+ auto begin = std::chrono::steady_clock::now();
+ BOOST_REQUIRE_NO_THROW(m_connection->PrepareDataCommand(statement));
+ auto end = std::chrono::steady_clock::now();
+ count += (end - begin);
+ m_connection->ClearMap();
+ }
+
+ std::cout << "Average " << statement.buffer << "\npreparation time: "
+ << std::chrono::duration_cast<std::chrono::microseconds>(count).count() / TIMES
+ << "[µs]\n";
+ }
};
BOOST_AUTO_TEST_SUITE(SQL_TEST)
{
SqlConnection encrypting_you_not(ENCRYPT_ME_NOT,
SqlConnection::Flag::RW);
- SqlConnection::DataCommandUniquePtr selectCommand;
+ SqlConnection::DataCommandHandlerUniquePtr selectCommand;
BOOST_REQUIRE_NO_THROW(selectCommand = encrypting_you_not.
- PrepareDataCommand(SELECT_TABLE));
+ PrepareDataCommand(SELECT_TABLE_BUF));
BOOST_REQUIRE_NO_THROW(selectCommand->Step());
std::string value;
BOOST_REQUIRE_NO_THROW(value = selectCommand->GetColumnString(0));
SqlConnection encrypting_you(ENCRYPT_ME,
SqlConnection::Flag::RW);
encrypting_you.SetKey(RAW_PASSWORD);
- SqlConnection::DataCommandUniquePtr selectCommand;
+ SqlConnection::DataCommandHandlerUniquePtr selectCommand;
BOOST_REQUIRE_NO_THROW(selectCommand = encrypting_you.
- PrepareDataCommand(SELECT_TABLE));
+ PrepareDataCommand(SELECT_TABLE_BUF));
BOOST_REQUIRE_NO_THROW(selectCommand->Step());
std::string value;
BOOST_REQUIRE_NO_THROW(value = selectCommand->GetColumnString(0));
BOOST_REQUIRE_NO_THROW(encrypting_you.SetKey(wrong_password));
- SqlConnection::DataCommandUniquePtr selectCommand;
+ SqlConnection::DataCommandHandlerUniquePtr selectCommand;
BOOST_REQUIRE_THROW(selectCommand = encrypting_you.PrepareDataCommand(
- SELECT_TABLE),
+ SELECT_TABLE_BUF),
SqlConnection::Exception::SyntaxError);
}
}
-
POSITIVE_TEST_CASE(sqlTestEfficiency)
{
MockCrypto mc(TEST_DB_CKM, RAW_PASSWORD);
- mc.countTimePreparedStatements(INSERT_COMMAND);
- mc.countTimePreparedStatements(SELECT_COMMAND);
- mc.countTimePreparedStatements(DELETE_COMMAND);
- mc.countTimePreparedStatementsSteps(INSERT_COMMAND);
- mc.countTimePreparedStatementsSteps(SELECT_COMMAND);
- mc.countTimePreparedStatementsSteps(DELETE_COMMAND);
+ mc.countTimePreparedStatements(INSERT_TEST_BUF);
+ mc.countTimePreparedStatements(SELECT_TEST_BUF);
+ mc.countTimePreparedStatements(DELETE_TEST_BUF);
+ mc.countTimePreparedStatementsSteps(INSERT_TEST_BUF);
+ mc.countTimePreparedStatementsSteps(SELECT_TEST_BUF);
+ mc.countTimePreparedStatementsSteps(DELETE_TEST_BUF);
+ mc.countTimePreparedStatementsReset(INSERT_TEST_BUF);
+ mc.countTimePreparedStatementsReset(SELECT_TEST_BUF);
+ mc.countTimePreparedStatementsReset(DELETE_TEST_BUF);
}
BOOST_AUTO_TEST_SUITE_END()