1 : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* vim: set ts=2 et sw=2 tw=80: */
3 : /* ***** BEGIN LICENSE BLOCK *****
4 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 : *
6 : * The contents of this file are subject to the Mozilla Public License Version
7 : * 1.1 (the "License"); you may not use this file except in compliance with
8 : * the License. You may obtain a copy of the License at
9 : * http://www.mozilla.org/MPL/
10 : *
11 : * Software distributed under the License is distributed on an "AS IS" basis,
12 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13 : * for the specific language governing rights and limitations under the
14 : * License.
15 : *
16 : * The Original Code is Indexed Database.
17 : *
18 : * The Initial Developer of the Original Code is
19 : * The Mozilla Foundation.
20 : * Portions created by the Initial Developer are Copyright (C) 2010
21 : * the Initial Developer. All Rights Reserved.
22 : *
23 : * Contributor(s):
24 : * Shawn Wilsher <me@shawnwilsher.com>
25 : * Ben Turner <bent.mozilla@gmail.com>
26 : *
27 : * Alternatively, the contents of this file may be used under the terms of
28 : * either the GNU General Public License Version 2 or later (the "GPL"), or
29 : * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
30 : * in which case the provisions of the GPL or the LGPL are applicable instead
31 : * of those above. If you wish to allow use of your version of this file only
32 : * under the terms of either the GPL or the LGPL, and not to allow others to
33 : * use your version of this file under the terms of the MPL, indicate your
34 : * decision by deleting the provisions above and replace them with the notice
35 : * and other provisions required by the GPL or the LGPL. If you do not delete
36 : * the provisions above, a recipient may use your version of this file under
37 : * the terms of any one of the MPL, the GPL or the LGPL.
38 : *
39 : * ***** END LICENSE BLOCK ***** */
40 :
41 :
42 : #include "IDBIndex.h"
43 :
44 : #include "nsIIDBKeyRange.h"
45 : #include "nsIJSContextStack.h"
46 :
47 : #include "nsDOMClassInfoID.h"
48 : #include "nsEventDispatcher.h"
49 : #include "nsThreadUtils.h"
50 : #include "mozilla/storage.h"
51 : #include "xpcpublic.h"
52 :
53 : #include "AsyncConnectionHelper.h"
54 : #include "IDBCursor.h"
55 : #include "IDBEvents.h"
56 : #include "IDBKeyRange.h"
57 : #include "IDBObjectStore.h"
58 : #include "IDBTransaction.h"
59 : #include "DatabaseInfo.h"
60 :
61 : USING_INDEXEDDB_NAMESPACE
62 :
63 : namespace {
64 :
65 : class GetKeyHelper : public AsyncConnectionHelper
66 146 : {
67 : public:
68 61 : GetKeyHelper(IDBTransaction* aTransaction,
69 : IDBRequest* aRequest,
70 : IDBIndex* aIndex,
71 : IDBKeyRange* aKeyRange)
72 : : AsyncConnectionHelper(aTransaction, aRequest), mIndex(aIndex),
73 61 : mKeyRange(aKeyRange)
74 61 : { }
75 :
76 : nsresult DoDatabaseWork(mozIStorageConnection* aConnection);
77 : nsresult GetSuccessResult(JSContext* aCx,
78 : jsval* aVal);
79 :
80 61 : void ReleaseMainThreadObjects()
81 : {
82 61 : mIndex = nsnull;
83 61 : mKeyRange = nsnull;
84 61 : AsyncConnectionHelper::ReleaseMainThreadObjects();
85 61 : }
86 :
87 : protected:
88 : // In-params.
89 : nsRefPtr<IDBIndex> mIndex;
90 : nsRefPtr<IDBKeyRange> mKeyRange;
91 :
92 : // Out-params.
93 : Key mKey;
94 : };
95 :
96 : class GetHelper : public GetKeyHelper
97 : {
98 : public:
99 25 : GetHelper(IDBTransaction* aTransaction,
100 : IDBRequest* aRequest,
101 : IDBIndex* aIndex,
102 : IDBKeyRange* aKeyRange)
103 25 : : GetKeyHelper(aTransaction, aRequest, aIndex, aKeyRange)
104 25 : { }
105 :
106 50 : ~GetHelper()
107 50 : {
108 25 : IDBObjectStore::ClearStructuredCloneBuffer(mCloneReadInfo.mCloneBuffer);
109 100 : }
110 :
111 : nsresult DoDatabaseWork(mozIStorageConnection* aConnection);
112 : nsresult GetSuccessResult(JSContext* aCx,
113 : jsval* aVal);
114 :
115 25 : void ReleaseMainThreadObjects()
116 : {
117 25 : IDBObjectStore::ClearStructuredCloneBuffer(mCloneReadInfo.mCloneBuffer);
118 25 : GetKeyHelper::ReleaseMainThreadObjects();
119 25 : }
120 :
121 : protected:
122 : StructuredCloneReadInfo mCloneReadInfo;
123 : };
124 :
125 : class GetAllKeysHelper : public GetKeyHelper
126 48 : {
127 : public:
128 12 : GetAllKeysHelper(IDBTransaction* aTransaction,
129 : IDBRequest* aRequest,
130 : IDBIndex* aIndex,
131 : IDBKeyRange* aKeyRange,
132 : const PRUint32 aLimit)
133 12 : : GetKeyHelper(aTransaction, aRequest, aIndex, aKeyRange), mLimit(aLimit)
134 12 : { }
135 :
136 : nsresult DoDatabaseWork(mozIStorageConnection* aConnection);
137 : nsresult GetSuccessResult(JSContext* aCx,
138 : jsval* aVal);
139 :
140 : protected:
141 : const PRUint32 mLimit;
142 : nsTArray<Key> mKeys;
143 : };
144 :
145 : class GetAllHelper : public GetKeyHelper
146 : {
147 : public:
148 12 : GetAllHelper(IDBTransaction* aTransaction,
149 : IDBRequest* aRequest,
150 : IDBIndex* aIndex,
151 : IDBKeyRange* aKeyRange,
152 : const PRUint32 aLimit)
153 12 : : GetKeyHelper(aTransaction, aRequest, aIndex, aKeyRange), mLimit(aLimit)
154 12 : { }
155 :
156 24 : ~GetAllHelper()
157 24 : {
158 12 : for (PRUint32 index = 0; index < mCloneReadInfos.Length(); index++) {
159 : IDBObjectStore::ClearStructuredCloneBuffer(
160 0 : mCloneReadInfos[index].mCloneBuffer);
161 : }
162 48 : }
163 :
164 : nsresult DoDatabaseWork(mozIStorageConnection* aConnection);
165 : nsresult GetSuccessResult(JSContext* aCx,
166 : jsval* aVal);
167 :
168 12 : void ReleaseMainThreadObjects()
169 : {
170 12 : for (PRUint32 index = 0; index < mCloneReadInfos.Length(); index++) {
171 : IDBObjectStore::ClearStructuredCloneBuffer(
172 0 : mCloneReadInfos[index].mCloneBuffer);
173 : }
174 12 : GetKeyHelper::ReleaseMainThreadObjects();
175 12 : }
176 :
177 : protected:
178 : const PRUint32 mLimit;
179 : nsTArray<StructuredCloneReadInfo> mCloneReadInfos;
180 : };
181 :
182 : class OpenKeyCursorHelper : public AsyncConnectionHelper
183 356 : {
184 : public:
185 89 : OpenKeyCursorHelper(IDBTransaction* aTransaction,
186 : IDBRequest* aRequest,
187 : IDBIndex* aIndex,
188 : IDBKeyRange* aKeyRange,
189 : IDBCursor::Direction aDirection)
190 : : AsyncConnectionHelper(aTransaction, aRequest), mIndex(aIndex),
191 89 : mKeyRange(aKeyRange), mDirection(aDirection)
192 89 : { }
193 :
194 : nsresult DoDatabaseWork(mozIStorageConnection* aConnection);
195 : nsresult GetSuccessResult(JSContext* aCx,
196 : jsval* aVal);
197 :
198 89 : void ReleaseMainThreadObjects()
199 : {
200 89 : mIndex = nsnull;
201 89 : mKeyRange = nsnull;
202 89 : AsyncConnectionHelper::ReleaseMainThreadObjects();
203 89 : }
204 :
205 : private:
206 : // In-params.
207 : nsRefPtr<IDBIndex> mIndex;
208 : nsRefPtr<IDBKeyRange> mKeyRange;
209 : const IDBCursor::Direction mDirection;
210 :
211 : // Out-params.
212 : Key mKey;
213 : Key mObjectKey;
214 : nsCString mContinueQuery;
215 : nsCString mContinueToQuery;
216 : Key mRangeKey;
217 : };
218 :
219 : class OpenCursorHelper : public AsyncConnectionHelper
220 : {
221 : public:
222 57 : OpenCursorHelper(IDBTransaction* aTransaction,
223 : IDBRequest* aRequest,
224 : IDBIndex* aIndex,
225 : IDBKeyRange* aKeyRange,
226 : IDBCursor::Direction aDirection)
227 : : AsyncConnectionHelper(aTransaction, aRequest), mIndex(aIndex),
228 57 : mKeyRange(aKeyRange), mDirection(aDirection)
229 57 : { }
230 :
231 114 : ~OpenCursorHelper()
232 114 : {
233 57 : IDBObjectStore::ClearStructuredCloneBuffer(mCloneReadInfo.mCloneBuffer);
234 228 : }
235 :
236 : nsresult DoDatabaseWork(mozIStorageConnection* aConnection);
237 : nsresult GetSuccessResult(JSContext* aCx,
238 : jsval* aVal);
239 :
240 57 : void ReleaseMainThreadObjects()
241 : {
242 57 : mIndex = nsnull;
243 57 : mKeyRange = nsnull;
244 57 : AsyncConnectionHelper::ReleaseMainThreadObjects();
245 57 : }
246 :
247 : private:
248 : // In-params.
249 : nsRefPtr<IDBIndex> mIndex;
250 : nsRefPtr<IDBKeyRange> mKeyRange;
251 : const IDBCursor::Direction mDirection;
252 :
253 : // Out-params.
254 : Key mKey;
255 : Key mObjectKey;
256 : StructuredCloneReadInfo mCloneReadInfo;
257 : nsCString mContinueQuery;
258 : nsCString mContinueToQuery;
259 : Key mRangeKey;
260 : };
261 :
262 : class CountHelper : public AsyncConnectionHelper
263 156 : {
264 : public:
265 39 : CountHelper(IDBTransaction* aTransaction,
266 : IDBRequest* aRequest,
267 : IDBIndex* aIndex,
268 : IDBKeyRange* aKeyRange)
269 : : AsyncConnectionHelper(aTransaction, aRequest), mIndex(aIndex),
270 39 : mKeyRange(aKeyRange), mCount(0)
271 39 : { }
272 :
273 : nsresult DoDatabaseWork(mozIStorageConnection* aConnection);
274 : nsresult GetSuccessResult(JSContext* aCx,
275 : jsval* aVal);
276 :
277 39 : void ReleaseMainThreadObjects()
278 : {
279 39 : mIndex = nsnull;
280 39 : mKeyRange = nsnull;
281 39 : AsyncConnectionHelper::ReleaseMainThreadObjects();
282 39 : }
283 :
284 : private:
285 : nsRefPtr<IDBIndex> mIndex;
286 : nsRefPtr<IDBKeyRange> mKeyRange;
287 : PRUint64 mCount;
288 : };
289 :
290 : inline
291 : already_AddRefed<IDBRequest>
292 246 : GenerateRequest(IDBIndex* aIndex)
293 : {
294 246 : NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
295 246 : IDBTransaction* transaction = aIndex->ObjectStore()->Transaction();
296 246 : IDBDatabase* database = transaction->Database();
297 246 : return IDBRequest::Create(aIndex, database, transaction);
298 : }
299 :
300 : } // anonymous namespace
301 :
302 : // static
303 : already_AddRefed<IDBIndex>
304 129 : IDBIndex::Create(IDBObjectStore* aObjectStore,
305 : const IndexInfo* aIndexInfo)
306 : {
307 129 : NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
308 129 : NS_ASSERTION(aObjectStore, "Null pointer!");
309 129 : NS_ASSERTION(aIndexInfo, "Null pointer!");
310 :
311 258 : nsRefPtr<IDBIndex> index = new IDBIndex();
312 :
313 129 : index->mObjectStore = aObjectStore;
314 129 : index->mId = aIndexInfo->id;
315 129 : index->mName = aIndexInfo->name;
316 129 : index->mKeyPath = aIndexInfo->keyPath;
317 129 : index->mKeyPathArray = aIndexInfo->keyPathArray;
318 129 : index->mUnique = aIndexInfo->unique;
319 129 : index->mMultiEntry = aIndexInfo->multiEntry;
320 :
321 129 : return index.forget();
322 : }
323 :
324 129 : IDBIndex::IDBIndex()
325 : : mId(LL_MININT),
326 129 : mUnique(false)
327 : {
328 129 : NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
329 129 : }
330 :
331 258 : IDBIndex::~IDBIndex()
332 : {
333 129 : NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
334 129 : }
335 :
336 1464 : NS_IMPL_CYCLE_COLLECTION_CLASS(IDBIndex)
337 :
338 105 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(IDBIndex)
339 105 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mObjectStore)
340 105 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
341 :
342 105 : NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(IDBIndex)
343 : // Don't unlink mObjectStore!
344 105 : NS_IMPL_CYCLE_COLLECTION_UNLINK_END
345 :
346 3288 : NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(IDBIndex)
347 1653 : NS_INTERFACE_MAP_ENTRY(nsIIDBIndex)
348 1044 : NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(IDBIndex)
349 915 : NS_INTERFACE_MAP_ENTRY(nsISupports)
350 738 : NS_INTERFACE_MAP_END
351 :
352 1899 : NS_IMPL_CYCLE_COLLECTING_ADDREF(IDBIndex)
353 2028 : NS_IMPL_CYCLE_COLLECTING_RELEASE(IDBIndex)
354 :
355 : DOMCI_DATA(IDBIndex, IDBIndex)
356 :
357 : NS_IMETHODIMP
358 15 : IDBIndex::GetName(nsAString& aName)
359 : {
360 15 : NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
361 :
362 15 : aName.Assign(mName);
363 15 : return NS_OK;
364 : }
365 :
366 : NS_IMETHODIMP
367 3 : IDBIndex::GetStoreName(nsAString& aStoreName)
368 : {
369 3 : NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
370 :
371 3 : return mObjectStore->GetName(aStoreName);
372 : }
373 :
374 : NS_IMETHODIMP
375 15 : IDBIndex::GetKeyPath(JSContext* aCx,
376 : jsval* aVal)
377 : {
378 15 : NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
379 :
380 15 : if (UsesKeyPathArray()) {
381 0 : JSObject* array = JS_NewArrayObject(aCx, mKeyPathArray.Length(), nsnull);
382 0 : if (!array) {
383 0 : NS_WARNING("Failed to make array!");
384 0 : return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
385 : }
386 :
387 0 : for (PRUint32 i = 0; i < mKeyPathArray.Length(); ++i) {
388 : jsval val;
389 0 : nsString tmp(mKeyPathArray[i]);
390 0 : if (!xpc::StringToJsval(aCx, tmp, &val)) {
391 0 : return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
392 : }
393 :
394 0 : if (!JS_SetElement(aCx, array, i, &val)) {
395 0 : return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
396 : }
397 : }
398 :
399 0 : *aVal = OBJECT_TO_JSVAL(array);
400 : }
401 : else {
402 30 : nsString tmp(mKeyPath);
403 15 : if (!xpc::StringToJsval(aCx, tmp, aVal)) {
404 0 : return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
405 : }
406 : }
407 15 : return NS_OK;
408 : }
409 :
410 : NS_IMETHODIMP
411 15 : IDBIndex::GetUnique(bool* aUnique)
412 : {
413 15 : NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
414 :
415 15 : *aUnique = mUnique;
416 15 : return NS_OK;
417 : }
418 :
419 : NS_IMETHODIMP
420 3 : IDBIndex::GetMultiEntry(bool* aMultiEntry)
421 : {
422 3 : NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
423 :
424 3 : *aMultiEntry = mMultiEntry;
425 3 : return NS_OK;
426 : }
427 :
428 : NS_IMETHODIMP
429 0 : IDBIndex::GetObjectStore(nsIIDBObjectStore** aObjectStore)
430 : {
431 0 : NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
432 :
433 0 : nsCOMPtr<nsIIDBObjectStore> objectStore(mObjectStore);
434 0 : objectStore.forget(aObjectStore);
435 0 : return NS_OK;
436 : }
437 :
438 : NS_IMETHODIMP
439 27 : IDBIndex::Get(const jsval& aKey,
440 : JSContext* aCx,
441 : nsIIDBRequest** _retval)
442 : {
443 27 : NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
444 :
445 27 : IDBTransaction* transaction = mObjectStore->Transaction();
446 27 : if (!transaction->IsOpen()) {
447 0 : return NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR;
448 : }
449 :
450 54 : nsRefPtr<IDBKeyRange> keyRange;
451 27 : nsresult rv = IDBKeyRange::FromJSVal(aCx, aKey, getter_AddRefs(keyRange));
452 27 : NS_ENSURE_SUCCESS(rv, rv);
453 :
454 27 : if (!keyRange) {
455 : // Must specify a key or keyRange for get().
456 2 : return NS_ERROR_DOM_INDEXEDDB_DATA_ERR;
457 : }
458 :
459 50 : nsRefPtr<IDBRequest> request = GenerateRequest(this);
460 25 : NS_ENSURE_TRUE(request, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
461 :
462 : nsRefPtr<GetHelper> helper =
463 75 : new GetHelper(transaction, request, this, keyRange);
464 25 : rv = helper->DispatchToTransactionPool();
465 25 : NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
466 :
467 25 : request.forget(_retval);
468 25 : return NS_OK;
469 : }
470 :
471 : NS_IMETHODIMP
472 14 : IDBIndex::GetKey(const jsval& aKey,
473 : JSContext* aCx,
474 : nsIIDBRequest** _retval)
475 : {
476 14 : NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
477 :
478 14 : IDBTransaction* transaction = mObjectStore->Transaction();
479 14 : if (!transaction->IsOpen()) {
480 0 : return NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR;
481 : }
482 :
483 28 : nsRefPtr<IDBKeyRange> keyRange;
484 14 : nsresult rv = IDBKeyRange::FromJSVal(aCx, aKey, getter_AddRefs(keyRange));
485 14 : NS_ENSURE_SUCCESS(rv, rv);
486 :
487 14 : if (!keyRange) {
488 : // Must specify a key or keyRange for get().
489 2 : return NS_ERROR_DOM_INDEXEDDB_DATA_ERR;
490 : }
491 :
492 24 : nsRefPtr<IDBRequest> request = GenerateRequest(this);
493 12 : NS_ENSURE_TRUE(request, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
494 :
495 : nsRefPtr<GetKeyHelper> helper =
496 36 : new GetKeyHelper(transaction, request, this, keyRange);
497 :
498 12 : rv = helper->DispatchToTransactionPool();
499 12 : NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
500 :
501 12 : request.forget(_retval);
502 12 : return NS_OK;
503 : }
504 :
505 : NS_IMETHODIMP
506 12 : IDBIndex::GetAll(const jsval& aKey,
507 : PRUint32 aLimit,
508 : JSContext* aCx,
509 : PRUint8 aOptionalArgCount,
510 : nsIIDBRequest** _retval)
511 : {
512 12 : NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
513 :
514 12 : IDBTransaction* transaction = mObjectStore->Transaction();
515 12 : if (!transaction->IsOpen()) {
516 0 : return NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR;
517 : }
518 :
519 : nsresult rv;
520 :
521 24 : nsRefPtr<IDBKeyRange> keyRange;
522 12 : if (aOptionalArgCount) {
523 10 : rv = IDBKeyRange::FromJSVal(aCx, aKey, getter_AddRefs(keyRange));
524 10 : NS_ENSURE_SUCCESS(rv, rv);
525 : }
526 :
527 12 : if (aOptionalArgCount < 2 || aLimit == 0) {
528 10 : aLimit = PR_UINT32_MAX;
529 : }
530 :
531 24 : nsRefPtr<IDBRequest> request = GenerateRequest(this);
532 12 : NS_ENSURE_TRUE(request, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
533 :
534 : nsRefPtr<GetAllHelper> helper =
535 36 : new GetAllHelper(transaction, request, this, keyRange, aLimit);
536 :
537 12 : rv = helper->DispatchToTransactionPool();
538 12 : NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
539 :
540 12 : request.forget(_retval);
541 12 : return NS_OK;
542 : }
543 :
544 : NS_IMETHODIMP
545 12 : IDBIndex::GetAllKeys(const jsval& aKey,
546 : PRUint32 aLimit,
547 : JSContext* aCx,
548 : PRUint8 aOptionalArgCount,
549 : nsIIDBRequest** _retval)
550 : {
551 12 : NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
552 :
553 12 : IDBTransaction* transaction = mObjectStore->Transaction();
554 12 : if (!transaction->IsOpen()) {
555 0 : return NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR;
556 : }
557 :
558 : nsresult rv;
559 :
560 24 : nsRefPtr<IDBKeyRange> keyRange;
561 12 : if (aOptionalArgCount) {
562 10 : rv = IDBKeyRange::FromJSVal(aCx, aKey, getter_AddRefs(keyRange));
563 10 : NS_ENSURE_SUCCESS(rv, rv);
564 : }
565 :
566 12 : if (aOptionalArgCount < 2 || aLimit == 0) {
567 10 : aLimit = PR_UINT32_MAX;
568 : }
569 :
570 24 : nsRefPtr<IDBRequest> request = GenerateRequest(this);
571 12 : NS_ENSURE_TRUE(request, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
572 :
573 : nsRefPtr<GetAllKeysHelper> helper =
574 36 : new GetAllKeysHelper(transaction, request, this, keyRange, aLimit);
575 :
576 12 : rv = helper->DispatchToTransactionPool();
577 12 : NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
578 :
579 12 : request.forget(_retval);
580 12 : return NS_OK;
581 : }
582 :
583 : NS_IMETHODIMP
584 57 : IDBIndex::OpenCursor(const jsval& aKey,
585 : const nsAString& aDirection,
586 : JSContext* aCx,
587 : PRUint8 aOptionalArgCount,
588 : nsIIDBRequest** _retval)
589 : {
590 57 : NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
591 :
592 57 : IDBTransaction* transaction = mObjectStore->Transaction();
593 57 : if (!transaction->IsOpen()) {
594 0 : return NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR;
595 : }
596 :
597 : nsresult rv;
598 :
599 57 : IDBCursor::Direction direction = IDBCursor::NEXT;
600 :
601 114 : nsRefPtr<IDBKeyRange> keyRange;
602 57 : if (aOptionalArgCount) {
603 39 : rv = IDBKeyRange::FromJSVal(aCx, aKey, getter_AddRefs(keyRange));
604 39 : NS_ENSURE_SUCCESS(rv, rv);
605 :
606 39 : if (aOptionalArgCount >= 2) {
607 6 : rv = IDBCursor::ParseDirection(aDirection, &direction);
608 6 : NS_ENSURE_SUCCESS(rv, rv);
609 : }
610 : }
611 :
612 114 : nsRefPtr<IDBRequest> request = GenerateRequest(this);
613 57 : NS_ENSURE_TRUE(request, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
614 :
615 : nsRefPtr<OpenCursorHelper> helper =
616 171 : new OpenCursorHelper(transaction, request, this, keyRange, direction);
617 :
618 57 : rv = helper->DispatchToTransactionPool();
619 57 : NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
620 :
621 57 : request.forget(_retval);
622 57 : return NS_OK;
623 : }
624 :
625 : NS_IMETHODIMP
626 89 : IDBIndex::OpenKeyCursor(const jsval& aKey,
627 : const nsAString& aDirection,
628 : JSContext* aCx,
629 : PRUint8 aOptionalArgCount,
630 : nsIIDBRequest** _retval)
631 : {
632 89 : NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
633 :
634 89 : IDBTransaction* transaction = mObjectStore->Transaction();
635 89 : if (!transaction->IsOpen()) {
636 0 : return NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR;
637 : }
638 :
639 : nsresult rv;
640 :
641 89 : IDBCursor::Direction direction = IDBCursor::NEXT;
642 :
643 178 : nsRefPtr<IDBKeyRange> keyRange;
644 89 : if (aOptionalArgCount) {
645 41 : rv = IDBKeyRange::FromJSVal(aCx, aKey, getter_AddRefs(keyRange));
646 41 : NS_ENSURE_SUCCESS(rv, rv);
647 :
648 41 : if (aOptionalArgCount >= 2) {
649 6 : rv = IDBCursor::ParseDirection(aDirection, &direction);
650 6 : NS_ENSURE_SUCCESS(rv, rv);
651 : }
652 : }
653 :
654 178 : nsRefPtr<IDBRequest> request = GenerateRequest(this);
655 89 : NS_ENSURE_TRUE(request, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
656 :
657 : nsRefPtr<OpenKeyCursorHelper> helper =
658 267 : new OpenKeyCursorHelper(transaction, request, this, keyRange, direction);
659 :
660 89 : rv = helper->DispatchToTransactionPool();
661 89 : NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
662 :
663 89 : request.forget(_retval);
664 89 : return NS_OK;
665 : }
666 :
667 : NS_IMETHODIMP
668 39 : IDBIndex::Count(const jsval& aKey,
669 : JSContext* aCx,
670 : PRUint8 aOptionalArgCount,
671 : nsIIDBRequest** _retval)
672 : {
673 39 : IDBTransaction* transaction = mObjectStore->Transaction();
674 39 : if (!transaction->IsOpen()) {
675 0 : return NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR;
676 : }
677 :
678 : nsresult rv;
679 :
680 78 : nsRefPtr<IDBKeyRange> keyRange;
681 39 : if (aOptionalArgCount) {
682 22 : rv = IDBKeyRange::FromJSVal(aCx, aKey, getter_AddRefs(keyRange));
683 22 : NS_ENSURE_SUCCESS(rv, rv);
684 : }
685 :
686 78 : nsRefPtr<IDBRequest> request = GenerateRequest(this);
687 39 : NS_ENSURE_TRUE(request, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
688 :
689 : nsRefPtr<CountHelper> helper =
690 117 : new CountHelper(transaction, request, this, keyRange);
691 39 : rv = helper->DispatchToTransactionPool();
692 39 : NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
693 :
694 39 : request.forget(_retval);
695 39 : return NS_OK;
696 : }
697 :
698 : nsresult
699 12 : GetKeyHelper::DoDatabaseWork(mozIStorageConnection* /* aConnection */)
700 : {
701 12 : NS_ASSERTION(mKeyRange, "Must have a key range here!");
702 :
703 24 : nsCString indexTable;
704 12 : if (mIndex->IsUnique()) {
705 1 : indexTable.AssignLiteral("unique_index_data");
706 : }
707 : else {
708 11 : indexTable.AssignLiteral("index_data");
709 : }
710 :
711 24 : nsCString keyRangeClause;
712 12 : mKeyRange->GetBindingClause(NS_LITERAL_CSTRING("value"), keyRangeClause);
713 :
714 12 : NS_ASSERTION(!keyRangeClause.IsEmpty(), "Huh?!");
715 :
716 12 : nsCString query = NS_LITERAL_CSTRING("SELECT object_data_key FROM ") +
717 12 : indexTable +
718 36 : NS_LITERAL_CSTRING(" WHERE index_id = :index_id") +
719 12 : keyRangeClause +
720 48 : NS_LITERAL_CSTRING(" LIMIT 1");
721 :
722 24 : nsCOMPtr<mozIStorageStatement> stmt = mTransaction->GetCachedStatement(query);
723 12 : NS_ENSURE_TRUE(stmt, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
724 :
725 24 : mozStorageStatementScoper scoper(stmt);
726 :
727 24 : nsresult rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("index_id"),
728 12 : mIndex->Id());
729 12 : NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
730 :
731 12 : rv = mKeyRange->BindToStatement(stmt);
732 12 : NS_ENSURE_SUCCESS(rv, rv);
733 :
734 : bool hasResult;
735 12 : rv = stmt->ExecuteStep(&hasResult);
736 12 : NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
737 :
738 12 : if (hasResult) {
739 10 : rv = mKey.SetFromStatement(stmt, 0);
740 10 : NS_ENSURE_SUCCESS(rv, rv);
741 : }
742 :
743 12 : return NS_OK;
744 : }
745 :
746 : nsresult
747 12 : GetKeyHelper::GetSuccessResult(JSContext* aCx,
748 : jsval* aVal)
749 : {
750 12 : return mKey.ToJSVal(aCx, aVal);
751 : }
752 :
753 : nsresult
754 25 : GetHelper::DoDatabaseWork(mozIStorageConnection* /* aConnection */)
755 : {
756 25 : NS_ASSERTION(mKeyRange, "Must have a key range here!");
757 :
758 50 : nsCString indexTable;
759 25 : if (mIndex->IsUnique()) {
760 7 : indexTable.AssignLiteral("unique_index_data");
761 : }
762 : else {
763 18 : indexTable.AssignLiteral("index_data");
764 : }
765 :
766 50 : nsCString keyRangeClause;
767 25 : mKeyRange->GetBindingClause(NS_LITERAL_CSTRING("value"), keyRangeClause);
768 :
769 25 : NS_ASSERTION(!keyRangeClause.IsEmpty(), "Huh?!");
770 :
771 25 : nsCString query = NS_LITERAL_CSTRING("SELECT data, file_ids FROM object_data "
772 25 : "INNER JOIN ") + indexTable +
773 75 : NS_LITERAL_CSTRING(" AS index_table ON object_data.id = ") +
774 75 : NS_LITERAL_CSTRING("index_table.object_data_id WHERE "
775 : "index_id = :index_id") +
776 25 : keyRangeClause +
777 100 : NS_LITERAL_CSTRING(" LIMIT 1");
778 :
779 50 : nsCOMPtr<mozIStorageStatement> stmt = mTransaction->GetCachedStatement(query);
780 25 : NS_ENSURE_TRUE(stmt, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
781 :
782 50 : mozStorageStatementScoper scoper(stmt);
783 :
784 50 : nsresult rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("index_id"),
785 25 : mIndex->Id());
786 25 : NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
787 :
788 25 : rv = mKeyRange->BindToStatement(stmt);
789 25 : NS_ENSURE_SUCCESS(rv, rv);
790 :
791 : bool hasResult;
792 25 : rv = stmt->ExecuteStep(&hasResult);
793 25 : NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
794 :
795 25 : if (hasResult) {
796 : rv = IDBObjectStore::GetStructuredCloneReadInfoFromStatement(stmt, 0, 1,
797 23 : mDatabase->Manager(), mCloneReadInfo);
798 23 : NS_ENSURE_SUCCESS(rv, rv);
799 : }
800 :
801 25 : return NS_OK;
802 : }
803 :
804 : nsresult
805 25 : GetHelper::GetSuccessResult(JSContext* aCx,
806 : jsval* aVal)
807 : {
808 25 : bool result = IDBObjectStore::DeserializeValue(aCx, mCloneReadInfo, aVal);
809 :
810 25 : mCloneReadInfo.mCloneBuffer.clear();
811 :
812 25 : NS_ENSURE_TRUE(result, NS_ERROR_FAILURE);
813 25 : return NS_OK;
814 : }
815 :
816 : nsresult
817 12 : GetAllKeysHelper::DoDatabaseWork(mozIStorageConnection* /* aConnection */)
818 : {
819 24 : nsCString tableName;
820 12 : if (mIndex->IsUnique()) {
821 0 : tableName.AssignLiteral("unique_index_data");
822 : }
823 : else {
824 12 : tableName.AssignLiteral("index_data");
825 : }
826 :
827 24 : nsCString keyRangeClause;
828 12 : if (mKeyRange) {
829 7 : mKeyRange->GetBindingClause(NS_LITERAL_CSTRING("value"), keyRangeClause);
830 : }
831 :
832 24 : nsCString limitClause;
833 12 : if (mLimit != PR_UINT32_MAX) {
834 2 : limitClause = NS_LITERAL_CSTRING(" LIMIT ");
835 2 : limitClause.AppendInt(mLimit);
836 : }
837 :
838 12 : nsCString query = NS_LITERAL_CSTRING("SELECT object_data_key FROM ") +
839 12 : tableName +
840 36 : NS_LITERAL_CSTRING(" WHERE index_id = :index_id") +
841 24 : keyRangeClause + limitClause;
842 :
843 24 : nsCOMPtr<mozIStorageStatement> stmt = mTransaction->GetCachedStatement(query);
844 12 : NS_ENSURE_TRUE(stmt, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
845 :
846 24 : mozStorageStatementScoper scoper(stmt);
847 :
848 24 : nsresult rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("index_id"),
849 12 : mIndex->Id());
850 12 : NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
851 :
852 12 : if (mKeyRange) {
853 7 : rv = mKeyRange->BindToStatement(stmt);
854 7 : NS_ENSURE_SUCCESS(rv, rv);
855 : }
856 :
857 12 : mKeys.SetCapacity(50);
858 :
859 : bool hasResult;
860 12 : while(NS_SUCCEEDED((rv = stmt->ExecuteStep(&hasResult))) && hasResult) {
861 44 : if (mKeys.Capacity() == mKeys.Length()) {
862 0 : mKeys.SetCapacity(mKeys.Capacity() * 2);
863 : }
864 :
865 44 : Key* key = mKeys.AppendElement();
866 44 : NS_ASSERTION(key, "This shouldn't fail!");
867 :
868 44 : rv = key->SetFromStatement(stmt, 0);
869 44 : NS_ENSURE_SUCCESS(rv, rv);
870 : }
871 12 : NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
872 :
873 12 : return NS_OK;
874 : }
875 :
876 : nsresult
877 12 : GetAllKeysHelper::GetSuccessResult(JSContext* aCx,
878 : jsval* aVal)
879 : {
880 12 : NS_ASSERTION(mKeys.Length() <= mLimit, "Too many results!");
881 :
882 24 : nsTArray<Key> keys;
883 12 : if (!mKeys.SwapElements(keys)) {
884 0 : NS_ERROR("Failed to swap elements!");
885 0 : return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
886 : }
887 :
888 24 : JSAutoRequest ar(aCx);
889 :
890 12 : JSObject* array = JS_NewArrayObject(aCx, 0, NULL);
891 12 : if (!array) {
892 0 : NS_WARNING("Failed to make array!");
893 0 : return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
894 : }
895 :
896 12 : if (!keys.IsEmpty()) {
897 12 : if (!JS_SetArrayLength(aCx, array, uint32_t(keys.Length()))) {
898 0 : NS_WARNING("Failed to set array length!");
899 0 : return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
900 : }
901 :
902 56 : for (uint32 index = 0, count = keys.Length(); index < count; index++) {
903 44 : const Key& key = keys[index];
904 44 : NS_ASSERTION(!key.IsUnset(), "Bad key!");
905 :
906 : jsval value;
907 44 : nsresult rv = key.ToJSVal(aCx, &value);
908 44 : if (NS_FAILED(rv)) {
909 0 : NS_WARNING("Failed to get jsval for key!");
910 0 : return rv;
911 : }
912 :
913 44 : if (!JS_SetElement(aCx, array, index, &value)) {
914 0 : NS_WARNING("Failed to set array element!");
915 0 : return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
916 : }
917 : }
918 : }
919 :
920 12 : *aVal = OBJECT_TO_JSVAL(array);
921 12 : return NS_OK;
922 : }
923 :
924 : nsresult
925 12 : GetAllHelper::DoDatabaseWork(mozIStorageConnection* /* aConnection */)
926 : {
927 24 : nsCString indexTable;
928 12 : if (mIndex->IsUnique()) {
929 0 : indexTable.AssignLiteral("unique_index_data");
930 : }
931 : else {
932 12 : indexTable.AssignLiteral("index_data");
933 : }
934 :
935 24 : nsCString keyRangeClause;
936 12 : if (mKeyRange) {
937 7 : mKeyRange->GetBindingClause(NS_LITERAL_CSTRING("value"), keyRangeClause);
938 : }
939 :
940 24 : nsCString limitClause;
941 12 : if (mLimit != PR_UINT32_MAX) {
942 2 : limitClause = NS_LITERAL_CSTRING(" LIMIT ");
943 2 : limitClause.AppendInt(mLimit);
944 : }
945 :
946 12 : nsCString query = NS_LITERAL_CSTRING("SELECT data, file_ids FROM object_data "
947 12 : "INNER JOIN ") + indexTable +
948 36 : NS_LITERAL_CSTRING(" AS index_table ON object_data.id = "
949 : "index_table.object_data_id "
950 : "WHERE index_id = :index_id") +
951 24 : keyRangeClause + limitClause;
952 :
953 24 : nsCOMPtr<mozIStorageStatement> stmt = mTransaction->GetCachedStatement(query);
954 12 : NS_ENSURE_TRUE(stmt, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
955 :
956 24 : mozStorageStatementScoper scoper(stmt);
957 :
958 24 : nsresult rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("index_id"),
959 12 : mIndex->Id());
960 12 : NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
961 :
962 12 : if (mKeyRange) {
963 7 : rv = mKeyRange->BindToStatement(stmt);
964 7 : NS_ENSURE_SUCCESS(rv, rv);
965 : }
966 :
967 12 : mCloneReadInfos.SetCapacity(50);
968 :
969 : bool hasResult;
970 12 : while(NS_SUCCEEDED((rv = stmt->ExecuteStep(&hasResult))) && hasResult) {
971 44 : if (mCloneReadInfos.Capacity() == mCloneReadInfos.Length()) {
972 0 : mCloneReadInfos.SetCapacity(mCloneReadInfos.Capacity() * 2);
973 : }
974 :
975 44 : StructuredCloneReadInfo* readInfo = mCloneReadInfos.AppendElement();
976 44 : NS_ASSERTION(readInfo, "This shouldn't fail!");
977 :
978 : rv = IDBObjectStore::GetStructuredCloneReadInfoFromStatement(stmt, 0, 1,
979 44 : mDatabase->Manager(), *readInfo);
980 44 : NS_ENSURE_SUCCESS(rv, rv);
981 : }
982 12 : NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
983 :
984 12 : return NS_OK;
985 : }
986 :
987 : nsresult
988 12 : GetAllHelper::GetSuccessResult(JSContext* aCx,
989 : jsval* aVal)
990 : {
991 12 : NS_ASSERTION(mCloneReadInfos.Length() <= mLimit, "Too many results!");
992 :
993 12 : nsresult rv = ConvertCloneReadInfosToArray(aCx, mCloneReadInfos, aVal);
994 :
995 12 : for (PRUint32 index = 0; index < mCloneReadInfos.Length(); index++) {
996 0 : mCloneReadInfos[index].mCloneBuffer.clear();
997 : }
998 :
999 12 : NS_ENSURE_SUCCESS(rv, rv);
1000 12 : return NS_OK;
1001 : }
1002 :
1003 : nsresult
1004 89 : OpenKeyCursorHelper::DoDatabaseWork(mozIStorageConnection* aConnection)
1005 : {
1006 89 : NS_ASSERTION(aConnection, "Passed a null connection!");
1007 :
1008 178 : nsCString table;
1009 89 : if (mIndex->IsUnique()) {
1010 31 : table.AssignLiteral("unique_index_data");
1011 : }
1012 : else {
1013 58 : table.AssignLiteral("index_data");
1014 : }
1015 :
1016 178 : NS_NAMED_LITERAL_CSTRING(value, "value");
1017 :
1018 178 : nsCString keyRangeClause;
1019 89 : if (mKeyRange) {
1020 33 : mKeyRange->GetBindingClause(value, keyRangeClause);
1021 : }
1022 :
1023 178 : nsCAutoString directionClause(" ORDER BY value ");
1024 89 : switch (mDirection) {
1025 : case IDBCursor::NEXT:
1026 : case IDBCursor::NEXT_UNIQUE:
1027 86 : directionClause += NS_LITERAL_CSTRING("ASC, object_data_key ASC");
1028 86 : break;
1029 :
1030 : case IDBCursor::PREV:
1031 2 : directionClause += NS_LITERAL_CSTRING("DESC, object_data_key DESC");
1032 2 : break;
1033 :
1034 : case IDBCursor::PREV_UNIQUE:
1035 1 : directionClause += NS_LITERAL_CSTRING("DESC, object_data_key ASC");
1036 1 : break;
1037 :
1038 : default:
1039 0 : NS_NOTREACHED("Unknown direction!");
1040 : }
1041 89 : nsCString firstQuery = NS_LITERAL_CSTRING("SELECT value, object_data_key "
1042 89 : "FROM ") + table +
1043 267 : NS_LITERAL_CSTRING(" WHERE index_id = :index_id") +
1044 89 : keyRangeClause + directionClause +
1045 356 : NS_LITERAL_CSTRING(" LIMIT 1");
1046 :
1047 : nsCOMPtr<mozIStorageStatement> stmt =
1048 178 : mTransaction->GetCachedStatement(firstQuery);
1049 89 : NS_ENSURE_TRUE(stmt, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
1050 :
1051 178 : mozStorageStatementScoper scoper(stmt);
1052 :
1053 178 : nsresult rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("index_id"),
1054 89 : mIndex->Id());
1055 89 : NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
1056 :
1057 89 : if (mKeyRange) {
1058 33 : rv = mKeyRange->BindToStatement(stmt);
1059 33 : NS_ENSURE_SUCCESS(rv, rv);
1060 : }
1061 :
1062 : bool hasResult;
1063 89 : rv = stmt->ExecuteStep(&hasResult);
1064 89 : NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
1065 :
1066 89 : if (!hasResult) {
1067 15 : mKey.Unset();
1068 15 : return NS_OK;
1069 : }
1070 :
1071 74 : rv = mKey.SetFromStatement(stmt, 0);
1072 74 : NS_ENSURE_SUCCESS(rv, rv);
1073 :
1074 74 : rv = mObjectKey.SetFromStatement(stmt, 1);
1075 74 : NS_ENSURE_SUCCESS(rv, rv);
1076 :
1077 : // Now we need to make the query to get the next match.
1078 74 : nsCAutoString queryStart = NS_LITERAL_CSTRING("SELECT value, object_data_key"
1079 74 : " FROM ") + table +
1080 296 : NS_LITERAL_CSTRING(" WHERE index_id = :id");
1081 :
1082 148 : NS_NAMED_LITERAL_CSTRING(rangeKey, "range_key");
1083 :
1084 74 : switch (mDirection) {
1085 : case IDBCursor::NEXT:
1086 70 : if (mKeyRange && !mKeyRange->Upper().IsUnset()) {
1087 21 : AppendConditionClause(value, rangeKey, true, !mKeyRange->IsUpperOpen(),
1088 21 : queryStart);
1089 21 : mRangeKey = mKeyRange->Upper();
1090 : }
1091 : mContinueQuery =
1092 : queryStart +
1093 140 : NS_LITERAL_CSTRING(" AND value >= :current_key AND "
1094 : "( value > :current_key OR "
1095 : " object_data_key > :object_key )") +
1096 70 : directionClause +
1097 210 : NS_LITERAL_CSTRING(" LIMIT ");
1098 : mContinueToQuery =
1099 : queryStart +
1100 140 : NS_LITERAL_CSTRING(" AND value >= :current_key ") +
1101 70 : directionClause +
1102 210 : NS_LITERAL_CSTRING(" LIMIT ");
1103 70 : break;
1104 :
1105 : case IDBCursor::NEXT_UNIQUE:
1106 1 : if (mKeyRange && !mKeyRange->Upper().IsUnset()) {
1107 1 : AppendConditionClause(value, rangeKey, true, !mKeyRange->IsUpperOpen(),
1108 1 : queryStart);
1109 1 : mRangeKey = mKeyRange->Upper();
1110 : }
1111 : mContinueQuery =
1112 2 : queryStart + NS_LITERAL_CSTRING(" AND value > :current_key") +
1113 1 : directionClause +
1114 3 : NS_LITERAL_CSTRING(" LIMIT ");
1115 : mContinueToQuery =
1116 2 : queryStart + NS_LITERAL_CSTRING(" AND value >= :current_key") +
1117 1 : directionClause +
1118 3 : NS_LITERAL_CSTRING(" LIMIT ");
1119 1 : break;
1120 :
1121 : case IDBCursor::PREV:
1122 2 : if (mKeyRange && !mKeyRange->Lower().IsUnset()) {
1123 0 : AppendConditionClause(value, rangeKey, false, !mKeyRange->IsLowerOpen(),
1124 0 : queryStart);
1125 0 : mRangeKey = mKeyRange->Lower();
1126 : }
1127 :
1128 : mContinueQuery =
1129 : queryStart +
1130 4 : NS_LITERAL_CSTRING(" AND value <= :current_key AND "
1131 : "( value < :current_key OR "
1132 : " object_data_key < :object_key )") +
1133 2 : directionClause +
1134 6 : NS_LITERAL_CSTRING(" LIMIT ");
1135 : mContinueToQuery =
1136 : queryStart +
1137 4 : NS_LITERAL_CSTRING(" AND value <= :current_key ") +
1138 2 : directionClause +
1139 6 : NS_LITERAL_CSTRING(" LIMIT ");
1140 2 : break;
1141 :
1142 : case IDBCursor::PREV_UNIQUE:
1143 1 : if (mKeyRange && !mKeyRange->Lower().IsUnset()) {
1144 0 : AppendConditionClause(value, rangeKey, false, !mKeyRange->IsLowerOpen(),
1145 0 : queryStart);
1146 0 : mRangeKey = mKeyRange->Lower();
1147 : }
1148 : mContinueQuery =
1149 : queryStart +
1150 2 : NS_LITERAL_CSTRING(" AND value < :current_key") +
1151 1 : directionClause +
1152 3 : NS_LITERAL_CSTRING(" LIMIT ");
1153 : mContinueToQuery =
1154 : queryStart +
1155 2 : NS_LITERAL_CSTRING(" AND value <= :current_key") +
1156 1 : directionClause +
1157 3 : NS_LITERAL_CSTRING(" LIMIT ");
1158 1 : break;
1159 :
1160 : default:
1161 0 : NS_NOTREACHED("Unknown direction type!");
1162 : }
1163 :
1164 74 : return NS_OK;
1165 : }
1166 :
1167 : nsresult
1168 89 : OpenKeyCursorHelper::GetSuccessResult(JSContext* aCx,
1169 : jsval* aVal)
1170 : {
1171 89 : if (mKey.IsUnset()) {
1172 15 : *aVal = JSVAL_VOID;
1173 15 : return NS_OK;
1174 : }
1175 :
1176 : nsRefPtr<IDBCursor> cursor =
1177 : IDBCursor::Create(mRequest, mTransaction, mIndex, mDirection, mRangeKey,
1178 148 : mContinueQuery, mContinueToQuery, mKey, mObjectKey);
1179 74 : NS_ENSURE_TRUE(cursor, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
1180 :
1181 74 : return WrapNative(aCx, cursor, aVal);
1182 : }
1183 :
1184 : nsresult
1185 57 : OpenCursorHelper::DoDatabaseWork(mozIStorageConnection* aConnection)
1186 : {
1187 57 : NS_ASSERTION(aConnection, "Passed a null connection!");
1188 :
1189 114 : nsCString indexTable;
1190 57 : if (mIndex->IsUnique()) {
1191 20 : indexTable.AssignLiteral("unique_index_data");
1192 : }
1193 : else {
1194 37 : indexTable.AssignLiteral("index_data");
1195 : }
1196 :
1197 114 : NS_NAMED_LITERAL_CSTRING(value, "index_table.value");
1198 :
1199 114 : nsCString keyRangeClause;
1200 57 : if (mKeyRange) {
1201 34 : mKeyRange->GetBindingClause(value, keyRangeClause);
1202 : }
1203 :
1204 114 : nsCAutoString directionClause(" ORDER BY index_table.value ");
1205 57 : switch (mDirection) {
1206 : case IDBCursor::NEXT:
1207 : case IDBCursor::NEXT_UNIQUE:
1208 : directionClause +=
1209 53 : NS_LITERAL_CSTRING("ASC, index_table.object_data_key ASC");
1210 53 : break;
1211 :
1212 : case IDBCursor::PREV:
1213 : directionClause +=
1214 3 : NS_LITERAL_CSTRING("DESC, index_table.object_data_key DESC");
1215 3 : break;
1216 :
1217 : case IDBCursor::PREV_UNIQUE:
1218 : directionClause +=
1219 1 : NS_LITERAL_CSTRING("DESC, index_table.object_data_key ASC");
1220 1 : break;
1221 :
1222 : default:
1223 0 : NS_NOTREACHED("Unknown direction!");
1224 : }
1225 :
1226 : nsCString firstQuery =
1227 57 : NS_LITERAL_CSTRING("SELECT index_table.value, "
1228 : "index_table.object_data_key, object_data.data, "
1229 : "object_data.file_ids FROM ") +
1230 57 : indexTable +
1231 171 : NS_LITERAL_CSTRING(" AS index_table INNER JOIN object_data ON "
1232 : "index_table.object_data_id = object_data.id "
1233 : "WHERE index_table.index_id = :id") +
1234 57 : keyRangeClause + directionClause +
1235 228 : NS_LITERAL_CSTRING(" LIMIT 1");
1236 :
1237 : nsCOMPtr<mozIStorageStatement> stmt =
1238 114 : mTransaction->GetCachedStatement(firstQuery);
1239 57 : NS_ENSURE_TRUE(stmt, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
1240 :
1241 114 : mozStorageStatementScoper scoper(stmt);
1242 :
1243 57 : nsresult rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("id"), mIndex->Id());
1244 57 : NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
1245 :
1246 57 : if (mKeyRange) {
1247 34 : rv = mKeyRange->BindToStatement(stmt);
1248 34 : NS_ENSURE_SUCCESS(rv, rv);
1249 : }
1250 :
1251 : bool hasResult;
1252 57 : rv = stmt->ExecuteStep(&hasResult);
1253 57 : NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
1254 :
1255 57 : if (!hasResult) {
1256 6 : mKey.Unset();
1257 6 : return NS_OK;
1258 : }
1259 :
1260 51 : rv = mKey.SetFromStatement(stmt, 0);
1261 51 : NS_ENSURE_SUCCESS(rv, rv);
1262 :
1263 51 : rv = mObjectKey.SetFromStatement(stmt, 1);
1264 51 : NS_ENSURE_SUCCESS(rv, rv);
1265 :
1266 : rv = IDBObjectStore::GetStructuredCloneReadInfoFromStatement(stmt, 2, 3,
1267 51 : mDatabase->Manager(), mCloneReadInfo);
1268 51 : NS_ENSURE_SUCCESS(rv, rv);
1269 :
1270 : // Now we need to make the query to get the next match.
1271 : nsCAutoString queryStart =
1272 51 : NS_LITERAL_CSTRING("SELECT index_table.value, "
1273 : "index_table.object_data_key, object_data.data, "
1274 : "object_data.file_ids FROM ") +
1275 51 : indexTable +
1276 204 : NS_LITERAL_CSTRING(" AS index_table INNER JOIN object_data ON "
1277 : "index_table.object_data_id = object_data.id "
1278 : "WHERE index_table.index_id = :id");
1279 :
1280 102 : NS_NAMED_LITERAL_CSTRING(rangeKey, "range_key");
1281 :
1282 102 : NS_NAMED_LITERAL_CSTRING(limit, " LIMIT ");
1283 :
1284 51 : switch (mDirection) {
1285 : case IDBCursor::NEXT:
1286 46 : if (mKeyRange && !mKeyRange->Upper().IsUnset()) {
1287 24 : AppendConditionClause(value, rangeKey, true, !mKeyRange->IsUpperOpen(),
1288 24 : queryStart);
1289 24 : mRangeKey = mKeyRange->Upper();
1290 : }
1291 : mContinueQuery =
1292 : queryStart +
1293 92 : NS_LITERAL_CSTRING(" AND index_table.value >= :current_key AND "
1294 : "( index_table.value > :current_key OR "
1295 : " index_table.object_data_key > :object_key ) ") +
1296 46 : directionClause + limit;
1297 : mContinueToQuery =
1298 : queryStart +
1299 92 : NS_LITERAL_CSTRING(" AND index_table.value >= :current_key") +
1300 46 : directionClause + limit;
1301 46 : break;
1302 :
1303 : case IDBCursor::NEXT_UNIQUE:
1304 1 : if (mKeyRange && !mKeyRange->Upper().IsUnset()) {
1305 1 : AppendConditionClause(value, rangeKey, true, !mKeyRange->IsUpperOpen(),
1306 1 : queryStart);
1307 1 : mRangeKey = mKeyRange->Upper();
1308 : }
1309 : mContinueQuery =
1310 : queryStart +
1311 2 : NS_LITERAL_CSTRING(" AND index_table.value > :current_key") +
1312 1 : directionClause + limit;
1313 : mContinueToQuery =
1314 : queryStart +
1315 2 : NS_LITERAL_CSTRING(" AND index_table.value >= :current_key") +
1316 1 : directionClause + limit;
1317 1 : break;
1318 :
1319 : case IDBCursor::PREV:
1320 3 : if (mKeyRange && !mKeyRange->Lower().IsUnset()) {
1321 1 : AppendConditionClause(value, rangeKey, false, !mKeyRange->IsLowerOpen(),
1322 1 : queryStart);
1323 1 : mRangeKey = mKeyRange->Lower();
1324 : }
1325 : mContinueQuery =
1326 : queryStart +
1327 6 : NS_LITERAL_CSTRING(" AND index_table.value <= :current_key AND "
1328 : "( index_table.value < :current_key OR "
1329 : " index_table.object_data_key < :object_key ) ") +
1330 3 : directionClause + limit;
1331 : mContinueToQuery =
1332 : queryStart +
1333 6 : NS_LITERAL_CSTRING(" AND index_table.value <= :current_key") +
1334 3 : directionClause + limit;
1335 3 : break;
1336 :
1337 : case IDBCursor::PREV_UNIQUE:
1338 1 : if (mKeyRange && !mKeyRange->Lower().IsUnset()) {
1339 0 : AppendConditionClause(value, rangeKey, false, !mKeyRange->IsLowerOpen(),
1340 0 : queryStart);
1341 0 : mRangeKey = mKeyRange->Lower();
1342 : }
1343 : mContinueQuery =
1344 : queryStart +
1345 2 : NS_LITERAL_CSTRING(" AND index_table.value < :current_key") +
1346 1 : directionClause + limit;
1347 : mContinueToQuery =
1348 : queryStart +
1349 2 : NS_LITERAL_CSTRING(" AND index_table.value <= :current_key") +
1350 1 : directionClause + limit;
1351 1 : break;
1352 :
1353 : default:
1354 0 : NS_NOTREACHED("Unknown direction type!");
1355 : }
1356 :
1357 51 : return NS_OK;
1358 : }
1359 :
1360 : nsresult
1361 57 : OpenCursorHelper::GetSuccessResult(JSContext* aCx,
1362 : jsval* aVal)
1363 : {
1364 57 : if (mKey.IsUnset()) {
1365 6 : *aVal = JSVAL_VOID;
1366 6 : return NS_OK;
1367 : }
1368 :
1369 : nsRefPtr<IDBCursor> cursor =
1370 : IDBCursor::Create(mRequest, mTransaction, mIndex, mDirection, mRangeKey,
1371 : mContinueQuery, mContinueToQuery, mKey, mObjectKey,
1372 102 : mCloneReadInfo);
1373 51 : NS_ENSURE_TRUE(cursor, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
1374 :
1375 51 : NS_ASSERTION(!mCloneReadInfo.mCloneBuffer.data(), "Should have swapped!");
1376 :
1377 51 : return WrapNative(aCx, cursor, aVal);
1378 : }
1379 :
1380 : nsresult
1381 39 : CountHelper::DoDatabaseWork(mozIStorageConnection* aConnection)
1382 : {
1383 78 : nsCString table;
1384 39 : if (mIndex->IsUnique()) {
1385 8 : table.AssignLiteral("unique_index_data");
1386 : }
1387 : else {
1388 31 : table.AssignLiteral("index_data");
1389 : }
1390 :
1391 78 : NS_NAMED_LITERAL_CSTRING(lowerKeyName, "lower_key");
1392 78 : NS_NAMED_LITERAL_CSTRING(upperKeyName, "upper_key");
1393 78 : NS_NAMED_LITERAL_CSTRING(value, "value");
1394 :
1395 78 : nsCAutoString keyRangeClause;
1396 39 : if (mKeyRange) {
1397 22 : if (!mKeyRange->Lower().IsUnset()) {
1398 : AppendConditionClause(value, lowerKeyName, false,
1399 16 : !mKeyRange->IsLowerOpen(), keyRangeClause);
1400 : }
1401 22 : if (!mKeyRange->Upper().IsUnset()) {
1402 : AppendConditionClause(value, upperKeyName, true,
1403 15 : !mKeyRange->IsUpperOpen(), keyRangeClause);
1404 : }
1405 : }
1406 :
1407 78 : nsCString query = NS_LITERAL_CSTRING("SELECT count(*) FROM ") + table +
1408 117 : NS_LITERAL_CSTRING(" WHERE index_id = :id") +
1409 78 : keyRangeClause;
1410 :
1411 78 : nsCOMPtr<mozIStorageStatement> stmt = mTransaction->GetCachedStatement(query);
1412 39 : NS_ENSURE_TRUE(stmt, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
1413 :
1414 78 : mozStorageStatementScoper scoper(stmt);
1415 :
1416 39 : nsresult rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("id"), mIndex->Id());
1417 39 : NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
1418 :
1419 39 : if (mKeyRange) {
1420 22 : if (!mKeyRange->Lower().IsUnset()) {
1421 16 : rv = mKeyRange->Lower().BindToStatement(stmt, lowerKeyName);
1422 16 : NS_ENSURE_SUCCESS(rv, rv);
1423 : }
1424 22 : if (!mKeyRange->Upper().IsUnset()) {
1425 15 : rv = mKeyRange->Upper().BindToStatement(stmt, upperKeyName);
1426 15 : NS_ENSURE_SUCCESS(rv, rv);
1427 : }
1428 : }
1429 :
1430 : bool hasResult;
1431 39 : rv = stmt->ExecuteStep(&hasResult);
1432 39 : NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
1433 39 : NS_ENSURE_TRUE(hasResult, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
1434 :
1435 39 : mCount = stmt->AsInt64(0);
1436 39 : return NS_OK;
1437 : }
1438 :
1439 : nsresult
1440 39 : CountHelper::GetSuccessResult(JSContext* aCx,
1441 : jsval* aVal)
1442 : {
1443 39 : return JS_NewNumberValue(aCx, static_cast<double>(mCount), aVal);
1444 4392 : }
|