mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-23 04:41:11 +00:00
1b86f6b5be
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
314 lines
15 KiB
C++
314 lines
15 KiB
C++
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
|
* vim: sw=2 ts=2 sts=2 expandtab
|
|
* 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/. */
|
|
|
|
#ifndef mozilla_storage_StorageBaseStatementInternal_h_
|
|
#define mozilla_storage_StorageBaseStatementInternal_h_
|
|
|
|
#include "nsISupports.h"
|
|
#include "nsCOMPtr.h"
|
|
#include "mozStorageHelper.h"
|
|
|
|
struct sqlite3;
|
|
struct sqlite3_stmt;
|
|
class mozIStorageBindingParamsArray;
|
|
class mozIStorageBindingParams;
|
|
class mozIStorageStatementCallback;
|
|
class mozIStoragePendingStatement;
|
|
|
|
namespace mozilla {
|
|
namespace storage {
|
|
|
|
#define STORAGEBASESTATEMENTINTERNAL_IID \
|
|
{ \
|
|
0xd18856c9, 0xbf07, 0x4ae2, { \
|
|
0x94, 0x5b, 0x1a, 0xdd, 0x49, 0x19, 0x55, 0x2a \
|
|
} \
|
|
}
|
|
|
|
class Connection;
|
|
class StatementData;
|
|
|
|
class AsyncStatementFinalizer;
|
|
|
|
/**
|
|
* Implementation-only interface and shared logix mix-in corresponding to
|
|
* mozIStorageBaseStatement. Both Statement and AsyncStatement inherit from
|
|
* this. The interface aspect makes them look the same to implementation innards
|
|
* that aren't publicly accessible. The mix-in avoids code duplication in
|
|
* common implementations of mozIStorageBaseStatement, albeit with some minor
|
|
* performance/space overhead because we have to use defines to officially
|
|
* implement the methods on Statement/AsyncStatement (and proxy to this base
|
|
* class.)
|
|
*/
|
|
class StorageBaseStatementInternal : public nsISupports {
|
|
public:
|
|
NS_DECLARE_STATIC_IID_ACCESSOR(STORAGEBASESTATEMENTINTERNAL_IID)
|
|
|
|
/**
|
|
* @return the connection that this statement belongs to.
|
|
*/
|
|
Connection* getOwner() { return mDBConnection; }
|
|
|
|
/**
|
|
* Return the asynchronous statement, creating it if required.
|
|
*
|
|
* This is for use by the asynchronous execution code for StatementData
|
|
* created by AsyncStatements. Statement internally uses this method to
|
|
* prepopulate StatementData with the sqlite3_stmt.
|
|
*
|
|
* @param[out] stmt
|
|
* The sqlite3_stmt for asynchronous use.
|
|
* @return The SQLite result code for creating the statement if created,
|
|
* SQLITE_OK if creation was not required.
|
|
*/
|
|
virtual int getAsyncStatement(sqlite3_stmt** _stmt) = 0;
|
|
|
|
/**
|
|
* Obtains the StatementData needed for asynchronous execution.
|
|
*
|
|
* This is for use by Connection to retrieve StatementData from statements
|
|
* when executeAsync is invoked.
|
|
*
|
|
* @param[out] _data
|
|
* A reference to a StatementData object that will be populated
|
|
* upon successful execution of this method.
|
|
* @return NS_OK if we were able to assemble the data, failure otherwise.
|
|
*/
|
|
virtual nsresult getAsynchronousStatementData(StatementData& _data) = 0;
|
|
|
|
/**
|
|
* Construct a new BindingParams to be owned by the provided binding params
|
|
* array. This method exists so that BindingParamsArray does not need
|
|
* factory logic to determine what type of BindingParams to instantiate.
|
|
*
|
|
* @param aOwner
|
|
* The binding params array to own the newly created binding params.
|
|
* @return The new mozIStorageBindingParams instance appropriate to the
|
|
* underlying statement type.
|
|
*/
|
|
virtual already_AddRefed<mozIStorageBindingParams> newBindingParams(
|
|
mozIStorageBindingParamsArray* aOwner) = 0;
|
|
|
|
protected: // mix-in bits are protected
|
|
StorageBaseStatementInternal();
|
|
|
|
RefPtr<Connection> mDBConnection;
|
|
sqlite3* mNativeConnection;
|
|
|
|
/**
|
|
* Our asynchronous statement.
|
|
*
|
|
* For Statement this is populated by the first invocation to
|
|
* getAsyncStatement.
|
|
*
|
|
* For AsyncStatement, this is null at creation time and initialized by the
|
|
* async thread when it calls getAsyncStatement the first time the statement
|
|
* is executed. (Or in the event of badly formed SQL, every time.)
|
|
*/
|
|
sqlite3_stmt* mAsyncStatement;
|
|
|
|
/**
|
|
* Initiate asynchronous finalization by dispatching an event to the
|
|
* asynchronous thread to finalize mAsyncStatement. This acquires a reference
|
|
* to this statement and proxies it back to the connection's owning thread
|
|
* for release purposes.
|
|
*
|
|
* In the event the asynchronous thread is already gone or we otherwise fail
|
|
* to dispatch an event to it we failover to invoking internalAsyncFinalize
|
|
* directly. (That's what the asynchronous finalizer would have called.)
|
|
*
|
|
* @note You must not call this method from your destructor because its
|
|
* operation assumes we are still alive. Call internalAsyncFinalize
|
|
* directly in that case.
|
|
*/
|
|
void asyncFinalize();
|
|
|
|
/**
|
|
* Cleanup the async sqlite3_stmt stored in mAsyncStatement if it exists by
|
|
* attempting to dispatch to the asynchronous thread if available, finalizing
|
|
* on this thread if it is not.
|
|
*
|
|
* @note Call this from your destructor, call asyncFinalize otherwise.
|
|
*/
|
|
void destructorAsyncFinalize();
|
|
|
|
NS_IMETHOD NewBindingParamsArray(mozIStorageBindingParamsArray** _array);
|
|
NS_IMETHOD ExecuteAsync(mozIStorageStatementCallback* aCallback,
|
|
mozIStoragePendingStatement** _stmt);
|
|
NS_IMETHOD EscapeStringForLIKE(const nsAString& aValue, char16_t aEscapeChar,
|
|
nsAString& _escapedString);
|
|
NS_IMETHOD EscapeUTF8StringForLIKE(const nsACString& aValue, char aEscapeChar,
|
|
nsACString& _escapedString);
|
|
|
|
// Needs access to internalAsyncFinalize
|
|
friend class AsyncStatementFinalizer;
|
|
};
|
|
|
|
NS_DEFINE_STATIC_IID_ACCESSOR(StorageBaseStatementInternal,
|
|
STORAGEBASESTATEMENTINTERNAL_IID)
|
|
|
|
#define NS_DECL_STORAGEBASESTATEMENTINTERNAL \
|
|
virtual Connection* getOwner(); \
|
|
virtual int getAsyncStatement(sqlite3_stmt** _stmt) override; \
|
|
virtual nsresult getAsynchronousStatementData(StatementData& _data) \
|
|
override; \
|
|
virtual already_AddRefed<mozIStorageBindingParams> newBindingParams( \
|
|
mozIStorageBindingParamsArray* aOwner) override;
|
|
|
|
/**
|
|
* Helper macro to implement the proxying implementations. Because we are
|
|
* implementing methods that are part of mozIStorageBaseStatement and the
|
|
* implementation classes already use NS_DECL_MOZISTORAGEBASESTATEMENT we don't
|
|
* need to provide declaration support.
|
|
*/
|
|
#define MIX_IMPL(_class, _optionalGuard, _method, _declArgs, _invokeArgs) \
|
|
NS_IMETHODIMP _class::_method _declArgs { \
|
|
_optionalGuard return StorageBaseStatementInternal::_method _invokeArgs; \
|
|
}
|
|
|
|
/**
|
|
* Define proxying implementation for the given _class. If a state invariant
|
|
* needs to be checked and an early return possibly performed, pass the clause
|
|
* to use as _optionalGuard.
|
|
*/
|
|
#define MIXIN_IMPL_STORAGEBASESTATEMENTINTERNAL(_class, _optionalGuard) \
|
|
MIX_IMPL(_class, _optionalGuard, NewBindingParamsArray, \
|
|
(mozIStorageBindingParamsArray * *_array), (_array)) \
|
|
MIX_IMPL(_class, _optionalGuard, ExecuteAsync, \
|
|
(mozIStorageStatementCallback * aCallback, \
|
|
mozIStoragePendingStatement * *_stmt), \
|
|
(aCallback, _stmt)) \
|
|
MIX_IMPL(_class, _optionalGuard, EscapeStringForLIKE, \
|
|
(const nsAString& aValue, char16_t aEscapeChar, \
|
|
nsAString& _escapedString), \
|
|
(aValue, aEscapeChar, _escapedString)) \
|
|
MIX_IMPL(_class, _optionalGuard, EscapeUTF8StringForLIKE, \
|
|
(const nsACString& aValue, char aEscapeChar, \
|
|
nsACString& _escapedString), \
|
|
(aValue, aEscapeChar, _escapedString))
|
|
|
|
/**
|
|
* Name-building helper for BIND_GEN_IMPL.
|
|
*/
|
|
#define BIND_NAME_CONCAT(_nameBit, _concatBit) Bind##_nameBit##_concatBit
|
|
|
|
/**
|
|
* We have type-specific convenience methods for C++ implementations in
|
|
* two different forms; by index and by name. The following macro allows
|
|
* us to avoid having to define repetitive things by hand.
|
|
*
|
|
* Because of limitations of macros and our desire to avoid requiring special
|
|
* permutations for the null and blob cases (whose argument count varies),
|
|
* we require that the argument declarations and corresponding invocation
|
|
* usages are passed in.
|
|
*
|
|
* @param _class
|
|
* The class name.
|
|
* @param _guard
|
|
* The guard clause to inject.
|
|
* @param _declName
|
|
* The argument list (with parens) for the ByName variants.
|
|
* @param _declIndex
|
|
* The argument list (with parens) for the ByIndex variants.
|
|
* @param _invArgs
|
|
* The invocation argumment list.
|
|
*/
|
|
#define BIND_GEN_IMPL(_class, _guard, _name, _declName, _declIndex, _invArgs) \
|
|
NS_IMETHODIMP _class::BIND_NAME_CONCAT(_name, ByName) _declName { \
|
|
_guard mozIStorageBindingParams* params = getParams(); \
|
|
NS_ENSURE_TRUE(params, NS_ERROR_OUT_OF_MEMORY); \
|
|
return params->BIND_NAME_CONCAT(_name, ByName) _invArgs; \
|
|
} \
|
|
NS_IMETHODIMP _class::BIND_NAME_CONCAT(_name, ByIndex) _declIndex { \
|
|
_guard mozIStorageBindingParams* params = getParams(); \
|
|
NS_ENSURE_TRUE(params, NS_ERROR_OUT_OF_MEMORY); \
|
|
return params->BIND_NAME_CONCAT(_name, ByIndex) _invArgs; \
|
|
}
|
|
|
|
/**
|
|
* Implement BindByName/BindByIndex for the given class.
|
|
*
|
|
* @param _class The class name.
|
|
* @param _optionalGuard The guard clause to inject.
|
|
*/
|
|
#define BIND_BASE_IMPLS(_class, _optionalGuard) \
|
|
NS_IMETHODIMP _class::BindByName(const nsACString& aName, \
|
|
nsIVariant* aValue) { \
|
|
_optionalGuard mozIStorageBindingParams* params = getParams(); \
|
|
NS_ENSURE_TRUE(params, NS_ERROR_OUT_OF_MEMORY); \
|
|
return params->BindByName(aName, aValue); \
|
|
} \
|
|
NS_IMETHODIMP _class::BindByIndex(uint32_t aIndex, nsIVariant* aValue) { \
|
|
_optionalGuard mozIStorageBindingParams* params = getParams(); \
|
|
NS_ENSURE_TRUE(params, NS_ERROR_OUT_OF_MEMORY); \
|
|
return params->BindByIndex(aIndex, aValue); \
|
|
}
|
|
|
|
/**
|
|
* Define the various Bind*ByIndex, Bind*ByName stubs that just end up proxying
|
|
* to the params object.
|
|
*/
|
|
#define BOILERPLATE_BIND_PROXIES(_class, _optionalGuard) \
|
|
BIND_BASE_IMPLS(_class, _optionalGuard) \
|
|
BIND_GEN_IMPL(_class, _optionalGuard, UTF8String, \
|
|
(const nsACString& aWhere, const nsACString& aValue), \
|
|
(uint32_t aWhere, const nsACString& aValue), (aWhere, aValue)) \
|
|
BIND_GEN_IMPL(_class, _optionalGuard, String, \
|
|
(const nsACString& aWhere, const nsAString& aValue), \
|
|
(uint32_t aWhere, const nsAString& aValue), (aWhere, aValue)) \
|
|
BIND_GEN_IMPL(_class, _optionalGuard, Double, \
|
|
(const nsACString& aWhere, double aValue), \
|
|
(uint32_t aWhere, double aValue), (aWhere, aValue)) \
|
|
BIND_GEN_IMPL(_class, _optionalGuard, Int32, \
|
|
(const nsACString& aWhere, int32_t aValue), \
|
|
(uint32_t aWhere, int32_t aValue), (aWhere, aValue)) \
|
|
BIND_GEN_IMPL(_class, _optionalGuard, Int64, \
|
|
(const nsACString& aWhere, int64_t aValue), \
|
|
(uint32_t aWhere, int64_t aValue), (aWhere, aValue)) \
|
|
BIND_GEN_IMPL(_class, _optionalGuard, Null, (const nsACString& aWhere), \
|
|
(uint32_t aWhere), (aWhere)) \
|
|
BIND_GEN_IMPL( \
|
|
_class, _optionalGuard, Blob, \
|
|
(const nsACString& aWhere, const uint8_t* aValue, uint32_t aValueSize), \
|
|
(uint32_t aWhere, const uint8_t* aValue, uint32_t aValueSize), \
|
|
(aWhere, aValue, aValueSize)) \
|
|
BIND_GEN_IMPL(_class, _optionalGuard, BlobArray, \
|
|
(const nsACString& aWhere, const nsTArray<uint8_t>& aValue), \
|
|
(uint32_t aWhere, const nsTArray<uint8_t>& aValue), \
|
|
(aWhere, aValue)) \
|
|
BIND_GEN_IMPL(_class, _optionalGuard, StringAsBlob, \
|
|
(const nsACString& aWhere, const nsAString& aValue), \
|
|
(uint32_t aWhere, const nsAString& aValue), (aWhere, aValue)) \
|
|
BIND_GEN_IMPL(_class, _optionalGuard, UTF8StringAsBlob, \
|
|
(const nsACString& aWhere, const nsACString& aValue), \
|
|
(uint32_t aWhere, const nsACString& aValue), (aWhere, aValue)) \
|
|
BIND_GEN_IMPL( \
|
|
_class, _optionalGuard, AdoptedBlob, \
|
|
(const nsACString& aWhere, uint8_t* aValue, uint32_t aValueSize), \
|
|
(uint32_t aWhere, uint8_t * aValue, uint32_t 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 mozilla
|
|
|
|
#endif // mozilla_storage_StorageBaseStatementInternal_h_
|