forked from qt-creator/qt-creator
Sqlite: Fix handling of blob and blob views
Change-Id: I90c31307ff3299975f820e191085ba93ed8afe0f Reviewed-by: Henning Gründl <henning.gruendl@qt.io> Reviewed-by: Tim Jenssen <tim.jenssen@qt.io>
This commit is contained in:
@@ -277,6 +277,30 @@ void BaseStatement::bind(int index, const Value &value)
|
|||||||
case ValueType::String:
|
case ValueType::String:
|
||||||
bind(index, value.toStringView());
|
bind(index, value.toStringView());
|
||||||
break;
|
break;
|
||||||
|
case ValueType::Blob:
|
||||||
|
bind(index, value.toBlobView());
|
||||||
|
break;
|
||||||
|
case ValueType::Null:
|
||||||
|
bind(index, NullValue{});
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void BaseStatement::bind(int index, ValueView value)
|
||||||
|
{
|
||||||
|
switch (value.type()) {
|
||||||
|
case ValueType::Integer:
|
||||||
|
bind(index, value.toInteger());
|
||||||
|
break;
|
||||||
|
case ValueType::Float:
|
||||||
|
bind(index, value.toFloat());
|
||||||
|
break;
|
||||||
|
case ValueType::String:
|
||||||
|
bind(index, value.toStringView());
|
||||||
|
break;
|
||||||
|
case ValueType::Blob:
|
||||||
|
bind(index, value.toBlobView());
|
||||||
|
break;
|
||||||
case ValueType::Null:
|
case ValueType::Null:
|
||||||
bind(index, NullValue{});
|
bind(index, NullValue{});
|
||||||
break;
|
break;
|
||||||
|
@@ -91,6 +91,7 @@ public:
|
|||||||
void bind(int index, Utils::span<const char *> values);
|
void bind(int index, Utils::span<const char *> values);
|
||||||
void bind(int index, Utils::SmallStringView value);
|
void bind(int index, Utils::SmallStringView value);
|
||||||
void bind(int index, const Value &value);
|
void bind(int index, const Value &value);
|
||||||
|
void bind(int index, ValueView value);
|
||||||
void bind(int index, BlobView blobView);
|
void bind(int index, BlobView blobView);
|
||||||
|
|
||||||
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)); }
|
||||||
|
@@ -44,11 +44,11 @@ class NullValue
|
|||||||
friend bool operator==(NullValue, NullValue) { return false; }
|
friend bool operator==(NullValue, NullValue) { return false; }
|
||||||
};
|
};
|
||||||
|
|
||||||
template<typename StringType>
|
template<typename StringType, typename BlobType>
|
||||||
class ValueBase
|
class ValueBase
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
using VariantType = Utils::variant<NullValue, long long, double, StringType, Blob>;
|
using VariantType = Utils::variant<NullValue, long long, double, StringType, BlobType>;
|
||||||
|
|
||||||
ValueBase() = default;
|
ValueBase() = default;
|
||||||
|
|
||||||
@@ -115,9 +115,12 @@ public:
|
|||||||
|
|
||||||
BlobView toBlobView() const
|
BlobView toBlobView() const
|
||||||
{
|
{
|
||||||
const Blob &blob = Utils::get<int(ValueType::Blob)>(value);
|
const BlobType &blob = Utils::get<int(ValueType::Blob)>(value);
|
||||||
|
if constexpr (std::is_same_v<BlobType, Blob>) {
|
||||||
return {blob.bytes};
|
return {blob.bytes};
|
||||||
|
} else {
|
||||||
|
return blob;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
explicit operator QVariant() const
|
explicit operator QVariant() const
|
||||||
{
|
{
|
||||||
@@ -245,7 +248,7 @@ public:
|
|||||||
VariantType value;
|
VariantType value;
|
||||||
};
|
};
|
||||||
|
|
||||||
class ValueView : public ValueBase<Utils::SmallStringView>
|
class ValueView : public ValueBase<Utils::SmallStringView, BlobView>
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
explicit ValueView(ValueBase &&base)
|
explicit ValueView(ValueBase &&base)
|
||||||
@@ -259,9 +262,9 @@ public:
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
class Value : public ValueBase<Utils::SmallString>
|
class Value : public ValueBase<Utils::SmallString, Blob>
|
||||||
{
|
{
|
||||||
using Base = ValueBase<Utils::SmallString>;
|
using Base = ValueBase<Utils::SmallString, Blob>;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
using Base::Base;
|
using Base::Base;
|
||||||
|
@@ -38,6 +38,7 @@
|
|||||||
#include <QDir>
|
#include <QDir>
|
||||||
|
|
||||||
#include <deque>
|
#include <deque>
|
||||||
|
#include <type_traits>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
@@ -50,6 +51,21 @@ using Sqlite::ReadWriteStatement;
|
|||||||
using Sqlite::Value;
|
using Sqlite::Value;
|
||||||
using Sqlite::WriteStatement;
|
using Sqlite::WriteStatement;
|
||||||
|
|
||||||
|
template<typename Type>
|
||||||
|
bool compareValue(SqliteTestStatement &statement, Type value, int column)
|
||||||
|
{
|
||||||
|
if constexpr (std::is_convertible_v<Type, long long> && !std::is_same_v<Type, double>)
|
||||||
|
return statement.fetchLongLongValue(column) == value;
|
||||||
|
else if constexpr (std::is_convertible_v<Type, double>)
|
||||||
|
return statement.fetchDoubleValue(column) == value;
|
||||||
|
else if constexpr (std::is_convertible_v<Type, Utils::SmallStringView>)
|
||||||
|
return statement.fetchSmallStringViewValue(column) == value;
|
||||||
|
else if constexpr (std::is_convertible_v<Type, Sqlite::BlobView>)
|
||||||
|
return statement.fetchBlobValue(column) == value;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
MATCHER_P3(HasValues, value1, value2, rowid,
|
MATCHER_P3(HasValues, value1, value2, rowid,
|
||||||
std::string(negation ? "isn't" : "is")
|
std::string(negation ? "isn't" : "is")
|
||||||
+ PrintToString(value1)
|
+ PrintToString(value1)
|
||||||
@@ -64,8 +80,7 @@ MATCHER_P3(HasValues, value1, value2, rowid,
|
|||||||
|
|
||||||
statement.next();
|
statement.next();
|
||||||
|
|
||||||
return statement.fetchSmallStringViewValue(0) == value1
|
return compareValue(statement, value1, 0) && compareValue(statement, value2, 1);
|
||||||
&& statement.fetchSmallStringViewValue(1) == value2;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
MATCHER_P(HasNullValues, rowid, std::string(negation ? "isn't null" : "is null"))
|
MATCHER_P(HasNullValues, rowid, std::string(negation ? "isn't null" : "is null"))
|
||||||
@@ -490,13 +505,98 @@ TEST_F(SqliteStatement, WriteNullValues)
|
|||||||
ASSERT_THAT(statement, HasNullValues(1));
|
ASSERT_THAT(statement, HasNullValues(1));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(SqliteStatement, WriteSqliteValues)
|
TEST_F(SqliteStatement, WriteSqliteIntegerValue)
|
||||||
|
{
|
||||||
|
WriteStatement statement("UPDATE test SET name=?, number=? WHERE rowid=?", database);
|
||||||
|
statement.write(1, 1, 1);
|
||||||
|
|
||||||
|
statement.write("see", Sqlite::Value{33}, 1);
|
||||||
|
|
||||||
|
ASSERT_THAT(statement, HasValues("see", 33, 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(SqliteStatement, WriteSqliteDoubeValue)
|
||||||
{
|
{
|
||||||
WriteStatement statement("UPDATE test SET name=?, number=? WHERE rowid=?", database);
|
WriteStatement statement("UPDATE test SET name=?, number=? WHERE rowid=?", database);
|
||||||
|
|
||||||
statement.write(Value{"see"}, Value{7.23}, Value{1});
|
statement.write("see", Value{7.23}, Value{1});
|
||||||
|
|
||||||
ASSERT_THAT(statement, HasValues("see", "7.23", 1));
|
ASSERT_THAT(statement, HasValues("see", 7.23, 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(SqliteStatement, WriteSqliteStringValue)
|
||||||
|
{
|
||||||
|
WriteStatement statement("UPDATE test SET name=?, number=? WHERE rowid=?", database);
|
||||||
|
|
||||||
|
statement.write("see", Value{"foo"}, Value{1});
|
||||||
|
|
||||||
|
ASSERT_THAT(statement, HasValues("see", "foo", 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(SqliteStatement, WriteSqliteBlobValue)
|
||||||
|
{
|
||||||
|
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 std::byte *>(chars);
|
||||||
|
Sqlite::BlobView bytes{bytePointer, sizeof(chars) - 1};
|
||||||
|
|
||||||
|
statement.write(Sqlite::Value{bytes});
|
||||||
|
|
||||||
|
ASSERT_THAT(readStatement.template value<Sqlite::Blob>(),
|
||||||
|
Optional(Field(&Sqlite::Blob::bytes, Eq(bytes))));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(SqliteStatement, WriteNullValueView)
|
||||||
|
{
|
||||||
|
WriteStatement statement("UPDATE test SET name=?, number=? WHERE rowid=?", database);
|
||||||
|
statement.write(1, 1, 1);
|
||||||
|
|
||||||
|
statement.write(Sqlite::NullValue{}, Sqlite::ValueView::create(Sqlite::NullValue{}), 1);
|
||||||
|
|
||||||
|
ASSERT_THAT(statement, HasNullValues(1));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(SqliteStatement, WriteSqliteIntegerValueView)
|
||||||
|
{
|
||||||
|
WriteStatement statement("UPDATE test SET name=?, number=? WHERE rowid=?", database);
|
||||||
|
statement.write(1, 1, 1);
|
||||||
|
|
||||||
|
statement.write("see", Sqlite::ValueView::create(33), 1);
|
||||||
|
|
||||||
|
ASSERT_THAT(statement, HasValues("see", 33, 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(SqliteStatement, WriteSqliteDoubeValueView)
|
||||||
|
{
|
||||||
|
WriteStatement statement("UPDATE test SET name=?, number=? WHERE rowid=?", database);
|
||||||
|
|
||||||
|
statement.write("see", Sqlite::ValueView::create(7.23), 1);
|
||||||
|
|
||||||
|
ASSERT_THAT(statement, HasValues("see", 7.23, 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(SqliteStatement, WriteSqliteStringValueView)
|
||||||
|
{
|
||||||
|
WriteStatement statement("UPDATE test SET name=?, number=? WHERE rowid=?", database);
|
||||||
|
|
||||||
|
statement.write("see", Sqlite::ValueView::create("foo"), 1);
|
||||||
|
|
||||||
|
ASSERT_THAT(statement, HasValues("see", "foo", 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(SqliteStatement, WriteSqliteBlobValueView)
|
||||||
|
{
|
||||||
|
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 std::byte *>(chars);
|
||||||
|
Sqlite::BlobView bytes{bytePointer, sizeof(chars) - 1};
|
||||||
|
|
||||||
|
statement.write(Sqlite::ValueView::create(bytes));
|
||||||
|
|
||||||
|
ASSERT_THAT(readStatement.template value<Sqlite::Blob>(),
|
||||||
|
Optional(Field(&Sqlite::Blob::bytes, Eq(bytes))));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(SqliteStatement, WriteEmptyBlobs)
|
TEST_F(SqliteStatement, WriteEmptyBlobs)
|
||||||
|
@@ -445,7 +445,7 @@ TEST(SqliteValue, BlobValueAndValueViewEquals)
|
|||||||
{
|
{
|
||||||
Utils::span<const std::byte> bytes{reinterpret_cast<const std::byte *>("abcd"), 4};
|
Utils::span<const std::byte> bytes{reinterpret_cast<const std::byte *>("abcd"), 4};
|
||||||
|
|
||||||
bool isEqual = Sqlite::ValueView::create(bytes) == Sqlite::Value{bytes};
|
bool isEqual = Sqlite::ValueView::create(Sqlite::BlobView{bytes}) == Sqlite::Value{bytes};
|
||||||
|
|
||||||
ASSERT_TRUE(isEqual);
|
ASSERT_TRUE(isEqual);
|
||||||
}
|
}
|
||||||
@@ -489,7 +489,7 @@ TEST(SqliteValue, ConvertFloatValueViewIntoValue)
|
|||||||
TEST(SqliteValue, ConvertBlobValueViewIntoValue)
|
TEST(SqliteValue, ConvertBlobValueViewIntoValue)
|
||||||
{
|
{
|
||||||
Utils::span<const std::byte> bytes{reinterpret_cast<const std::byte *>("abcd"), 4};
|
Utils::span<const std::byte> bytes{reinterpret_cast<const std::byte *>("abcd"), 4};
|
||||||
auto view = Sqlite::ValueView::create(bytes);
|
auto view = Sqlite::ValueView::create(Sqlite::BlobView{bytes});
|
||||||
|
|
||||||
Sqlite::Value value{view};
|
Sqlite::Value value{view};
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user