From 18374bb6db020327a4d9cae4be97e2cdfb563407 Mon Sep 17 00:00:00 2001 From: Dustin Van Tate Testa Date: Sun, 3 Aug 2025 02:14:46 +0000 Subject: [PATCH 1/2] string_view support --- include/SQLiteCpp/Column.h | 15 +++++++++++++++ include/SQLiteCpp/Statement.h | 23 +++++++++++++++++++++++ src/Column.cpp | 17 +++++++++++++++++ src/Statement.cpp | 24 ++++++++++++++++++++++++ 4 files changed, 79 insertions(+) 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..840d691e 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). * 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..48c3e7d7 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) { From 0c823761d5405ea9de002787984b837927779a21 Mon Sep 17 00:00:00 2001 From: Dustin Van Tate Testa Date: Mon, 4 Aug 2025 20:17:33 +0000 Subject: [PATCH 2/2] named parameter overloads, pass string_view by value --- include/SQLiteCpp/Statement.h | 55 +++++++++++++++++++++++++++++++++-- src/Statement.cpp | 4 +-- 2 files changed, 55 insertions(+), 4 deletions(-) diff --git a/include/SQLiteCpp/Statement.h b/include/SQLiteCpp/Statement.h index 840d691e..1fc6a567 100644 --- a/include/SQLiteCpp/Statement.h +++ b/include/SQLiteCpp/Statement.h @@ -149,7 +149,7 @@ 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 std::string_view& aValue); + void bind(const int aIndex, const std::string_view aValue); #endif /** @@ -178,7 +178,7 @@ class SQLITECPP_API Statement * * @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); + 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). @@ -242,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) * @@ -269,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) * @@ -343,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) * @@ -370,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/Statement.cpp b/src/Statement.cpp index 48c3e7d7..68fc23d4 100644 --- a/src/Statement.cpp +++ b/src/Statement.cpp @@ -114,7 +114,7 @@ void Statement::bind(const int aIndex, const double aValue) * * @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) { +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); @@ -146,7 +146,7 @@ void Statement::bind(const int aIndex, const void* apValue, const int aSize) #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) +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);