forked from qt-creator/qt-creator
Sqlite: Add blob support
Change-Id: Ic2ec5f20c8585241b9e9aaa8465e70b6ab4f004c Reviewed-by: Thomas Hartmann <thomas.hartmann@qt.io>
This commit is contained in:
@@ -197,6 +197,17 @@ void BaseStatement::bind(int index, Utils::SmallStringView text)
|
|||||||
checkForBindingError(resultCode);
|
checkForBindingError(resultCode);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void BaseStatement::bind(int index, Utils::span<const byte> bytes)
|
||||||
|
{
|
||||||
|
int resultCode = sqlite3_bind_blob64(m_compiledStatement.get(),
|
||||||
|
index,
|
||||||
|
bytes.data(),
|
||||||
|
static_cast<long long>(bytes.size()),
|
||||||
|
SQLITE_STATIC);
|
||||||
|
if (resultCode != SQLITE_OK)
|
||||||
|
checkForBindingError(resultCode);
|
||||||
|
}
|
||||||
|
|
||||||
void BaseStatement::bind(int index, const Value &value)
|
void BaseStatement::bind(int index, const Value &value)
|
||||||
{
|
{
|
||||||
switch (value.type()) {
|
switch (value.type()) {
|
||||||
@@ -433,8 +444,9 @@ Database &BaseStatement::database() const
|
|||||||
return m_database;
|
return m_database;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename StringType>
|
namespace {
|
||||||
static StringType textForColumn(sqlite3_stmt *sqlStatment, int column)
|
template<typename StringType>
|
||||||
|
StringType textForColumn(sqlite3_stmt *sqlStatment, int column)
|
||||||
{
|
{
|
||||||
const char *text = reinterpret_cast<const char*>(sqlite3_column_text(sqlStatment, column));
|
const char *text = reinterpret_cast<const char*>(sqlite3_column_text(sqlStatment, column));
|
||||||
std::size_t size = std::size_t(sqlite3_column_bytes(sqlStatment, column));
|
std::size_t size = std::size_t(sqlite3_column_bytes(sqlStatment, column));
|
||||||
@@ -442,20 +454,40 @@ static StringType textForColumn(sqlite3_stmt *sqlStatment, int column)
|
|||||||
return StringType(text, size);
|
return StringType(text, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename StringType>
|
Utils::span<const byte> blobForColumn(sqlite3_stmt *sqlStatment, int column)
|
||||||
static StringType convertToTextForColumn(sqlite3_stmt *sqlStatment, int column)
|
{
|
||||||
|
const byte *blob = reinterpret_cast<const byte *>(sqlite3_column_blob(sqlStatment, column));
|
||||||
|
std::size_t size = std::size_t(sqlite3_column_bytes(sqlStatment, column));
|
||||||
|
|
||||||
|
return {blob, size};
|
||||||
|
}
|
||||||
|
|
||||||
|
Utils::span<const byte> convertToBlobForColumn(sqlite3_stmt *sqlStatment, int column)
|
||||||
|
{
|
||||||
|
int dataType = sqlite3_column_type(sqlStatment, column);
|
||||||
|
if (dataType == SQLITE_BLOB)
|
||||||
|
return blobForColumn(sqlStatment, column);
|
||||||
|
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename StringType>
|
||||||
|
StringType convertToTextForColumn(sqlite3_stmt *sqlStatment, int column)
|
||||||
{
|
{
|
||||||
int dataType = sqlite3_column_type(sqlStatment, column);
|
int dataType = sqlite3_column_type(sqlStatment, column);
|
||||||
switch (dataType) {
|
switch (dataType) {
|
||||||
case SQLITE_INTEGER:
|
case SQLITE_INTEGER:
|
||||||
case SQLITE_FLOAT:
|
case SQLITE_FLOAT:
|
||||||
case SQLITE3_TEXT: return textForColumn<StringType>(sqlStatment, column);
|
case SQLITE3_TEXT:
|
||||||
|
return textForColumn<StringType>(sqlStatment, column);
|
||||||
case SQLITE_BLOB:
|
case SQLITE_BLOB:
|
||||||
case SQLITE_NULL: break;
|
case SQLITE_NULL:
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
return StringType{"", 0};
|
return StringType{"", 0};
|
||||||
}
|
}
|
||||||
|
} // namespace
|
||||||
|
|
||||||
int BaseStatement::fetchIntValue(int column) const
|
int BaseStatement::fetchIntValue(int column) const
|
||||||
{
|
{
|
||||||
@@ -501,6 +533,14 @@ double BaseStatement::fetchDoubleValue(int column) const
|
|||||||
return sqlite3_column_double(m_compiledStatement.get(), column);
|
return sqlite3_column_double(m_compiledStatement.get(), column);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Utils::span<const byte> BaseStatement::fetchBlobValue(int column) const
|
||||||
|
{
|
||||||
|
checkIfIsReadyToFetchValues();
|
||||||
|
checkColumnIsValid(column);
|
||||||
|
|
||||||
|
return convertToBlobForColumn(m_compiledStatement.get(), column);
|
||||||
|
}
|
||||||
|
|
||||||
template<>
|
template<>
|
||||||
double BaseStatement::fetchValue<double>(int column) const
|
double BaseStatement::fetchValue<double>(int column) const
|
||||||
{
|
{
|
||||||
|
@@ -33,6 +33,7 @@
|
|||||||
#include <utils/smallstringvector.h>
|
#include <utils/smallstringvector.h>
|
||||||
|
|
||||||
#include <utils/optional.h>
|
#include <utils/optional.h>
|
||||||
|
#include <utils/span.h>
|
||||||
|
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
@@ -69,6 +70,7 @@ public:
|
|||||||
double fetchDoubleValue(int column) const;
|
double fetchDoubleValue(int column) const;
|
||||||
Utils::SmallStringView fetchSmallStringViewValue(int column) const;
|
Utils::SmallStringView fetchSmallStringViewValue(int column) const;
|
||||||
ValueView fetchValueView(int column) const;
|
ValueView fetchValueView(int column) const;
|
||||||
|
Utils::span<const byte> fetchBlobValue(int column) const;
|
||||||
template<typename Type>
|
template<typename Type>
|
||||||
Type fetchValue(int column) const;
|
Type fetchValue(int column) const;
|
||||||
int columnCount() const;
|
int columnCount() const;
|
||||||
@@ -80,6 +82,7 @@ public:
|
|||||||
void bind(int index, void *pointer);
|
void bind(int index, void *pointer);
|
||||||
void bind(int index, Utils::SmallStringView fetchValue);
|
void bind(int index, Utils::SmallStringView fetchValue);
|
||||||
void bind(int index, const Value &fetchValue);
|
void bind(int index, const Value &fetchValue);
|
||||||
|
void bind(int index, Utils::span<const byte> bytes);
|
||||||
|
|
||||||
void bind(int index, uint value) { bind(index, static_cast<long long>(value)); }
|
void bind(int index, uint value) { bind(index, static_cast<long long>(value)); }
|
||||||
|
|
||||||
@@ -362,6 +365,7 @@ private:
|
|||||||
operator long long() { return statement.fetchLongLongValue(column); }
|
operator long long() { return statement.fetchLongLongValue(column); }
|
||||||
operator double() { return statement.fetchDoubleValue(column); }
|
operator double() { return statement.fetchDoubleValue(column); }
|
||||||
operator Utils::SmallStringView() { return statement.fetchSmallStringViewValue(column); }
|
operator Utils::SmallStringView() { return statement.fetchSmallStringViewValue(column); }
|
||||||
|
operator Utils::span<const Sqlite::byte>() { return statement.fetchBlobValue(column); }
|
||||||
operator ValueView() { return statement.fetchValueView(column); }
|
operator ValueView() { return statement.fetchValueView(column); }
|
||||||
|
|
||||||
StatementImplementation &statement;
|
StatementImplementation &statement;
|
||||||
|
@@ -73,4 +73,6 @@ enum class OpenMode : char
|
|||||||
|
|
||||||
enum class ChangeType : int { Delete = 9, Insert = 18, Update = 23 };
|
enum class ChangeType : int { Delete = 9, Insert = 18, Update = 23 };
|
||||||
|
|
||||||
|
enum class byte : unsigned char {};
|
||||||
|
|
||||||
} // namespace Sqlite
|
} // namespace Sqlite
|
||||||
|
@@ -230,30 +230,6 @@ QDataStream &operator>>(QDataStream &in, vector<Type> &vector)
|
|||||||
return in;
|
return in;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
ostream &operator<<(ostream &out, const vector<T> &vector)
|
|
||||||
{
|
|
||||||
out << "[";
|
|
||||||
|
|
||||||
for (auto current = vector.begin(); current != vector.end(); ++current) {
|
|
||||||
std::ostringstream entryStream;
|
|
||||||
entryStream << *current;
|
|
||||||
std::string entryString = entryStream.str();
|
|
||||||
|
|
||||||
if (entryString.size() > 4)
|
|
||||||
out << "\n\t";
|
|
||||||
|
|
||||||
out << entryString;
|
|
||||||
|
|
||||||
if (std::next(current) != vector.end())
|
|
||||||
out << ", ";
|
|
||||||
}
|
|
||||||
|
|
||||||
out << "]";
|
|
||||||
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace std
|
} // namespace std
|
||||||
|
|
||||||
QT_BEGIN_NAMESPACE
|
QT_BEGIN_NAMESPACE
|
||||||
|
@@ -58,6 +58,7 @@ using testing::Ne;
|
|||||||
using testing::NiceMock;
|
using testing::NiceMock;
|
||||||
using testing::Not;
|
using testing::Not;
|
||||||
using testing::NotNull;
|
using testing::NotNull;
|
||||||
|
using testing::Optional;
|
||||||
using testing::Pair;
|
using testing::Pair;
|
||||||
using testing::PrintToString;
|
using testing::PrintToString;
|
||||||
using testing::Property;
|
using testing::Property;
|
||||||
|
@@ -348,4 +348,24 @@ std::ostream &operator<<(std::ostream &out, const Diagnostic &diag);
|
|||||||
} // namespace Internal
|
} // namespace Internal
|
||||||
} // namespace CppTools
|
} // namespace CppTools
|
||||||
|
|
||||||
|
namespace std {
|
||||||
|
template<typename T>
|
||||||
|
ostream &operator<<(ostream &out, const vector<T> &vector)
|
||||||
|
{
|
||||||
|
out << "[";
|
||||||
|
|
||||||
|
for (auto current = vector.begin(); current != vector.end(); ++current) {
|
||||||
|
out << *current;
|
||||||
|
|
||||||
|
if (std::next(current) != vector.end())
|
||||||
|
out << ", ";
|
||||||
|
}
|
||||||
|
|
||||||
|
out << "]";
|
||||||
|
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace std
|
||||||
|
|
||||||
void setFilePathCache(ClangBackEnd::FilePathCaching *filePathCache);
|
void setFilePathCache(ClangBackEnd::FilePathCaching *filePathCache);
|
||||||
|
@@ -38,6 +38,14 @@
|
|||||||
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
namespace Sqlite {
|
||||||
|
bool operator==(Utils::span<const byte> first, Utils::span<const byte> second)
|
||||||
|
{
|
||||||
|
return first.size() == second.size()
|
||||||
|
&& std::memcmp(first.data(), second.data(), first.size()) == 0;
|
||||||
|
}
|
||||||
|
} // namespace Sqlite
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
using Sqlite::Database;
|
using Sqlite::Database;
|
||||||
@@ -288,6 +296,30 @@ TEST_F(SqliteStatement, BindPointer)
|
|||||||
ASSERT_THAT(statement.fetchIntValue(0), 1);
|
ASSERT_THAT(statement.fetchIntValue(0), 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(SqliteStatement, BindBlob)
|
||||||
|
{
|
||||||
|
SqliteTestStatement statement("WITH T(blob) AS (VALUES (?)) SELECT blob FROM T", database);
|
||||||
|
const unsigned char chars[] = "aaafdfdlll";
|
||||||
|
auto bytePointer = reinterpret_cast<const Sqlite::byte *>(chars);
|
||||||
|
Utils::span<const Sqlite::byte> bytes{bytePointer, sizeof(chars) - 1};
|
||||||
|
|
||||||
|
statement.bind(1, bytes);
|
||||||
|
statement.next();
|
||||||
|
|
||||||
|
ASSERT_THAT(statement.fetchBlobValue(0), Eq(bytes));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(SqliteStatement, BindEmptyBlob)
|
||||||
|
{
|
||||||
|
SqliteTestStatement statement("WITH T(blob) AS (VALUES (?)) SELECT blob FROM T", database);
|
||||||
|
Utils::span<const Sqlite::byte> bytes;
|
||||||
|
|
||||||
|
statement.bind(1, bytes);
|
||||||
|
statement.next();
|
||||||
|
|
||||||
|
ASSERT_THAT(statement.fetchBlobValue(0), IsEmpty());
|
||||||
|
}
|
||||||
|
|
||||||
TEST_F(SqliteStatement, BindIntegerByParameter)
|
TEST_F(SqliteStatement, BindIntegerByParameter)
|
||||||
{
|
{
|
||||||
SqliteTestStatement statement("SELECT name, number FROM test WHERE number=@number", database);
|
SqliteTestStatement statement("SELECT name, number FROM test WHERE number=@number", database);
|
||||||
@@ -332,41 +364,49 @@ TEST_F(SqliteStatement, BindIndexIsZeroIsThrowingBindingIndexIsOutOfBoundNull)
|
|||||||
ASSERT_THROW(statement.bind(0, Sqlite::NullValue{}), Sqlite::BindingIndexIsOutOfRange);
|
ASSERT_THROW(statement.bind(0, Sqlite::NullValue{}), Sqlite::BindingIndexIsOutOfRange);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(SqliteStatement, BindIndexIsTpLargeIsThrowingBindingIndexIsOutOfBoundLongLong)
|
TEST_F(SqliteStatement, BindIndexIsToLargeIsThrowingBindingIndexIsOutOfBoundLongLong)
|
||||||
{
|
{
|
||||||
SqliteTestStatement statement("SELECT name, number FROM test WHERE number=$1", database);
|
SqliteTestStatement statement("SELECT name, number FROM test WHERE number=$1", database);
|
||||||
|
|
||||||
ASSERT_THROW(statement.bind(2, 40LL), Sqlite::BindingIndexIsOutOfRange);
|
ASSERT_THROW(statement.bind(2, 40LL), Sqlite::BindingIndexIsOutOfRange);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(SqliteStatement, BindIndexIsTpLargeIsThrowingBindingIndexIsOutOfBoundStringView)
|
TEST_F(SqliteStatement, BindIndexIsToLargeIsThrowingBindingIndexIsOutOfBoundStringView)
|
||||||
{
|
{
|
||||||
SqliteTestStatement statement("SELECT name, number FROM test WHERE number=$1", database);
|
SqliteTestStatement statement("SELECT name, number FROM test WHERE number=$1", database);
|
||||||
|
|
||||||
ASSERT_THROW(statement.bind(2, "foo"), Sqlite::BindingIndexIsOutOfRange);
|
ASSERT_THROW(statement.bind(2, "foo"), Sqlite::BindingIndexIsOutOfRange);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(SqliteStatement, BindIndexIsTpLargeIsThrowingBindingIndexIsOutOfBoundStringFloat)
|
TEST_F(SqliteStatement, BindIndexIsToLargeIsThrowingBindingIndexIsOutOfBoundStringFloat)
|
||||||
{
|
{
|
||||||
SqliteTestStatement statement("SELECT name, number FROM test WHERE number=$1", database);
|
SqliteTestStatement statement("SELECT name, number FROM test WHERE number=$1", database);
|
||||||
|
|
||||||
ASSERT_THROW(statement.bind(2, 2.), Sqlite::BindingIndexIsOutOfRange);
|
ASSERT_THROW(statement.bind(2, 2.), Sqlite::BindingIndexIsOutOfRange);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(SqliteStatement, BindIndexIsTpLargeIsThrowingBindingIndexIsOutOfBoundPointer)
|
TEST_F(SqliteStatement, BindIndexIsToLargeIsThrowingBindingIndexIsOutOfBoundPointer)
|
||||||
{
|
{
|
||||||
SqliteTestStatement statement("SELECT name, number FROM test WHERE number=$1", database);
|
SqliteTestStatement statement("SELECT name, number FROM test WHERE number=$1", database);
|
||||||
|
|
||||||
ASSERT_THROW(statement.bind(2, nullptr), Sqlite::BindingIndexIsOutOfRange);
|
ASSERT_THROW(statement.bind(2, nullptr), Sqlite::BindingIndexIsOutOfRange);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(SqliteStatement, BindIndexIsTpLargeIsThrowingBindingIndexIsOutOfBoundValue)
|
TEST_F(SqliteStatement, BindIndexIsToLargeIsThrowingBindingIndexIsOutOfBoundValue)
|
||||||
{
|
{
|
||||||
SqliteTestStatement statement("SELECT name, number FROM test WHERE number=$1", database);
|
SqliteTestStatement statement("SELECT name, number FROM test WHERE number=$1", database);
|
||||||
|
|
||||||
ASSERT_THROW(statement.bind(2, Sqlite::Value{1}), Sqlite::BindingIndexIsOutOfRange);
|
ASSERT_THROW(statement.bind(2, Sqlite::Value{1}), Sqlite::BindingIndexIsOutOfRange);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(SqliteStatement, BindIndexIsToLargeIsThrowingBindingIndexIsOutOfBoundBlob)
|
||||||
|
{
|
||||||
|
SqliteTestStatement statement("WITH T(blob) AS (VALUES (?)) SELECT blob FROM T", database);
|
||||||
|
Utils::span<const Sqlite::byte> bytes;
|
||||||
|
|
||||||
|
ASSERT_THROW(statement.bind(2, bytes), Sqlite::BindingIndexIsOutOfRange);
|
||||||
|
}
|
||||||
|
|
||||||
TEST_F(SqliteStatement, WrongBindingNameThrowingBindingIndexIsOutOfBound)
|
TEST_F(SqliteStatement, WrongBindingNameThrowingBindingIndexIsOutOfBound)
|
||||||
{
|
{
|
||||||
SqliteTestStatement statement("SELECT name, number FROM test WHERE number=@name", database);
|
SqliteTestStatement statement("SELECT name, number FROM test WHERE number=@name", database);
|
||||||
@@ -431,6 +471,40 @@ TEST_F(SqliteStatement, WriteSqliteValues)
|
|||||||
ASSERT_THAT(statement, HasValues("see", "7.23", 1));
|
ASSERT_THAT(statement, HasValues("see", "7.23", 1));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(SqliteStatement, WriteEmptyBlobs)
|
||||||
|
{
|
||||||
|
SqliteTestStatement statement("WITH T(blob) AS (VALUES (?)) SELECT blob FROM T", database);
|
||||||
|
|
||||||
|
Utils::span<const Sqlite::byte> bytes;
|
||||||
|
|
||||||
|
statement.write(bytes);
|
||||||
|
|
||||||
|
ASSERT_THAT(statement.fetchBlobValue(0), IsEmpty());
|
||||||
|
}
|
||||||
|
|
||||||
|
class Blob
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Blob(Utils::span<const Sqlite::byte> bytes)
|
||||||
|
: bytes(bytes.begin(), bytes.end())
|
||||||
|
{}
|
||||||
|
|
||||||
|
std::vector<Sqlite::byte> bytes;
|
||||||
|
};
|
||||||
|
|
||||||
|
TEST_F(SqliteStatement, WriteBlobs)
|
||||||
|
{
|
||||||
|
SqliteTestStatement statement("INSERT INTO test VALUES ('blob', 40, ?)", database);
|
||||||
|
SqliteTestStatement readStatement("SELECT value FROM test WHERE name = 'blob'", database);
|
||||||
|
const unsigned char chars[] = "aaafdfdlll";
|
||||||
|
auto bytePointer = reinterpret_cast<const Sqlite::byte *>(chars);
|
||||||
|
Utils::span<const Sqlite::byte> bytes{bytePointer, sizeof(chars) - 1};
|
||||||
|
|
||||||
|
statement.write(bytes);
|
||||||
|
|
||||||
|
ASSERT_THAT(readStatement.template value<Blob>(), Optional(Field(&Blob::bytes, Eq(bytes))));
|
||||||
|
}
|
||||||
|
|
||||||
TEST_F(SqliteStatement, BindNamedValues)
|
TEST_F(SqliteStatement, BindNamedValues)
|
||||||
{
|
{
|
||||||
SqliteTestStatement statement("UPDATE test SET name=@name, number=@number WHERE rowid=@id", database);
|
SqliteTestStatement statement("UPDATE test SET name=@name, number=@number WHERE rowid=@id", database);
|
||||||
@@ -633,6 +707,46 @@ TEST_F(SqliteStatement, GetStructOutputValuesAndContainerQueryTupleValues)
|
|||||||
Output{"bar", "blah", 1}));
|
Output{"bar", "blah", 1}));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(SqliteStatement, GetBlobValues)
|
||||||
|
{
|
||||||
|
database.execute("INSERT INTO test VALUES ('blob', 40, x'AABBCCDD')");
|
||||||
|
ReadStatement statement("SELECT value FROM test WHERE name='blob'", database);
|
||||||
|
const int value = 0xDDCCBBAA;
|
||||||
|
auto bytePointer = reinterpret_cast<const Sqlite::byte *>(&value);
|
||||||
|
Utils::span<const Sqlite::byte> bytes{bytePointer, 4};
|
||||||
|
|
||||||
|
auto values = statement.values<Blob>(1);
|
||||||
|
|
||||||
|
ASSERT_THAT(values, ElementsAre(Field(&Blob::bytes, Eq(bytes))));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(SqliteStatement, GetEmptyBlobValueForInteger)
|
||||||
|
{
|
||||||
|
ReadStatement statement("SELECT value FROM test WHERE name='poo'", database);
|
||||||
|
|
||||||
|
auto value = statement.value<Blob>();
|
||||||
|
|
||||||
|
ASSERT_THAT(value, Optional(Field(&Blob::bytes, IsEmpty())));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(SqliteStatement, GetEmptyBlobValueForFloat)
|
||||||
|
{
|
||||||
|
ReadStatement statement("SELECT number FROM test WHERE name='foo'", database);
|
||||||
|
|
||||||
|
auto value = statement.value<Blob>();
|
||||||
|
|
||||||
|
ASSERT_THAT(value, Optional(Field(&Blob::bytes, IsEmpty())));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(SqliteStatement, GetEmptyBlobValueForText)
|
||||||
|
{
|
||||||
|
ReadStatement statement("SELECT number FROM test WHERE name='bar'", database);
|
||||||
|
|
||||||
|
auto value = statement.value<Blob>();
|
||||||
|
|
||||||
|
ASSERT_THAT(value, Optional(Field(&Blob::bytes, IsEmpty())));
|
||||||
|
}
|
||||||
|
|
||||||
TEST_F(SqliteStatement, GetOptionalSingleValueAndMultipleQueryValue)
|
TEST_F(SqliteStatement, GetOptionalSingleValueAndMultipleQueryValue)
|
||||||
{
|
{
|
||||||
ReadStatement statement("SELECT name FROM test WHERE name=? AND number=? AND value=?", database);
|
ReadStatement statement("SELECT name FROM test WHERE name=? AND number=? AND value=?", database);
|
||||||
|
@@ -85,6 +85,15 @@ TEST(SqliteValue, ConstructStringFromQString)
|
|||||||
ASSERT_THAT(value.toStringView(), Eq("foo"));
|
ASSERT_THAT(value.toStringView(), Eq("foo"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST(SqliteValue, ConstructStringFromBlob)
|
||||||
|
{
|
||||||
|
// Utils::span<const Sqlite::byte> bytes{reinterpret_cast<const Sqlite::byte *>("abcd"), 4};
|
||||||
|
|
||||||
|
// Sqlite::Value value{bytes};
|
||||||
|
|
||||||
|
//ASSERT_THAT(value.toBlob(), Eq(bytes));
|
||||||
|
}
|
||||||
|
|
||||||
TEST(SqliteValue, ConstructNullFromNullQVariant)
|
TEST(SqliteValue, ConstructNullFromNullQVariant)
|
||||||
{
|
{
|
||||||
QVariant variant{};
|
QVariant variant{};
|
||||||
|
Reference in New Issue
Block a user