Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions include/SQLiteCpp/Column.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@

#include <string>
#include <memory>
#if __cplusplus >= 201703L // C++17
#include <string_view>
#endif

// Forward declarations to avoid inclusion of <sqlite3.h> in a header
struct sqlite3_stmt;
Expand Down Expand Up @@ -103,6 +106,18 @@ class SQLITECPP_API Column
*/
std::string getString() const;

#if __cplusplus >= 201703L // C++17
/**
* @brief Return a std::string_view for a TEXT or BLOB column.
*
* Note this correctly handles strings that contain null bytes.
*
* @warning returned string_view is only valid until there is a type
* conversion or the statement is stepped or reset.
*/
std::string_view getStringView() const;
#endif

/**
* @brief Return the type of the value of the column using sqlite3_column_type()
*
Expand Down
136 changes: 86 additions & 50 deletions include/SQLiteCpp/Database.h
Original file line number Diff line number Diff line change
Expand Up @@ -523,63 +523,99 @@ class SQLITECPP_API Database
void loadExtension(const char* apExtensionName, const char* apEntryPointName);

/**
* @brief Set the key for the current sqlite database instance.
*
* This is the equivalent of the sqlite3_key call and should thus be called
* directly after opening the database.
* Open encrypted database -> call db.key("secret") -> database ready
*
* @param[in] aKey Key to decode/encode the database
*
* @throw SQLite::Exception in case of error
*/
void key(const std::string& aKey) const;
* @brief Set the key for the current sqlite database instance.
*
* This is the equivalent of the sqlite3_key call and should thus be called
* directly after opening the database.
* Open encrypted database -> call db.key("secret") -> database ready
*
* @param[in] aKey Key to decode/encode the database
*
* @throw SQLite::Exception in case of error
*/
void key(const char* apKey, const int aSize ) const;

/**
* @brief Set the key for the current sqlite database instance.
*
* This is the equivalent of the sqlite3_key call and should thus be called
* directly after opening the database.
* Open encrypted database -> call db.key("secret") -> database ready
*
* @param[in] aKey Key to decode/encode the database
*
* @throw SQLite::Exception in case of error
*/
void key(const std::string& aKey) const
{
key(aKey.data(), aKey.size());
}

/**
* @brief Reset the key for the current sqlite database instance.
*
* This is the equivalent of the sqlite3_rekey call and should thus be called
* after the database has been opened with a valid key. To decrypt a
* database, call this method with an empty string.
* Open normal database -> call db.rekey("secret") -> encrypted database, database ready
* Open encrypted database -> call db.key("secret") -> call db.rekey("newsecret") -> change key, database ready
* Open encrypted database -> call db.key("secret") -> call db.rekey("") -> decrypted database, database ready
*
* @param[in] apNewKey New key to encode the database
* @param[in] aSize Length of apNewKey
*
* @throw SQLite::Exception in case of error
*/
void rekey(const char* apNewKey, const int aSize ) const;

/**
* @brief Reset the key for the current sqlite database instance.
*
* This is the equivalent of the sqlite3_rekey call and should thus be called
* after the database has been opened with a valid key. To decrypt a
* database, call this method with an empty string.
* Open normal database -> call db.rekey("secret") -> encrypted database, database ready
* Open encrypted database -> call db.key("secret") -> call db.rekey("newsecret") -> change key, database ready
* Open encrypted database -> call db.key("secret") -> call db.rekey("") -> decrypted database, database ready
*
* @param[in] aNewKey New key to encode the database
*
* @throw SQLite::Exception in case of error
*/
void rekey(const std::string& aNewKey) const;
* @brief Reset the key for the current sqlite database instance.
*
* This is the equivalent of the sqlite3_rekey call and should thus be called
* after the database has been opened with a valid key. To decrypt a
* database, call this method with an empty string.
* Open normal database -> call db.rekey("secret") -> encrypted database, database ready
* Open encrypted database -> call db.key("secret") -> call db.rekey("newsecret") -> change key, database ready
* Open encrypted database -> call db.key("secret") -> call db.rekey("") -> decrypted database, database ready
*
* @param[in] aNewKey New key to encode the database
*
* @throw SQLite::Exception in case of error
*/
void rekey(const std::string& aNewKey) const
{
rekey(aNewKey.data(), aNewKey.size());
}

/**
* @brief Test if a file contains an unencrypted database.
*
* This is a simple test that reads the first bytes of a database file and
* compares them to the standard header for unencrypted databases. If the
* header does not match the standard string, we assume that we have an
* encrypted file.
*
* @param[in] aFilename path/uri to a file
*
* @return true if the database has the standard header.
*
* @throw SQLite::Exception in case of error
*/
* @brief Test if a file contains an unencrypted database.
*
* This is a simple test that reads the first bytes of a database file and
* compares them to the standard header for unencrypted databases. If the
* header does not match the standard string, we assume that we have an
* encrypted file.
*
* @param[in] aFilename path/uri to a file
*
* @return true if the database has the standard header.
*
* @throw SQLite::Exception in case of error
*/
static bool isUnencrypted(const std::string& aFilename);

/**
* @brief Parse SQLite header data from a database file.
*
* This function reads the first 100 bytes of a SQLite database file
* and reconstructs groups of individual bytes into the associated fields
* in a Header object.
*
* @param[in] aFilename path/uri to a file
*
* @return Header object containing file data
*
* @throw SQLite::Exception in case of error
*/
* @brief Parse SQLite header data from a database file.
*
* This function reads the first 100 bytes of a SQLite database file
* and reconstructs groups of individual bytes into the associated fields
* in a Header object.
*
* @param[in] aFilename path/uri to a file
*
* @return Header object containing file data
*
* @throw SQLite::Exception in case of error
*/
static Header getHeaderInfo(const std::string& aFilename);

// Parse SQLite header data from a database file.
Expand Down
74 changes: 74 additions & 0 deletions include/SQLiteCpp/Statement.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@
#include <string>
#include <map>
#include <memory>
#if __cplusplus >= 201703L // C++17
#include <string_view>
#endif

// Forward declarations to avoid inclusion of <sqlite3.h> in a header
struct sqlite3;
Expand Down Expand Up @@ -139,6 +142,16 @@ class SQLITECPP_API Statement
* @brief Bind a double (64bits float) value to a parameter "?", "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement (aIndex >= 1)
*/
void bind(const int aIndex, const double aValue);

#if __cplusplus >= 201703L // C++17
/**
* @brief Bind a string value to a parameter "?", "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement (aIndex >= 1)
*
* @note Uses the SQLITE_TRANSIENT flag, making a copy of the data, for SQLite internal use
*/
void bind(const int aIndex, const std::string_view aValue);
#endif

/**
* @brief Bind a string value to a parameter "?", "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement (aIndex >= 1)
*
Expand All @@ -157,6 +170,16 @@ class SQLITECPP_API Statement
* @note Uses the SQLITE_TRANSIENT flag, making a copy of the data, for SQLite internal use
*/
void bind(const int aIndex, const void* apValue, const int aSize);
#if __cplusplus >= 201703L // C++17
/**
* @brief Bind a string value to a parameter "?", "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement (aIndex >= 1).
*
* The string can contain null characters as it is binded using its size.
*
* @warning Uses the SQLITE_STATIC flag, avoiding a copy of the data. The string must remains unchanged while executing the statement.
*/
void bindNoCopy(const int aIndex, const std::string_view aValue);
#endif
/**
* @brief Bind a string value to a parameter "?", "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement (aIndex >= 1).
*
Expand Down Expand Up @@ -219,6 +242,17 @@ class SQLITECPP_API Statement
{
bind(getIndex(apName), aValue);
}
#if __cplusplus >= 201703L // C++17
/**
* @brief Bind a string value to a named parameter "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement (aIndex >= 1)
*
* @note Uses the SQLITE_TRANSIENT flag, making a copy of the data, for SQLite internal use
*/
void bind(const char* apName, const std::string_view aValue)
{
bind(getIndex(apName), aValue);
}
#endif
/**
* @brief Bind a string value to a named parameter "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement (aIndex >= 1)
*
Expand Down Expand Up @@ -246,6 +280,19 @@ class SQLITECPP_API Statement
{
bind(getIndex(apName), apValue, aSize);
}
#if __cplusplus >= 201703L // C++17
/**
* @brief Bind a string value to a named parameter "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement (aIndex >= 1)
*
* The string can contain null characters as it is binded using its size.
*
* @warning Uses the SQLITE_STATIC flag, avoiding a copy of the data. The string must remains unchanged while executing the statement.
*/
void bindNoCopy(const char* apName, const std::string_view aValue)
{
bindNoCopy(getIndex(apName), aValue);
}
#endif
/**
* @brief Bind a string value to a named parameter "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement (aIndex >= 1)
*
Expand Down Expand Up @@ -320,6 +367,18 @@ class SQLITECPP_API Statement
{
bind(aName.c_str(), aValue);
}

#if __cplusplus >= 201703L // C++17
/**
* @brief Bind a string value to a named parameter "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement (aIndex >= 1)
*
* @note Uses the SQLITE_TRANSIENT flag, making a copy of the data, for SQLite internal use
*/
void bind(const std::string& aName, const std::string_view aValue)
{
bind(aName.c_str(), aValue);
}
#endif
/**
* @brief Bind a string value to a named parameter "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement (aIndex >= 1)
*
Expand Down Expand Up @@ -347,6 +406,21 @@ class SQLITECPP_API Statement
{
bind(aName.c_str(), apValue, aSize);
}

#if __cplusplus >= 201703L // C++17
/**
* @brief Bind a string value to a named parameter "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement (aIndex >= 1)
*
* The string can contain null characters as it is binded using its size.
*
* @warning Uses the SQLITE_STATIC flag, avoiding a copy of the data. The string must remains unchanged while executing the statement.
*/
void bindNoCopy(const std::string& aName, const std::string_view aValue)
{
bindNoCopy(aName.c_str(), aValue);
}
#endif

/**
* @brief Bind a string value to a named parameter "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement (aIndex >= 1)
*
Expand Down
17 changes: 17 additions & 0 deletions src/Column.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,23 @@ std::string Column::getString() const
return std::string(data, sqlite3_column_bytes(mStmtPtr.get(), mIndex));
}

#if __cplusplus >= 201703L // C++17
// Return a std::string_view to a TEXT or BLOB column
std::string_view Column::getStringView() const
{
// Note: using sqlite3_column_blob and not sqlite3_column_text
// - no need for sqlite3_column_text to add a \0 on the end, as we're getting the bytes length directly
// however, we need to call sqlite3_column_bytes() to ensure correct format. It's a noop on a BLOB
// or a TEXT value with the correct encoding (UTF-8). Otherwise it'll do a conversion to TEXT (UTF-8).
(void)sqlite3_column_bytes(mStmtPtr.get(), mIndex);
auto data = static_cast<const char *>(sqlite3_column_blob(mStmtPtr.get(), mIndex));

// SQLite docs: "The safest policy is to invoke… sqlite3_column_blob() followed by sqlite3_column_bytes()"
// Note: std::string is ok to pass nullptr as first arg, if length is 0
return std::string_view(data, sqlite3_column_bytes(mStmtPtr.get(), mIndex));
}
#endif

// Return the type of the value of the column
int Column::getType() const noexcept
{
Expand Down
23 changes: 13 additions & 10 deletions src/Database.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -224,31 +224,31 @@ void Database::loadExtension(const char* apExtensionName, const char *apEntryPoi
}

// Set the key for the current sqlite database instance.
void Database::key(const std::string& aKey) const
void Database::key(const char* apKey, const int aSize) const
{
int passLen = static_cast<int>(aKey.length());
#ifdef SQLITE_HAS_CODEC
if (passLen > 0)
if (aSize > 0)
{
const int ret = sqlite3_key(getHandle(), aKey.c_str(), passLen);
const int ret = sqlite3_key(getHandle(), apKey, aSize);
check(ret);
}
#else // SQLITE_HAS_CODEC
if (passLen > 0)
// Silence unused parameter warning
static_cast<void>(apKey);
if (aSize > 0)
{
throw SQLite::Exception("No encryption support, recompile with SQLITE_HAS_CODEC to enable.");
}
#endif // SQLITE_HAS_CODEC
}

// Reset the key for the current sqlite database instance.
void Database::rekey(const std::string& aNewKey) const
void Database::rekey(const char* apNewKey, const int aSize) const
{
#ifdef SQLITE_HAS_CODEC
int passLen = aNewKey.length();
if (passLen > 0)
if (aSize > 0)
{
const int ret = sqlite3_rekey(getHandle(), aNewKey.c_str(), passLen);
const int ret = sqlite3_rekey(getHandle(), apNewKey, aSize);
check(ret);
}
else
Expand All @@ -257,7 +257,10 @@ void Database::rekey(const std::string& aNewKey) const
check(ret);
}
#else // SQLITE_HAS_CODEC
static_cast<void>(aNewKey); // silence unused parameter warning
// Silence unused parameter warnings
static_cast<void>(apNewKey);
static_cast<void>(aSize);

throw SQLite::Exception("No encryption support, recompile with SQLITE_HAS_CODEC to enable.");
#endif // SQLITE_HAS_CODEC
}
Expand Down
Loading