Skip to content

Commit 97ac1d0

Browse files
committed
Add support to create Statements with various unicode encodings.
Support for char16_t, u16string & u16string_view. Support for char8_t, u8string & u8string_view. Support for wchar_t, wstring & wstring_view (on 2-byte platforms).
1 parent bd5bf79 commit 97ac1d0

File tree

3 files changed

+224
-0
lines changed

3 files changed

+224
-0
lines changed

include/SQLiteCpp/Statement.h

Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
#include <string>
1717
#include <map>
1818
#include <memory>
19+
#include <cstdint>
1920

2021
// Forward declarations to avoid inclusion of <sqlite3.h> in a header
2122
struct sqlite3;
@@ -74,6 +75,110 @@ class Statement
7475
Statement(aDatabase, aQuery.c_str())
7576
{}
7677

78+
#ifdef __cpp_unicode_characters
79+
/**
80+
* @brief Compile and register the SQL query for the provided SQLite Database Connection
81+
*
82+
* @param[in] aDatabase the SQLite Database Connection
83+
* @param[in] aQuery an UTF-16 encoded query string
84+
*
85+
* Exception is thrown in case of error, then the Statement object is NOT constructed.
86+
*/
87+
Statement(const Database& aDatabase, const std::u16string& aQuery);
88+
89+
#if WCHAR_MAX == 0xffff
90+
/**
91+
* @brief Compile and register the SQL query for the provided SQLite Database Connection
92+
*
93+
* @param[in] aDatabase the SQLite Database Connection
94+
* @param[in] aQuery an UTF-16 encoded query string
95+
*
96+
* Exception is thrown in case of error, then the Statement object is NOT constructed.
97+
*/
98+
Statement(const Database& aDatabase, const std::wstring& aQuery) :
99+
#ifdef __cpp_lib_string_view
100+
Statement(aDatabase, std::u16string_view(reinterpret_cast<const char16_t*>(aQuery.data()), aQuery.size()))
101+
#else
102+
Statement(aDatabase, std::u16string(reinterpret_cast<const char16_t*>(aQuery.data()), aQuery.size()))
103+
#endif
104+
{}
105+
#endif // WCHAR_MAX == 0xffff
106+
#endif // __cpp_unicode_characters
107+
108+
#ifdef __cpp_lib_string_view
109+
/**
110+
* @brief Compile and register the SQL query for the provided SQLite Database Connection
111+
*
112+
* @param[in] aDatabase the SQLite Database Connection
113+
* @param[in] aQuery an UTF-16 encoded query string
114+
*
115+
* Exception is thrown in case of error, then the Statement object is NOT constructed.
116+
*/
117+
Statement(const Database& aDatabase, const std::string_view aQuery);
118+
119+
#ifdef __cpp_char8_t
120+
/**
121+
* @brief Compile and register the SQL query for the provided SQLite Database Connection
122+
*
123+
* @param[in] aDatabase the SQLite Database Connection
124+
* @param[in] aQuery an UTF-8 encoded query string
125+
*
126+
* Exception is thrown in case of error, then the Statement object is NOT constructed.
127+
*/
128+
Statement(const Database& aDatabase, const std::u8string_view aQuery) :
129+
Statement(aDatabase, std::string_view(reinterpret_cast<const char*>(aQuery.data()), aQuery.size()))
130+
{}
131+
#endif // __cpp_char8_t
132+
133+
/**
134+
* @brief Compile and register the SQL query for the provided SQLite Database Connection
135+
*
136+
* @param[in] aDatabase the SQLite Database Connection
137+
* @param[in] apQuery an UTF-16 encoded query string
138+
*
139+
* Exception is thrown in case of error, then the Statement object is NOT constructed.
140+
*/
141+
Statement(const Database& aDatabase, const char16_t* apQuery) :
142+
Statement(aDatabase, std::u16string_view(apQuery))
143+
{}
144+
145+
/**
146+
* @brief Compile and register the SQL query for the provided SQLite Database Connection
147+
*
148+
* @param[in] aDatabase the SQLite Database Connection
149+
* @param[in] aQuery an UTF-16 encoded query string
150+
*
151+
* Exception is thrown in case of error, then the Statement object is NOT constructed.
152+
*/
153+
Statement(const Database& aDatabase, std::u16string_view aQuery);
154+
155+
#if WCHAR_MAX == 0xffff
156+
/**
157+
* @brief Compile and register the SQL query for the provided SQLite Database Connection
158+
*
159+
* @param[in] aDatabase the SQLite Database Connection
160+
* @param[in] apQuery an UTF-16 encoded query string
161+
*
162+
* Exception is thrown in case of error, then the Statement object is NOT constructed.
163+
*/
164+
Statement(const Database& aDatabase, const wchar_t* apQuery) :
165+
Statement(aDatabase, std::u16string_view(reinterpret_cast<const char16_t*>(apQuery)))
166+
{}
167+
168+
/**
169+
* @brief Compile and register the SQL query for the provided SQLite Database Connection
170+
*
171+
* @param[in] aDatabase the SQLite Database Connection
172+
* @param[in] aQuery an UTF-16 encoded query string
173+
*
174+
* Exception is thrown in case of error, then the Statement object is NOT constructed.
175+
*/
176+
Statement(const Database& aDatabase, const std::wstring_view aQuery) :
177+
Statement(aDatabase, std::u16string_view(reinterpret_cast<const char16_t*>(aQuery.data()), aQuery.size()))
178+
{}
179+
#endif // WCHAR_MAX == 0xffff
180+
#endif // __cpp_lib_string_view
181+
77182
// Statement is non-copyable
78183
Statement(const Statement&) = delete;
79184
Statement& operator=(const Statement&) = delete;
@@ -690,6 +795,24 @@ class Statement
690795
*/
691796
TStatementPtr prepareStatement();
692797

798+
#ifdef __cpp_unicode_characters
799+
/**
800+
* @brief Prepare statement object.
801+
*
802+
* @return Shared pointer to prepared statement object
803+
*/
804+
TStatementPtr prepareStatement(const std::u16string& query);
805+
#endif // __cpp_unicode_characters
806+
807+
#ifdef __cpp_lib_string_view
808+
/**
809+
* @brief Prepare statement object.
810+
*
811+
* @return Shared pointer to prepared statement object
812+
*/
813+
TStatementPtr prepareStatement(const std::u16string_view query);
814+
#endif // __cpp_lib_string_view
815+
693816
/**
694817
* @brief Return a prepared statement object.
695818
*

src/Statement.cpp

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,32 @@ Statement::Statement(const Database& aDatabase, const char* apQuery) :
2828
mColumnCount = sqlite3_column_count(mpPreparedStatement.get());
2929
}
3030

31+
#ifdef __cpp_unicode_characters
32+
Statement::Statement(const Database& aDatabase, const std::u16string& aQuery) :
33+
mpSQLite(aDatabase.getHandle()),
34+
mpPreparedStatement(prepareStatement(aQuery)) // prepare the SQL query (needs Database friendship)
35+
{
36+
mColumnCount = sqlite3_column_count(mpPreparedStatement.get());
37+
}
38+
#endif // __cpp_unicode_characters
39+
40+
#ifdef __cpp_lib_string_view
41+
Statement::Statement(const Database& aDatabase, const std::string_view aQuery) :
42+
mQuery(aQuery),
43+
mpSQLite(aDatabase.getHandle()),
44+
mpPreparedStatement(prepareStatement()) // prepare the SQL query (needs Database friendship)
45+
{
46+
mColumnCount = sqlite3_column_count(mpPreparedStatement.get());
47+
}
48+
49+
Statement::Statement(const Database& aDatabase, const std::u16string_view aQuery) :
50+
mpSQLite(aDatabase.getHandle()),
51+
mpPreparedStatement(prepareStatement(aQuery)) // prepare the SQL query (needs Database friendship)
52+
{
53+
mColumnCount = sqlite3_column_count(mpPreparedStatement.get());
54+
}
55+
#endif // __cpp_lib_string_view
56+
3157
Statement::Statement(Statement&& aStatement) noexcept :
3258
mQuery(std::move(aStatement.mQuery)),
3359
mpSQLite(aStatement.mpSQLite),
@@ -354,6 +380,42 @@ Statement::TStatementPtr Statement::prepareStatement()
354380
});
355381
}
356382

383+
#ifdef __cpp_unicode_characters
384+
Statement::TStatementPtr Statement::prepareStatement(const std::u16string& query)
385+
{
386+
sqlite3_stmt* statement;
387+
const int ret = sqlite3_prepare16_v2(mpSQLite, query.data(),
388+
static_cast<int>(query.size() * sizeof(char16_t)), &statement, nullptr);
389+
if (SQLITE_OK != ret)
390+
{
391+
throw SQLite::Exception(mpSQLite, ret);
392+
}
393+
mQuery = sqlite3_sql(statement);
394+
return Statement::TStatementPtr(statement, [](sqlite3_stmt* stmt)
395+
{
396+
sqlite3_finalize(stmt);
397+
});
398+
}
399+
#endif // __cpp_unicode_characters
400+
401+
#ifdef __cpp_lib_string_view
402+
Statement::TStatementPtr Statement::prepareStatement(const std::u16string_view query)
403+
{
404+
sqlite3_stmt* statement;
405+
const int ret = sqlite3_prepare16_v2(mpSQLite, query.data(),
406+
static_cast<int>(query.size() * sizeof(char16_t)), &statement, nullptr);
407+
if (SQLITE_OK != ret)
408+
{
409+
throw SQLite::Exception(mpSQLite, ret);
410+
}
411+
mQuery = sqlite3_sql(statement);
412+
return Statement::TStatementPtr(statement, [](sqlite3_stmt* stmt)
413+
{
414+
sqlite3_finalize(stmt);
415+
});
416+
}
417+
#endif // __cpp_lib_string_view
418+
357419
// Return prepered statement object or throw
358420
sqlite3_stmt* Statement::getPreparedStatement() const
359421
{

tests/Statement_test.cpp

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,45 @@ TEST(Statement, moveConstructor)
142142

143143
#endif
144144

145+
#ifdef __cpp_unicode_characters
146+
TEST(Statement, unicode)
147+
{
148+
// Create a new database
149+
SQLite::Database db(":memory:", SQLite::OPEN_READWRITE | SQLite::OPEN_CREATE);
150+
EXPECT_EQ(0, db.exec("CREATE TABLE test (id INTEGER PRIMARY KEY, value TEXT)"));
151+
152+
const std::string sql("SELECT * FROM test");
153+
const std::u16string sql_u16(u"SELECT * FROM test");
154+
155+
SQLite::Statement query_u16_char(db, sql_u16.c_str());
156+
EXPECT_EQ(sql, query_u16_char.getQuery());
157+
158+
SQLite::Statement query_u16_str(db, sql_u16);
159+
EXPECT_EQ(sql, query_u16_str.getQuery());
160+
161+
EXPECT_THROW(SQLite::Statement(db, u"SELECT * FROM test2"), SQLite::Exception);
162+
163+
#if WCHAR_MAX == 0xffff
164+
const std::wstring sql_w(L"SELECT * FROM test");
165+
166+
SQLite::Statement query_w_char(db, sql_w.c_str());
167+
EXPECT_EQ(sql, query_w_char.getQuery());
168+
SQLite::Statement query_w_str(db, sql_w);
169+
EXPECT_EQ(sql, query_w_str.getQuery());
170+
#endif
171+
172+
#ifdef __cpp_char8_t
173+
const std::u8string sql_u8(u8"SELECT * FROM test");
174+
175+
SQLite::Statement query_u8_char(db, sql_u8.c_str());
176+
EXPECT_EQ(sql, query_u8_char.getQuery());
177+
SQLite::Statement query_u8_str(db, sql_u8);
178+
EXPECT_EQ(sql, query_u8_str.getQuery());
179+
EXPECT_THROW(SQLite::Statement(db, u8"SELECT * FROM test2"), SQLite::Exception);
180+
#endif
181+
}
182+
#endif
183+
145184
TEST(Statement, executeStep)
146185
{
147186
// Create a new database

0 commit comments

Comments
 (0)