1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/storage/src/mozStorageBindingParams.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,491 @@ 1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- 1.5 + * vim: sw=2 ts=2 et lcs=trail\:.,tab\:>~ : 1.6 + * This Source Code Form is subject to the terms of the Mozilla Public 1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.9 + 1.10 +#include <limits.h> 1.11 + 1.12 +#include "nsString.h" 1.13 + 1.14 +#include "mozStorageError.h" 1.15 +#include "mozStoragePrivateHelpers.h" 1.16 +#include "mozStorageBindingParams.h" 1.17 +#include "mozStorageBindingParamsArray.h" 1.18 +#include "Variant.h" 1.19 + 1.20 +namespace mozilla { 1.21 +namespace storage { 1.22 + 1.23 +//////////////////////////////////////////////////////////////////////////////// 1.24 +//// Local Helper Objects 1.25 + 1.26 +namespace { 1.27 + 1.28 +struct BindingColumnData 1.29 +{ 1.30 + BindingColumnData(sqlite3_stmt *aStmt, 1.31 + int aColumn) 1.32 + : stmt(aStmt) 1.33 + , column(aColumn) 1.34 + { 1.35 + } 1.36 + sqlite3_stmt *stmt; 1.37 + int column; 1.38 +}; 1.39 + 1.40 +//////////////////////////////////////////////////////////////////////////////// 1.41 +//// Variant Specialization Functions (variantToSQLiteT) 1.42 + 1.43 +int 1.44 +sqlite3_T_int(BindingColumnData aData, 1.45 + int aValue) 1.46 +{ 1.47 + return ::sqlite3_bind_int(aData.stmt, aData.column + 1, aValue); 1.48 +} 1.49 + 1.50 +int 1.51 +sqlite3_T_int64(BindingColumnData aData, 1.52 + sqlite3_int64 aValue) 1.53 +{ 1.54 + return ::sqlite3_bind_int64(aData.stmt, aData.column + 1, aValue); 1.55 +} 1.56 + 1.57 +int 1.58 +sqlite3_T_double(BindingColumnData aData, 1.59 + double aValue) 1.60 +{ 1.61 + return ::sqlite3_bind_double(aData.stmt, aData.column + 1, aValue); 1.62 +} 1.63 + 1.64 +int 1.65 +sqlite3_T_text(BindingColumnData aData, 1.66 + const nsCString& aValue) 1.67 +{ 1.68 + return ::sqlite3_bind_text(aData.stmt, 1.69 + aData.column + 1, 1.70 + aValue.get(), 1.71 + aValue.Length(), 1.72 + SQLITE_TRANSIENT); 1.73 +} 1.74 + 1.75 +int 1.76 +sqlite3_T_text16(BindingColumnData aData, 1.77 + const nsString& aValue) 1.78 +{ 1.79 + return ::sqlite3_bind_text16(aData.stmt, 1.80 + aData.column + 1, 1.81 + aValue.get(), 1.82 + aValue.Length() * 2, // Length in bytes! 1.83 + SQLITE_TRANSIENT); 1.84 +} 1.85 + 1.86 +int 1.87 +sqlite3_T_null(BindingColumnData aData) 1.88 +{ 1.89 + return ::sqlite3_bind_null(aData.stmt, aData.column + 1); 1.90 +} 1.91 + 1.92 +int 1.93 +sqlite3_T_blob(BindingColumnData aData, 1.94 + const void *aBlob, 1.95 + int aSize) 1.96 +{ 1.97 + return ::sqlite3_bind_blob(aData.stmt, aData.column + 1, aBlob, aSize, 1.98 + NS_Free); 1.99 + 1.100 +} 1.101 + 1.102 +#include "variantToSQLiteT_impl.h" 1.103 + 1.104 +} // anonymous namespace 1.105 + 1.106 +//////////////////////////////////////////////////////////////////////////////// 1.107 +//// BindingParams 1.108 + 1.109 +BindingParams::BindingParams(mozIStorageBindingParamsArray *aOwningArray, 1.110 + Statement *aOwningStatement) 1.111 +: mLocked(false) 1.112 +, mOwningArray(aOwningArray) 1.113 +, mOwningStatement(aOwningStatement) 1.114 +{ 1.115 + (void)mOwningStatement->GetParameterCount(&mParamCount); 1.116 + (void)mParameters.SetCapacity(mParamCount); 1.117 +} 1.118 + 1.119 +BindingParams::BindingParams(mozIStorageBindingParamsArray *aOwningArray) 1.120 +: mLocked(false) 1.121 +, mOwningArray(aOwningArray) 1.122 +, mOwningStatement(nullptr) 1.123 +, mParamCount(0) 1.124 +{ 1.125 +} 1.126 + 1.127 +AsyncBindingParams::AsyncBindingParams( 1.128 + mozIStorageBindingParamsArray *aOwningArray 1.129 +) 1.130 +: BindingParams(aOwningArray) 1.131 +{ 1.132 +} 1.133 + 1.134 +void 1.135 +BindingParams::lock() 1.136 +{ 1.137 + NS_ASSERTION(mLocked == false, "Parameters have already been locked!"); 1.138 + mLocked = true; 1.139 + 1.140 + // We no longer need to hold a reference to our statement or our owning array. 1.141 + // The array owns us at this point, and it will own a reference to the 1.142 + // statement. 1.143 + mOwningStatement = nullptr; 1.144 + mOwningArray = nullptr; 1.145 +} 1.146 + 1.147 +void 1.148 +BindingParams::unlock(Statement *aOwningStatement) 1.149 +{ 1.150 + NS_ASSERTION(mLocked == true, "Parameters were not yet locked!"); 1.151 + mLocked = false; 1.152 + mOwningStatement = aOwningStatement; 1.153 +} 1.154 + 1.155 +const mozIStorageBindingParamsArray * 1.156 +BindingParams::getOwner() const 1.157 +{ 1.158 + return mOwningArray; 1.159 +} 1.160 + 1.161 +PLDHashOperator 1.162 +AsyncBindingParams::iterateOverNamedParameters(const nsACString &aName, 1.163 + nsIVariant *aValue, 1.164 + void *voidClosureThunk) 1.165 +{ 1.166 + NamedParameterIterationClosureThunk *closureThunk = 1.167 + static_cast<NamedParameterIterationClosureThunk *>(voidClosureThunk); 1.168 + 1.169 + // We do not accept any forms of names other than ":name", but we need to add 1.170 + // the colon for SQLite. 1.171 + nsAutoCString name(":"); 1.172 + name.Append(aName); 1.173 + int oneIdx = ::sqlite3_bind_parameter_index(closureThunk->statement, 1.174 + name.get()); 1.175 + 1.176 + if (oneIdx == 0) { 1.177 + nsAutoCString errMsg(aName); 1.178 + errMsg.Append(NS_LITERAL_CSTRING(" is not a valid named parameter.")); 1.179 + closureThunk->err = new Error(SQLITE_RANGE, errMsg.get()); 1.180 + return PL_DHASH_STOP; 1.181 + } 1.182 + 1.183 + // XPCVariant's AddRef and Release are not thread-safe and so we must not do 1.184 + // anything that would invoke them here on the async thread. As such we can't 1.185 + // cram aValue into self->mParameters using ReplaceObjectAt so that we can 1.186 + // freeload off of the BindingParams::Bind implementation. 1.187 + int rc = variantToSQLiteT(BindingColumnData(closureThunk->statement, 1.188 + oneIdx - 1), 1.189 + aValue); 1.190 + if (rc != SQLITE_OK) { 1.191 + // We had an error while trying to bind. Now we need to create an error 1.192 + // object with the right message. Note that we special case 1.193 + // SQLITE_MISMATCH, but otherwise get the message from SQLite. 1.194 + const char *msg = "Could not covert nsIVariant to SQLite type."; 1.195 + if (rc != SQLITE_MISMATCH) 1.196 + msg = ::sqlite3_errmsg(::sqlite3_db_handle(closureThunk->statement)); 1.197 + 1.198 + closureThunk->err = new Error(rc, msg); 1.199 + return PL_DHASH_STOP; 1.200 + } 1.201 + return PL_DHASH_NEXT; 1.202 +} 1.203 + 1.204 +//////////////////////////////////////////////////////////////////////////////// 1.205 +//// nsISupports 1.206 + 1.207 +NS_IMPL_ISUPPORTS( 1.208 + BindingParams 1.209 +, mozIStorageBindingParams 1.210 +, IStorageBindingParamsInternal 1.211 +) 1.212 + 1.213 + 1.214 +//////////////////////////////////////////////////////////////////////////////// 1.215 +//// IStorageBindingParamsInternal 1.216 + 1.217 +already_AddRefed<mozIStorageError> 1.218 +BindingParams::bind(sqlite3_stmt *aStatement) 1.219 +{ 1.220 + // Iterate through all of our stored data, and bind it. 1.221 + for (int32_t i = 0; i < mParameters.Count(); i++) { 1.222 + int rc = variantToSQLiteT(BindingColumnData(aStatement, i), mParameters[i]); 1.223 + if (rc != SQLITE_OK) { 1.224 + // We had an error while trying to bind. Now we need to create an error 1.225 + // object with the right message. Note that we special case 1.226 + // SQLITE_MISMATCH, but otherwise get the message from SQLite. 1.227 + const char *msg = "Could not covert nsIVariant to SQLite type."; 1.228 + if (rc != SQLITE_MISMATCH) 1.229 + msg = ::sqlite3_errmsg(::sqlite3_db_handle(aStatement)); 1.230 + 1.231 + nsCOMPtr<mozIStorageError> err(new Error(rc, msg)); 1.232 + return err.forget(); 1.233 + } 1.234 + } 1.235 + 1.236 + return nullptr; 1.237 +} 1.238 + 1.239 +already_AddRefed<mozIStorageError> 1.240 +AsyncBindingParams::bind(sqlite3_stmt * aStatement) 1.241 +{ 1.242 + // We should bind by index using the super-class if there is nothing in our 1.243 + // hashtable. 1.244 + if (!mNamedParameters.Count()) 1.245 + return BindingParams::bind(aStatement); 1.246 + 1.247 + // Enumerate over everyone in the map, propagating them into mParameters if 1.248 + // we can and creating an error immediately when we cannot. 1.249 + NamedParameterIterationClosureThunk closureThunk = {this, aStatement, nullptr}; 1.250 + (void)mNamedParameters.EnumerateRead(iterateOverNamedParameters, 1.251 + (void *)&closureThunk); 1.252 + 1.253 + return closureThunk.err.forget(); 1.254 +} 1.255 + 1.256 + 1.257 +/////////////////////////////////////////////////////////////////////////////// 1.258 +//// mozIStorageBindingParams 1.259 + 1.260 +NS_IMETHODIMP 1.261 +BindingParams::BindByName(const nsACString &aName, 1.262 + nsIVariant *aValue) 1.263 +{ 1.264 + NS_ENSURE_FALSE(mLocked, NS_ERROR_UNEXPECTED); 1.265 + 1.266 + // Get the column index that we need to store this at. 1.267 + uint32_t index; 1.268 + nsresult rv = mOwningStatement->GetParameterIndex(aName, &index); 1.269 + NS_ENSURE_SUCCESS(rv, rv); 1.270 + 1.271 + return BindByIndex(index, aValue); 1.272 +} 1.273 + 1.274 +NS_IMETHODIMP 1.275 +AsyncBindingParams::BindByName(const nsACString &aName, 1.276 + nsIVariant *aValue) 1.277 +{ 1.278 + NS_ENSURE_FALSE(mLocked, NS_ERROR_UNEXPECTED); 1.279 + 1.280 + mNamedParameters.Put(aName, aValue); 1.281 + return NS_OK; 1.282 +} 1.283 + 1.284 + 1.285 +NS_IMETHODIMP 1.286 +BindingParams::BindUTF8StringByName(const nsACString &aName, 1.287 + const nsACString &aValue) 1.288 +{ 1.289 + nsCOMPtr<nsIVariant> value(new UTF8TextVariant(aValue)); 1.290 + NS_ENSURE_TRUE(value, NS_ERROR_OUT_OF_MEMORY); 1.291 + 1.292 + return BindByName(aName, value); 1.293 +} 1.294 + 1.295 +NS_IMETHODIMP 1.296 +BindingParams::BindStringByName(const nsACString &aName, 1.297 + const nsAString &aValue) 1.298 +{ 1.299 + nsCOMPtr<nsIVariant> value(new TextVariant(aValue)); 1.300 + NS_ENSURE_TRUE(value, NS_ERROR_OUT_OF_MEMORY); 1.301 + 1.302 + return BindByName(aName, value); 1.303 +} 1.304 + 1.305 +NS_IMETHODIMP 1.306 +BindingParams::BindDoubleByName(const nsACString &aName, 1.307 + double aValue) 1.308 +{ 1.309 + nsCOMPtr<nsIVariant> value(new FloatVariant(aValue)); 1.310 + NS_ENSURE_TRUE(value, NS_ERROR_OUT_OF_MEMORY); 1.311 + 1.312 + return BindByName(aName, value); 1.313 +} 1.314 + 1.315 +NS_IMETHODIMP 1.316 +BindingParams::BindInt32ByName(const nsACString &aName, 1.317 + int32_t aValue) 1.318 +{ 1.319 + nsCOMPtr<nsIVariant> value(new IntegerVariant(aValue)); 1.320 + NS_ENSURE_TRUE(value, NS_ERROR_OUT_OF_MEMORY); 1.321 + 1.322 + return BindByName(aName, value); 1.323 +} 1.324 + 1.325 +NS_IMETHODIMP 1.326 +BindingParams::BindInt64ByName(const nsACString &aName, 1.327 + int64_t aValue) 1.328 +{ 1.329 + nsCOMPtr<nsIVariant> value(new IntegerVariant(aValue)); 1.330 + NS_ENSURE_TRUE(value, NS_ERROR_OUT_OF_MEMORY); 1.331 + 1.332 + return BindByName(aName, value); 1.333 +} 1.334 + 1.335 +NS_IMETHODIMP 1.336 +BindingParams::BindNullByName(const nsACString &aName) 1.337 +{ 1.338 + nsCOMPtr<nsIVariant> value(new NullVariant()); 1.339 + NS_ENSURE_TRUE(value, NS_ERROR_OUT_OF_MEMORY); 1.340 + 1.341 + return BindByName(aName, value); 1.342 +} 1.343 + 1.344 +NS_IMETHODIMP 1.345 +BindingParams::BindBlobByName(const nsACString &aName, 1.346 + const uint8_t *aValue, 1.347 + uint32_t aValueSize) 1.348 +{ 1.349 + NS_ENSURE_ARG_MAX(aValueSize, INT_MAX); 1.350 + std::pair<const void *, int> data( 1.351 + static_cast<const void *>(aValue), 1.352 + int(aValueSize) 1.353 + ); 1.354 + nsCOMPtr<nsIVariant> value(new BlobVariant(data)); 1.355 + NS_ENSURE_TRUE(value, NS_ERROR_OUT_OF_MEMORY); 1.356 + 1.357 + return BindByName(aName, value); 1.358 +} 1.359 + 1.360 + 1.361 +NS_IMETHODIMP 1.362 +BindingParams::BindAdoptedBlobByName(const nsACString &aName, 1.363 + uint8_t *aValue, 1.364 + uint32_t aValueSize) 1.365 +{ 1.366 + NS_ENSURE_ARG_MAX(aValueSize, INT_MAX); 1.367 + std::pair<uint8_t *, int> data( 1.368 + aValue, 1.369 + int(aValueSize) 1.370 + ); 1.371 + nsCOMPtr<nsIVariant> value(new AdoptedBlobVariant(data)); 1.372 + 1.373 + return BindByName(aName, value); 1.374 +} 1.375 + 1.376 +NS_IMETHODIMP 1.377 +BindingParams::BindByIndex(uint32_t aIndex, 1.378 + nsIVariant *aValue) 1.379 +{ 1.380 + NS_ENSURE_FALSE(mLocked, NS_ERROR_UNEXPECTED); 1.381 + ENSURE_INDEX_VALUE(aIndex, mParamCount); 1.382 + 1.383 + // Store the variant for later use. 1.384 + NS_ENSURE_TRUE(mParameters.ReplaceObjectAt(aValue, aIndex), 1.385 + NS_ERROR_OUT_OF_MEMORY); 1.386 + return NS_OK; 1.387 +} 1.388 + 1.389 +NS_IMETHODIMP 1.390 +AsyncBindingParams::BindByIndex(uint32_t aIndex, 1.391 + nsIVariant *aValue) 1.392 +{ 1.393 + NS_ENSURE_FALSE(mLocked, NS_ERROR_UNEXPECTED); 1.394 + // In the asynchronous case we do not know how many parameters there are to 1.395 + // bind to, so we cannot check the validity of aIndex. 1.396 + 1.397 + // Store the variant for later use. 1.398 + NS_ENSURE_TRUE(mParameters.ReplaceObjectAt(aValue, aIndex), 1.399 + NS_ERROR_OUT_OF_MEMORY); 1.400 + return NS_OK; 1.401 +} 1.402 + 1.403 +NS_IMETHODIMP 1.404 +BindingParams::BindUTF8StringByIndex(uint32_t aIndex, 1.405 + const nsACString &aValue) 1.406 +{ 1.407 + nsCOMPtr<nsIVariant> value(new UTF8TextVariant(aValue)); 1.408 + NS_ENSURE_TRUE(value, NS_ERROR_OUT_OF_MEMORY); 1.409 + 1.410 + return BindByIndex(aIndex, value); 1.411 +} 1.412 + 1.413 +NS_IMETHODIMP 1.414 +BindingParams::BindStringByIndex(uint32_t aIndex, 1.415 + const nsAString &aValue) 1.416 +{ 1.417 + nsCOMPtr<nsIVariant> value(new TextVariant(aValue)); 1.418 + NS_ENSURE_TRUE(value, NS_ERROR_OUT_OF_MEMORY); 1.419 + 1.420 + return BindByIndex(aIndex, value); 1.421 +} 1.422 + 1.423 +NS_IMETHODIMP 1.424 +BindingParams::BindDoubleByIndex(uint32_t aIndex, 1.425 + double aValue) 1.426 +{ 1.427 + nsCOMPtr<nsIVariant> value(new FloatVariant(aValue)); 1.428 + NS_ENSURE_TRUE(value, NS_ERROR_OUT_OF_MEMORY); 1.429 + 1.430 + return BindByIndex(aIndex, value); 1.431 +} 1.432 + 1.433 +NS_IMETHODIMP 1.434 +BindingParams::BindInt32ByIndex(uint32_t aIndex, 1.435 + int32_t aValue) 1.436 +{ 1.437 + nsCOMPtr<nsIVariant> value(new IntegerVariant(aValue)); 1.438 + NS_ENSURE_TRUE(value, NS_ERROR_OUT_OF_MEMORY); 1.439 + 1.440 + return BindByIndex(aIndex, value); 1.441 +} 1.442 + 1.443 +NS_IMETHODIMP 1.444 +BindingParams::BindInt64ByIndex(uint32_t aIndex, 1.445 + int64_t aValue) 1.446 +{ 1.447 + nsCOMPtr<nsIVariant> value(new IntegerVariant(aValue)); 1.448 + NS_ENSURE_TRUE(value, NS_ERROR_OUT_OF_MEMORY); 1.449 + 1.450 + return BindByIndex(aIndex, value); 1.451 +} 1.452 + 1.453 +NS_IMETHODIMP 1.454 +BindingParams::BindNullByIndex(uint32_t aIndex) 1.455 +{ 1.456 + nsCOMPtr<nsIVariant> value(new NullVariant()); 1.457 + NS_ENSURE_TRUE(value, NS_ERROR_OUT_OF_MEMORY); 1.458 + 1.459 + return BindByIndex(aIndex, value); 1.460 +} 1.461 + 1.462 +NS_IMETHODIMP 1.463 +BindingParams::BindBlobByIndex(uint32_t aIndex, 1.464 + const uint8_t *aValue, 1.465 + uint32_t aValueSize) 1.466 +{ 1.467 + NS_ENSURE_ARG_MAX(aValueSize, INT_MAX); 1.468 + std::pair<const void *, int> data( 1.469 + static_cast<const void *>(aValue), 1.470 + int(aValueSize) 1.471 + ); 1.472 + nsCOMPtr<nsIVariant> value(new BlobVariant(data)); 1.473 + NS_ENSURE_TRUE(value, NS_ERROR_OUT_OF_MEMORY); 1.474 + 1.475 + return BindByIndex(aIndex, value); 1.476 +} 1.477 + 1.478 +NS_IMETHODIMP 1.479 +BindingParams::BindAdoptedBlobByIndex(uint32_t aIndex, 1.480 + uint8_t *aValue, 1.481 + uint32_t aValueSize) 1.482 +{ 1.483 + NS_ENSURE_ARG_MAX(aValueSize, INT_MAX); 1.484 + std::pair<uint8_t *, int> data( 1.485 + static_cast<uint8_t *>(aValue), 1.486 + int(aValueSize) 1.487 + ); 1.488 + nsCOMPtr<nsIVariant> value(new AdoptedBlobVariant(data)); 1.489 + 1.490 + return BindByIndex(aIndex, value); 1.491 +} 1.492 + 1.493 +} // namespace storage 1.494 +} // namespace mozilla