michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- michael@0: * vim: sw=2 ts=2 et lcs=trail\:.,tab\:>~ : michael@0: * This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include michael@0: michael@0: #include "nsString.h" michael@0: michael@0: #include "mozStorageError.h" michael@0: #include "mozStoragePrivateHelpers.h" michael@0: #include "mozStorageBindingParams.h" michael@0: #include "mozStorageBindingParamsArray.h" michael@0: #include "Variant.h" michael@0: michael@0: namespace mozilla { michael@0: namespace storage { michael@0: michael@0: //////////////////////////////////////////////////////////////////////////////// michael@0: //// Local Helper Objects michael@0: michael@0: namespace { michael@0: michael@0: struct BindingColumnData michael@0: { michael@0: BindingColumnData(sqlite3_stmt *aStmt, michael@0: int aColumn) michael@0: : stmt(aStmt) michael@0: , column(aColumn) michael@0: { michael@0: } michael@0: sqlite3_stmt *stmt; michael@0: int column; michael@0: }; michael@0: michael@0: //////////////////////////////////////////////////////////////////////////////// michael@0: //// Variant Specialization Functions (variantToSQLiteT) michael@0: michael@0: int michael@0: sqlite3_T_int(BindingColumnData aData, michael@0: int aValue) michael@0: { michael@0: return ::sqlite3_bind_int(aData.stmt, aData.column + 1, aValue); michael@0: } michael@0: michael@0: int michael@0: sqlite3_T_int64(BindingColumnData aData, michael@0: sqlite3_int64 aValue) michael@0: { michael@0: return ::sqlite3_bind_int64(aData.stmt, aData.column + 1, aValue); michael@0: } michael@0: michael@0: int michael@0: sqlite3_T_double(BindingColumnData aData, michael@0: double aValue) michael@0: { michael@0: return ::sqlite3_bind_double(aData.stmt, aData.column + 1, aValue); michael@0: } michael@0: michael@0: int michael@0: sqlite3_T_text(BindingColumnData aData, michael@0: const nsCString& aValue) michael@0: { michael@0: return ::sqlite3_bind_text(aData.stmt, michael@0: aData.column + 1, michael@0: aValue.get(), michael@0: aValue.Length(), michael@0: SQLITE_TRANSIENT); michael@0: } michael@0: michael@0: int michael@0: sqlite3_T_text16(BindingColumnData aData, michael@0: const nsString& aValue) michael@0: { michael@0: return ::sqlite3_bind_text16(aData.stmt, michael@0: aData.column + 1, michael@0: aValue.get(), michael@0: aValue.Length() * 2, // Length in bytes! michael@0: SQLITE_TRANSIENT); michael@0: } michael@0: michael@0: int michael@0: sqlite3_T_null(BindingColumnData aData) michael@0: { michael@0: return ::sqlite3_bind_null(aData.stmt, aData.column + 1); michael@0: } michael@0: michael@0: int michael@0: sqlite3_T_blob(BindingColumnData aData, michael@0: const void *aBlob, michael@0: int aSize) michael@0: { michael@0: return ::sqlite3_bind_blob(aData.stmt, aData.column + 1, aBlob, aSize, michael@0: NS_Free); michael@0: michael@0: } michael@0: michael@0: #include "variantToSQLiteT_impl.h" michael@0: michael@0: } // anonymous namespace michael@0: michael@0: //////////////////////////////////////////////////////////////////////////////// michael@0: //// BindingParams michael@0: michael@0: BindingParams::BindingParams(mozIStorageBindingParamsArray *aOwningArray, michael@0: Statement *aOwningStatement) michael@0: : mLocked(false) michael@0: , mOwningArray(aOwningArray) michael@0: , mOwningStatement(aOwningStatement) michael@0: { michael@0: (void)mOwningStatement->GetParameterCount(&mParamCount); michael@0: (void)mParameters.SetCapacity(mParamCount); michael@0: } michael@0: michael@0: BindingParams::BindingParams(mozIStorageBindingParamsArray *aOwningArray) michael@0: : mLocked(false) michael@0: , mOwningArray(aOwningArray) michael@0: , mOwningStatement(nullptr) michael@0: , mParamCount(0) michael@0: { michael@0: } michael@0: michael@0: AsyncBindingParams::AsyncBindingParams( michael@0: mozIStorageBindingParamsArray *aOwningArray michael@0: ) michael@0: : BindingParams(aOwningArray) michael@0: { michael@0: } michael@0: michael@0: void michael@0: BindingParams::lock() michael@0: { michael@0: NS_ASSERTION(mLocked == false, "Parameters have already been locked!"); michael@0: mLocked = true; michael@0: michael@0: // We no longer need to hold a reference to our statement or our owning array. michael@0: // The array owns us at this point, and it will own a reference to the michael@0: // statement. michael@0: mOwningStatement = nullptr; michael@0: mOwningArray = nullptr; michael@0: } michael@0: michael@0: void michael@0: BindingParams::unlock(Statement *aOwningStatement) michael@0: { michael@0: NS_ASSERTION(mLocked == true, "Parameters were not yet locked!"); michael@0: mLocked = false; michael@0: mOwningStatement = aOwningStatement; michael@0: } michael@0: michael@0: const mozIStorageBindingParamsArray * michael@0: BindingParams::getOwner() const michael@0: { michael@0: return mOwningArray; michael@0: } michael@0: michael@0: PLDHashOperator michael@0: AsyncBindingParams::iterateOverNamedParameters(const nsACString &aName, michael@0: nsIVariant *aValue, michael@0: void *voidClosureThunk) michael@0: { michael@0: NamedParameterIterationClosureThunk *closureThunk = michael@0: static_cast(voidClosureThunk); michael@0: michael@0: // We do not accept any forms of names other than ":name", but we need to add michael@0: // the colon for SQLite. michael@0: nsAutoCString name(":"); michael@0: name.Append(aName); michael@0: int oneIdx = ::sqlite3_bind_parameter_index(closureThunk->statement, michael@0: name.get()); michael@0: michael@0: if (oneIdx == 0) { michael@0: nsAutoCString errMsg(aName); michael@0: errMsg.Append(NS_LITERAL_CSTRING(" is not a valid named parameter.")); michael@0: closureThunk->err = new Error(SQLITE_RANGE, errMsg.get()); michael@0: return PL_DHASH_STOP; michael@0: } michael@0: michael@0: // XPCVariant's AddRef and Release are not thread-safe and so we must not do michael@0: // anything that would invoke them here on the async thread. As such we can't michael@0: // cram aValue into self->mParameters using ReplaceObjectAt so that we can michael@0: // freeload off of the BindingParams::Bind implementation. michael@0: int rc = variantToSQLiteT(BindingColumnData(closureThunk->statement, michael@0: oneIdx - 1), michael@0: aValue); michael@0: if (rc != SQLITE_OK) { michael@0: // We had an error while trying to bind. Now we need to create an error michael@0: // object with the right message. Note that we special case michael@0: // SQLITE_MISMATCH, but otherwise get the message from SQLite. michael@0: const char *msg = "Could not covert nsIVariant to SQLite type."; michael@0: if (rc != SQLITE_MISMATCH) michael@0: msg = ::sqlite3_errmsg(::sqlite3_db_handle(closureThunk->statement)); michael@0: michael@0: closureThunk->err = new Error(rc, msg); michael@0: return PL_DHASH_STOP; michael@0: } michael@0: return PL_DHASH_NEXT; michael@0: } michael@0: michael@0: //////////////////////////////////////////////////////////////////////////////// michael@0: //// nsISupports michael@0: michael@0: NS_IMPL_ISUPPORTS( michael@0: BindingParams michael@0: , mozIStorageBindingParams michael@0: , IStorageBindingParamsInternal michael@0: ) michael@0: michael@0: michael@0: //////////////////////////////////////////////////////////////////////////////// michael@0: //// IStorageBindingParamsInternal michael@0: michael@0: already_AddRefed michael@0: BindingParams::bind(sqlite3_stmt *aStatement) michael@0: { michael@0: // Iterate through all of our stored data, and bind it. michael@0: for (int32_t i = 0; i < mParameters.Count(); i++) { michael@0: int rc = variantToSQLiteT(BindingColumnData(aStatement, i), mParameters[i]); michael@0: if (rc != SQLITE_OK) { michael@0: // We had an error while trying to bind. Now we need to create an error michael@0: // object with the right message. Note that we special case michael@0: // SQLITE_MISMATCH, but otherwise get the message from SQLite. michael@0: const char *msg = "Could not covert nsIVariant to SQLite type."; michael@0: if (rc != SQLITE_MISMATCH) michael@0: msg = ::sqlite3_errmsg(::sqlite3_db_handle(aStatement)); michael@0: michael@0: nsCOMPtr err(new Error(rc, msg)); michael@0: return err.forget(); michael@0: } michael@0: } michael@0: michael@0: return nullptr; michael@0: } michael@0: michael@0: already_AddRefed michael@0: AsyncBindingParams::bind(sqlite3_stmt * aStatement) michael@0: { michael@0: // We should bind by index using the super-class if there is nothing in our michael@0: // hashtable. michael@0: if (!mNamedParameters.Count()) michael@0: return BindingParams::bind(aStatement); michael@0: michael@0: // Enumerate over everyone in the map, propagating them into mParameters if michael@0: // we can and creating an error immediately when we cannot. michael@0: NamedParameterIterationClosureThunk closureThunk = {this, aStatement, nullptr}; michael@0: (void)mNamedParameters.EnumerateRead(iterateOverNamedParameters, michael@0: (void *)&closureThunk); michael@0: michael@0: return closureThunk.err.forget(); michael@0: } michael@0: michael@0: michael@0: /////////////////////////////////////////////////////////////////////////////// michael@0: //// mozIStorageBindingParams michael@0: michael@0: NS_IMETHODIMP michael@0: BindingParams::BindByName(const nsACString &aName, michael@0: nsIVariant *aValue) michael@0: { michael@0: NS_ENSURE_FALSE(mLocked, NS_ERROR_UNEXPECTED); michael@0: michael@0: // Get the column index that we need to store this at. michael@0: uint32_t index; michael@0: nsresult rv = mOwningStatement->GetParameterIndex(aName, &index); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: return BindByIndex(index, aValue); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: AsyncBindingParams::BindByName(const nsACString &aName, michael@0: nsIVariant *aValue) michael@0: { michael@0: NS_ENSURE_FALSE(mLocked, NS_ERROR_UNEXPECTED); michael@0: michael@0: mNamedParameters.Put(aName, aValue); michael@0: return NS_OK; michael@0: } michael@0: michael@0: michael@0: NS_IMETHODIMP michael@0: BindingParams::BindUTF8StringByName(const nsACString &aName, michael@0: const nsACString &aValue) michael@0: { michael@0: nsCOMPtr value(new UTF8TextVariant(aValue)); michael@0: NS_ENSURE_TRUE(value, NS_ERROR_OUT_OF_MEMORY); michael@0: michael@0: return BindByName(aName, value); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: BindingParams::BindStringByName(const nsACString &aName, michael@0: const nsAString &aValue) michael@0: { michael@0: nsCOMPtr value(new TextVariant(aValue)); michael@0: NS_ENSURE_TRUE(value, NS_ERROR_OUT_OF_MEMORY); michael@0: michael@0: return BindByName(aName, value); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: BindingParams::BindDoubleByName(const nsACString &aName, michael@0: double aValue) michael@0: { michael@0: nsCOMPtr value(new FloatVariant(aValue)); michael@0: NS_ENSURE_TRUE(value, NS_ERROR_OUT_OF_MEMORY); michael@0: michael@0: return BindByName(aName, value); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: BindingParams::BindInt32ByName(const nsACString &aName, michael@0: int32_t aValue) michael@0: { michael@0: nsCOMPtr value(new IntegerVariant(aValue)); michael@0: NS_ENSURE_TRUE(value, NS_ERROR_OUT_OF_MEMORY); michael@0: michael@0: return BindByName(aName, value); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: BindingParams::BindInt64ByName(const nsACString &aName, michael@0: int64_t aValue) michael@0: { michael@0: nsCOMPtr value(new IntegerVariant(aValue)); michael@0: NS_ENSURE_TRUE(value, NS_ERROR_OUT_OF_MEMORY); michael@0: michael@0: return BindByName(aName, value); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: BindingParams::BindNullByName(const nsACString &aName) michael@0: { michael@0: nsCOMPtr value(new NullVariant()); michael@0: NS_ENSURE_TRUE(value, NS_ERROR_OUT_OF_MEMORY); michael@0: michael@0: return BindByName(aName, value); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: BindingParams::BindBlobByName(const nsACString &aName, michael@0: const uint8_t *aValue, michael@0: uint32_t aValueSize) michael@0: { michael@0: NS_ENSURE_ARG_MAX(aValueSize, INT_MAX); michael@0: std::pair data( michael@0: static_cast(aValue), michael@0: int(aValueSize) michael@0: ); michael@0: nsCOMPtr value(new BlobVariant(data)); michael@0: NS_ENSURE_TRUE(value, NS_ERROR_OUT_OF_MEMORY); michael@0: michael@0: return BindByName(aName, value); michael@0: } michael@0: michael@0: michael@0: NS_IMETHODIMP michael@0: BindingParams::BindAdoptedBlobByName(const nsACString &aName, michael@0: uint8_t *aValue, michael@0: uint32_t aValueSize) michael@0: { michael@0: NS_ENSURE_ARG_MAX(aValueSize, INT_MAX); michael@0: std::pair data( michael@0: aValue, michael@0: int(aValueSize) michael@0: ); michael@0: nsCOMPtr value(new AdoptedBlobVariant(data)); michael@0: michael@0: return BindByName(aName, value); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: BindingParams::BindByIndex(uint32_t aIndex, michael@0: nsIVariant *aValue) michael@0: { michael@0: NS_ENSURE_FALSE(mLocked, NS_ERROR_UNEXPECTED); michael@0: ENSURE_INDEX_VALUE(aIndex, mParamCount); michael@0: michael@0: // Store the variant for later use. michael@0: NS_ENSURE_TRUE(mParameters.ReplaceObjectAt(aValue, aIndex), michael@0: NS_ERROR_OUT_OF_MEMORY); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: AsyncBindingParams::BindByIndex(uint32_t aIndex, michael@0: nsIVariant *aValue) michael@0: { michael@0: NS_ENSURE_FALSE(mLocked, NS_ERROR_UNEXPECTED); michael@0: // In the asynchronous case we do not know how many parameters there are to michael@0: // bind to, so we cannot check the validity of aIndex. michael@0: michael@0: // Store the variant for later use. michael@0: NS_ENSURE_TRUE(mParameters.ReplaceObjectAt(aValue, aIndex), michael@0: NS_ERROR_OUT_OF_MEMORY); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: BindingParams::BindUTF8StringByIndex(uint32_t aIndex, michael@0: const nsACString &aValue) michael@0: { michael@0: nsCOMPtr value(new UTF8TextVariant(aValue)); michael@0: NS_ENSURE_TRUE(value, NS_ERROR_OUT_OF_MEMORY); michael@0: michael@0: return BindByIndex(aIndex, value); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: BindingParams::BindStringByIndex(uint32_t aIndex, michael@0: const nsAString &aValue) michael@0: { michael@0: nsCOMPtr value(new TextVariant(aValue)); michael@0: NS_ENSURE_TRUE(value, NS_ERROR_OUT_OF_MEMORY); michael@0: michael@0: return BindByIndex(aIndex, value); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: BindingParams::BindDoubleByIndex(uint32_t aIndex, michael@0: double aValue) michael@0: { michael@0: nsCOMPtr value(new FloatVariant(aValue)); michael@0: NS_ENSURE_TRUE(value, NS_ERROR_OUT_OF_MEMORY); michael@0: michael@0: return BindByIndex(aIndex, value); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: BindingParams::BindInt32ByIndex(uint32_t aIndex, michael@0: int32_t aValue) michael@0: { michael@0: nsCOMPtr value(new IntegerVariant(aValue)); michael@0: NS_ENSURE_TRUE(value, NS_ERROR_OUT_OF_MEMORY); michael@0: michael@0: return BindByIndex(aIndex, value); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: BindingParams::BindInt64ByIndex(uint32_t aIndex, michael@0: int64_t aValue) michael@0: { michael@0: nsCOMPtr value(new IntegerVariant(aValue)); michael@0: NS_ENSURE_TRUE(value, NS_ERROR_OUT_OF_MEMORY); michael@0: michael@0: return BindByIndex(aIndex, value); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: BindingParams::BindNullByIndex(uint32_t aIndex) michael@0: { michael@0: nsCOMPtr value(new NullVariant()); michael@0: NS_ENSURE_TRUE(value, NS_ERROR_OUT_OF_MEMORY); michael@0: michael@0: return BindByIndex(aIndex, value); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: BindingParams::BindBlobByIndex(uint32_t aIndex, michael@0: const uint8_t *aValue, michael@0: uint32_t aValueSize) michael@0: { michael@0: NS_ENSURE_ARG_MAX(aValueSize, INT_MAX); michael@0: std::pair data( michael@0: static_cast(aValue), michael@0: int(aValueSize) michael@0: ); michael@0: nsCOMPtr value(new BlobVariant(data)); michael@0: NS_ENSURE_TRUE(value, NS_ERROR_OUT_OF_MEMORY); michael@0: michael@0: return BindByIndex(aIndex, value); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: BindingParams::BindAdoptedBlobByIndex(uint32_t aIndex, michael@0: uint8_t *aValue, michael@0: uint32_t aValueSize) michael@0: { michael@0: NS_ENSURE_ARG_MAX(aValueSize, INT_MAX); michael@0: std::pair data( michael@0: static_cast(aValue), michael@0: int(aValueSize) michael@0: ); michael@0: nsCOMPtr value(new AdoptedBlobVariant(data)); michael@0: michael@0: return BindByIndex(aIndex, value); michael@0: } michael@0: michael@0: } // namespace storage michael@0: } // namespace mozilla