diff --git a/include/SQLiteCpp/Column.h b/include/SQLiteCpp/Column.h index bf5760ab..a6b20998 100644 --- a/include/SQLiteCpp/Column.h +++ b/include/SQLiteCpp/Column.h @@ -16,6 +16,9 @@ #include #include +#if __cplusplus >= 201703L // C++17 +#include +#endif // Forward declarations to avoid inclusion of in a header struct sqlite3_stmt; @@ -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() * diff --git a/include/SQLiteCpp/Statement.h b/include/SQLiteCpp/Statement.h index 3b9b0a70..1fc6a567 100644 --- a/include/SQLiteCpp/Statement.h +++ b/include/SQLiteCpp/Statement.h @@ -18,6 +18,9 @@ #include #include #include +#if __cplusplus >= 201703L // C++17 +#include +#endif // Forward declarations to avoid inclusion of in a header struct sqlite3; @@ -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) * @@ -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). * @@ -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) * @@ -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) * @@ -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) * @@ -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& 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) * diff --git a/src/Column.cpp b/src/Column.cpp index c7d18c51..151e16fe 100644 --- a/src/Column.cpp +++ b/src/Column.cpp @@ -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(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 { diff --git a/src/Statement.cpp b/src/Statement.cpp index e4a264f4..68fc23d4 100644 --- a/src/Statement.cpp +++ b/src/Statement.cpp @@ -108,6 +108,20 @@ void Statement::bind(const int aIndex, const double aValue) check(ret); } +#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 Statement::bind(const int aIndex, const std::string_view aValue) { + const int ret = sqlite3_bind_text(getPreparedStatement(), aIndex, aValue.data(), + static_cast(aValue.size()), SQLITE_TRANSIENT); + check(ret); +} +#endif + + // Bind a string value to a parameter "?", "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement void Statement::bind(const int aIndex, const std::string& aValue) { @@ -130,6 +144,16 @@ void Statement::bind(const int aIndex, const void* apValue, const int aSize) check(ret); } +#if __cplusplus >= 201703L // C++17 +// Bind a string value to a parameter "?", "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement +void Statement::bindNoCopy(const int aIndex, const std::string_view aValue) +{ + const int ret = sqlite3_bind_text(getPreparedStatement(), aIndex, aValue.data(), + static_cast(aValue.size()), SQLITE_STATIC); + check(ret); +} +#endif + // Bind a string value to a parameter "?", "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement void Statement::bindNoCopy(const int aIndex, const std::string& aValue) {