Skip to content

Commit 02410d4

Browse files
committed
Add support to column values 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 883782f commit 02410d4

File tree

3 files changed

+232
-2
lines changed

3 files changed

+232
-2
lines changed

include/SQLiteCpp/Column.h

Lines changed: 127 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,49 @@ class Column
102102
*/
103103
std::string getString() const;
104104

105+
#ifdef __cpp_unicode_characters
106+
/**
107+
* @brief Return a pointer to the text value (NULL terminated UTF-16 string) of the column.
108+
*
109+
* @warning The value pointed at is only valid while the statement is valid (ie. not finalized),
110+
* thus you must copy it before using it beyond its scope (to a std::u16string for instance).
111+
*/
112+
const char16_t* getU16Text(const char16_t* apDefaultValue = u"") const noexcept;
113+
/**
114+
* @brief Return a std::u16string for a TEXT column.
115+
*
116+
* Note this correctly handles strings that contain null bytes.
117+
*/
118+
std::u16string getU16String() const;
119+
#if WCHAR_MAX == 0xffff
120+
/**
121+
* @brief Return a pointer to the text value (NULL terminated UTF-16 string) of the column.
122+
*
123+
* @warning The value pointed at is only valid while the statement is valid (ie. not finalized),
124+
* thus you must copy it before using it beyond its scope (to a std::wstring for instance).
125+
*/
126+
const wchar_t* getWText(const wchar_t* apDefaultValue = L"") const noexcept;
127+
/**
128+
* @brief Return a std::wstring for a TEXT column.
129+
*/
130+
std::wstring getWString() const;
131+
#endif // WCHAR_MAX == 0xffff
132+
#endif // __cpp_unicode_characters
133+
#ifdef __cpp_char8_t
134+
/**
135+
* @brief Return a pointer to the text value (NULL terminated UTF-8 string) of the column.
136+
*
137+
* @warning The value pointed at is only valid while the statement is valid (ie. not finalized),
138+
* thus you must copy it before using it beyond its scope (to a std::u8string for instance).
139+
*/
140+
const char8_t* getU8Text(const char8_t* apDefaultValue = u8"") const noexcept;
141+
/**
142+
* @brief Return a std::u8string for a TEXT or BLOB column.
143+
*
144+
* Note this correctly handles strings that contain null bytes.
145+
*/
146+
std::u8string getU8String() const;
147+
#endif // __cpp_char8_t
105148
/**
106149
* @brief Return the type of the value of the column using sqlite3_column_type()
107150
*
@@ -142,7 +185,10 @@ class Column
142185
}
143186

144187
/**
145-
* @brief Return the number of bytes used by the text (or blob) value of the column
188+
* @brief Return the number of bytes used by the UTF-8 text (or blob) value of the column
189+
*
190+
* Can cause conversion to text and between UTF-8/UTF-16 encodings
191+
* Be careful when using with getBytes16() and UTF-16 functions
146192
*
147193
* Return either :
148194
* - size in bytes (not in characters) of the string returned by getText() without the '\0' terminator
@@ -152,6 +198,19 @@ class Column
152198
*/
153199
int getBytes() const noexcept;
154200

201+
/**
202+
* @brief Return the number of bytes used by the UTF-16 text value of the column
203+
*
204+
* Can cause conversion to text and between UTF-8/UTF-16 encodings
205+
* Be careful when using with getBytes() and UTF-8 functions
206+
*
207+
* Return either :
208+
* - size in bytes (not in characters) of the string returned by getText() without the '\0' terminator
209+
* - size in bytes of the string representation of the numerical value (integer or double)
210+
* - 0 for a NULL value
211+
*/
212+
int getBytes16() const noexcept;
213+
155214
/// Alias returning the number of bytes used by the text (or blob) value of the column
156215
int size() const noexcept
157216
{
@@ -226,6 +285,73 @@ class Column
226285
return getString();
227286
}
228287

288+
#ifdef __cpp_unicode_characters
289+
/**
290+
* @brief Inline cast operator to char16_t*
291+
*
292+
* @see getU16String
293+
*/
294+
operator const char16_t* () const
295+
{
296+
return getU16Text();
297+
}
298+
/**
299+
* @brief Inline cast operator to std::u16string
300+
*
301+
* Handles UTF-16 TEXT
302+
*
303+
* @see getU16String
304+
*/
305+
operator std::u16string() const
306+
{
307+
return getU16String();
308+
}
309+
#if WCHAR_MAX == 0xffff
310+
/**
311+
* @brief Inline cast operator to wchar_t*
312+
*
313+
* @see getWText
314+
*/
315+
operator const wchar_t* () const
316+
{
317+
return getWText();
318+
}
319+
/**
320+
* @brief Inline cast operator to std::wstring
321+
*
322+
* Handles UTF-16 TEXT
323+
*
324+
* @see getWString
325+
*/
326+
operator std::wstring() const
327+
{
328+
return getWString();
329+
}
330+
#endif // WCHAR_MAX == 0xffff
331+
#endif // __cpp_unicode_characters
332+
#ifdef __cpp_char8_t
333+
/**
334+
* @brief Inline cast operator to char8_t*
335+
*
336+
* @see getU8Text
337+
*/
338+
operator const char8_t* () const
339+
{
340+
return getU8Text();
341+
}
342+
/**
343+
* @brief Inline cast operator to std::u8string
344+
*
345+
* Handles BLOB or TEXT, which may contain null bytes within
346+
*
347+
* @see getU8String
348+
*/
349+
operator std::u8string() const
350+
{
351+
return getU8String();
352+
}
353+
#endif // __cpp_char8_t
354+
229355
private:
230356
Statement::TStatementPtr mStmtPtr; ///< Shared Pointer to the prepared SQLite Statement Object
231357
int mIndex; ///< Index of the column in the row of result, starting at 0

src/Column.cpp

Lines changed: 52 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -102,18 +102,69 @@ std::string Column::getString() const
102102
return std::string(data, sqlite3_column_bytes(mStmtPtr.get(), mIndex));
103103
}
104104

105+
#ifdef __cpp_unicode_characters
106+
const char16_t* Column::getU16Text(const char16_t* apDefaultValue /* = u"" */) const noexcept
107+
{
108+
auto pText = static_cast<const char16_t*>(sqlite3_column_text16(mStmtPtr.get(), mIndex));
109+
return (pText ? pText : apDefaultValue);
110+
}
111+
std::u16string Column::getU16String() const
112+
{
113+
(void)sqlite3_column_bytes16(mStmtPtr.get(), mIndex);
114+
auto data = static_cast<const char16_t*>(sqlite3_column_blob(mStmtPtr.get(), mIndex));
115+
return std::u16string(data, sqlite3_column_bytes16(mStmtPtr.get(), mIndex) / sizeof(char16_t));
116+
}
117+
#if WCHAR_MAX == 0xffff
118+
const wchar_t* Column::getWText(const wchar_t* apDefaultValue /* = L"" */) const noexcept
119+
{
120+
auto pText = static_cast<const wchar_t*>(sqlite3_column_text16(mStmtPtr.get(), mIndex));
121+
return (pText ? pText : apDefaultValue);
122+
}
123+
std::wstring Column::getWString() const
124+
{
125+
(void)sqlite3_column_bytes16(mStmtPtr.get(), mIndex);
126+
auto data = static_cast<const wchar_t*>(sqlite3_column_blob(mStmtPtr.get(), mIndex));
127+
return std::wstring(data, sqlite3_column_bytes16(mStmtPtr.get(), mIndex) / sizeof(wchar_t));
128+
}
129+
#endif // WCHAR_MAX == 0xffff
130+
#endif // __cpp_unicode_characters
131+
#ifdef __cpp_char8_t
132+
const char8_t* Column::getU8Text(const char8_t* apDefaultValue /* = u8"" */) const noexcept
133+
{
134+
auto pText = reinterpret_cast<const char8_t*>(sqlite3_column_text(mStmtPtr.get(), mIndex));
135+
return (pText ? pText : apDefaultValue);
136+
}
137+
std::u8string Column::getU8String() const
138+
{
139+
(void)sqlite3_column_bytes(mStmtPtr.get(), mIndex);
140+
auto data = static_cast<const char8_t*>(sqlite3_column_blob(mStmtPtr.get(), mIndex));
141+
return std::u8string(data, sqlite3_column_bytes(mStmtPtr.get(), mIndex));
142+
}
143+
#endif // __cpp_char8_t
144+
145+
105146
// Return the type of the value of the column
106147
int Column::getType() const noexcept
107148
{
108149
return sqlite3_column_type(mStmtPtr.get(), mIndex);
109150
}
110151

111-
// Return the number of bytes used by the text value of the column
152+
// Return the number of bytes used by the UTF-8 text value of the column
153+
// Can cause conversion to text and between UTF-8/UTF-16 encodings
154+
// Be careful when using with getBytes16() and UTF-16 functions
112155
int Column::getBytes() const noexcept
113156
{
114157
return sqlite3_column_bytes(mStmtPtr.get(), mIndex);
115158
}
116159

160+
// Return the number of bytes used by the UTF-16 text value of the column
161+
// Can cause conversion to text and between UTF-8/UTF-16 encodings
162+
// Be careful when using with getBytes() and UTF-8 functions
163+
int Column::getBytes16() const noexcept
164+
{
165+
return sqlite3_column_bytes16(mStmtPtr.get(), mIndex);
166+
}
167+
117168
// Standard std::ostream inserter
118169
std::ostream& operator<<(std::ostream& aStream, const Column& aColumn)
119170
{

tests/Column_test.cpp

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,59 @@ static void test_column_basis(bool utf16)
197197
const SQLite::Column dbl = query.getColumn(3);
198198
EXPECT_EQ(0.123, dbl.getDouble());
199199
}
200+
201+
#ifdef __cpp_unicode_characters
202+
query.reset();
203+
query.executeStep();
204+
205+
{
206+
const auto first_u16 = u"first";
207+
208+
const std::u16string str16 = query.getColumn(1);
209+
const char16_t * txt16 = query.getColumn(1);
210+
211+
EXPECT_EQ(5, str16.length());
212+
EXPECT_EQ(10, query.getColumn(1).getBytes16());
213+
EXPECT_EQ(0, memcmp(first_u16, str16.data(), str16.length() * sizeof(char16_t)));
214+
EXPECT_EQ(5, std::char_traits<char16_t>::length(txt16));
215+
EXPECT_EQ(0, std::char_traits<char16_t>::compare(first_u16, txt16, 6));
216+
}
217+
218+
#if WCHAR_MAX == 0xffff
219+
query.reset();
220+
query.executeStep();
221+
222+
{
223+
const auto first_w = L"first";
224+
225+
const std::wstring wstr = query.getColumn(1);
226+
const wchar_t* wtxt = query.getColumn(1);
227+
228+
EXPECT_EQ(5, wstr.length());
229+
EXPECT_EQ(0, memcmp(first_w, wstr.data(), wstr.length() * sizeof(wchar_t)));
230+
EXPECT_EQ(5, std::char_traits<wchar_t>::length(wtxt));
231+
EXPECT_EQ(0, std::char_traits<wchar_t>::compare(first_w, wtxt, 6));
232+
}
233+
#endif // WCHAR_MAX == 0xffff
234+
235+
#endif // __cpp_unicode_characters
236+
237+
#ifdef __cpp_char8_t
238+
query.reset();
239+
query.executeStep();
240+
241+
{
242+
const auto first_u8 = u8"first";
243+
244+
const std::u8string str8 = query.getColumn(1);
245+
const char8_t* txt8 = query.getColumn(1);
246+
247+
EXPECT_EQ(5, str8.length());
248+
EXPECT_EQ(0, memcmp(first_u8, str8.data(), str8.length() * sizeof(char8_t)));
249+
EXPECT_EQ(5, std::char_traits<char8_t>::length(txt8));
250+
EXPECT_EQ(0, std::char_traits<char8_t>::compare(first_u8, txt8, 6));
251+
}
252+
#endif // __cpp_char8_t
200253
}
201254

202255
TEST(Column, basis)

0 commit comments

Comments
 (0)