Bug 483318 - Implement binding arrays to mozIStorageStatementParams. r=asuth,lina,Standard8
Use `carray`` extension to bind array of numerics and strings, so we can avoid large and slow IN clauses, and cache prepared statements having a variable number of parameters. The extension is statically loaded and available in every connection. Consumers are encouraged to use the explicit `bindArrayXXX` methods, as the generic `bindByIndex` and `bindByName` methods are too lenient, especially from Javascript. Note `carray`` only supports UTF8 encoded strings, the API will convert the encoding when UTF16 is passed in. These new variants are not exposed to Rust yet, as the existing comment suggests they were intended for primitive types. It could be done in the future, if necessary. Differential Revision: https://phabricator.services.mozilla.com/D225334
This commit is contained in:
@@ -289,7 +289,23 @@ NS_DEFINE_STATIC_IID_ACCESSOR(StorageBaseStatementInternal,
|
|||||||
_class, _optionalGuard, AdoptedBlob, \
|
_class, _optionalGuard, AdoptedBlob, \
|
||||||
(const nsACString& aWhere, uint8_t* aValue, uint32_t aValueSize), \
|
(const nsACString& aWhere, uint8_t* aValue, uint32_t aValueSize), \
|
||||||
(uint32_t aWhere, uint8_t * aValue, uint32_t aValueSize), \
|
(uint32_t aWhere, uint8_t * aValue, uint32_t aValueSize), \
|
||||||
(aWhere, aValue, aValueSize))
|
(aWhere, aValue, aValueSize)) \
|
||||||
|
BIND_GEN_IMPL(_class, _optionalGuard, ArrayOfIntegers, \
|
||||||
|
(const nsACString& aWhere, const nsTArray<int64_t>& aValue), \
|
||||||
|
(uint32_t aWhere, const nsTArray<int64_t>& aValue), \
|
||||||
|
(aWhere, aValue)) \
|
||||||
|
BIND_GEN_IMPL(_class, _optionalGuard, ArrayOfDoubles, \
|
||||||
|
(const nsACString& aWhere, const nsTArray<double>& aValue), \
|
||||||
|
(uint32_t aWhere, const nsTArray<double>& aValue), \
|
||||||
|
(aWhere, aValue)) \
|
||||||
|
BIND_GEN_IMPL(_class, _optionalGuard, ArrayOfStrings, \
|
||||||
|
(const nsACString& aWhere, const nsTArray<nsString>& aValue), \
|
||||||
|
(uint32_t aWhere, const nsTArray<nsString>& aValue), \
|
||||||
|
(aWhere, aValue)) \
|
||||||
|
BIND_GEN_IMPL(_class, _optionalGuard, ArrayOfUTF8Strings, \
|
||||||
|
(const nsACString& aWhere, const nsTArray<nsCString>& aValue), \
|
||||||
|
(uint32_t aWhere, const nsTArray<nsCString>& aValue), \
|
||||||
|
(aWhere, aValue))
|
||||||
|
|
||||||
} // namespace storage
|
} // namespace storage
|
||||||
} // namespace mozilla
|
} // namespace mozilla
|
||||||
|
|||||||
@@ -7,11 +7,10 @@
|
|||||||
#ifndef mozilla_storage_Variant_h__
|
#ifndef mozilla_storage_Variant_h__
|
||||||
#define mozilla_storage_Variant_h__
|
#define mozilla_storage_Variant_h__
|
||||||
|
|
||||||
#include <utility>
|
#include "nsIInterfaceRequestor.h"
|
||||||
|
|
||||||
#include "nsIVariant.h"
|
#include "nsIVariant.h"
|
||||||
|
#include "nsCOMPtr.h"
|
||||||
#include "nsString.h"
|
#include "nsString.h"
|
||||||
#include "nsTArray.h"
|
|
||||||
|
|
||||||
#define VARIANT_BASE_IID \
|
#define VARIANT_BASE_IID \
|
||||||
{ /* 78888042-0fa3-4f7a-8b19-7996f99bf1aa */ \
|
{ /* 78888042-0fa3-4f7a-8b19-7996f99bf1aa */ \
|
||||||
@@ -30,6 +29,9 @@
|
|||||||
* nsCString -> TEXT (use UTF8TextVariant)
|
* nsCString -> TEXT (use UTF8TextVariant)
|
||||||
* uint8_t[] -> BLOB (use BlobVariant)
|
* uint8_t[] -> BLOB (use BlobVariant)
|
||||||
* nullptr -> NULL (use NullVariant)
|
* nullptr -> NULL (use NullVariant)
|
||||||
|
* int64_t[] -> ARRAY (use ArrayOfIntegersVariant)
|
||||||
|
* double[] -> ARRAY (use ArrayOfDoublesVariant)
|
||||||
|
* nsCString[] -> ARRAY (use ArrayOfUTF8StringsVariant)
|
||||||
*
|
*
|
||||||
* The kvstore component also reuses this class as a common implementation
|
* The kvstore component also reuses this class as a common implementation
|
||||||
* of a simple threadsafe variant for the storage of primitive values only.
|
* of a simple threadsafe variant for the storage of primitive values only.
|
||||||
@@ -39,18 +41,33 @@
|
|||||||
* Bug 1494102 tracks that work.
|
* Bug 1494102 tracks that work.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
namespace mozilla {
|
namespace mozilla::storage {
|
||||||
namespace storage {
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
//// Base Class
|
//// Base Class
|
||||||
|
|
||||||
class Variant_base : public nsIVariant {
|
class Variant_base : public nsIVariant, public nsIInterfaceRequestor {
|
||||||
public:
|
public:
|
||||||
NS_DECL_THREADSAFE_ISUPPORTS
|
NS_DECL_THREADSAFE_ISUPPORTS
|
||||||
NS_DECL_NSIVARIANT
|
NS_DECL_NSIVARIANT
|
||||||
NS_DECLARE_STATIC_IID_ACCESSOR(VARIANT_BASE_IID)
|
NS_DECLARE_STATIC_IID_ACCESSOR(VARIANT_BASE_IID)
|
||||||
|
|
||||||
|
NS_IMETHOD
|
||||||
|
GetInterface(const nsIID& aIID, void** aResult) override {
|
||||||
|
NS_ENSURE_ARG_POINTER(aResult);
|
||||||
|
*aResult = nullptr;
|
||||||
|
|
||||||
|
// This is used to recognize nsIVariant instances derived from Variant_base
|
||||||
|
// from other implementations like XPCVariant that may not be thread-safe.
|
||||||
|
if (aIID.Equals(VARIANT_BASE_IID) || aIID.Equals(NS_GET_IID(nsIVariant))) {
|
||||||
|
nsCOMPtr<nsIVariant> result(static_cast<nsIVariant*>(this));
|
||||||
|
result.forget(aResult);
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
return NS_NOINTERFACE;
|
||||||
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual ~Variant_base() = default;
|
virtual ~Variant_base() = default;
|
||||||
};
|
};
|
||||||
@@ -71,8 +88,8 @@ struct variant_traits {
|
|||||||
|
|
||||||
template <typename DataType, bool Adopting = false>
|
template <typename DataType, bool Adopting = false>
|
||||||
struct variant_storage_traits {
|
struct variant_storage_traits {
|
||||||
typedef DataType ConstructorType;
|
using ConstructorType = DataType;
|
||||||
typedef DataType StorageType;
|
using StorageType = DataType;
|
||||||
static inline void storage_conversion(const ConstructorType aData,
|
static inline void storage_conversion(const ConstructorType aData,
|
||||||
StorageType* _storage) {
|
StorageType* _storage) {
|
||||||
*_storage = aData;
|
*_storage = aData;
|
||||||
@@ -85,30 +102,30 @@ struct variant_storage_traits {
|
|||||||
|
|
||||||
template <typename DataType, bool Adopting = false>
|
template <typename DataType, bool Adopting = false>
|
||||||
struct variant_boolean_traits {
|
struct variant_boolean_traits {
|
||||||
typedef typename variant_storage_traits<DataType, Adopting>::StorageType
|
using StorageType =
|
||||||
StorageType;
|
typename variant_storage_traits<DataType, Adopting>::StorageType;
|
||||||
static inline nsresult asBool(const StorageType&, bool*) { NO_CONVERSION }
|
static inline nsresult asBool(const StorageType&, bool*) { NO_CONVERSION }
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename DataType, bool Adopting = false>
|
template <typename DataType, bool Adopting = false>
|
||||||
struct variant_integer_traits {
|
struct variant_integer_traits {
|
||||||
typedef typename variant_storage_traits<DataType, Adopting>::StorageType
|
using StorageType =
|
||||||
StorageType;
|
typename variant_storage_traits<DataType, Adopting>::StorageType;
|
||||||
static inline nsresult asInt32(const StorageType&, int32_t*) { NO_CONVERSION }
|
static inline nsresult asInt32(const StorageType&, int32_t*) { NO_CONVERSION }
|
||||||
static inline nsresult asInt64(const StorageType&, int64_t*) { NO_CONVERSION }
|
static inline nsresult asInt64(const StorageType&, int64_t*) { NO_CONVERSION }
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename DataType, bool Adopting = false>
|
template <typename DataType, bool Adopting = false>
|
||||||
struct variant_float_traits {
|
struct variant_float_traits {
|
||||||
typedef typename variant_storage_traits<DataType, Adopting>::StorageType
|
using StorageType =
|
||||||
StorageType;
|
typename variant_storage_traits<DataType, Adopting>::StorageType;
|
||||||
static inline nsresult asDouble(const StorageType&, double*) { NO_CONVERSION }
|
static inline nsresult asDouble(const StorageType&, double*) { NO_CONVERSION }
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename DataType, bool Adopting = false>
|
template <typename DataType, bool Adopting = false>
|
||||||
struct variant_text_traits {
|
struct variant_text_traits {
|
||||||
typedef typename variant_storage_traits<DataType, Adopting>::StorageType
|
using StorageType =
|
||||||
StorageType;
|
typename variant_storage_traits<DataType, Adopting>::StorageType;
|
||||||
static inline nsresult asUTF8String(const StorageType&, nsACString&) {
|
static inline nsresult asUTF8String(const StorageType&, nsACString&) {
|
||||||
NO_CONVERSION
|
NO_CONVERSION
|
||||||
}
|
}
|
||||||
@@ -118,9 +135,9 @@ struct variant_text_traits {
|
|||||||
};
|
};
|
||||||
|
|
||||||
template <typename DataType, bool Adopting = false>
|
template <typename DataType, bool Adopting = false>
|
||||||
struct variant_blob_traits {
|
struct variant_array_traits {
|
||||||
typedef typename variant_storage_traits<DataType, Adopting>::StorageType
|
using StorageType =
|
||||||
StorageType;
|
typename variant_storage_traits<DataType, Adopting>::StorageType;
|
||||||
static inline nsresult asArray(const StorageType&, uint16_t*, uint32_t*,
|
static inline nsresult asArray(const StorageType&, uint16_t*, uint32_t*,
|
||||||
void**) {
|
void**) {
|
||||||
NO_CONVERSION
|
NO_CONVERSION
|
||||||
@@ -164,8 +181,9 @@ struct variant_traits<int64_t> {
|
|||||||
template <>
|
template <>
|
||||||
struct variant_integer_traits<int64_t> {
|
struct variant_integer_traits<int64_t> {
|
||||||
static inline nsresult asInt32(int64_t aValue, int32_t* _result) {
|
static inline nsresult asInt32(int64_t aValue, int32_t* _result) {
|
||||||
if (aValue > INT32_MAX || aValue < INT32_MIN)
|
if (aValue > INT32_MAX || aValue < INT32_MIN) {
|
||||||
return NS_ERROR_CANNOT_CONVERT_DATA;
|
return NS_ERROR_CANNOT_CONVERT_DATA;
|
||||||
|
}
|
||||||
|
|
||||||
*_result = static_cast<int32_t>(aValue);
|
*_result = static_cast<int32_t>(aValue);
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
@@ -210,8 +228,8 @@ struct variant_traits<nsString> {
|
|||||||
};
|
};
|
||||||
template <>
|
template <>
|
||||||
struct variant_storage_traits<nsString> {
|
struct variant_storage_traits<nsString> {
|
||||||
typedef const nsAString& ConstructorType;
|
using ConstructorType = const nsAString&;
|
||||||
typedef nsString StorageType;
|
using StorageType = nsString;
|
||||||
static inline void storage_conversion(ConstructorType aText,
|
static inline void storage_conversion(ConstructorType aText,
|
||||||
StorageType* _outData) {
|
StorageType* _outData) {
|
||||||
*_outData = aText;
|
*_outData = aText;
|
||||||
@@ -237,8 +255,8 @@ struct variant_traits<nsCString> {
|
|||||||
};
|
};
|
||||||
template <>
|
template <>
|
||||||
struct variant_storage_traits<nsCString> {
|
struct variant_storage_traits<nsCString> {
|
||||||
typedef const nsACString& ConstructorType;
|
using ConstructorType = const nsACString&;
|
||||||
typedef nsCString StorageType;
|
using StorageType = nsCString;
|
||||||
static inline void storage_conversion(ConstructorType aText,
|
static inline void storage_conversion(ConstructorType aText,
|
||||||
StorageType* _outData) {
|
StorageType* _outData) {
|
||||||
*_outData = aText;
|
*_outData = aText;
|
||||||
@@ -259,88 +277,152 @@ struct variant_text_traits<nsCString> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* BLOB types
|
* ARRAY types
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
// NOLINTBEGIN(bugprone-macro-parentheses)
|
||||||
|
|
||||||
|
#define SPECIALIZE_ARRAY_TO_NUMERIC_VARIANT(Type, DataType) \
|
||||||
|
template <> \
|
||||||
|
struct variant_traits<Type[]> { \
|
||||||
|
static inline uint16_t type() { return nsIDataType::VTYPE_ARRAY; } \
|
||||||
|
}; \
|
||||||
|
\
|
||||||
|
template <> \
|
||||||
|
struct variant_storage_traits<Type[], false> { \
|
||||||
|
using ConstructorType = std::pair<const void*, int>; \
|
||||||
|
using StorageType = FallibleTArray<Type>; \
|
||||||
|
static inline void storage_conversion(ConstructorType aArrayAndLength, \
|
||||||
|
StorageType* _outData) { \
|
||||||
|
_outData->Clear(); \
|
||||||
|
MOZ_ALWAYS_TRUE(_outData->AppendElements( \
|
||||||
|
static_cast<const Type*>(aArrayAndLength.first), \
|
||||||
|
aArrayAndLength.second, fallible)); \
|
||||||
|
} \
|
||||||
|
static inline void destroy(const StorageType& _outData) {} \
|
||||||
|
}; \
|
||||||
|
\
|
||||||
|
template <> \
|
||||||
|
struct variant_storage_traits<Type[], true> { \
|
||||||
|
using ConstructorType = std::pair<Type*, int>; \
|
||||||
|
using StorageType = std::pair<Type*, int>; \
|
||||||
|
static inline void storage_conversion(ConstructorType aArrayAndLength, \
|
||||||
|
StorageType* _outData) { \
|
||||||
|
*_outData = aArrayAndLength; \
|
||||||
|
} \
|
||||||
|
static inline void destroy(StorageType& aArrayAndLength) { \
|
||||||
|
if (aArrayAndLength.first) { \
|
||||||
|
free(aArrayAndLength.first); \
|
||||||
|
aArrayAndLength.first = nullptr; \
|
||||||
|
} \
|
||||||
|
} \
|
||||||
|
}; \
|
||||||
|
\
|
||||||
|
template <> \
|
||||||
|
struct variant_array_traits<Type[], false> { \
|
||||||
|
static inline nsresult asArray(FallibleTArray<Type>& aData, \
|
||||||
|
uint16_t* _type, uint32_t* _size, \
|
||||||
|
void** _result) { \
|
||||||
|
/* For empty arrays, we return nullptr. */ \
|
||||||
|
if (aData.Length() == 0) { \
|
||||||
|
*_result = nullptr; \
|
||||||
|
*_type = DataType; \
|
||||||
|
*_size = 0; \
|
||||||
|
return NS_OK; \
|
||||||
|
} \
|
||||||
|
/* Otherwise, we copy the array. */ \
|
||||||
|
*_result = moz_xmemdup(aData.Elements(), aData.Length() * sizeof(Type)); \
|
||||||
|
*_type = DataType; \
|
||||||
|
*_size = aData.Length(); \
|
||||||
|
return NS_OK; \
|
||||||
|
} \
|
||||||
|
}; \
|
||||||
|
\
|
||||||
|
template <> \
|
||||||
|
struct variant_array_traits<Type[], true> { \
|
||||||
|
static inline nsresult asArray(std::pair<Type*, int>& aData, \
|
||||||
|
uint16_t* _type, uint32_t* _size, \
|
||||||
|
void** _result) { \
|
||||||
|
/* For empty arrays, we return nullptr. */ \
|
||||||
|
if (aData.second == 0) { \
|
||||||
|
*_result = nullptr; \
|
||||||
|
*_type = DataType; \
|
||||||
|
*_size = 0; \
|
||||||
|
return NS_OK; \
|
||||||
|
} \
|
||||||
|
/* Otherwise, transfer the data out. */ \
|
||||||
|
*_result = aData.first; \
|
||||||
|
aData.first = nullptr; \
|
||||||
|
/* If we asked for it twice, better not use adopting! */ \
|
||||||
|
MOZ_ASSERT(*_result); \
|
||||||
|
*_type = DataType; \
|
||||||
|
*_size = aData.second; \
|
||||||
|
return NS_OK; \
|
||||||
|
} \
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOLINTEND(bugprone-macro-parentheses)
|
||||||
|
|
||||||
|
SPECIALIZE_ARRAY_TO_NUMERIC_VARIANT(uint8_t, nsIDataType::VTYPE_UINT8);
|
||||||
|
SPECIALIZE_ARRAY_TO_NUMERIC_VARIANT(int64_t, nsIDataType::VTYPE_INT64);
|
||||||
|
SPECIALIZE_ARRAY_TO_NUMERIC_VARIANT(double, nsIDataType::VTYPE_DOUBLE);
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
struct variant_traits<uint8_t[]> {
|
struct variant_traits<nsCString[]> {
|
||||||
static inline uint16_t type() { return nsIDataType::VTYPE_ARRAY; }
|
static inline uint16_t type() { return nsIDataType::VTYPE_ARRAY; }
|
||||||
};
|
};
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
struct variant_storage_traits<uint8_t[], false> {
|
struct variant_storage_traits<nsCString[], false> {
|
||||||
typedef std::pair<const void*, int> ConstructorType;
|
using ConstructorType = std::pair<const void*, int>;
|
||||||
typedef FallibleTArray<uint8_t> StorageType;
|
using StorageType = FallibleTArray<nsCString>;
|
||||||
static inline void storage_conversion(ConstructorType aBlob,
|
static inline void storage_conversion(ConstructorType aArrayAndLength,
|
||||||
StorageType* _outData) {
|
StorageType* _outData) {
|
||||||
_outData->Clear();
|
_outData->Clear();
|
||||||
(void)_outData->AppendElements(static_cast<const uint8_t*>(aBlob.first),
|
if (!_outData->SetCapacity(aArrayAndLength.second, fallible)) {
|
||||||
aBlob.second, fallible);
|
MOZ_ASSERT_UNREACHABLE("Cannot allocate.");
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
// We can avoid copying the strings as we're asking SQLite to do it on bind
|
||||||
|
// by using SQLITE_TRANSIENT.
|
||||||
|
const nsCString* str = static_cast<const nsCString*>(aArrayAndLength.first);
|
||||||
|
for (int32_t i = 0; i < aArrayAndLength.second; ++i, str++) {
|
||||||
|
MOZ_ALWAYS_TRUE(_outData->AppendElement(*str, fallible));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static inline void destroy(const StorageType& _outData) {}
|
static inline void destroy(const StorageType& _outData) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
struct variant_storage_traits<uint8_t[], true> {
|
struct variant_array_traits<nsCString[], false> {
|
||||||
typedef std::pair<uint8_t*, int> ConstructorType;
|
static inline nsresult asArray(FallibleTArray<nsCString>& aData,
|
||||||
typedef std::pair<uint8_t*, int> StorageType;
|
|
||||||
static inline void storage_conversion(ConstructorType aBlob,
|
|
||||||
StorageType* _outData) {
|
|
||||||
*_outData = aBlob;
|
|
||||||
}
|
|
||||||
static inline void destroy(StorageType& aData) {
|
|
||||||
if (aData.first) {
|
|
||||||
free(aData.first);
|
|
||||||
aData.first = nullptr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
template <>
|
|
||||||
struct variant_blob_traits<uint8_t[], false> {
|
|
||||||
static inline nsresult asArray(FallibleTArray<uint8_t>& aData,
|
|
||||||
uint16_t* _type, uint32_t* _size,
|
uint16_t* _type, uint32_t* _size,
|
||||||
void** _result) {
|
void** _result) {
|
||||||
// For empty blobs, we return nullptr.
|
// For empty arrays, we return nullptr.
|
||||||
if (aData.Length() == 0) {
|
if (aData.Length() == 0) {
|
||||||
*_result = nullptr;
|
*_result = nullptr;
|
||||||
*_type = nsIDataType::VTYPE_UINT8;
|
*_type = nsIDataType::VTYPE_UTF8STRING;
|
||||||
*_size = 0;
|
*_size = 0;
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Otherwise, we copy the array.
|
// Otherwise, we copy the array.
|
||||||
*_result = moz_xmemdup(aData.Elements(), aData.Length() * sizeof(uint8_t));
|
// This memory will be freed up after Sqlite made its own copy in
|
||||||
|
// sqlite3_T_array. The string buffers are owned by mData.
|
||||||
// Set type and size
|
const char** strings =
|
||||||
*_type = nsIDataType::VTYPE_UINT8;
|
(const char**)moz_xmalloc(sizeof(char*) * aData.Length());
|
||||||
|
const char** iter = strings;
|
||||||
|
for (const nsCString& str : aData) {
|
||||||
|
*iter = str.get();
|
||||||
|
iter++;
|
||||||
|
}
|
||||||
|
*_result = strings;
|
||||||
|
*_type = nsIDataType::VTYPE_UTF8STRING;
|
||||||
*_size = aData.Length();
|
*_size = aData.Length();
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
template <>
|
|
||||||
struct variant_blob_traits<uint8_t[], true> {
|
|
||||||
static inline nsresult asArray(std::pair<uint8_t*, int>& aData,
|
|
||||||
uint16_t* _type, uint32_t* _size,
|
|
||||||
void** _result) {
|
|
||||||
// For empty blobs, we return nullptr.
|
|
||||||
if (aData.second == 0) {
|
|
||||||
*_result = nullptr;
|
|
||||||
*_type = nsIDataType::VTYPE_UINT8;
|
|
||||||
*_size = 0;
|
|
||||||
return NS_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Otherwise, transfer the data out.
|
|
||||||
*_result = aData.first;
|
|
||||||
aData.first = nullptr;
|
|
||||||
MOZ_ASSERT(*_result); // We asked for it twice, better not use adopting!
|
|
||||||
|
|
||||||
// Set type and size
|
|
||||||
*_type = nsIDataType::VTYPE_UINT8;
|
|
||||||
*_size = aData.second;
|
|
||||||
return NS_OK;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* nullptr type
|
* nullptr type
|
||||||
*/
|
*/
|
||||||
@@ -405,11 +487,11 @@ class Variant final : public Variant_base {
|
|||||||
|
|
||||||
NS_IMETHOD GetAsArray(uint16_t* _type, nsIID*, uint32_t* _size,
|
NS_IMETHOD GetAsArray(uint16_t* _type, nsIID*, uint32_t* _size,
|
||||||
void** _data) override {
|
void** _data) override {
|
||||||
return variant_blob_traits<DataType, Adopting>::asArray(mData, _type, _size,
|
return variant_array_traits<DataType, Adopting>::asArray(mData, _type,
|
||||||
_data);
|
_size, _data);
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
protected:
|
||||||
typename variant_storage_traits<DataType, Adopting>::StorageType mData;
|
typename variant_storage_traits<DataType, Adopting>::StorageType mData;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -418,17 +500,21 @@ class Variant final : public Variant_base {
|
|||||||
|
|
||||||
// Currently, BooleanVariant is only useful for kvstore.
|
// Currently, BooleanVariant is only useful for kvstore.
|
||||||
// Bug 1494102 tracks implementing full boolean variant support for mozStorage.
|
// Bug 1494102 tracks implementing full boolean variant support for mozStorage.
|
||||||
typedef Variant<bool> BooleanVariant;
|
using BooleanVariant = Variant<bool>;
|
||||||
|
|
||||||
typedef Variant<int64_t> IntegerVariant;
|
using IntegerVariant = Variant<int64_t>;
|
||||||
typedef Variant<double> FloatVariant;
|
using FloatVariant = Variant<double>;
|
||||||
typedef Variant<nsString> TextVariant;
|
using TextVariant = Variant<nsString>;
|
||||||
typedef Variant<nsCString> UTF8TextVariant;
|
using UTF8TextVariant = Variant<nsCString>;
|
||||||
typedef Variant<uint8_t[], false> BlobVariant;
|
using BlobVariant = Variant<uint8_t[], false>;
|
||||||
typedef Variant<uint8_t[], true> AdoptedBlobVariant;
|
using AdoptedBlobVariant = Variant<uint8_t[], true>;
|
||||||
|
using ArrayOfIntegersVariant = Variant<int64_t[], false>;
|
||||||
|
using AdoptedArrayOfIntegersVariant = Variant<int64_t[], true>;
|
||||||
|
using ArrayOfDoublesVariant = Variant<double[], false>;
|
||||||
|
using AdoptedArrayOfDoublesVariant = Variant<double[], true>;
|
||||||
|
using ArrayOfUTF8StringsVariant = Variant<nsCString[], false>;
|
||||||
|
|
||||||
} // namespace storage
|
} // namespace mozilla::storage
|
||||||
} // namespace mozilla
|
|
||||||
|
|
||||||
#include "Variant_inl.h"
|
#include "Variant_inl.h"
|
||||||
|
|
||||||
|
|||||||
@@ -22,7 +22,8 @@ namespace storage {
|
|||||||
//// Variant_base
|
//// Variant_base
|
||||||
|
|
||||||
inline NS_IMPL_ADDREF(Variant_base) inline NS_IMPL_RELEASE(
|
inline NS_IMPL_ADDREF(Variant_base) inline NS_IMPL_RELEASE(
|
||||||
Variant_base) inline NS_IMPL_QUERY_INTERFACE(Variant_base, nsIVariant)
|
Variant_base) inline NS_IMPL_QUERY_INTERFACE(Variant_base, nsIVariant,
|
||||||
|
nsIInterfaceRequestor)
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
//// nsIVariant
|
//// nsIVariant
|
||||||
|
|||||||
@@ -103,6 +103,7 @@ if not CONFIG["MOZ_AVOID_DISK_REMNANT_ON_CLOSE"]:
|
|||||||
|
|
||||||
LOCAL_INCLUDES += [
|
LOCAL_INCLUDES += [
|
||||||
"/dom/base",
|
"/dom/base",
|
||||||
|
"/third_party/sqlite3/ext",
|
||||||
"/third_party/sqlite3/src",
|
"/third_party/sqlite3/src",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ interface mozIStorageBindingParams : nsISupports {
|
|||||||
* The name of the parameter to bind aValue to.
|
* The name of the parameter to bind aValue to.
|
||||||
* @param aValue
|
* @param aValue
|
||||||
* The value to bind.
|
* The value to bind.
|
||||||
|
* @warning To bind an array use a specific `bindArrayOf` method instead.
|
||||||
*/
|
*/
|
||||||
void bindByName(in AUTF8String aName,
|
void bindByName(in AUTF8String aName,
|
||||||
in nsIVariant aValue);
|
in nsIVariant aValue);
|
||||||
@@ -56,6 +57,17 @@ interface mozIStorageBindingParams : nsISupports {
|
|||||||
in octetPtr aValue,
|
in octetPtr aValue,
|
||||||
in unsigned long aValueSize);
|
in unsigned long aValueSize);
|
||||||
|
|
||||||
|
// These allow to bind arrays through the carray() tabled-valued function,
|
||||||
|
// thus they should only be used when the query contains `carray(?N)`.
|
||||||
|
void bindArrayOfIntegersByName(in AUTF8String aName,
|
||||||
|
in Array<int64_t> aValue);
|
||||||
|
void bindArrayOfDoublesByName(in AUTF8String aName,
|
||||||
|
in Array<double> aValue);
|
||||||
|
void bindArrayOfStringsByName(in AUTF8String aName,
|
||||||
|
in Array<AString> aValue);
|
||||||
|
void bindArrayOfUTF8StringsByName(in AUTF8String aName,
|
||||||
|
in Array<AUTF8String> aValue);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Binds aValue to the parameter with the index aIndex.
|
* Binds aValue to the parameter with the index aIndex.
|
||||||
*
|
*
|
||||||
@@ -63,6 +75,7 @@ interface mozIStorageBindingParams : nsISupports {
|
|||||||
* The zero-based index of the parameter to bind aValue to.
|
* The zero-based index of the parameter to bind aValue to.
|
||||||
* @param aValue
|
* @param aValue
|
||||||
* The value to bind.
|
* The value to bind.
|
||||||
|
* @warning To bind an array use a specific `bindArrayOf` method instead.
|
||||||
*/
|
*/
|
||||||
void bindByIndex(in unsigned long aIndex,
|
void bindByIndex(in unsigned long aIndex,
|
||||||
in nsIVariant aValue);
|
in nsIVariant aValue);
|
||||||
@@ -100,4 +113,15 @@ interface mozIStorageBindingParams : nsISupports {
|
|||||||
void bindAdoptedBlobByIndex(in unsigned long aIndex,
|
void bindAdoptedBlobByIndex(in unsigned long aIndex,
|
||||||
in octetPtr aValue,
|
in octetPtr aValue,
|
||||||
in unsigned long aValueSize);
|
in unsigned long aValueSize);
|
||||||
|
|
||||||
|
// These allow to bind arrays through the carray() tabled-valued function,
|
||||||
|
// thus they should only be used when the query contains `carray(?N)`.
|
||||||
|
void bindArrayOfIntegersByIndex(in unsigned long aIndex,
|
||||||
|
in Array<int64_t> aValue);
|
||||||
|
void bindArrayOfDoublesByIndex(in unsigned long aIndex,
|
||||||
|
in Array<double> aValue);
|
||||||
|
void bindArrayOfStringsByIndex(in unsigned long aIndex,
|
||||||
|
in Array<AString> aValue);
|
||||||
|
void bindArrayOfUTF8StringsByIndex(in unsigned long aIndex,
|
||||||
|
in Array<AUTF8String> aValue);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -13,6 +13,7 @@
|
|||||||
#include "mozStoragePrivateHelpers.h"
|
#include "mozStoragePrivateHelpers.h"
|
||||||
#include "mozStorageBindingParams.h"
|
#include "mozStorageBindingParams.h"
|
||||||
#include "Variant.h"
|
#include "Variant.h"
|
||||||
|
#include "sqlite3_static_ext.h"
|
||||||
|
|
||||||
namespace mozilla::storage {
|
namespace mozilla::storage {
|
||||||
|
|
||||||
@@ -63,6 +64,25 @@ int sqlite3_T_blob(BindingColumnData aData, const void* aBlob, int aSize) {
|
|||||||
return ::sqlite3_bind_blob(aData.stmt, aData.column + 1, aBlob, aSize, free);
|
return ::sqlite3_bind_blob(aData.stmt, aData.column + 1, aBlob, aSize, free);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int sqlite3_T_array(BindingColumnData aData, void* aArray, int aSize,
|
||||||
|
int aType) {
|
||||||
|
// In debug builds ensure that the statement includes at least one `carray()`.
|
||||||
|
MOZ_ASSERT(
|
||||||
|
::strstr(::sqlite3_sql(aData.stmt), "carray("),
|
||||||
|
"Binding arrays to SQL statements requires using the carray() function.");
|
||||||
|
|
||||||
|
if (aType == CARRAY_TEXT) {
|
||||||
|
// We don't manage the string buffers lifecycle, thus let SQLite make its
|
||||||
|
// own copy.
|
||||||
|
int srv = ::sqlite3_carray_bind(aData.stmt, aData.column + 1, aArray, aSize,
|
||||||
|
aType, SQLITE_TRANSIENT);
|
||||||
|
free(aArray);
|
||||||
|
return srv;
|
||||||
|
}
|
||||||
|
return ::sqlite3_carray_bind(aData.stmt, aData.column + 1, aArray, aSize,
|
||||||
|
aType, free);
|
||||||
|
}
|
||||||
|
|
||||||
#include "variantToSQLiteT_impl.h"
|
#include "variantToSQLiteT_impl.h"
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
@@ -290,6 +310,61 @@ BindingParams::BindAdoptedBlobByName(const nsACString& aName, uint8_t* aValue,
|
|||||||
return BindByName(aName, value);
|
return BindByName(aName, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
NS_IMETHODIMP
|
||||||
|
BindingParams::BindArrayOfIntegersByName(const nsACString& aName,
|
||||||
|
const nsTArray<int64_t>& aValue) {
|
||||||
|
NS_ENSURE_ARG_MAX(aValue.Length(), INT_MAX);
|
||||||
|
std::pair<const void*, int> data(static_cast<const void*>(aValue.Elements()),
|
||||||
|
int(aValue.Length()));
|
||||||
|
nsCOMPtr<nsIVariant> value(new ArrayOfIntegersVariant(data));
|
||||||
|
NS_ENSURE_TRUE(value, NS_ERROR_OUT_OF_MEMORY);
|
||||||
|
|
||||||
|
return BindByName(aName, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
NS_IMETHODIMP
|
||||||
|
BindingParams::BindArrayOfDoublesByName(const nsACString& aName,
|
||||||
|
const nsTArray<double>& aValue) {
|
||||||
|
NS_ENSURE_ARG_MAX(aValue.Length(), INT_MAX);
|
||||||
|
std::pair<const void*, int> data(static_cast<const void*>(aValue.Elements()),
|
||||||
|
int(aValue.Length()));
|
||||||
|
nsCOMPtr<nsIVariant> value(new ArrayOfDoublesVariant(data));
|
||||||
|
NS_ENSURE_TRUE(value, NS_ERROR_OUT_OF_MEMORY);
|
||||||
|
|
||||||
|
return BindByName(aName, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
NS_IMETHODIMP
|
||||||
|
BindingParams::BindArrayOfStringsByName(const nsACString& aName,
|
||||||
|
const nsTArray<nsString>& aValue) {
|
||||||
|
NS_ENSURE_ARG_MAX(aValue.Length(), INT_MAX);
|
||||||
|
nsTArray<nsCString> UTF8Strings(aValue.Length());
|
||||||
|
for (const nsString& str : aValue) {
|
||||||
|
UTF8Strings.AppendElement(NS_ConvertUTF16toUTF8(str));
|
||||||
|
}
|
||||||
|
std::pair<const void*, int> data(
|
||||||
|
static_cast<const void*>(UTF8Strings.Elements()),
|
||||||
|
int(UTF8Strings.Length()));
|
||||||
|
// The variant will make a copy of all the buffers.
|
||||||
|
nsCOMPtr<nsIVariant> value(new ArrayOfUTF8StringsVariant(data));
|
||||||
|
NS_ENSURE_TRUE(value, NS_ERROR_OUT_OF_MEMORY);
|
||||||
|
|
||||||
|
return BindByName(aName, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
NS_IMETHODIMP
|
||||||
|
BindingParams::BindArrayOfUTF8StringsByName(const nsACString& aName,
|
||||||
|
const nsTArray<nsCString>& aValue) {
|
||||||
|
NS_ENSURE_ARG_MAX(aValue.Length(), INT_MAX);
|
||||||
|
std::pair<const void*, int> data(static_cast<const void*>(aValue.Elements()),
|
||||||
|
int(aValue.Length()));
|
||||||
|
// The variant will make a copy of all the buffers.
|
||||||
|
nsCOMPtr<nsIVariant> value(new ArrayOfUTF8StringsVariant(data));
|
||||||
|
NS_ENSURE_TRUE(value, NS_ERROR_OUT_OF_MEMORY);
|
||||||
|
|
||||||
|
return BindByName(aName, value);
|
||||||
|
}
|
||||||
|
|
||||||
NS_IMETHODIMP
|
NS_IMETHODIMP
|
||||||
BindingParams::BindByIndex(uint32_t aIndex, nsIVariant* aValue) {
|
BindingParams::BindByIndex(uint32_t aIndex, nsIVariant* aValue) {
|
||||||
NS_ENSURE_FALSE(mLocked, NS_ERROR_UNEXPECTED);
|
NS_ENSURE_FALSE(mLocked, NS_ERROR_UNEXPECTED);
|
||||||
@@ -414,4 +489,59 @@ BindingParams::BindAdoptedBlobByIndex(uint32_t aIndex, uint8_t* aValue,
|
|||||||
return BindByIndex(aIndex, value);
|
return BindByIndex(aIndex, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
NS_IMETHODIMP
|
||||||
|
BindingParams::BindArrayOfIntegersByIndex(uint32_t aIndex,
|
||||||
|
const nsTArray<int64_t>& aValue) {
|
||||||
|
NS_ENSURE_ARG_MAX(aValue.Length(), INT_MAX);
|
||||||
|
std::pair<const void*, int> data(static_cast<const void*>(aValue.Elements()),
|
||||||
|
int(aValue.Length()));
|
||||||
|
nsCOMPtr<nsIVariant> value(new ArrayOfIntegersVariant(data));
|
||||||
|
NS_ENSURE_TRUE(value, NS_ERROR_OUT_OF_MEMORY);
|
||||||
|
|
||||||
|
return BindByIndex(aIndex, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
NS_IMETHODIMP
|
||||||
|
BindingParams::BindArrayOfDoublesByIndex(uint32_t aIndex,
|
||||||
|
const nsTArray<double>& aValue) {
|
||||||
|
NS_ENSURE_ARG_MAX(aValue.Length(), INT_MAX);
|
||||||
|
std::pair<const void*, int> data(static_cast<const void*>(aValue.Elements()),
|
||||||
|
int(aValue.Length()));
|
||||||
|
nsCOMPtr<nsIVariant> value(new ArrayOfDoublesVariant(data));
|
||||||
|
NS_ENSURE_TRUE(value, NS_ERROR_OUT_OF_MEMORY);
|
||||||
|
|
||||||
|
return BindByIndex(aIndex, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
NS_IMETHODIMP
|
||||||
|
BindingParams::BindArrayOfStringsByIndex(uint32_t aIndex,
|
||||||
|
const nsTArray<nsString>& aValue) {
|
||||||
|
NS_ENSURE_ARG_MAX(aValue.Length(), INT_MAX);
|
||||||
|
nsTArray<nsCString> UTF8Strings(aValue.Length());
|
||||||
|
for (const nsString& str : aValue) {
|
||||||
|
UTF8Strings.AppendElement(NS_ConvertUTF16toUTF8(str).get());
|
||||||
|
}
|
||||||
|
std::pair<const void*, int> data(
|
||||||
|
static_cast<const void*>(UTF8Strings.Elements()),
|
||||||
|
int(UTF8Strings.Length()));
|
||||||
|
// The variant will make a copy of all the buffers.
|
||||||
|
nsCOMPtr<nsIVariant> value(new ArrayOfUTF8StringsVariant(data));
|
||||||
|
NS_ENSURE_TRUE(value, NS_ERROR_OUT_OF_MEMORY);
|
||||||
|
|
||||||
|
return BindByIndex(aIndex, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
NS_IMETHODIMP
|
||||||
|
BindingParams::BindArrayOfUTF8StringsByIndex(
|
||||||
|
uint32_t aIndex, const nsTArray<nsCString>& aValue) {
|
||||||
|
NS_ENSURE_ARG_MAX(aValue.Length(), INT_MAX);
|
||||||
|
std::pair<const void*, int> data(static_cast<const void*>(aValue.Elements()),
|
||||||
|
int(aValue.Length()));
|
||||||
|
// The variant will make a copy of all the buffers.
|
||||||
|
nsCOMPtr<nsIVariant> value(new ArrayOfUTF8StringsVariant(data));
|
||||||
|
NS_ENSURE_TRUE(value, NS_ERROR_OUT_OF_MEMORY);
|
||||||
|
|
||||||
|
return BindByIndex(aIndex, value);
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace mozilla::storage
|
} // namespace mozilla::storage
|
||||||
|
|||||||
@@ -42,6 +42,7 @@
|
|||||||
#include "SQLCollations.h"
|
#include "SQLCollations.h"
|
||||||
#include "FileSystemModule.h"
|
#include "FileSystemModule.h"
|
||||||
#include "mozStorageHelper.h"
|
#include "mozStorageHelper.h"
|
||||||
|
#include "sqlite3_static_ext.h"
|
||||||
|
|
||||||
#include "mozilla/Assertions.h"
|
#include "mozilla/Assertions.h"
|
||||||
#include "mozilla/Logging.h"
|
#include "mozilla/Logging.h"
|
||||||
@@ -169,6 +170,12 @@ int sqlite3_T_blob(sqlite3_context* aCtx, const void* aData, int aSize) {
|
|||||||
return SQLITE_OK;
|
return SQLITE_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int sqlite3_T_array(sqlite3_context* aCtx, const void* aData, int aSize,
|
||||||
|
int aType) {
|
||||||
|
// Not supported for now.
|
||||||
|
return SQLITE_MISUSE;
|
||||||
|
}
|
||||||
|
|
||||||
#include "variantToSQLiteT_impl.h"
|
#include "variantToSQLiteT_impl.h"
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|||||||
@@ -13,9 +13,9 @@
|
|||||||
#include "nsError.h"
|
#include "nsError.h"
|
||||||
#include "mozilla/Mutex.h"
|
#include "mozilla/Mutex.h"
|
||||||
#include "mozilla/CondVar.h"
|
#include "mozilla/CondVar.h"
|
||||||
#include "nsQueryObject.h"
|
|
||||||
#include "nsThreadUtils.h"
|
#include "nsThreadUtils.h"
|
||||||
#include "nsJSUtils.h"
|
#include "nsJSUtils.h"
|
||||||
|
#include "nsIInterfaceRequestorUtils.h"
|
||||||
|
|
||||||
#include "Variant.h"
|
#include "Variant.h"
|
||||||
#include "mozStoragePrivateHelpers.h"
|
#include "mozStoragePrivateHelpers.h"
|
||||||
@@ -95,8 +95,9 @@ void checkAndLogStatementPerformance(sqlite3_stmt* aStatement) {
|
|||||||
|
|
||||||
// CREATE INDEX always sorts (sorting is a necessary step in creating
|
// CREATE INDEX always sorts (sorting is a necessary step in creating
|
||||||
// an index). So ignore the warning there.
|
// an index). So ignore the warning there.
|
||||||
if (::strstr(sql, "CREATE INDEX") || ::strstr(sql, "CREATE UNIQUE INDEX"))
|
if (::strstr(sql, "CREATE INDEX") || ::strstr(sql, "CREATE UNIQUE INDEX")) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
nsAutoCString message("Suboptimal indexes for the SQL statement ");
|
nsAutoCString message("Suboptimal indexes for the SQL statement ");
|
||||||
#ifdef MOZ_STORAGE_SORTWARNING_SQL_DUMP
|
#ifdef MOZ_STORAGE_SORTWARNING_SQL_DUMP
|
||||||
@@ -147,11 +148,14 @@ nsIVariant* convertJSValToVariant(JSContext* aCtx, const JS::Value& aValue) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Variant_base* convertVariantToStorageVariant(nsIVariant* aVariant) {
|
Variant_base* convertVariantToStorageVariant(nsIVariant* aVariant) {
|
||||||
RefPtr<Variant_base> variant = do_QueryObject(aVariant);
|
nsCOMPtr<nsIInterfaceRequestor> variant = do_QueryInterface(aVariant);
|
||||||
if (variant) {
|
if (variant) {
|
||||||
// JS helpers already convert the JS representation to a Storage Variant,
|
// JS helpers already convert the JS representation to a Storage Variant,
|
||||||
// in such a case there's nothing left to do here, so just pass-through.
|
// in such a case there's nothing left to do here, so just pass-through.
|
||||||
return variant;
|
RefPtr<Variant_base> variantObj = do_GetInterface(variant);
|
||||||
|
if (variantObj) {
|
||||||
|
return variantObj;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!aVariant) return new NullVariant();
|
if (!aVariant) return new NullVariant();
|
||||||
@@ -199,6 +203,10 @@ Variant_base* convertVariantToStorageVariant(nsIVariant* aVariant) {
|
|||||||
NS_ENSURE_SUCCESS(rv, nullptr);
|
NS_ENSURE_SUCCESS(rv, nullptr);
|
||||||
return new TextVariant(v);
|
return new TextVariant(v);
|
||||||
}
|
}
|
||||||
|
case nsIDataType::VTYPE_EMPTY:
|
||||||
|
case nsIDataType::VTYPE_EMPTY_ARRAY:
|
||||||
|
case nsIDataType::VTYPE_VOID:
|
||||||
|
return new NullVariant();
|
||||||
case nsIDataType::VTYPE_ARRAY: {
|
case nsIDataType::VTYPE_ARRAY: {
|
||||||
uint16_t type;
|
uint16_t type;
|
||||||
nsIID iid;
|
nsIID iid;
|
||||||
@@ -212,17 +220,19 @@ Variant_base* convertVariantToStorageVariant(nsIVariant* aVariant) {
|
|||||||
// Take ownership of the data avoiding a further copy.
|
// Take ownership of the data avoiding a further copy.
|
||||||
return new AdoptedBlobVariant(v);
|
return new AdoptedBlobVariant(v);
|
||||||
}
|
}
|
||||||
|
// We don't convert other kind of arrays because it makes the API more
|
||||||
|
// error prone, especially on the javascript side where it may not be
|
||||||
|
// so uncommon to mistakenly pass an array instead of a primitive.
|
||||||
|
// Consumers should instead use the dedicated `BindArrayOf` methods.
|
||||||
[[fallthrough]];
|
[[fallthrough]];
|
||||||
}
|
}
|
||||||
case nsIDataType::VTYPE_EMPTY:
|
|
||||||
case nsIDataType::VTYPE_EMPTY_ARRAY:
|
|
||||||
case nsIDataType::VTYPE_VOID:
|
|
||||||
return new NullVariant();
|
|
||||||
case nsIDataType::VTYPE_ID:
|
case nsIDataType::VTYPE_ID:
|
||||||
case nsIDataType::VTYPE_INTERFACE:
|
case nsIDataType::VTYPE_INTERFACE:
|
||||||
case nsIDataType::VTYPE_INTERFACE_IS:
|
case nsIDataType::VTYPE_INTERFACE_IS:
|
||||||
default:
|
default:
|
||||||
NS_WARNING("Unsupported variant type");
|
NS_WARNING(
|
||||||
|
nsPrintfCString("Unsupported variant type: %d", dataType).get());
|
||||||
|
MOZ_ASSERT_UNREACHABLE("Tried to bind an unsupported Variant type");
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ UNIFIED_SOURCES += [
|
|||||||
"test_async_callbacks_with_spun_event_loops.cpp",
|
"test_async_callbacks_with_spun_event_loops.cpp",
|
||||||
"test_async_thread_naming.cpp",
|
"test_async_thread_naming.cpp",
|
||||||
"test_asyncStatementExecution_transaction.cpp",
|
"test_asyncStatementExecution_transaction.cpp",
|
||||||
|
"test_binding_arrays.cpp",
|
||||||
"test_binding_params.cpp",
|
"test_binding_params.cpp",
|
||||||
"test_file_perms.cpp",
|
"test_file_perms.cpp",
|
||||||
"test_interruptSynchronousConnection.cpp",
|
"test_interruptSynchronousConnection.cpp",
|
||||||
|
|||||||
@@ -18,6 +18,7 @@
|
|||||||
#include "nsIThread.h"
|
#include "nsIThread.h"
|
||||||
#include "nsThreadUtils.h"
|
#include "nsThreadUtils.h"
|
||||||
#include "mozilla/ReentrantMonitor.h"
|
#include "mozilla/ReentrantMonitor.h"
|
||||||
|
#include "mozilla/AutoSQLiteLifetime.h"
|
||||||
|
|
||||||
#include "mozIStorageService.h"
|
#include "mozIStorageService.h"
|
||||||
#include "mozIStorageConnection.h"
|
#include "mozIStorageConnection.h"
|
||||||
@@ -159,7 +160,9 @@ class HookSqliteMutex {
|
|||||||
~HookSqliteMutex() {
|
~HookSqliteMutex() {
|
||||||
do_check_ok(sqlite3_shutdown());
|
do_check_ok(sqlite3_shutdown());
|
||||||
do_check_ok(::sqlite3_config(SQLITE_CONFIG_MUTEX, &orig_mutex_methods));
|
do_check_ok(::sqlite3_config(SQLITE_CONFIG_MUTEX, &orig_mutex_methods));
|
||||||
do_check_ok(sqlite3_initialize());
|
mozilla::AutoSQLiteLifetime::Init();
|
||||||
|
int rc = mozilla::AutoSQLiteLifetime::getInitResult();
|
||||||
|
MOZ_RELEASE_ASSERT(rc == SQLITE_OK);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
174
storage/test/gtest/test_binding_arrays.cpp
Normal file
174
storage/test/gtest/test_binding_arrays.cpp
Normal file
@@ -0,0 +1,174 @@
|
|||||||
|
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
||||||
|
* vim: sw=2 ts=2 et lcs=trail\:.,tab\:>~ :
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
#include "storage_test_harness.h"
|
||||||
|
|
||||||
|
#include "mozStorageHelper.h"
|
||||||
|
|
||||||
|
using namespace mozilla;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This file tests binding and reading out array parameters through the
|
||||||
|
* mozIStorageStatement API.
|
||||||
|
*/
|
||||||
|
|
||||||
|
TEST(storage_binding_arrays, Integers)
|
||||||
|
{
|
||||||
|
nsCOMPtr<mozIStorageConnection> db(getMemoryDatabase());
|
||||||
|
|
||||||
|
(void)db->ExecuteSimpleSQL("CREATE TABLE test (val BLOB)"_ns);
|
||||||
|
|
||||||
|
nsCOMPtr<mozIStorageStatement> insert, select;
|
||||||
|
(void)db->CreateStatement(
|
||||||
|
"INSERT INTO test (val) SELECT value FROM carray(?1)"_ns,
|
||||||
|
getter_AddRefs(insert));
|
||||||
|
(void)db->CreateStatement("SELECT val FROM test WHERE val IN carray(?1)"_ns,
|
||||||
|
getter_AddRefs(select));
|
||||||
|
|
||||||
|
nsTArray<int64_t> inserted = {1, 2};
|
||||||
|
{
|
||||||
|
mozStorageStatementScoper scoper(insert);
|
||||||
|
bool hasResult;
|
||||||
|
do_check_true(
|
||||||
|
NS_SUCCEEDED(insert->BindArrayOfIntegersByIndex(0, inserted)));
|
||||||
|
do_check_true(NS_SUCCEEDED(insert->ExecuteStep(&hasResult)));
|
||||||
|
do_check_false(hasResult);
|
||||||
|
}
|
||||||
|
|
||||||
|
nsAutoCString result;
|
||||||
|
{
|
||||||
|
mozStorageStatementScoper scoper(select);
|
||||||
|
do_check_true(
|
||||||
|
NS_SUCCEEDED(select->BindArrayOfIntegersByIndex(0, inserted)));
|
||||||
|
bool hasResult;
|
||||||
|
for (auto expected : inserted) {
|
||||||
|
do_check_true(NS_SUCCEEDED(select->ExecuteStep(&hasResult)));
|
||||||
|
do_check_true(hasResult);
|
||||||
|
int64_t result;
|
||||||
|
do_check_true(NS_SUCCEEDED(select->GetInt64(0, &result)));
|
||||||
|
do_check_true(result == expected);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(storage_binding_arrays, Doubles)
|
||||||
|
{
|
||||||
|
nsCOMPtr<mozIStorageConnection> db(getMemoryDatabase());
|
||||||
|
|
||||||
|
(void)db->ExecuteSimpleSQL("CREATE TABLE test (val BLOB)"_ns);
|
||||||
|
|
||||||
|
nsCOMPtr<mozIStorageStatement> insert, select;
|
||||||
|
(void)db->CreateStatement(
|
||||||
|
"INSERT INTO test (val) SELECT value FROM carray(?1)"_ns,
|
||||||
|
getter_AddRefs(insert));
|
||||||
|
(void)db->CreateStatement("SELECT val FROM test WHERE val IN carray(?1)"_ns,
|
||||||
|
getter_AddRefs(select));
|
||||||
|
|
||||||
|
nsTArray<double> inserted = {1.1, 2.2};
|
||||||
|
{
|
||||||
|
mozStorageStatementScoper scoper(insert);
|
||||||
|
bool hasResult;
|
||||||
|
do_check_true(NS_SUCCEEDED(insert->BindArrayOfDoublesByIndex(0, inserted)));
|
||||||
|
do_check_true(NS_SUCCEEDED(insert->ExecuteStep(&hasResult)));
|
||||||
|
do_check_false(hasResult);
|
||||||
|
}
|
||||||
|
|
||||||
|
nsAutoCString result;
|
||||||
|
{
|
||||||
|
mozStorageStatementScoper scoper(select);
|
||||||
|
do_check_true(NS_SUCCEEDED(select->BindArrayOfDoublesByIndex(0, inserted)));
|
||||||
|
bool hasResult;
|
||||||
|
for (auto expected : inserted) {
|
||||||
|
do_check_true(NS_SUCCEEDED(select->ExecuteStep(&hasResult)));
|
||||||
|
do_check_true(hasResult);
|
||||||
|
double result;
|
||||||
|
do_check_true(NS_SUCCEEDED(select->GetDouble(0, &result)));
|
||||||
|
do_check_true(result == expected);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(storage_binding_arrays, UTF8Strings)
|
||||||
|
{
|
||||||
|
nsCOMPtr<mozIStorageConnection> db(getMemoryDatabase());
|
||||||
|
|
||||||
|
(void)db->ExecuteSimpleSQL("CREATE TABLE test (val BLOB)"_ns);
|
||||||
|
|
||||||
|
nsCOMPtr<mozIStorageStatement> insert, select;
|
||||||
|
(void)db->CreateStatement(
|
||||||
|
"INSERT INTO test (val) SELECT value FROM carray(?1)"_ns,
|
||||||
|
getter_AddRefs(insert));
|
||||||
|
(void)db->CreateStatement("SELECT val FROM test WHERE val IN carray(?1)"_ns,
|
||||||
|
getter_AddRefs(select));
|
||||||
|
|
||||||
|
nsTArray<nsCString> inserted = {"test1"_ns, "test2"_ns};
|
||||||
|
{
|
||||||
|
mozStorageStatementScoper scoper(insert);
|
||||||
|
bool hasResult;
|
||||||
|
do_check_true(
|
||||||
|
NS_SUCCEEDED(insert->BindArrayOfUTF8StringsByIndex(0, inserted)));
|
||||||
|
do_check_true(NS_SUCCEEDED(insert->ExecuteStep(&hasResult)));
|
||||||
|
do_check_false(hasResult);
|
||||||
|
}
|
||||||
|
|
||||||
|
nsAutoCString result;
|
||||||
|
{
|
||||||
|
mozStorageStatementScoper scoper(select);
|
||||||
|
bool hasResult;
|
||||||
|
do_check_true(
|
||||||
|
NS_SUCCEEDED(select->BindArrayOfUTF8StringsByIndex(0, inserted)));
|
||||||
|
for (const auto& expected : inserted) {
|
||||||
|
do_check_true(NS_SUCCEEDED(select->ExecuteStep(&hasResult)));
|
||||||
|
do_check_true(hasResult);
|
||||||
|
nsCString result;
|
||||||
|
do_check_true(NS_SUCCEEDED(select->GetUTF8String(0, result)));
|
||||||
|
do_check_true(result.Equals(expected));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(storage_binding_arrays, AsyncStatement_BindingParamsArray)
|
||||||
|
{
|
||||||
|
nsCOMPtr<mozIStorageConnection> db(getMemoryDatabase());
|
||||||
|
|
||||||
|
(void)db->ExecuteSimpleSQL("CREATE TABLE test (val BLOB)"_ns);
|
||||||
|
|
||||||
|
nsCOMPtr<mozIStorageAsyncStatement> insert;
|
||||||
|
(void)db->CreateAsyncStatement(
|
||||||
|
"INSERT INTO test (val) SELECT value FROM carray(:values)"_ns,
|
||||||
|
getter_AddRefs(insert));
|
||||||
|
nsTArray<int64_t> insertedIntegers = {1, 2};
|
||||||
|
nsTArray<nsCString> insertedStrings = {"test1"_ns, "test2"_ns};
|
||||||
|
nsCOMPtr<mozIStorageBindingParamsArray> paramsArray;
|
||||||
|
insert->NewBindingParamsArray(getter_AddRefs(paramsArray));
|
||||||
|
nsCOMPtr<mozIStorageBindingParams> intParams;
|
||||||
|
paramsArray->NewBindingParams(getter_AddRefs(intParams));
|
||||||
|
do_check_true(NS_SUCCEEDED(
|
||||||
|
intParams->BindArrayOfIntegersByName("values"_ns, insertedIntegers)));
|
||||||
|
do_check_true(NS_SUCCEEDED(paramsArray->AddParams(intParams)));
|
||||||
|
intParams = nullptr;
|
||||||
|
nsCOMPtr<mozIStorageBindingParams> strParams;
|
||||||
|
paramsArray->NewBindingParams(getter_AddRefs(strParams));
|
||||||
|
do_check_true(NS_SUCCEEDED(
|
||||||
|
strParams->BindArrayOfUTF8StringsByName("values"_ns, insertedStrings)));
|
||||||
|
do_check_true(NS_SUCCEEDED(paramsArray->AddParams(strParams)));
|
||||||
|
strParams = nullptr;
|
||||||
|
do_check_true(NS_SUCCEEDED(insert->BindParameters(paramsArray)));
|
||||||
|
paramsArray = nullptr;
|
||||||
|
blocking_async_execute(insert);
|
||||||
|
insert->Finalize();
|
||||||
|
|
||||||
|
nsCOMPtr<mozIStorageStatement> select;
|
||||||
|
(void)db->CreateStatement("SELECT count(*) FROM test"_ns,
|
||||||
|
getter_AddRefs(select));
|
||||||
|
bool hasResult;
|
||||||
|
do_check_true(NS_SUCCEEDED(select->ExecuteStep(&hasResult)));
|
||||||
|
do_check_true(hasResult);
|
||||||
|
do_check_true(select->AsInt64(0) == 4);
|
||||||
|
select->Finalize();
|
||||||
|
|
||||||
|
blocking_async_close(db);
|
||||||
|
}
|
||||||
128
storage/test/unit/test_bindArray.js
Normal file
128
storage/test/unit/test_bindArray.js
Normal file
@@ -0,0 +1,128 @@
|
|||||||
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file tests binding arrays to statements.
|
||||||
|
*/
|
||||||
|
|
||||||
|
add_task(async function test_errors() {
|
||||||
|
let db = Services.storage.openSpecialDatabase("memory");
|
||||||
|
let stmt = db.createStatement("SELECT * FROM carray(?1)");
|
||||||
|
|
||||||
|
Assert.throws(
|
||||||
|
() => stmt.bindArrayOfIntegersByIndex(0, 1),
|
||||||
|
/NS_ERROR_XPC_CANT_CONVERT_PRIMITIVE_TO_ARRAY/,
|
||||||
|
"Not an array"
|
||||||
|
);
|
||||||
|
Assert.throws(
|
||||||
|
() => stmt.bindArrayOfIntegersByIndex(0, "string"),
|
||||||
|
/NS_ERROR_XPC_CANT_CONVERT_PRIMITIVE_TO_ARRAY/,
|
||||||
|
"Not an array"
|
||||||
|
);
|
||||||
|
Assert.throws(
|
||||||
|
() => stmt.bindArrayOfIntegersByIndex(0, null),
|
||||||
|
/NS_ERROR_XPC_CANT_CONVERT_PRIMITIVE_TO_ARRAY/,
|
||||||
|
"Not an array"
|
||||||
|
);
|
||||||
|
Assert.throws(
|
||||||
|
() => stmt.bindArrayOfUTF8StringsByIndex(0, null),
|
||||||
|
/NS_ERROR_XPC_CANT_CONVERT_PRIMITIVE_TO_ARRAY/,
|
||||||
|
"Not an array"
|
||||||
|
);
|
||||||
|
|
||||||
|
stmt.finalize();
|
||||||
|
db.close();
|
||||||
|
});
|
||||||
|
|
||||||
|
add_task(async function test_bind_empty_array() {
|
||||||
|
let db = Services.storage.openSpecialDatabase("memory");
|
||||||
|
let stmt = db.createStatement("SELECT * FROM carray(?1)");
|
||||||
|
stmt.bindArrayOfIntegersByIndex(0, []);
|
||||||
|
Assert.ok(!stmt.executeStep(), "Execution succeeds with no results");
|
||||||
|
stmt.finalize();
|
||||||
|
db.close();
|
||||||
|
});
|
||||||
|
|
||||||
|
add_task(async function test_bind() {
|
||||||
|
let db = getOpenedDatabase();
|
||||||
|
db.executeSimpleSQL(`
|
||||||
|
CREATE TABLE test (
|
||||||
|
id INTEGER,
|
||||||
|
value BLOB /* no affinity */
|
||||||
|
)
|
||||||
|
`);
|
||||||
|
|
||||||
|
db.executeSimpleSQL(`
|
||||||
|
INSERT INTO test (value)
|
||||||
|
VALUES
|
||||||
|
(1),
|
||||||
|
(2),
|
||||||
|
(1.1),
|
||||||
|
(2.2),
|
||||||
|
("test1"),
|
||||||
|
("test2")
|
||||||
|
`);
|
||||||
|
|
||||||
|
function bindStatement(stmt, results) {
|
||||||
|
if (Number.isInteger(results[0])) {
|
||||||
|
stmt.bindArrayOfIntegersByIndex(0, results);
|
||||||
|
stmt.bindArrayOfIntegersByName("values", results);
|
||||||
|
} else if (typeof results[0] == "number") {
|
||||||
|
stmt.bindArrayOfDoublesByIndex(0, results);
|
||||||
|
stmt.bindArrayOfDoublesByName("values", results);
|
||||||
|
} else if (typeof results[0] == "string") {
|
||||||
|
stmt.bindArrayOfStringsByIndex(0, results);
|
||||||
|
stmt.bindArrayOfStringsByName("values", results);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let results of [[1, 2], [1.1, 2.2], ["test1", "test2"], []]) {
|
||||||
|
info("sync statement");
|
||||||
|
let query = `
|
||||||
|
SELECT value FROM test
|
||||||
|
WHERE value IN carray(?1)
|
||||||
|
AND value IN carray(:values)
|
||||||
|
`;
|
||||||
|
let stmt = db.createStatement(query);
|
||||||
|
bindStatement(stmt, results);
|
||||||
|
for (let result of results) {
|
||||||
|
Assert.ok(stmt.executeStep());
|
||||||
|
Assert.equal(stmt.row.value, result);
|
||||||
|
}
|
||||||
|
stmt.finalize();
|
||||||
|
|
||||||
|
info("async statement");
|
||||||
|
stmt = db.createAsyncStatement(query);
|
||||||
|
bindStatement(stmt, results);
|
||||||
|
let rv = await new Promise((resolve, reject) => {
|
||||||
|
let rows = [];
|
||||||
|
stmt.executeAsync({
|
||||||
|
handleResult(resultSet) {
|
||||||
|
let row = null;
|
||||||
|
do {
|
||||||
|
row = resultSet.getNextRow();
|
||||||
|
if (row) {
|
||||||
|
rows.push(row);
|
||||||
|
}
|
||||||
|
} while (row);
|
||||||
|
},
|
||||||
|
handleError(error) {
|
||||||
|
reject(new Error(`Failed to execute statement: ${error.message}`));
|
||||||
|
},
|
||||||
|
handleCompletion(reason) {
|
||||||
|
if (reason == Ci.mozIStorageStatementCallback.REASON_FINISHED) {
|
||||||
|
resolve(rows.map(r => r.getResultByIndex(0)));
|
||||||
|
} else {
|
||||||
|
reject(new Error("Statement failed to execute or was cancelled"));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
Assert.deepEqual(rv, results);
|
||||||
|
stmt.finalize();
|
||||||
|
}
|
||||||
|
// we are the last test using this connection and since it has gone async
|
||||||
|
// we *must* call asyncClose on it.
|
||||||
|
await asyncClose(db);
|
||||||
|
});
|
||||||
@@ -782,6 +782,12 @@ function test_bind_no_such_name_async_deferred() {
|
|||||||
test_bind_no_such_name_async_deferred.asyncOnly = true;
|
test_bind_no_such_name_async_deferred.asyncOnly = true;
|
||||||
|
|
||||||
function test_bind_bogus_type_by_index() {
|
function test_bind_bogus_type_by_index() {
|
||||||
|
if (AppConstants.DEBUG) {
|
||||||
|
// Skip this test as in debug builds this is a fatal assert.
|
||||||
|
run_next_test();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// We try to bind a JS Object here that should fail to bind.
|
// We try to bind a JS Object here that should fail to bind.
|
||||||
let stmt = makeTestStatement("INSERT INTO test (blober) VALUES (?)");
|
let stmt = makeTestStatement("INSERT INTO test (blober) VALUES (?)");
|
||||||
|
|
||||||
@@ -794,6 +800,12 @@ function test_bind_bogus_type_by_index() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function test_bind_bogus_type_by_name() {
|
function test_bind_bogus_type_by_name() {
|
||||||
|
if (AppConstants.DEBUG) {
|
||||||
|
// Skip this test as in debug builds this is a fatal assert.
|
||||||
|
run_next_test();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// We try to bind a JS Object here that should fail to bind.
|
// We try to bind a JS Object here that should fail to bind.
|
||||||
let stmt = makeTestStatement("INSERT INTO test (blober) VALUES (:blob)");
|
let stmt = makeTestStatement("INSERT INTO test (blober) VALUES (:blob)");
|
||||||
|
|
||||||
|
|||||||
@@ -9,6 +9,8 @@ support-files = [
|
|||||||
"VacuumParticipant.sys.mjs",
|
"VacuumParticipant.sys.mjs",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
["test_bindArray.js"]
|
||||||
|
|
||||||
["test_bug-365166.js"]
|
["test_bug-365166.js"]
|
||||||
|
|
||||||
["test_bug-393952.js"]
|
["test_bug-393952.js"]
|
||||||
|
|||||||
@@ -87,20 +87,31 @@ int variantToSQLiteT(T aObj, nsIVariant* aValue) {
|
|||||||
void* data;
|
void* data;
|
||||||
nsresult rv = aValue->GetAsArray(&arrayType, &iid, &count, &data);
|
nsresult rv = aValue->GetAsArray(&arrayType, &iid, &count, &data);
|
||||||
NS_ENSURE_SUCCESS(rv, SQLITE_MISMATCH);
|
NS_ENSURE_SUCCESS(rv, SQLITE_MISMATCH);
|
||||||
|
if (arrayType == nsIDataType::VTYPE_UINT8) {
|
||||||
// Check to make sure it's a supported type.
|
// The function should free the array accordingly!
|
||||||
NS_ASSERTION(arrayType == nsIDataType::VTYPE_UINT8,
|
return sqlite3_T_blob(aObj, data, count);
|
||||||
"Invalid type passed! You may leak!");
|
}
|
||||||
if (arrayType != nsIDataType::VTYPE_UINT8) {
|
// Empty array is handled as NULL.
|
||||||
// Technically this could leak with certain data types, but somebody was
|
if (count == 0) {
|
||||||
// being stupid passing us this anyway.
|
return sqlite3_T_null(aObj);
|
||||||
free(data);
|
}
|
||||||
return SQLITE_MISMATCH;
|
if (arrayType == nsIDataType::VTYPE_INT32 ||
|
||||||
|
arrayType == nsIDataType::VTYPE_INT64) {
|
||||||
|
return sqlite3_T_array(aObj, data, count, CARRAY_INT64);
|
||||||
|
}
|
||||||
|
if (arrayType == nsIDataType::VTYPE_FLOAT ||
|
||||||
|
arrayType == nsIDataType::VTYPE_DOUBLE) {
|
||||||
|
return sqlite3_T_array(aObj, data, count, CARRAY_DOUBLE);
|
||||||
|
}
|
||||||
|
if (arrayType == nsIDataType::VTYPE_UTF8STRING) {
|
||||||
|
return sqlite3_T_array(aObj, data, count, CARRAY_TEXT);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Finally do our thing. The function should free the array accordingly!
|
MOZ_DIAGNOSTIC_ASSERT(false, "Unsupported type in Storage bound array");
|
||||||
int rc = sqlite3_T_blob(aObj, data, count);
|
// Technically this could leak with certain data types, but somebody was
|
||||||
return rc;
|
// being incautious passing us this anyway.
|
||||||
|
free(data);
|
||||||
|
return SQLITE_MISMATCH;
|
||||||
}
|
}
|
||||||
// Maybe, it'll be possible to convert these
|
// Maybe, it'll be possible to convert these
|
||||||
// in future too.
|
// in future too.
|
||||||
|
|||||||
@@ -1528,13 +1528,16 @@ PT.Remove.prototype = {
|
|||||||
await removeThem();
|
await removeThem();
|
||||||
|
|
||||||
this.undo = async function () {
|
this.undo = async function () {
|
||||||
|
let createdItems = [];
|
||||||
for (let info of removedItems) {
|
for (let info of removedItems) {
|
||||||
try {
|
try {
|
||||||
await createItemsFromBookmarksTree(info, true);
|
await createItemsFromBookmarksTree(info, true);
|
||||||
|
createdItems.push(info);
|
||||||
} catch (ex) {
|
} catch (ex) {
|
||||||
console.error(`Unable to undo removal of ${info.guid}`);
|
console.error(`Unable to undo removal of ${info.guid}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
removedItems = createdItems;
|
||||||
};
|
};
|
||||||
this.redo = removeThem;
|
this.redo = removeThem;
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -2107,9 +2107,8 @@ add_task(async function test_remove_invalid_url() {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
let guids = [folderGuid, guid];
|
await PT.Remove([folderGuid, guid]).transact();
|
||||||
await PT.Remove(guids).transact();
|
await ensureNonExistent(folderGuid, guid, folderedGuid);
|
||||||
await ensureNonExistent(...guids, folderedGuid);
|
|
||||||
// Shouldn't throw, should restore the folder but not the bookmarks.
|
// Shouldn't throw, should restore the folder but not the bookmarks.
|
||||||
await PT.undo();
|
await PT.undo();
|
||||||
await ensureNonExistent(guid, folderedGuid);
|
await ensureNonExistent(guid, folderedGuid);
|
||||||
@@ -2118,7 +2117,7 @@ add_task(async function test_remove_invalid_url() {
|
|||||||
"The folder should have been re-created"
|
"The folder should have been re-created"
|
||||||
);
|
);
|
||||||
await PT.redo();
|
await PT.redo();
|
||||||
await ensureNonExistent(guids, folderedGuid);
|
await ensureNonExistent(folderGuid, guid, folderedGuid);
|
||||||
// Cleanup
|
// Cleanup
|
||||||
await PT.clearTransactionsHistory();
|
await PT.clearTransactionsHistory();
|
||||||
observer.reset();
|
observer.reset();
|
||||||
|
|||||||
@@ -581,9 +581,7 @@ add_task(async function stop_search() {
|
|||||||
let histogram = TelemetryTestUtils.getAndClearKeyedHistogram(
|
let histogram = TelemetryTestUtils.getAndClearKeyedHistogram(
|
||||||
SEARCH_TELEMETRY_LATENCY
|
SEARCH_TELEMETRY_LATENCY
|
||||||
);
|
);
|
||||||
let controller = new SearchSuggestionController(() => {
|
let controller = new SearchSuggestionController();
|
||||||
do_throw("The callback shouldn't be called after stop()");
|
|
||||||
});
|
|
||||||
let resultPromise = controller.fetch("mo", false, getEngine);
|
let resultPromise = controller.fetch("mo", false, getEngine);
|
||||||
controller.stop();
|
controller.stop();
|
||||||
await resultPromise.then(result => {
|
await resultPromise.then(result => {
|
||||||
|
|||||||
@@ -126,6 +126,11 @@ AutoSQLiteLifetime::AutoSQLiteLifetime() {
|
|||||||
MOZ_CRASH("multiple instances of AutoSQLiteLifetime constructed!");
|
MOZ_CRASH("multiple instances of AutoSQLiteLifetime constructed!");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Init();
|
||||||
|
}
|
||||||
|
|
||||||
|
// static
|
||||||
|
void AutoSQLiteLifetime::Init() {
|
||||||
#ifdef MOZ_MEMORY
|
#ifdef MOZ_MEMORY
|
||||||
sResult = ::sqlite3_config(SQLITE_CONFIG_MALLOC, &memMethods);
|
sResult = ::sqlite3_config(SQLITE_CONFIG_MALLOC, &memMethods);
|
||||||
#else
|
#else
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ class AutoSQLiteLifetime final {
|
|||||||
public:
|
public:
|
||||||
AutoSQLiteLifetime();
|
AutoSQLiteLifetime();
|
||||||
~AutoSQLiteLifetime();
|
~AutoSQLiteLifetime();
|
||||||
|
static void Init();
|
||||||
static int getInitResult() { return AutoSQLiteLifetime::sResult; }
|
static int getInitResult() { return AutoSQLiteLifetime::sResult; }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user