sbLocalDatabaseGUIDArray.cpp
Go to the documentation of this file.
1 /*
2  *=BEGIN SONGBIRD GPL
3  *
4  * This file is part of the Songbird web player.
5  *
6  * Copyright(c) 2005-2010 POTI, Inc.
7  * http://www.songbirdnest.com
8  *
9  * This file may be licensed under the terms of of the
10  * GNU General Public License Version 2 (the ``GPL'').
11  *
12  * Software distributed under the License is distributed
13  * on an ``AS IS'' basis, WITHOUT WARRANTY OF ANY KIND, either
14  * express or implied. See the GPL for the specific language
15  * governing rights and limitations.
16  *
17  * You should have received a copy of the GPL along with this
18  * program. If not, go to http://www.gnu.org/licenses/gpl.html
19  * or write to the Free Software Foundation, Inc.,
20  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
21  *
22  *=END SONGBIRD GPL
23  */
24 
26 #include "sbLocalDatabaseQuery.h"
31 #include "sbLocalDatabaseLibrary.h"
32 
33 #include <algorithm>
34 
35 #include <DatabaseQuery.h>
36 #include <nsComponentManagerUtils.h>
37 #include <nsServiceManagerUtils.h>
38 #include <nsCOMPtr.h>
39 #include <nsIStringEnumerator.h>
40 #include <nsIURI.h>
41 #include <nsIWeakReference.h>
42 #include <nsMemory.h>
43 #include <nsStringGlue.h>
44 #include <prlog.h>
45 #include <prprf.h>
46 #include <sbSQLBuilderCID.h>
47 #include <sbIDatabaseQuery.h>
48 #include <sbIDatabaseResult.h>
49 #include <sbILibrary.h>
50 #include <sbIPropertyArray.h>
51 #include <sbIPropertyInfo.h>
52 #include <sbIPropertyManager.h>
53 #include <sbILocalDatabasePropertyCache.h>
54 #include <sbISQLBuilder.h>
56 #include <sbPropertiesCID.h>
58 #include <sbStandardProperties.h>
59 #include <sbStringUtils.h>
60 #include <sbMemoryUtils.h>
61 #include <sbThreadUtils.h>
62 
63 #define DEFAULT_FETCH_SIZE 20
64 
65 // Fetch all guids asynchronously, disabled by default.
66 //#define FORCE_FETCH_ALL_GUIDS_ASYNC
67 
68 #define COUNT_COLUMN NS_LITERAL_STRING("count(1)")
69 #define GUID_COLUMN NS_LITERAL_STRING("guid")
70 #define OBJSORTABLE_COLUMN NS_LITERAL_STRING("obj_sortable")
71 #define MEDIAITEMID_COLUMN NS_LITERAL_STRING("media_item_id")
72 #define PROPERTIES_TABLE NS_LITERAL_STRING("resource_properties")
73 #define MEDIAITEMS_TABLE NS_LITERAL_STRING("media_items")
74 
75 #ifdef PR_LOGGING
76 static const PRLogModuleInfo *gLocalDatabaseGUIDArrayLog = nsnull;
77 #endif /* PR_LOGGING */
78 
79 #define TRACE(args) PR_LOG(gLocalDatabaseGUIDArrayLog, PR_LOG_DEBUG, args)
80 #define LOG(args) PR_LOG(gLocalDatabaseGUIDArrayLog, PR_LOG_WARN, args)
81 
85 
87  mNeedNewKey(PR_FALSE),
88  mBaseConstraintValue(0),
89  mFetchSize(DEFAULT_FETCH_SIZE),
90  mLength(0),
91  mPrimarySortsCount(0),
92  mCacheMonitor(nsnull),
93  mIsDistinct(PR_FALSE),
94  mDistinctWithSortableValues(PR_FALSE),
95  mValid(PR_FALSE),
96  mQueriesValid(PR_FALSE),
97  mNullsFirst(PR_FALSE),
98  mPrefetchedRows(PR_FALSE),
99  mIsFullLibrary(PR_FALSE),
100  mSuppress(0)
101 {
102 #ifdef PR_LOGGING
103  if (!gLocalDatabaseGUIDArrayLog) {
104  gLocalDatabaseGUIDArrayLog = PR_NewLogModule("sbLocalDatabaseGUIDArray");
105  }
106 #endif
107  mPropIdsLock =
108  nsAutoLock::NewLock("sbLocalDatabaseGUIDArray::mPropIdsLock");
109 
110  mCacheMonitor =
111  nsAutoMonitor::NewMonitor("sbLocalDatabaseGUIDArray::mCacheMonitor");
112  NS_WARN_IF_FALSE(mCacheMonitor, "Failed to create mCacheMonitor.");
113 }
114 
115 sbLocalDatabaseGUIDArray::~sbLocalDatabaseGUIDArray()
116 {
117  // We need to remove the cached lengths when a guid array is destroyed
118  // because it may have been used as a transient array for enumeration
119  // of items in a medialist or library which will make the cached lengths
120  // invalid.
121  if(mLengthCache && !mCachedLengthKey.IsEmpty()) {
122  mLengthCache->RemoveCachedLength(mCachedLengthKey);
123  mLengthCache->RemoveCachedNonNullLength(mCachedLengthKey);
124  }
125 
126  if(mCacheMonitor) {
127  nsAutoMonitor::DestroyMonitor(mCacheMonitor);
128  }
129 
130  if (mPropIdsLock) {
131  nsAutoLock::DestroyLock(mPropIdsLock);
132  }
133 }
134 
135 NS_IMETHODIMP
136 sbLocalDatabaseGUIDArray::GetDatabaseGUID(nsAString& aDatabaseGUID)
137 {
138  aDatabaseGUID = mDatabaseGUID;
139 
140  return NS_OK;
141 }
142 NS_IMETHODIMP
143 sbLocalDatabaseGUIDArray::SetDatabaseGUID(const nsAString& aDatabaseGUID)
144 {
145  mDatabaseGUID = aDatabaseGUID;
146 
147  QueryInvalidate();
148 
149  return Invalidate(PR_FALSE);
150 }
151 
152 NS_IMETHODIMP
153 sbLocalDatabaseGUIDArray::GetDatabaseLocation(nsIURI** aDatabaseLocation)
154 {
155  NS_ENSURE_ARG_POINTER(aDatabaseLocation);
156 
157  // Okay if it's null.
158  if (!mDatabaseLocation) {
159  *aDatabaseLocation = nsnull;
160  return NS_OK;
161  }
162 
163  nsresult rv = mDatabaseLocation->Clone(aDatabaseLocation);
164  NS_ENSURE_SUCCESS(rv, rv);
165 
166  return NS_OK;
167 }
168 
169 NS_IMETHODIMP
170 sbLocalDatabaseGUIDArray::SetDatabaseLocation(nsIURI* aDatabaseLocation)
171 {
172  mDatabaseLocation = aDatabaseLocation;
173 
174  QueryInvalidate();
175 
176  return Invalidate(PR_FALSE);
177 }
178 
179 NS_IMETHODIMP
180 sbLocalDatabaseGUIDArray::GetBaseTable(nsAString& aBaseTable)
181 {
182  aBaseTable = mBaseTable;
183 
184  return NS_OK;
185 }
186 NS_IMETHODIMP
187 sbLocalDatabaseGUIDArray::SetBaseTable(const nsAString& aBaseTable)
188 {
189  mBaseTable = aBaseTable;
190 
191  QueryInvalidate();
192 
193  return Invalidate(PR_FALSE);
194 }
195 
196 NS_IMETHODIMP
197 sbLocalDatabaseGUIDArray::GetBaseConstraintColumn(nsAString& aBaseConstraintColumn)
198 {
199  aBaseConstraintColumn = mBaseConstraintColumn;
200 
201  return NS_OK;
202 }
203 NS_IMETHODIMP
204 sbLocalDatabaseGUIDArray::SetBaseConstraintColumn(const nsAString& aBaseConstraintColumn)
205 {
206  mBaseConstraintColumn = aBaseConstraintColumn;
207 
208  QueryInvalidate();
209 
210  return Invalidate(PR_FALSE);
211 }
212 
213 NS_IMETHODIMP
214 sbLocalDatabaseGUIDArray::GetBaseConstraintValue(PRUint32 *aBaseConstraintValue)
215 {
216  NS_ENSURE_ARG_POINTER(aBaseConstraintValue);
217  *aBaseConstraintValue = mBaseConstraintValue;
218 
219  return NS_OK;
220 }
221 NS_IMETHODIMP
222 sbLocalDatabaseGUIDArray::SetBaseConstraintValue(PRUint32 aBaseConstraintValue)
223 {
224  mBaseConstraintValue = aBaseConstraintValue;
225 
226  QueryInvalidate();
227 
228  return Invalidate(PR_FALSE);
229 }
230 
231 NS_IMETHODIMP
232 sbLocalDatabaseGUIDArray::GetFetchSize(PRUint32 *aFetchSize)
233 {
234  NS_ENSURE_ARG_POINTER(aFetchSize);
235  *aFetchSize = mFetchSize;
236 
237  return NS_OK;
238 }
239 NS_IMETHODIMP
240 sbLocalDatabaseGUIDArray::SetFetchSize(PRUint32 aFetchSize)
241 {
242  mFetchSize = aFetchSize;
243 
244  return NS_OK;
245 }
246 
247 NS_IMETHODIMP
248 sbLocalDatabaseGUIDArray::GetIsDistinct(PRBool *aIsDistinct)
249 {
250  NS_ENSURE_ARG_POINTER(aIsDistinct);
251  *aIsDistinct = mIsDistinct;
252  return NS_OK;
253 }
254 NS_IMETHODIMP
255 sbLocalDatabaseGUIDArray::SetIsDistinct(PRBool aIsDistinct)
256 {
257  mIsDistinct = aIsDistinct;
258 
259  QueryInvalidate();
260 
261  return Invalidate(PR_FALSE);
262 }
263 
264 NS_IMETHODIMP
265 sbLocalDatabaseGUIDArray::GetIsValid(PRBool *aIsValid)
266 {
267  NS_ENSURE_ARG_POINTER(aIsValid);
268  *aIsValid = mValid;
269  return NS_OK;
270 }
271 
272 NS_IMETHODIMP
273 sbLocalDatabaseGUIDArray::GetDistinctWithSortableValues(PRBool *aDistinctWithSortableValues)
274 {
275  NS_ENSURE_ARG_POINTER(aDistinctWithSortableValues);
276  *aDistinctWithSortableValues = mDistinctWithSortableValues;
277  return NS_OK;
278 }
279 
280 NS_IMETHODIMP
281 sbLocalDatabaseGUIDArray::SetDistinctWithSortableValues(PRBool aDistinctWithSortableValues)
282 {
283  mDistinctWithSortableValues = aDistinctWithSortableValues;
284 
285  QueryInvalidate();
286 
287  return Invalidate(PR_FALSE);
288 }
289 
290 
291 NS_IMETHODIMP
292 sbLocalDatabaseGUIDArray::GetListener(sbILocalDatabaseGUIDArrayListener** aListener)
293 {
294  NS_ENSURE_ARG_POINTER(aListener);
295 
296  if (mListener) {
297  nsresult rv;
298  nsCOMPtr<sbILocalDatabaseGUIDArrayListener> listener =
299  do_QueryReferent(mListener, &rv);
300  NS_ENSURE_SUCCESS(rv, rv);
301 
302  if (listener) {
303  NS_ADDREF(*aListener = listener);
304  return NS_OK;
305  }
306  }
307 
308  *aListener = nsnull;
309  return NS_OK;
310 }
311 
312 NS_IMETHODIMP
313 sbLocalDatabaseGUIDArray::SetListener(sbILocalDatabaseGUIDArrayListener* aListener)
314 {
315  nsresult rv = NS_OK;
316 
317  if (aListener) {
318  mListener = do_GetWeakReference(aListener, &rv);
319  }
320  else {
321  mListener = nsnull;
322  }
323  NS_ENSURE_SUCCESS(rv, rv);
324 
325  return NS_OK;
326 }
327 
328 NS_IMETHODIMP
329 sbLocalDatabaseGUIDArray::GetPropertyCache(sbILocalDatabasePropertyCache** aPropertyCache)
330 {
331  NS_ENSURE_ARG_POINTER(aPropertyCache);
332  NS_IF_ADDREF(*aPropertyCache = mPropertyCache);
333  return NS_OK;
334 }
335 NS_IMETHODIMP
336 sbLocalDatabaseGUIDArray::SetPropertyCache(sbILocalDatabasePropertyCache* aPropertyCache)
337 {
338  // If we already had a property cache we need to remove the guid array from
339  // the list of depedent guid arrays for the property cache.
340  if(mPropertyCache) {
341  mPropertyCache->RemoveDependentGUIDArray(this);
342  }
343 
344  mPropertyCache = aPropertyCache;
345 
346  // Add dependent guid array
347  if(mPropertyCache) {
348  mPropertyCache->AddDependentGUIDArray(this);
349  }
350 
351  return NS_OK;
352 }
353 
354 NS_IMETHODIMP
355 sbLocalDatabaseGUIDArray::GetLength(PRUint32 *aLength)
356 {
357  NS_ENSURE_ARG_POINTER(aLength);
358  nsresult rv;
359 
360  if (mValid == PR_FALSE) {
361  rv = Initialize();
362  NS_ENSURE_SUCCESS(rv, rv);
363  }
364 
365  *aLength = mLength;
366 
367  return NS_OK;
368 }
369 
370 NS_IMETHODIMP
371 sbLocalDatabaseGUIDArray::SetLengthCache(
373 {
374  mLengthCache = aLengthCache;
375 
376  return NS_OK;
377 }
378 
379 NS_IMETHODIMP
380 sbLocalDatabaseGUIDArray::GetLengthCache(
382 {
383  NS_ENSURE_ARG_POINTER(aLengthCache);
384 
385  NS_IF_ADDREF(*aLengthCache = mLengthCache);
386 
387  return NS_OK;
388 }
389 
390 nsresult sbLocalDatabaseGUIDArray::AddSortInternal(const nsAString& aProperty,
391  PRBool aAscending,
392  PRBool aSecondary) {
393 
394  // TODO: Check for valid properties
395  SortSpec* ss = mSorts.AppendElement();
396  NS_ENSURE_TRUE(ss, NS_ERROR_OUT_OF_MEMORY);
397 
398  // Can't sort by ordinal for media_item table default to created
399  if (aProperty.Equals(NS_LITERAL_STRING(SB_PROPERTY_ORDINAL)) &&
400  !mBaseTable.Equals(NS_LITERAL_STRING("simple_media_lists"))) {
401  NS_WARNING("Trying to sort by ordinal on a non-media list");
402  ss->property = NS_LITERAL_STRING(SB_PROPERTY_CREATED);
403  }
404  else {
405  ss->property = aProperty;
406  }
407  ss->ascending = aAscending;
408  ss->secondary = aSecondary;
409 
410  if (mPropertyCache) {
411  nsresult rv = mPropertyCache->GetPropertyDBID(aProperty, &ss->propertyId);
412  NS_ENSURE_SUCCESS(rv, rv);
413  }
414 
415  return NS_OK;
416 }
417 
418 nsresult sbLocalDatabaseGUIDArray::ClearSecondarySorts() {
419  for (PRUint32 i = 0; i < mSorts.Length(); i++) {
420  const SortSpec& ss = mSorts[i];
421  if (ss.secondary) {
422  mSorts.RemoveElementAt(i);
423  i--;
424  }
425  }
426  return NS_OK;
427 }
428 
429 namespace
430 {
434  const PRUint32 * FindPropID(const PRUint32 * aPropIDs,
435  PRUint32 aCount,
436  PRUint32 aPropID)
437  {
438  const PRUint32 * result = std::lower_bound(aPropIDs, aPropIDs + aCount, aPropID);
439  if (result != aPropIDs + aCount && *result == aPropID) {
440  return result;
441  }
442  return aPropIDs + aCount;
443  }
444 }
445 
446 NS_IMETHODIMP
447 sbLocalDatabaseGUIDArray::MayInvalidate(PRUint32 * aDirtyPropIDs,
448  PRUint32 aCount)
449 {
450  PRUint32 propertyDBID = 0;
451  nsresult rv = NS_ERROR_UNEXPECTED;
452 
453 
454  // First we check to see if we need to remove cached lengths.
455  if (mLengthCache) {
456  nsAutoLock mon(mPropIdsLock);
457  for (PRUint32 index = 0; index < aCount; ++index) {
458  std::set<PRUint32>::iterator itEntry =
459  mPropIdsUsedInCacheKey.find(aDirtyPropIDs[index]);
460  if(itEntry != mPropIdsUsedInCacheKey.end()) {
461  mLengthCache->RemoveCachedLength(mCachedLengthKey);
462  mLengthCache->RemoveCachedNonNullLength(mCachedLengthKey);
463  break;
464  }
465  }
466  }
467 
468  // Calc the end of the collection for iterator purposes
469  const PRUint32 * const dirtyPropIDsEnd = aDirtyPropIDs + aCount;
470 
471  // Go through the filters and see if we should invalidate
472  PRUint32 filterCount = mFilters.Length();
473  for (PRUint32 index = 0; index < filterCount; index++) {
474  const FilterSpec& refSpec = mFilters.ElementAt(index);
475 
476  rv = mPropertyCache->GetPropertyDBID(refSpec.property, &propertyDBID);
477  if(NS_FAILED(rv)) {
478  continue;
479  }
480 
481  const PRUint32 * const found = FindPropID(aDirtyPropIDs,
482  aCount,
483  propertyDBID);
484 
485  if (found != dirtyPropIDsEnd) {
486  // Return right away, no use in continuing if we're invalid.
487  return Invalidate(PR_TRUE);
488  }
489  }
490 
491  // Go through the properties we use to sort to see if we should invalidate.
492  PRUint32 sortCount = mSorts.Length();
493  for (PRUint32 index = 0; index < sortCount; index++) {
494  const SortSpec& sortSpec = mSorts.ElementAt(index);
495 
496  const PRUint32 * const found = FindPropID(aDirtyPropIDs,
497  aCount,
498  sortSpec.propertyId);
499  if (found != dirtyPropIDsEnd) {
500  // Return right away, no use in continuing if we're invalid.
501  return Invalidate(PR_TRUE);
502  }
503  }
504 
505  return NS_OK;
506 }
507 
508 NS_IMETHODIMP
509 sbLocalDatabaseGUIDArray::AddSort(const nsAString& aProperty,
510  PRBool aAscending)
511 {
512  nsresult rv;
513 
514  // clear any existing secondary sort, appending this primary sort
515  // will add its own secondary sorts.
516  rv = ClearSecondarySorts();
517  NS_ENSURE_SUCCESS(rv, rv);
518 
519  // add the primary sort
520  rv = AddSortInternal(aProperty, aAscending, PR_FALSE);
521  NS_ENSURE_SUCCESS(rv, rv);
522 
523  mPrimarySortsCount++;
524 
525  if (!mIsDistinct) {
526 
527  if (!mPropMan) {
528  mPropMan = do_GetService(SB_PROPERTYMANAGER_CONTRACTID, &rv);
529  NS_ENSURE_SUCCESS(rv, rv);
530  }
531 
532  // read the list of secondary sort properties for this primary sort
533  nsCOMPtr<sbIPropertyInfo> propertyInfo;
534  rv = mPropMan->GetPropertyInfo(aProperty,
535  getter_AddRefs(propertyInfo));
536  NS_ENSURE_SUCCESS(rv, rv);
537 
538  nsCOMPtr<sbIPropertyArray> secondarySortProperties;
539  rv =
540  propertyInfo->GetSecondarySort(getter_AddRefs(secondarySortProperties));
541  NS_ENSURE_SUCCESS(rv, rv);
542 
543  if (secondarySortProperties) {
544  PRUint32 secondarySortPropertyCount;
545  rv = secondarySortProperties->GetLength(&secondarySortPropertyCount);
546  NS_ENSURE_SUCCESS(rv, rv);
547 
548  // for all secondary sort properties, add the sort to mSorts
549  for (PRUint32 i=0; i<secondarySortPropertyCount; i++) {
550  nsCOMPtr<sbIProperty> property;
551  rv = secondarySortProperties->GetPropertyAt(i, getter_AddRefs(property));
552  // we cannot support secondary sort on toplevel properties, so skip them
553  if (!SB_IsTopLevelProperty(aProperty)) {
554  // don't completely fail AddSort when a dependent property is not found,
555  // just don't add that secondary sort
556  NS_ASSERTION(NS_SUCCEEDED(rv), "Failed to get dependent property!");
557  if (NS_SUCCEEDED(rv)) {
558  nsString propertyID;
559  rv = property->GetId(propertyID);
560  NS_ASSERTION(NS_SUCCEEDED(rv), "Failed to get dependent property ID!");
561  if (NS_SUCCEEDED(rv)) {
562  nsString propertyValue;
563  rv = property->GetValue(propertyValue);
564  NS_ASSERTION(NS_SUCCEEDED(rv),
565  "Failed to get dependent property value!");
566  if (NS_SUCCEEDED(rv)) {
567  AddSortInternal(propertyID,
568  propertyValue.EqualsLiteral("a"),
569  PR_TRUE);
570  }
571  }
572  }
573  }
574  }
575  }
576  }
577 
578  QueryInvalidate();
579 
580  return Invalidate(PR_FALSE);
581 }
582 
583 NS_IMETHODIMP
584 sbLocalDatabaseGUIDArray::ClearSorts()
585 {
586  mSorts.Clear();
587 
588  mPrimarySortsCount = 0;
589 
590  QueryInvalidate();
591 
592  return Invalidate(PR_FALSE);
593 }
594 
595 NS_IMETHODIMP
596 sbLocalDatabaseGUIDArray::GetCurrentSort(sbIPropertyArray** aCurrentSort)
597 {
598  NS_ENSURE_ARG_POINTER(aCurrentSort);
599 
600  nsresult rv;
601 
602  nsCOMPtr<sbIMutablePropertyArray> sort =
603  do_CreateInstance(SB_MUTABLEPROPERTYARRAY_CONTRACTID, &rv);
604  NS_ENSURE_SUCCESS(rv, rv);
605 
606  rv = sort->SetStrict(PR_FALSE);
607  NS_ENSURE_SUCCESS(rv, rv);
608 
609  for (PRUint32 i = 0; i < mSorts.Length(); i++) {
610  const SortSpec& ss = mSorts[i];
611  if (!ss.secondary) {
612  rv = sort->AppendProperty(ss.property,
613  ss.ascending ? NS_LITERAL_STRING("a") :
614  NS_LITERAL_STRING("d"));
615  NS_ENSURE_SUCCESS(rv, rv);
616  }
617  }
618 
619  NS_ADDREF(*aCurrentSort = sort);
620  return NS_OK;
621 }
622 
623 NS_IMETHODIMP
624 sbLocalDatabaseGUIDArray::AddFilter(const nsAString& aProperty,
625  nsIStringEnumerator *aValues,
626  PRBool aIsSearch)
627 {
628  NS_ENSURE_ARG_POINTER(aValues);
629 
630  nsresult rv;
631 
632  FilterSpec* fs = mFilters.AppendElement();
633  NS_ENSURE_TRUE(fs, NS_ERROR_OUT_OF_MEMORY);
634 
635  fs->property = aProperty;
636  fs->isSearch = aIsSearch;
637 
638  // Copy the values from the enumerator into an array
639  PRBool hasMore;
640  rv = aValues->HasMore(&hasMore);
641  NS_ENSURE_SUCCESS(rv, rv);
642  while (hasMore) {
643  nsAutoString s;
644  rv = aValues->GetNext(s);
645  NS_ENSURE_SUCCESS(rv, rv);
646  nsString* success = fs->values.AppendElement(s);
647  NS_ENSURE_TRUE(success, NS_ERROR_OUT_OF_MEMORY);
648  rv = aValues->HasMore(&hasMore);
649  NS_ENSURE_SUCCESS(rv, rv);
650  }
651 
652  QueryInvalidate();
653 
654  return Invalidate(PR_FALSE);
655 }
656 
657 NS_IMETHODIMP
658 sbLocalDatabaseGUIDArray::ClearFilters()
659 {
660  mFilters.Clear();
661 
662  QueryInvalidate();
663 
664  return Invalidate(PR_FALSE);
665 }
666 
667 NS_IMETHODIMP
668 sbLocalDatabaseGUIDArray::IsIndexCached(PRUint32 aIndex,
669  PRBool *_retval)
670 {
671  NS_ENSURE_ARG_POINTER(_retval);
672 
673  {
674  nsAutoMonitor mon(mCacheMonitor);
675  if (aIndex < mCache.Length()) {
676  ArrayItem* item = mCache[aIndex];
677  if (item) {
678  *_retval = PR_TRUE;
679  return NS_OK;
680  }
681  }
682  }
683 
684  *_retval = PR_FALSE;
685  return NS_OK;
686 }
687 
688 NS_IMETHODIMP
689 sbLocalDatabaseGUIDArray::GetSortPropertyValueByIndex(PRUint32 aIndex,
690  nsAString& _retval)
691 {
692  nsresult rv;
693 
694  ArrayItem* item;
695  rv = GetByIndexInternal(aIndex, &item);
696  if (rv == NS_ERROR_INVALID_ARG) {
697  return rv;
698  }
699  NS_ENSURE_SUCCESS(rv, rv);
700 
701  _retval.Assign(item->sortPropertyValue);
702  return NS_OK;
703 }
704 
705 NS_IMETHODIMP
706 sbLocalDatabaseGUIDArray::GetMediaItemIdByIndex(PRUint32 aIndex,
707  PRUint32* _retval)
708 {
709  NS_ENSURE_ARG_POINTER(_retval);
710 
711  nsresult rv;
712 
713  ArrayItem* item;
714  rv = GetByIndexInternal(aIndex, &item);
715  if (rv == NS_ERROR_INVALID_ARG) {
716  return rv;
717  }
718  NS_ENSURE_SUCCESS(rv, rv);
719 
720  *_retval = item->mediaItemId;
721  return NS_OK;
722 }
723 
724 NS_IMETHODIMP
725 sbLocalDatabaseGUIDArray::GetOrdinalByIndex(PRUint32 aIndex,
726  nsAString& _retval)
727 {
728  nsresult rv;
729 
730  ArrayItem* item;
731  rv = GetByIndexInternal(aIndex, &item);
732  if (rv == NS_ERROR_INVALID_ARG) {
733  return rv;
734  }
735  NS_ENSURE_SUCCESS(rv, rv);
736 
737  _retval.Assign(item->ordinal);
738  return NS_OK;
739 }
740 
741 NS_IMETHODIMP
742 sbLocalDatabaseGUIDArray::GetGuidByIndex(PRUint32 aIndex,
743  nsAString& _retval)
744 {
745  nsresult rv;
746 
747  ArrayItem* item;
748  rv = GetByIndexInternal(aIndex, &item);
749  if (rv == NS_ERROR_INVALID_ARG) {
750  return rv;
751  }
752  NS_ENSURE_SUCCESS(rv, rv);
753 
754  _retval.Assign(item->guid);
755  return NS_OK;
756 }
757 
758 NS_IMETHODIMP
759 sbLocalDatabaseGUIDArray::GetRowidByIndex(PRUint32 aIndex,
760  PRUint64* _retval)
761 {
762  NS_ENSURE_ARG_POINTER(_retval);
763  nsresult rv;
764 
765  ArrayItem* item;
766  rv = GetByIndexInternal(aIndex, &item);
767  if (rv == NS_ERROR_INVALID_ARG) {
768  return rv;
769  }
770  NS_ENSURE_SUCCESS(rv, rv);
771 
772  *_retval = item->rowid;
773  return NS_OK;
774 }
775 
776 NS_IMETHODIMP
777 sbLocalDatabaseGUIDArray::GetViewItemUIDByIndex(PRUint32 aIndex,
778  nsAString& _retval)
779 {
780  nsresult rv;
781 
782  ArrayItem* item;
783  rv = GetByIndexInternal(aIndex, &item);
784  if (rv == NS_ERROR_INVALID_ARG) {
785  return rv;
786  }
787  NS_ENSURE_SUCCESS(rv, rv);
788 
789  // the viewItemUID is just a concatenation of rowid and mediaitemid in the
790  // form: "rowid-mediaitemid"
791  _retval.Truncate();
792  AppendInt(_retval, item->rowid);
793  _retval.Append('-');
794  _retval.AppendInt(item->mediaItemId);
795  return NS_OK;
796 }
797 
798 NS_IMETHODIMP
799 sbLocalDatabaseGUIDArray::Invalidate(PRBool aInvalidateLength)
800 {
801  TRACE(("sbLocalDatabaseGUIDArray[0x%.8x] - Invalidate", this));
802 
803  // Even if the GUID array is already invalid we'll invalidate the cached
804  // length to ensure that everything is consistent. We need to do this because
805  // certain operations don't invalidate the cached length which causes the array
806  // to be in an invalid state. This same array is later invalidated again but
807  // the length should also be invalidated.
808  if (aInvalidateLength) {
809  if (mLengthCache) {
810  mLengthCache->RemoveCachedLength(mCachedLengthKey);
811  mLengthCache->RemoveCachedNonNullLength(mCachedLengthKey);
812  }
813 
814  // After we've invalidated we're likely to need a new key.
815  mNeedNewKey = PR_TRUE;
816  }
817 
818  if (mValid == PR_FALSE || mSuppress > 0) {
819  return NS_OK;
820  }
821  nsresult rv;
822 
823  nsCOMPtr<sbILocalDatabaseGUIDArrayListener> listener;
824  rv = GetMTListener(getter_AddRefs(listener));
825  NS_ENSURE_SUCCESS(rv, rv);
826 
827  if (listener) {
828  listener->OnBeforeInvalidate(aInvalidateLength);
829  }
830 
831  // Scope for monitor.
832  {
833  nsAutoMonitor mon(mCacheMonitor);
834 
835  mCache.Clear();
836  mGuidToFirstIndexMap.Clear();
837  mViewItemUIDToIndexMap.Clear();
838  mPrefetchedRows = PR_FALSE;
839 
840  if (mPrimarySortKeyPositionCache.IsInitialized()) {
841  mPrimarySortKeyPositionCache.Clear();
842  }
843 
844  mValid = PR_FALSE;
845  }
846 
847  rv = GetMTListener(getter_AddRefs(listener));
848  NS_ENSURE_SUCCESS(rv, rv);
849  if (listener) {
850  listener->OnAfterInvalidate();
851  }
852 
853  return NS_OK;
854 }
855 
860 NS_IMETHODIMP
861 sbLocalDatabaseGUIDArray::Clone(sbILocalDatabaseGUIDArray** _retval)
862 {
863  NS_ENSURE_ARG_POINTER(_retval);
864 
865  sbLocalDatabaseGUIDArray* newArray;
866  NS_NEWXPCOM(newArray, sbLocalDatabaseGUIDArray);
867  NS_ENSURE_TRUE(newArray, NS_ERROR_OUT_OF_MEMORY);
868 
869  nsCOMPtr<sbILocalDatabaseGUIDArray> guidArray(newArray);
870  nsresult rv = CloneInto(guidArray);
871  NS_ENSURE_SUCCESS(rv, rv);
872 
873  guidArray.forget(_retval);
874 
875  return NS_OK;
876 }
877 
878 NS_IMETHODIMP
879 sbLocalDatabaseGUIDArray::CloneInto(sbILocalDatabaseGUIDArray* aDest)
880 {
881  NS_ENSURE_ARG_POINTER(aDest);
882 
883  nsresult rv = aDest->SetDatabaseGUID(mDatabaseGUID);
884  NS_ENSURE_SUCCESS(rv, rv);
885 
886  rv = aDest->SetDatabaseLocation(mDatabaseLocation);
887  NS_ENSURE_SUCCESS(rv, rv);
888 
889  rv = aDest->SetBaseTable(mBaseTable);
890  NS_ENSURE_SUCCESS(rv, rv);
891 
892  rv = aDest->SetBaseConstraintColumn(mBaseConstraintColumn);
893  NS_ENSURE_SUCCESS(rv, rv);
894 
895  rv = aDest->SetBaseConstraintValue(mBaseConstraintValue);
896  NS_ENSURE_SUCCESS(rv, rv);
897 
898  rv = aDest->SetFetchSize(mFetchSize);
899  NS_ENSURE_SUCCESS(rv, rv);
900 
901  rv = aDest->SetPropertyCache(mPropertyCache);
902  NS_ENSURE_SUCCESS(rv, rv);
903 
904  rv = aDest->SetIsDistinct(mIsDistinct);
905  NS_ENSURE_SUCCESS(rv, rv);
906 
907  rv = aDest->SetDistinctWithSortableValues(mDistinctWithSortableValues);
908  NS_ENSURE_SUCCESS(rv, rv);
909 
910  PRUint32 sortCount = mSorts.Length();
911  for (PRUint32 index = 0; index < sortCount; index++) {
912  const SortSpec refSpec = mSorts.ElementAt(index);
913  if (!refSpec.secondary) {
914  rv = aDest->AddSort(refSpec.property, refSpec.ascending);
915  NS_ENSURE_SUCCESS(rv, rv);
916  }
917  }
918 
919  PRUint32 filterCount = mFilters.Length();
920  for (PRUint32 index = 0; index < filterCount; index++) {
921  const FilterSpec refSpec = mFilters.ElementAt(index);
922 
923  nsTArray<nsString>* stringArray =
924  const_cast<nsTArray<nsString>*>(&refSpec.values);
925  NS_ENSURE_STATE(stringArray);
926 
927  nsCOMPtr<nsIStringEnumerator> enumerator =
928  new sbTArrayStringEnumerator(stringArray);
929  NS_ENSURE_TRUE(enumerator, NS_ERROR_OUT_OF_MEMORY);
930 
931  rv = aDest->AddFilter(refSpec.property, enumerator, refSpec.isSearch);
932  NS_ENSURE_SUCCESS(rv, rv);
933  }
934 
935  rv = aDest->SetLengthCache(mLengthCache);
936  NS_ENSURE_SUCCESS(rv, rv);
937 
938  return NS_OK;
939 }
940 
941 NS_IMETHODIMP
942 sbLocalDatabaseGUIDArray::RemoveByIndex(PRUint32 aIndex)
943 {
944  nsresult rv;
945 
946  nsAutoMonitor mon(mCacheMonitor);
947 
948  if (mValid == PR_FALSE) {
949  rv = Initialize();
950  NS_ENSURE_SUCCESS(rv, rv);
951  }
952 
953  NS_ENSURE_TRUE(aIndex < mLength, NS_ERROR_INVALID_ARG);
954 
955  // Remove the specified element from the cache
956  {
957  if (aIndex < mCache.Length()) {
958  nsString guid;
959  rv = GetGuidByIndex(aIndex, guid);
960  NS_ENSURE_SUCCESS(rv, rv);
961  mGuidToFirstIndexMap.Remove(guid);
962 
963  nsString viewItemUID;
964  rv = GetViewItemUIDByIndex(aIndex, viewItemUID);
965  NS_ENSURE_SUCCESS(rv, rv);
966 
967  mViewItemUIDToIndexMap.Remove(viewItemUID);
968 
969  mCache.RemoveElementAt(aIndex);
970  }
971  }
972 
973  // Adjust the null length of the array. Made sure we decrement the non
974  // null lengths only if the removed element lies within that area
975  if (mNullsFirst) {
976  if (aIndex < mNonNullLength) {
977  mNonNullLength--;
978  }
979  }
980  else {
981  if (aIndex > mLength - mNonNullLength - 1) {
982  mNonNullLength--;
983  }
984  }
985 
986  // Finally, adjust the size of the full array
987  mLength--;
988 
989  // Remove Cached Lengths
990  if (mLengthCache) {
991  mLengthCache->RemoveCachedLength(mCachedLengthKey);
992  mLengthCache->RemoveCachedNonNullLength(mCachedLengthKey);
993  }
994 
995  return NS_OK;
996 }
997 
998 NS_IMETHODIMP
999 sbLocalDatabaseGUIDArray::GetFirstIndexByPrefix(const nsAString& aValue,
1000  PRUint32* _retval)
1001 {
1002  NS_ENSURE_ARG_POINTER(_retval);
1003 
1004  nsresult rv;
1005  PRInt32 dbOk;
1006 
1007  if (mValid == PR_FALSE) {
1008  rv = Initialize();
1009  NS_ENSURE_SUCCESS(rv, rv);
1010  }
1011 
1012  nsCOMPtr<sbIDatabaseQuery> query;
1013  rv = MakeQuery(mPrefixSearchStatement, getter_AddRefs(query));
1014  NS_ENSURE_SUCCESS(rv, rv);
1015 
1016  rv = query->BindStringParameter(0, aValue);
1017  NS_ENSURE_SUCCESS(rv, rv);
1018 
1019  rv = query->Execute(&dbOk);
1020  NS_ENSURE_SUCCESS(rv, rv);
1021  NS_ENSURE_TRUE(dbOk == 0, NS_ERROR_FAILURE);
1022 
1023  nsCOMPtr<sbIDatabaseResult> result;
1024  rv = query->GetResultObject(getter_AddRefs(result));
1025  NS_ENSURE_TRUE(result, NS_ERROR_FAILURE);
1026 
1027  PRUint32 rowCount;
1028  rv = result->GetRowCount(&rowCount);
1029  NS_ENSURE_SUCCESS(rv, rv);
1030 
1031  if (rowCount == 0) {
1032  *_retval = 0;
1033  return NS_OK;
1034  }
1035 
1036  nsAutoString indexStr;
1037  rv = result->GetRowCell(0, 0, indexStr);
1038  NS_ENSURE_SUCCESS(rv, rv);
1039 
1040  PRUint32 index;
1041  index = indexStr.ToInteger(&rv);
1042  NS_ENSURE_SUCCESS(rv, rv);
1043 
1044  // If the result is equal to the non null length, we know it was not found
1045  if (index == mNonNullLength) {
1046  return NS_ERROR_NOT_AVAILABLE;
1047  }
1048 
1049  // Check to see if the returned index actually starts with the requested
1050  // prefix
1051  nsAutoString value;
1052  rv = GetSortPropertyValueByIndex(index, value);
1053  NS_ENSURE_SUCCESS(rv, rv);
1054 
1055  if (!StringBeginsWith(value, aValue)) {
1056  return NS_ERROR_NOT_AVAILABLE;
1057  }
1058 
1059  *_retval = index;
1060  return NS_OK;
1061 }
1062 
1063 NS_IMETHODIMP
1064 sbLocalDatabaseGUIDArray::GetFirstIndexByGuid(const nsAString& aGuid,
1065  PRUint32* _retval)
1066 {
1067  TRACE(("sbLocalDatabaseGUIDArray[0x%.8x] - GetFirstIndexByGuid", this));
1068 
1069  NS_ENSURE_ARG_POINTER(_retval);
1070 
1071  nsresult rv;
1072 
1073  nsAutoMonitor mon(mCacheMonitor);
1074 
1075  if (mValid == PR_FALSE) {
1076  rv = Initialize();
1077  NS_ENSURE_SUCCESS(rv, rv);
1078  }
1079 
1080  // If this is a guid array on a simple media list we can't do any
1081  // optimizations that depend on returning the first matching guid we find
1082  PRBool uniqueGuids = PR_TRUE;
1083  if (mBaseTable.EqualsLiteral("simple_media_lists")) {
1084  uniqueGuids = PR_FALSE;
1085  }
1086 
1087  PRUint32 firstUncached = 0;
1088 
1089  // First check to see if the guid is cached. If we have unique guids, we
1090  // can use the guid-to-index map as a shortcut
1091  if (uniqueGuids) {
1092  if (mGuidToFirstIndexMap.Get(aGuid, _retval)) {
1093  return NS_OK;
1094  }
1095 
1096  // If we are fully cached and the guid was not found, then we know that
1097  // it does not exist in this array
1098  if (mCache.Length() == mLength) {
1099  return NS_ERROR_NOT_AVAILABLE;
1100  }
1101 
1102  // If it wasn't found, we need to find the first uncached row
1103  PRBool found = PR_FALSE;
1104  for (PRUint32 i = 0; !found && i < mCache.Length(); i++) {
1105  if (!mCache[i]) {
1106  firstUncached = i;
1107  found = PR_TRUE;
1108  }
1109  }
1110 
1111  // If we didn't find any uncached rows and the cache size is the same as
1112  // the array length, we know the guid isn't in this array
1113  if (!found && mCache.Length() == mLength) {
1114  return NS_ERROR_NOT_AVAILABLE;
1115  }
1116  }
1117  else {
1118  // Since we could have duplicate guids, just search the array for the guid
1119  // from the beginning to the first uncached item
1120  PRBool foundFirstUncached = PR_FALSE;
1121  for (PRUint32 i = 0; !foundFirstUncached && i < mCache.Length(); i++) {
1122  ArrayItem* item = mCache[i];
1123  if (item) {
1124  if (item->guid.Equals(aGuid)) {
1125  *_retval = i;
1126  return NS_OK;
1127  }
1128  }
1129  else {
1130  firstUncached = i;
1131  foundFirstUncached = PR_TRUE;
1132  }
1133  }
1134 
1135  // If we didn't find any uncached rows and the cache size is the same as
1136  // the array length, we know the guid isn't in this array
1137  if (!foundFirstUncached && mCache.Length() == mLength) {
1138  return NS_ERROR_NOT_AVAILABLE;
1139  }
1140  }
1141 
1142  // So the guid we are looking for is not cached. Cache the rest of the
1143  // array and search it
1144  rv = FetchRows(firstUncached, mLength);
1145  NS_ENSURE_SUCCESS(rv, rv);
1146 
1147  NS_ASSERTION(mLength == mCache.Length(), "Full read didn't work");
1148 
1149  // Either the guid is in the map or it just not in our array
1150  if (mGuidToFirstIndexMap.Get(aGuid, _retval)) {
1151  return NS_OK;
1152  }
1153 
1154  return NS_ERROR_NOT_AVAILABLE;
1155 }
1156 
1157 /* aViewItemUID is a concatenation of a mediaitems rowid and mediaitemid from
1158  * its entry in the library's database of the form "rowid-mediaitemid" */
1159 NS_IMETHODIMP
1160 sbLocalDatabaseGUIDArray::GetIndexByViewItemUID
1161  (const nsAString& aViewItemUID,
1162  PRUint32* _retval)
1163 {
1164  TRACE(("sbLocalDatabaseGUIDArray[0x%.8x] - GetIndexByRowid", this));
1165  NS_ENSURE_ARG_POINTER(_retval);
1166 
1167  nsresult rv;
1168 
1169  nsAutoMonitor mon(mCacheMonitor);
1170 
1171  if (mValid == PR_FALSE) {
1172  rv = Initialize();
1173  NS_ENSURE_SUCCESS(rv, rv);
1174  }
1175 
1176  // First check to see if we have this in cache
1177  if (mViewItemUIDToIndexMap.Get(aViewItemUID, _retval)) {
1178  return NS_OK;
1179  }
1180 
1181  PRUint32 firstUncached = 0;
1182 
1183  // If no, we need to cache the entire guid array. Find the first uncached
1184  // row so we can trigger the load
1185  PRBool found = PR_FALSE;
1186  for (PRUint32 i = 0; !found && i < mCache.Length(); i++) {
1187  if (!mCache[i]) {
1188  firstUncached = i;
1189  found = PR_TRUE;
1190  }
1191  }
1192 
1193  // If all rows are cached, it was not found
1194  if (!found && mLength == mCache.Length()) {
1195  return NS_ERROR_NOT_AVAILABLE;
1196  }
1197 
1198  rv = FetchRows(firstUncached, mLength);
1199  NS_ENSURE_SUCCESS(rv, rv);
1200  NS_ASSERTION(mLength == mCache.Length(), "Full read didn't work");
1201 
1202  // Either the guid is in the map or it just not in our array
1203  if (mViewItemUIDToIndexMap.Get(aViewItemUID, _retval)) {
1204  return NS_OK;
1205  }
1206 
1207  return NS_ERROR_NOT_AVAILABLE;
1208 }
1209 
1210 NS_IMETHODIMP
1211 sbLocalDatabaseGUIDArray::ContainsGuid(const nsAString& aGuid,
1212  PRBool* _retval)
1213 {
1214  TRACE(("sbLocalDatabaseGUIDArray[0x%.8x] - ContainsGuid", this));
1215  NS_ENSURE_ARG_POINTER(_retval);
1216  nsresult rv;
1217 
1218  nsAutoMonitor mon(mCacheMonitor);
1219 
1220  if (mValid == PR_FALSE) {
1221  rv = Initialize();
1222  NS_ENSURE_SUCCESS(rv, rv);
1223  }
1224 
1225  // Since we don't actually care where in the array the GUID appears,
1226  // we can take advantage of mGuidToFirstIndexMap even when this
1227  // is NOT a distinct array.
1228 
1229  // First check to see if the guid is cached.
1230  PRUint32 index;
1231  if (mGuidToFirstIndexMap.Get(aGuid, &index)) {
1232  *_retval = PR_TRUE;
1233  return NS_OK;
1234  }
1235 
1236  PRUint32 firstUncached = 0;
1237  // If we are fully cached and the guid was not found, then we know that
1238  // it does not exist in this array
1239  if (mCache.Length() == mLength) {
1240  *_retval = PR_FALSE;
1241  return NS_OK;
1242  }
1243 
1244  // If it wasn't found, we need to find the first uncached row
1245  PRBool found = PR_FALSE;
1246  for (PRUint32 i = 0; !found && i < mCache.Length(); i++) {
1247  if (!mCache[i]) {
1248  firstUncached = i;
1249  found = PR_TRUE;
1250  }
1251  }
1252 
1253  // If we didn't find any uncached rows and the cache size is the same as
1254  // the array length, we know the guid isn't in this array
1255  if (!found && mCache.Length() == mLength) {
1256  *_retval = PR_FALSE;
1257  return NS_OK;
1258  }
1259 
1260  // So the guid we are looking for is not cached. Cache the rest of the
1261  // array and search it
1262  rv = FetchRows(firstUncached, mLength);
1263  NS_ENSURE_SUCCESS(rv, rv);
1264  NS_ASSERTION(mLength == mCache.Length(), "Full read didn't work");
1265 
1266  // Either the guid is in the map or it is just not in our array
1267  *_retval = mGuidToFirstIndexMap.Get(aGuid, &index);
1268  return NS_OK;
1269 }
1270 
1271 NS_IMETHODIMP
1272 sbLocalDatabaseGUIDArray::SuppressInvalidation(PRBool aSuppress)
1273 {
1274  if(aSuppress) {
1275  mSuppress++;
1276  }
1277  else if(--mSuppress <= 0) {
1278  mSuppress = 0;
1279 
1280  // We don't need to invalidate the lengths here because the only
1281  // time suppress invalidation is used is when the cascade filters
1282  // are being rebuilt. This means no new property values will show
1283  // up and no new items will be created in the library associated
1284  // with the GUID array.
1285  nsresult rv = Invalidate(PR_FALSE);
1286  NS_ENSURE_SUCCESS(rv, rv);
1287  }
1288 
1289  return NS_OK;
1290 }
1291 
1292 nsresult
1293 sbLocalDatabaseGUIDArray::Initialize()
1294 {
1295  TRACE(("sbLocalDatabaseGUIDArray[0x%.8x] - Initialize", this));
1296  NS_ASSERTION(mPropertyCache, "No property cache!");
1297 
1298  nsresult rv = NS_ERROR_UNEXPECTED;
1299 
1300  // Make sure we have a database and a base table
1301  if (mDatabaseGUID.IsEmpty() || mBaseTable.IsEmpty()) {
1302  return NS_ERROR_UNEXPECTED;
1303  }
1304 
1305  // Make sure we have at least one sort
1306  if (mSorts.Length() == 0) {
1307  return NS_ERROR_UNEXPECTED;
1308  }
1309 
1310  if (!mGuidToFirstIndexMap.IsInitialized()) {
1311  PRBool success = mGuidToFirstIndexMap.Init();
1312  NS_ENSURE_TRUE(success, NS_ERROR_OUT_OF_MEMORY);
1313  }
1314 
1315  if (!mViewItemUIDToIndexMap.IsInitialized()) {
1316  PRBool success = mViewItemUIDToIndexMap.Init();
1317  NS_ENSURE_TRUE(success, NS_ERROR_OUT_OF_MEMORY);
1318  }
1319 
1320  if (mValid == PR_TRUE) {
1321  // There's no cached length information associated with the array
1322  // when it's being initialized (or re-initialized).
1323  rv = Invalidate(PR_FALSE);
1324  NS_ENSURE_SUCCESS(rv, rv);
1325  }
1326 
1327  rv = mPropertyCache->Write();
1328  NS_ENSURE_SUCCESS(rv, rv);
1329 
1330  rv = UpdateQueries();
1331  NS_ENSURE_SUCCESS(rv, rv);
1332 
1333  rv = UpdateLength();
1334  NS_ENSURE_SUCCESS(rv, rv);
1335 
1336  /*
1337  * Determine where to put null values based on the null sort policy of the
1338  * primary sort property and how it is being sorted
1339  */
1340  if (!mPropMan) {
1341  mPropMan = do_GetService(SB_PROPERTYMANAGER_CONTRACTID, &rv);
1342  NS_ENSURE_SUCCESS(rv, rv);
1343  }
1344 
1345  nsCOMPtr<sbIPropertyInfo> info;
1346  rv = mPropMan->GetPropertyInfo(mSorts[0].property, getter_AddRefs(info));
1347  NS_ENSURE_SUCCESS(rv, rv);
1348 
1349  PRUint32 nullSort;
1350  rv = info->GetNullSort(&nullSort);
1351  NS_ENSURE_SUCCESS(rv, rv);
1352 
1353  switch (nullSort) {
1355  mNullsFirst = mSorts[0].ascending;
1356  break;
1358  mNullsFirst = !mSorts[0].ascending;
1359  break;
1361  mNullsFirst = PR_TRUE;
1362  break;
1364  mNullsFirst = PR_FALSE;
1365  break;
1366  }
1367 
1368  if (mNullsFirst) {
1369  mStatementX = mNullGuidRangeStatement;
1370  mStatementY = mFullGuidRangeStatement;
1371  mLengthX = mLength - mNonNullLength;
1372  }
1373  else {
1374  mStatementX = mFullGuidRangeStatement;
1375  mStatementY = mNullGuidRangeStatement;
1376  mLengthX = mNonNullLength;
1377  }
1378 
1379  // Figure out if there is an active search filter,
1380  // as this impacts how we do secondary sorting at the moment.
1381  mHasActiveSearch = PR_FALSE;
1382  PRUint32 filterCount = mFilters.Length();
1383  for (PRUint32 index = 0; index < filterCount; index++) {
1384  const FilterSpec& refSpec = mFilters.ElementAt(index);
1385 
1386  nsTArray<nsString>* stringArray =
1387  const_cast<nsTArray<nsString>*>(&refSpec.values);
1388  NS_ENSURE_STATE(stringArray);
1389 
1390  if (refSpec.isSearch && stringArray->Length() > 0) {
1391  mHasActiveSearch = PR_TRUE;
1392  break;
1393  }
1394  }
1395 
1396  mValid = PR_TRUE;
1397 
1398  return NS_OK;
1399 }
1400 
1401 nsresult
1402 sbLocalDatabaseGUIDArray::UpdateLength()
1403 {
1404  nsresult rv;
1405 
1406  nsAutoMonitor mon(mCacheMonitor);
1407 
1408  // If we have a fetch size of 0 or PR_UINT32_MAX it means
1409  // we're supposed to fetch everything. If this is
1410  // the case, and we don't have to worry about the
1411  // non null query, then we can skip the count query by
1412  // just fetching and using the resulting length.
1413  if ((mFetchSize == PR_UINT32_MAX || mFetchSize == 0) &&
1414  mNonNullCountQuery.IsEmpty() && mNullGuidRangeQuery.IsEmpty())
1415  {
1416  rv = ReadRowRange(mFullGuidRangeStatement,
1417  0,
1418  PR_UINT32_MAX,
1419  0,
1420  PR_FALSE);
1421  NS_ENSURE_SUCCESS(rv, rv);
1422 
1423  mLength = mCache.Length();
1424  mNonNullLength = mLength;
1425  }
1426  else {
1427  // Try and get the cached length first.
1428  if (mCachedLengthKey.IsEmpty() || mNeedNewKey) {
1429  GenerateCachedLengthKey();
1430  mNeedNewKey = PR_FALSE;
1431  }
1432  if (mLengthCache) {
1433  rv = mLengthCache->GetCachedLength(mCachedLengthKey, &mLength);
1434  // Not in cache, run the query.
1435  if(NS_FAILED(rv)) {
1436  // Otherwise, use separate queries to establish the length.
1437  rv = RunLengthQuery(mFullCountStatement, &mLength);
1438  NS_ENSURE_SUCCESS(rv, rv);
1439 
1440  rv = mLengthCache->AddCachedLength(mCachedLengthKey, mLength);
1441  NS_ENSURE_SUCCESS(rv, rv);
1442  }
1443  }
1444  else {
1445  rv = RunLengthQuery(mFullCountStatement, &mLength);
1446  NS_ENSURE_SUCCESS(rv, rv);
1447  }
1448 
1449  // We need to get the non-null count separately.
1450  if (!mNonNullCountQuery.IsEmpty()) {
1451  if (mLengthCache) {
1452  rv = mLengthCache->GetCachedNonNullLength(mCachedLengthKey,
1453  &mNonNullLength);
1454  // Not in cache, run the query.
1455  if(NS_FAILED(rv)) {
1456  rv = RunLengthQuery(mNonNullCountStatement, &mNonNullLength);
1457  NS_ENSURE_SUCCESS(rv, rv);
1458 
1459  rv = mLengthCache->AddCachedNonNullLength(mCachedLengthKey, mLength);
1460  NS_ENSURE_SUCCESS(rv, rv);
1461  }
1462  }
1463  else {
1464  rv = RunLengthQuery(mNonNullCountStatement, &mNonNullLength);
1465  NS_ENSURE_SUCCESS(rv, rv);
1466  }
1467  }
1468  else {
1469  mNonNullLength = mLength;
1470  }
1471  }
1472 
1473  return NS_OK;
1474 }
1475 
1476 nsresult
1477 sbLocalDatabaseGUIDArray::RunLengthQuery(sbIDatabasePreparedStatement *aStatement,
1478  PRUint32* _retval)
1479 {
1480  nsresult rv;
1481  PRInt32 dbOk;
1482 
1483  nsCOMPtr<sbIDatabaseQuery> query;
1484  rv = MakeQuery(aStatement, getter_AddRefs(query));
1485  NS_ENSURE_SUCCESS(rv, rv);
1486 
1487  // Execute the length query
1488  rv = query->Execute(&dbOk);
1489  NS_ENSURE_SUCCESS(rv, rv);
1490  NS_ENSURE_TRUE(dbOk == 0, NS_ERROR_FAILURE);
1491 
1492  nsCOMPtr<sbIDatabaseResult> result;
1493  rv = query->GetResultObject(getter_AddRefs(result));
1494  NS_ENSURE_TRUE(result, NS_ERROR_FAILURE);
1495 
1496  PRUint32 rowCount;
1497  rv = result->GetRowCount(&rowCount);
1498  NS_ENSURE_SUCCESS(rv, rv);
1499 
1500  // Make sure we get one row back
1501  NS_ENSURE_TRUE(rowCount == 1, NS_ERROR_UNEXPECTED);
1502 
1503  nsAutoString countStr;
1504  rv = result->GetRowCell(0, 0, countStr);
1505  NS_ENSURE_SUCCESS(rv, rv);
1506 
1507  *_retval = countStr.ToInteger(&rv);
1508  NS_ENSURE_SUCCESS(rv, rv);
1509 
1510  return NS_OK;
1511 }
1512 
1513 nsresult
1514 sbLocalDatabaseGUIDArray::UpdateQueries()
1515 {
1516  // No need to update the queries, they're still valid
1517  if (mQueriesValid) {
1518  return NS_OK;
1519  }
1520  /*
1521  * Generate a SQL statement that applies the current filter, search, and
1522  * primary sort for the supplied base table and constraints.
1523  */
1524  nsresult rv;
1525 
1526  /*
1527  * We're going to use this query to prepare the sql statements.
1528  * This speeds things up _significantly_
1529  */
1530  nsCOMPtr<sbIDatabaseQuery> query =
1531  do_CreateInstance(SONGBIRD_DATABASEQUERY_CONTRACTID, &rv);
1532  NS_ENSURE_SUCCESS(rv, rv);
1533 
1534  rv = query->SetDatabaseGUID(mDatabaseGUID);
1535  NS_ENSURE_SUCCESS(rv, rv);
1536 
1537  if (mDatabaseLocation) {
1538  rv = query->SetDatabaseLocation(mDatabaseLocation);
1539  NS_ENSURE_SUCCESS(rv, rv);
1540  }
1541 
1542  /*
1543  * We need four different queries to do the magic here:
1544  *
1545  * mFullCountQuery - A query that returns the count of the full dataset
1546  * with filters applied
1547  * mFullGuidRangeQuery - A query that returns a list of guids with filters
1548  * applied and sorted by the primary sort
1549  * mNonNullCountQuery - A query that returns the count of the rows in the
1550  * dataset whose primary sort key value is not null
1551  * mNullGuidRangeQuery - A query that returns a list of guids whose primary
1552  * sort key value is null with filters applied
1553  */
1554  nsAutoPtr<sbLocalDatabaseQuery> ldq;
1555 
1556  ldq = new sbLocalDatabaseQuery(mBaseTable,
1557  mBaseConstraintColumn,
1558  mBaseConstraintValue,
1559  NS_LITERAL_STRING("member_media_item_id"),
1560  &mFilters,
1561  &mSorts,
1562  mIsDistinct,
1563  mDistinctWithSortableValues,
1564  mPropertyCache);
1565 
1566  // Full Count Query
1567  rv = ldq->GetFullCountQuery(mFullCountQuery);
1568  NS_ENSURE_SUCCESS(rv, rv);
1569 
1570  rv = query->PrepareQuery(mFullCountQuery,
1571  getter_AddRefs(mFullCountStatement));
1572  NS_ENSURE_SUCCESS(rv, rv);
1573 
1574  // Full Guid Range Query
1575  rv = ldq->GetFullGuidRangeQuery(mFullGuidRangeQuery);
1576  NS_ENSURE_SUCCESS(rv, rv);
1577 
1578  rv = query->PrepareQuery(mFullGuidRangeQuery,
1579  getter_AddRefs(mFullGuidRangeStatement));
1580  NS_ENSURE_SUCCESS(rv, rv);
1581 
1582  // Non Null Count Query
1583  rv = ldq->GetNonNullCountQuery(mNonNullCountQuery);
1584  NS_ENSURE_SUCCESS(rv, rv);
1585 
1586  rv = query->PrepareQuery(mNonNullCountQuery,
1587  getter_AddRefs(mNonNullCountStatement));
1588  NS_ENSURE_SUCCESS(rv, rv);
1589 
1590  // Null Guid Range Query
1591  rv = ldq->GetNullGuidRangeQuery(mNullGuidRangeQuery);
1592  NS_ENSURE_SUCCESS(rv, rv);
1593 
1594  rv = query->PrepareQuery(mNullGuidRangeQuery,
1595  getter_AddRefs(mNullGuidRangeStatement));
1596  NS_ENSURE_SUCCESS(rv, rv);
1597 
1598  // Prefix Search Query
1599  rv = ldq->GetPrefixSearchQuery(mPrefixSearchQuery);
1600  NS_ENSURE_SUCCESS(rv, rv);
1601 
1602  rv = query->PrepareQuery(mPrefixSearchQuery,
1603  getter_AddRefs(mPrefixSearchStatement));
1604  NS_ENSURE_SUCCESS(rv, rv);
1605 
1606  /*
1607  * Generate the resort query, if needed.
1608  * Only multiple primary sorts need a resort query, since any number of
1609  * secondary sorts are otherwise handled by the main query via
1610  * obj_secondary_sortable
1611  */
1612  PRUint32 numSorts = mSorts.Length();
1613  if (numSorts > 1 && !mIsDistinct) {
1614  rv = ldq->GetResortQuery(mResortQuery);
1615  NS_ENSURE_SUCCESS(rv, rv);
1616 
1617  rv = query->PrepareQuery(mResortQuery,
1618  getter_AddRefs(mResortStatement));
1619  NS_ENSURE_SUCCESS(rv, rv);
1620 
1621  rv = ldq->GetNullResortQuery(mNullResortQuery);
1622  NS_ENSURE_SUCCESS(rv, rv);
1623 
1624  rv = query->PrepareQuery(mNullResortQuery,
1625  getter_AddRefs(mNullResortStatement));
1626  NS_ENSURE_SUCCESS(rv, rv);
1627 
1628  /*
1629  * Generate the primary sort key position query
1630  */
1631  rv = ldq->GetPrefixSearchQuery(mPrimarySortKeyPositionQuery);
1632  NS_ENSURE_SUCCESS(rv, rv);
1633 
1634  rv = query->PrepareQuery(mPrimarySortKeyPositionQuery,
1635  getter_AddRefs(mPrimarySortKeyPositionStatement));
1636  NS_ENSURE_SUCCESS(rv, rv);
1637  }
1638 
1639  mIsFullLibrary = ldq->GetIsFullLibrary();
1640 
1641  mQueriesValid = PR_TRUE;
1642 
1643  // If we're updating queries, it's likely we'll need to update the key
1644  // we're using so re-generate now.
1645  GenerateCachedLengthKey();
1646 
1647  return NS_OK;
1648 }
1649 
1650 nsresult
1651 sbLocalDatabaseGUIDArray::MakeQuery(sbIDatabasePreparedStatement *aStatement,
1652  sbIDatabaseQuery** _retval)
1653 {
1654  NS_ENSURE_ARG_POINTER(_retval);
1655 
1656  nsresult rv;
1657 
1658 #ifdef PR_LOGGING
1659  nsString queryString;
1660  rv = aStatement->GetQueryString(queryString);
1661  if (NS_SUCCEEDED(rv)) {
1662  LOG(("sbLocalDatabaseGUIDArray[0x%.8x] - MakeQuery: %s",
1663  this, NS_ConvertUTF16toUTF8(queryString).get()));
1664  }
1665 #endif
1666 
1667  nsCOMPtr<sbIDatabaseQuery> query =
1668  do_CreateInstance(SONGBIRD_DATABASEQUERY_CONTRACTID, &rv);
1669  NS_ENSURE_SUCCESS(rv, rv);
1670 
1671  rv = query->SetDatabaseGUID(mDatabaseGUID);
1672  NS_ENSURE_SUCCESS(rv, rv);
1673 
1674  if (mDatabaseLocation) {
1675  rv = query->SetDatabaseLocation(mDatabaseLocation);
1676  NS_ENSURE_SUCCESS(rv, rv);
1677  }
1678 
1679  rv = query->AddPreparedStatement(aStatement);
1680  NS_ENSURE_SUCCESS(rv, rv);
1681 
1682  NS_ADDREF(*_retval = query);
1683  return NS_OK;
1684 }
1685 
1686 nsresult
1687 sbLocalDatabaseGUIDArray::FetchRows(PRUint32 aRequestedIndex,
1688  PRUint32 aFetchSize)
1689 {
1690  nsresult rv;
1691 
1692  // FetchRows always gets called with mCacheMonitor already acquired!
1693  // No need to acquire the lock in this method.
1694 
1695  /*
1696  * Nothing to fetch if not valid
1697  */
1698  if (mValid == PR_FALSE)
1699  return NS_OK;
1700 
1701  /*
1702  * To read the full media library, two queries are used -- one for when the
1703  * primary sort key has values and one for when the primary sort key has
1704  * no values (null values). When sorting, items where the primary sort key
1705  * has no values may be sorted to the beginning or to the end of the list.
1706  * The diagram below describes this situation:
1707  *
1708  * A B C
1709  * +--------------X-------------+-----------------Y----------------+
1710  * +------------------------------+
1711  * D E
1712  *
1713  * Array AC (A is at index 0 and C is the maximum index of the array)
1714  * represents the entire media library. Index B represents the first item
1715  * in the array that requires a different query as described above. Requests
1716  * for items in [A, B - 1] use query X, and requests for items in [B, C] use
1717  * query Y.
1718  *
1719  * Index range DE (inclusive) represents some sub-array within AC. Depending
1720  * on the relationship between DE to index B, different strategies need to be
1721  * used to query the data:
1722  *
1723  * - If DE lies entirely within [A, B - 1], use query X to return the data
1724  * - If DE lies entirely within [B, C], use query Y to return the data
1725  * - If DE lies in both [A, B - 1] and [B, C], use query X to return the data
1726  * in [A, B - 1] and Y to return data in[B, C]
1727  *
1728  * Note the start index of query Y must be relative to index B, not index A.
1729  */
1730 
1731  PRUint32 indexB = mLengthX;
1732  PRUint32 indexC = mLength - 1;
1733 
1734  // Edge cases mean fetch everything. Just return if nothing to fetch.
1735  if (aFetchSize == PR_UINT32_MAX || aFetchSize == 0) {
1736  aFetchSize = mLength;
1737  }
1738  if (!aFetchSize)
1739  return NS_OK;
1740 
1741  /*
1742  * Divide the array up into cells and figure out what cell to fetch
1743  */
1744  PRUint32 cell = aRequestedIndex / aFetchSize;
1745 
1746  PRUint32 indexD = cell * aFetchSize;
1747  PRUint32 indexE = indexD + aFetchSize - 1;
1748  if (indexE > indexC) {
1749  indexE = indexC;
1750  }
1751  PRUint32 lengthDE = indexE - indexD + 1;
1752 
1753  /*
1754  * If DE lies entirely within [A, B - 1], use query X to return the data
1755  */
1756  if (indexE < indexB) {
1757  rv = ReadRowRange(mStatementX,
1758  indexD,
1759  lengthDE,
1760  indexD,
1761  mNullsFirst);
1762  NS_ENSURE_SUCCESS(rv, rv);
1763  }
1764  else {
1765  /*
1766  * If DE lies entirely within [B, C], use query Y to return the data
1767  */
1768  if (indexD >= indexB) {
1769  rv = ReadRowRange(mStatementY,
1770  indexD - indexB,
1771  lengthDE,
1772  indexD,
1773  !mNullsFirst);
1774  NS_ENSURE_SUCCESS(rv, rv);
1775  }
1776  else {
1777  /*
1778  * If DE lies in both [A, B - 1] and [B, C], use query X to return the
1779  * data in [A, B - 1] and Y to return data in[B, C]
1780  */
1781  rv = ReadRowRange(mStatementX,
1782  indexD,
1783  indexB - indexD,
1784  indexD,
1785  mNullsFirst);
1786  NS_ENSURE_SUCCESS(rv, rv);
1787 
1788  rv = ReadRowRange(mStatementY,
1789  0,
1790  indexE - indexB + 1,
1791  indexB,
1792  !mNullsFirst);
1793  NS_ENSURE_SUCCESS(rv, rv);
1794  }
1795  }
1796 
1797  /*
1798  * If we're paired with a property cache, cache the proeprties for the
1799  * range we just fetched
1800  */
1801 /*
1802  if (mPropertyCache) {
1803  const PRUnichar** guids = new const PRUnichar*[lengthDE];
1804  for (PRUint32 i = 0; i < lengthDE; i++) {
1805  guids[i] = mCache[i + indexD]->guid.get();
1806  }
1807  rv = mPropertyCache->CacheProperties(guids, lengthDE);
1808  NS_ENSURE_SUCCESS(rv, rv);
1809  delete[] guids;
1810  }
1811 */
1812 return NS_OK;
1813 }
1814 
1815 nsresult
1816 sbLocalDatabaseGUIDArray::ReadRowRange(sbIDatabasePreparedStatement *aStatement,
1817  PRUint32 aStartIndex,
1818  PRUint32 aCount,
1819  PRUint32 aDestIndexOffset,
1820  PRBool aIsNull)
1821 {
1822  nsresult rv;
1823  PRInt32 dbOk;
1824 
1825  NS_ENSURE_ARG_MIN(aStartIndex, 0);
1826  NS_ENSURE_ARG_MIN(aCount, 1);
1827  NS_ENSURE_ARG_MIN(aDestIndexOffset, 0);
1828 
1829  LOG(("ReadRowRange start %d count %d dest offset %d isnull %d\n",
1830  aStartIndex,
1831  aCount,
1832  aDestIndexOffset,
1833  aIsNull));
1834 
1835  // ReadRowRange always gets called with mCacheMonitor acquired!
1836  // No need to acquire this lock in this method.
1837 
1838  /*
1839  * Set up the query with limit and offset parameters and run it
1840  */
1841  nsCOMPtr<sbIDatabaseQuery> query;
1842  rv = MakeQuery(aStatement, getter_AddRefs(query));
1843  NS_ENSURE_SUCCESS(rv, rv);
1844 
1845  rv = query->BindInt64Parameter(0, aCount);
1846  NS_ENSURE_SUCCESS(rv, rv);
1847 
1848  rv = query->BindInt32Parameter(1, aStartIndex);
1849  NS_ENSURE_SUCCESS(rv, rv);
1850 
1851  rv = query->Execute(&dbOk);
1852  NS_ENSURE_SUCCESS(rv, rv);
1853  NS_ENSURE_TRUE(dbOk == 0, NS_ERROR_FAILURE);
1854 
1855  nsCOMPtr<sbIDatabaseResult> result;
1856  rv = query->GetResultObject(getter_AddRefs(result));
1857  NS_ENSURE_TRUE(result, NS_ERROR_FAILURE);
1858 
1859  PRUint32 rowCount;
1860  rv = result->GetRowCount(&rowCount);
1861  NS_ENSURE_SUCCESS(rv, rv);
1862 
1863  /*
1864  * If asked to fetch everything, assume we got
1865  * the right number of rows.
1866  */
1867  if (aCount == PR_UINT32_MAX) {
1868  aCount = rowCount;
1869  }
1870 
1871  /*
1872  * We need to apply additional levels of sorts if either of the following
1873  * conditions is true:
1874  * - Multiple sorts have been added via AddSort
1875  * - A single primary sort is active but it has secondary sorts and we are
1876  * processing the null values: null values mean there was no row to hold
1877  * the secondary sort data
1878  */
1879  PRBool needsSorting = (mPrimarySortsCount > 1) ||
1880  (mSorts.Length() > 1 && aIsNull);
1881 
1882  /*
1883  * Resize the cache so we can fit the new data
1884  */
1885  if (mCache.Length() < aDestIndexOffset + aCount) {
1886  LOG(("SetLength %d to %d", mCache.Length(), aDestIndexOffset + aCount));
1887  PRBool success = mCache.SetLength(aDestIndexOffset + aCount);
1888  NS_ENSURE_TRUE(success, NS_ERROR_OUT_OF_MEMORY);
1889  }
1890 
1891  nsAutoString lastSortedValue;
1892  PRUint32 firstIndex = 0;
1893  PRBool isFirstValue = PR_TRUE;
1894  PRBool isFirstSort = PR_TRUE;
1895  for (PRUint32 i = 0; i < rowCount; i++) {
1896  PRUint32 index = i + aDestIndexOffset;
1897 
1898  nsString mediaItemIdStr;
1899  rv = result->GetRowCell(i, 0, mediaItemIdStr);
1900  NS_ENSURE_SUCCESS(rv, rv);
1901 
1902  PRUint32 mediaItemId = mediaItemIdStr.ToInteger(&rv);
1903  NS_ENSURE_SUCCESS(rv, rv);
1904 
1905  nsString guid;
1906  rv = result->GetRowCell(i, 1, guid);
1907  NS_ENSURE_SUCCESS(rv, rv);
1908 
1909  nsString value;
1910  rv = result->GetRowCell(i, 2, value);
1911  NS_ENSURE_SUCCESS(rv, rv);
1912 
1913  nsString ordinal;
1914  rv = result->GetRowCell(i, 3, ordinal);
1915  NS_ENSURE_SUCCESS(rv, rv);
1916 
1917  nsString rowidStr;
1918  rv = result->GetRowCell(i, 4, rowidStr);
1919  NS_ENSURE_SUCCESS(rv, rv);
1920 
1921  PRUint64 rowid = nsString_ToUint64(rowidStr, &rv);
1922  NS_ENSURE_SUCCESS(rv, rv);
1923 
1924  ArrayItem* item = new ArrayItem(mediaItemId, guid, value, ordinal, rowid);
1925  NS_ENSURE_TRUE(item, NS_ERROR_OUT_OF_MEMORY);
1926 
1927  nsAutoPtr<ArrayItem>* success =
1928  mCache.ReplaceElementsAt(index, 1, item);
1929  NS_ENSURE_TRUE(success, NS_ERROR_OUT_OF_MEMORY);
1930 
1931  TRACE(("ReplaceElementsAt %d %s", index,
1932  NS_ConvertUTF16toUTF8(item->guid).get()));
1933 
1934  if (needsSorting) {
1935  if (isFirstValue || !lastSortedValue.Equals(item->sortPropertyValue)) {
1936  if (!isFirstValue) {
1937  rv = SortRows(aDestIndexOffset + firstIndex,
1938  index - 1,
1939  lastSortedValue,
1940  isFirstSort,
1941  PR_FALSE,
1942  PR_FALSE,
1943  aIsNull);
1944  NS_ENSURE_SUCCESS(rv, rv);
1945  isFirstSort = PR_FALSE;
1946  }
1947  lastSortedValue.Assign(item->sortPropertyValue);
1948  firstIndex = i;
1949  isFirstValue = PR_FALSE;
1950  }
1951  }
1952  }
1953 
1954  LOG(("Replaced Elements %d to %d",
1955  aDestIndexOffset,
1956  rowCount + aDestIndexOffset));
1957 
1958  if (needsSorting) {
1959  rv = SortRows(aDestIndexOffset + firstIndex,
1960  aDestIndexOffset + rowCount - 1,
1961  lastSortedValue,
1962  isFirstSort,
1963  PR_TRUE,
1964  isFirstSort == PR_TRUE,
1965  aIsNull);
1966  NS_ENSURE_SUCCESS(rv, rv);
1967  }
1968 
1969  // Record the indexes of the guids
1970  for (PRUint32 i = 0; i < rowCount; i++) {
1971  PRUint32 index = i + aDestIndexOffset;
1972 
1973  ArrayItem* item = mCache[index];
1974  NS_ASSERTION(item, "Null item in cache?");
1975 
1976  // Add the new guid to the guid to first index map.
1977  PRUint32 firstGuidIndex;
1978  PRBool found = mGuidToFirstIndexMap.Get(item->guid, &firstGuidIndex);
1979  if (!found || index < firstGuidIndex) {
1980  PRBool added = mGuidToFirstIndexMap.Put(item->guid, index);
1981  NS_ENSURE_TRUE(added, NS_ERROR_OUT_OF_MEMORY);
1982  }
1983 
1984  // Add the concatenated rowid and mediaitemid (a viewItemUID)
1985  // as a key mapping to index so that we readily recover that index
1986  nsAutoString viewItemUID;
1987  AppendInt(viewItemUID, item->rowid);
1988  viewItemUID.Append('-');
1989  viewItemUID.AppendInt(item->mediaItemId);
1990 
1991  PRBool added = mViewItemUIDToIndexMap.Put(viewItemUID, index);
1992  NS_ENSURE_TRUE(added, NS_ERROR_OUT_OF_MEMORY);
1993  }
1994 
1995  /*
1996  * If the number of rows returned is less than what was requested, this is
1997  * really bad. Fill the rest in so we don't crash.
1998  */
1999  if (rowCount < aCount) {
2000  char* message = PR_smprintf("Did not get the requested number of rows, "
2001  "requested %d got %d", aCount, rowCount);
2002  NS_WARNING(message);
2003  PR_smprintf_free(message);
2004  for (PRUint32 i = 0; i < aCount - rowCount; i++) {
2005  ArrayItem* item = new ArrayItem(0,
2006  NS_LITERAL_STRING("error"),
2007  NS_LITERAL_STRING("error"),
2008  EmptyString(),
2009  0);
2010  NS_ENSURE_TRUE(item, NS_ERROR_OUT_OF_MEMORY);
2011 
2012  nsAutoPtr<ArrayItem>* success =
2013  mCache.ReplaceElementsAt(i + rowCount + aDestIndexOffset, 1, item);
2014  NS_ENSURE_TRUE(success, NS_ERROR_OUT_OF_MEMORY);
2015  }
2016  }
2017  return NS_OK;
2018 }
2019 
2020 /* static */ int
2021 sbLocalDatabaseGUIDArray::SortBags(const void* a, const void* b, void* closure)
2022 {
2024  *static_cast<sbILocalDatabaseResourcePropertyBag* const *>(a);
2025 
2027  *static_cast<sbILocalDatabaseResourcePropertyBag* const *>(b);
2028 
2029  nsTArray<SortSpec>* sorts = static_cast<nsTArray<SortSpec>*>(closure);
2030  NS_ASSERTION(sorts->Length() > 1, "Multisorting with single sort!");
2031 
2032  nsresult rv;
2033  for (PRUint32 i = 1; i < sorts->Length(); i++) {
2034  PRUint32 propertyId = sorts->ElementAt(i).propertyId;
2035  PRBool ascending = sorts->ElementAt(i).ascending;
2036 
2037  nsString valueA;
2038  rv = bagA->GetSortablePropertyByID(propertyId, valueA);
2039  NS_ENSURE_SUCCESS(rv, rv);
2040 
2041  nsString valueB;
2042  rv = bagB->GetSortablePropertyByID(propertyId, valueB);
2043  NS_ENSURE_SUCCESS(rv, rv);
2044 
2045  if (valueA == valueB) {
2046  continue;
2047  }
2048 
2049  if (ascending) {
2050  return valueA > valueB ? 1 : -1;
2051  }
2052  else {
2053  return valueA < valueB ? 1 : -1;
2054  }
2055 
2056  }
2057 
2058  // If we reach here, the two bags are equal. Use the mediaItemId of the bags
2059  // to break the tie
2060  PRUint32 mediaItemIdA;
2061  rv = bagA->GetMediaItemId(&mediaItemIdA);
2062  NS_ENSURE_SUCCESS(rv, rv);
2063 
2064  PRUint32 mediaItemIdB;
2065  rv = bagB->GetMediaItemId(&mediaItemIdB);
2066  NS_ENSURE_SUCCESS(rv, rv);
2067 
2068  return mediaItemIdA > mediaItemIdB ? 1 : -1;
2069 }
2070 
2071 nsresult
2072 sbLocalDatabaseGUIDArray::SortRows(PRUint32 aStartIndex,
2073  PRUint32 aEndIndex,
2074  const nsAString& aKey,
2075  PRBool aIsFirst,
2076  PRBool aIsLast,
2077  PRBool aIsOnly,
2078  PRBool aIsNull)
2079 {
2080  nsresult rv;
2081  PRInt32 dbOk;
2082 
2083  LOG(("Sorting rows %d to %d on %s, isfirst %d islast %d isonly %d isnull %d\n",
2084  aStartIndex,
2085  aEndIndex,
2086  NS_ConvertUTF16toUTF8(aKey).get(),
2087  aIsFirst,
2088  aIsLast,
2089  aIsOnly,
2090  aIsNull));
2091 
2092  // SortRows always gets called with mCacheMonitor already acquired!
2093  // No need to acquire this lock in this method.
2094 
2095  /*
2096  * If this is only one row and it is not the first, last, and only row in the
2097  * window, we don't need to sort it
2098  */
2099  if (!aIsFirst && !aIsLast && !aIsOnly && aStartIndex == aEndIndex) {
2100  return NS_OK;
2101  }
2102 
2103  PRUint32 rangeLength = aEndIndex - aStartIndex + 1;
2104 
2105  // XXX Disable memory sorting in the general case, since it appears to slow things
2106  // down with the index fix from bug 8612. Enable it however when an FTS search
2107  // is active, as joining the FTS table can severely slow the resort query.
2108 
2109  // We can sort these rows in memory in the case where the entire group of
2110  // rows lies within the fetched chunk, meaning for a distinct primary sort
2111  // value, the rows with this value is not the first or last row in the
2112  // chunk. It also is not the only value in the chunk.
2113  if (mHasActiveSearch && !aIsFirst && !aIsLast && !aIsOnly && mPropertyCache) {
2114  nsTArray<const PRUnichar*> guids(rangeLength);
2115  for (PRUint32 i = aStartIndex; i <= aEndIndex; i++) {
2116  const PRUnichar** appended =
2117  guids.AppendElement(mCache[i]->guid.get());
2118  NS_ENSURE_TRUE(appended, NS_ERROR_OUT_OF_MEMORY);
2119  }
2120 
2121  // Grab the propety bags of all the items that are in the range that
2122  // we're sorting
2123  PRUint32 bagsCount = 0;
2124  sbILocalDatabaseResourcePropertyBag** bags = nsnull;
2125  rv = mPropertyCache->GetProperties(guids.Elements(),
2126  rangeLength,
2127  &bagsCount,
2128  &bags);
2129  NS_ENSURE_SUCCESS(rv, rv);
2131  bags);
2132 
2133  // Do the sort
2134  NS_QuickSort(bags,
2135  bagsCount,
2137  SortBags,
2138  &mSorts);
2139 
2140  // Update mCache with the results of the sort. Create a copy of all the
2141  // items that we are going to reorder, then update the cache in the order
2142  // of the sorted bags
2143  nsClassHashtable<nsStringHashKey, ArrayItem> lookup;
2144  PRBool success = lookup.Init(bagsCount);
2145  NS_ENSURE_TRUE(success, NS_ERROR_OUT_OF_MEMORY);
2146 
2147  for (PRUint32 i = aStartIndex; i <= aEndIndex; i++) {
2148  ArrayItem* item = mCache[i];
2149  nsAutoPtr<ArrayItem> copy(new ArrayItem(*item));
2150  NS_ENSURE_TRUE(copy, NS_ERROR_OUT_OF_MEMORY);
2151  success = lookup.Put(copy->guid, copy);
2152  NS_ENSURE_TRUE(success, NS_ERROR_OUT_OF_MEMORY);
2153  copy.forget();
2154  }
2155 
2156  for (PRUint32 i = 0; i < bagsCount; i++) {
2157  nsString guid;
2158  rv = bags[i]->GetGuid(guid);
2159  NS_ENSURE_SUCCESS(rv, rv);
2160 
2161  ArrayItem* item;
2162  PRBool found = lookup.Get(guid, &item);
2163  NS_ENSURE_TRUE(found, NS_ERROR_UNEXPECTED);
2164  nsAutoPtr<ArrayItem> copy(new ArrayItem(*item));
2165  NS_ENSURE_TRUE(copy, NS_ERROR_OUT_OF_MEMORY);
2166  nsAutoPtr<ArrayItem>* replaced =
2167  mCache.ReplaceElementsAt(i + aStartIndex,
2168  1,
2169  copy.get());
2170  NS_ENSURE_TRUE(replaced, NS_ERROR_OUT_OF_MEMORY);
2171  copy.forget();
2172  }
2173 
2174  return NS_OK;
2175  }
2176 
2177  nsCOMPtr<sbIDatabaseQuery> query;
2178  if(aIsNull) {
2179  rv = MakeQuery(mNullResortStatement, getter_AddRefs(query));
2180  NS_ENSURE_SUCCESS(rv, rv);
2181  }
2182  else {
2183  rv = MakeQuery(mResortStatement, getter_AddRefs(query));
2184  NS_ENSURE_SUCCESS(rv, rv);
2185 
2186  rv = query->BindStringParameter(0, aKey);
2187  NS_ENSURE_SUCCESS(rv, rv);
2188  }
2189 
2190  rv = query->Execute(&dbOk);
2191  NS_ENSURE_SUCCESS(rv, rv);
2192  NS_ENSURE_TRUE(dbOk == 0, NS_ERROR_FAILURE);
2193 
2194  nsCOMPtr<sbIDatabaseResult> result;
2195  rv = query->GetResultObject(getter_AddRefs(result));
2196  NS_ENSURE_TRUE(result, NS_ERROR_FAILURE);
2197 
2198  PRUint32 rowCount;
2199  rv = result->GetRowCount(&rowCount);
2200  NS_ENSURE_SUCCESS(rv, rv);
2201 
2202  /*
2203  * Make sure we get at least the number of rows back from the query that
2204  * we need to replace
2205  */
2206  if (rangeLength > rowCount) {
2207  return NS_ERROR_UNEXPECTED;
2208  }
2209 
2210  /*
2211  * Figure out the offset into the result set we should use to copy the query
2212  * results into the cache. If the range we are sorting is equal to the
2213  * length of the entire array, we know we don't need an offset.
2214  */
2215  PRUint32 offset = 0;
2216  if(rangeLength != mLength) {
2217  /* If this is the only sort being done for a window (indicated when aIsOnly
2218  * is true), we have no reference point to determine the offset, so we must
2219  * query for it.
2220  */
2221  if (aIsOnly) {
2222  /*
2223  * If we are resorting a null range, we can use the cached non null length
2224  * to calculate the offset
2225  */
2226  if (aIsNull) {
2227  if (mNullsFirst) {
2228  offset = 0;
2229  }
2230  else {
2231  offset = aStartIndex - mNonNullLength;
2232  }
2233  }
2234  else {
2235  PRUint32 position;
2236  rv = GetPrimarySortKeyPosition(aKey, &position);
2237  NS_ENSURE_SUCCESS(rv, rv);
2238  offset = aStartIndex - position;
2239  }
2240  }
2241  else {
2242  /*
2243  * If this range is at the top of the window, set the offset such that
2244  * we will copy the tail end of the result set
2245  */
2246  if (aIsFirst) {
2247  offset = rowCount - rangeLength;
2248  }
2249  else {
2250  /*
2251  * Otherwise just copy the entire result set into the cache
2252  */
2253  offset = 0;
2254  }
2255  }
2256  }
2257 
2258  // Copy the rows from the query result into the cache starting at the
2259  // calculated offset
2260  for (PRUint32 i = 0; i < rangeLength; i++) {
2261 
2262  PRUint32 row = offset + i;
2263  nsAutoPtr<ArrayItem>& item = mCache[i + aStartIndex];
2264 
2265  nsString mediaItemIdStr;
2266  rv = result->GetRowCell(row, 0, mediaItemIdStr);
2267  NS_ENSURE_SUCCESS(rv, rv);
2268 
2269  item->mediaItemId = mediaItemIdStr.ToInteger(&rv);
2270  NS_ENSURE_SUCCESS(rv, rv);
2271 
2272  rv = result->GetRowCell(row, 1, item->guid);
2273  NS_ENSURE_SUCCESS(rv, rv);
2274 
2275  rv = result->GetRowCell(row, 2, item->ordinal);
2276  NS_ENSURE_SUCCESS(rv, rv);
2277 
2278  nsString rowidStr;
2279  rv = result->GetRowCell(row, 3, rowidStr);
2280  NS_ENSURE_SUCCESS(rv, rv);
2281 
2282  item->rowid = nsString_ToUint64(rowidStr, &rv);
2283  NS_ENSURE_SUCCESS(rv, rv);
2284  }
2285 
2286  return NS_OK;
2287 }
2288 
2289 nsresult
2290 sbLocalDatabaseGUIDArray::GetPrimarySortKeyPosition(const nsAString& aValue,
2291  PRUint32 *_retval)
2292 {
2293  nsresult rv;
2294 
2295  /*
2296  * Make sure the cache is initalized
2297  */
2298  if (!mPrimarySortKeyPositionCache.IsInitialized()) {
2299  mPrimarySortKeyPositionCache.Init(100);
2300  }
2301 
2302  PRUint32 position;
2303  if (!mPrimarySortKeyPositionCache.Get(aValue, &position)) {
2304  PRInt32 dbOk;
2305 
2306  nsCOMPtr<sbIDatabaseQuery> query;
2307  rv = MakeQuery(mPrimarySortKeyPositionStatement,
2308  getter_AddRefs(query));
2309  NS_ENSURE_SUCCESS(rv, rv);
2310 
2311  rv = query->BindStringParameter(0, aValue);
2312  NS_ENSURE_SUCCESS(rv, rv);
2313 
2314  rv = query->Execute(&dbOk);
2315  NS_ENSURE_SUCCESS(rv, rv);
2316  NS_ENSURE_TRUE(dbOk == 0, NS_ERROR_FAILURE);
2317 
2318  nsCOMPtr<sbIDatabaseResult> result;
2319  rv = query->GetResultObject(getter_AddRefs(result));
2320  NS_ENSURE_TRUE(result, NS_ERROR_FAILURE);
2321 
2322  PRUint32 rowCount;
2323  rv = result->GetRowCount(&rowCount);
2324  NS_ENSURE_SUCCESS(rv, rv);
2325 
2326  NS_ENSURE_TRUE(rowCount == 1, NS_ERROR_UNEXPECTED);
2327 
2328  nsAutoString countStr;
2329  rv = result->GetRowCell(0, 0, countStr);
2330  NS_ENSURE_SUCCESS(rv, rv);
2331 
2332  position = countStr.ToInteger(&rv);
2333  NS_ENSURE_SUCCESS(rv, rv);
2334 
2335  mPrimarySortKeyPositionCache.Put(aValue, position);
2336  }
2337 
2338  *_retval = position;
2339  return NS_OK;
2340 }
2341 
2342 nsresult
2343 sbLocalDatabaseGUIDArray::GetByIndexInternal(PRUint32 aIndex,
2344  ArrayItem** _retval)
2345 {
2346  nsresult rv;
2347 
2348  TRACE(("GetByIndexInternal %d %d", aIndex, mLength));
2349 
2350  nsAutoMonitor mon(mCacheMonitor);
2351 
2352  if (mValid == PR_FALSE) {
2353  rv = Initialize();
2354  NS_ENSURE_SUCCESS(rv, rv);
2355  }
2356 
2357  NS_ENSURE_TRUE(aIndex < mLength, NS_ERROR_INVALID_ARG);
2358 
2359  /*
2360  * Check to see if we have this index in cache
2361  */
2362  if (aIndex < mCache.Length()) {
2363  ArrayItem* item = mCache[aIndex];
2364  if (item) {
2365  TRACE(("Cache hit, got %s", NS_ConvertUTF16toUTF8(item->guid).get()));
2366  *_retval = item;
2367  return NS_OK;
2368  }
2369  }
2370 
2371  TRACE(("MISS"));
2372 
2373 #if defined(FORCE_FETCH_ALL_GUIDS_ASYNC)
2374  /*
2375  * Cache miss
2376  */
2377  rv = FetchRows(aIndex, mFetchSize);
2378  NS_ENSURE_SUCCESS(rv, rv);
2379  NS_ENSURE_TRUE(aIndex < mCache.Length(), NS_ERROR_FAILURE);
2380 
2381  /*
2382  * Prefetch all rows. Do this from an asynchronous event on the main thread
2383  * so that any pending UI events can complete.
2384  */
2385  if (!mPrefetchedRows) {
2386  mPrefetchedRows = PR_TRUE;
2388  &sbLocalDatabaseGUIDArray::FetchRows,
2389  NS_ERROR_FAILURE,
2390  static_cast<PRUint32>(0),
2391  static_cast<PRUint32>(0));
2392  }
2393 #else
2394  /*
2395  * Cache miss, cache all GUIDs. FetchRows takes care of its own locking.
2396  */
2397  rv = FetchRows(0, 0);
2398  NS_ENSURE_SUCCESS(rv, rv);
2399  NS_ENSURE_TRUE(aIndex < mCache.Length(), NS_ERROR_FAILURE);
2400 #endif
2401 
2402  *_retval = mCache[aIndex];
2403 
2404  return NS_OK;
2405 }
2406 
2407 PRInt32
2408 sbLocalDatabaseGUIDArray::GetPropertyId(const nsAString& aProperty)
2409 {
2410  return SB_GetPropertyId(aProperty, mPropertyCache);
2411 }
2412 
2413 nsresult
2414 sbLocalDatabaseGUIDArray::GetMTListener(
2415  sbILocalDatabaseGUIDArrayListener ** aListener)
2416 {
2417  NS_ENSURE_ARG_POINTER(aListener);
2418 
2419  nsresult rv;
2420 
2421  // if the listener has gone away just return null
2422  if (!mListener) {
2423  *aListener = nsnull;
2424  return NS_OK;
2425  }
2426  nsCOMPtr<nsIWeakReference> weak;
2427  nsCOMPtr<sbILocalDatabaseGUIDArrayListener> listener;
2428  if (!NS_IsMainThread()) {
2429  nsCOMPtr<nsIThread> mainThread;
2430  rv = NS_GetMainThread(getter_AddRefs(mainThread));
2431  NS_ENSURE_SUCCESS(rv, rv);
2432 
2433  rv = do_GetProxyForObject(mainThread,
2434  mListener.get(),
2435  NS_PROXY_SYNC | NS_PROXY_ALWAYS,
2436  getter_AddRefs(weak));
2437  NS_ENSURE_SUCCESS(rv, rv);
2438 
2439  listener = do_QueryReferent(weak, &rv);
2440  NS_ENSURE_SUCCESS(rv, rv);
2441 
2442  if (!listener) {
2443  *aListener = nsnull;
2444  return NS_OK;
2445  }
2446  nsCOMPtr<sbILocalDatabaseGUIDArrayListener> proxiedListener;
2447  rv = do_GetProxyForObject(mainThread,
2448  listener.get(),
2449  NS_PROXY_SYNC | NS_PROXY_ALWAYS,
2450  getter_AddRefs(proxiedListener));
2451  NS_ENSURE_SUCCESS(rv, rv);
2452  proxiedListener.forget(aListener);
2453  return NS_OK;
2454  }
2455 
2456  listener = do_QueryReferent(mListener);
2457 
2458  listener.forget(aListener);
2459  return NS_OK;
2460 }
2461 
2462 void
2463 sbLocalDatabaseGUIDArray::GenerateCachedLengthKey()
2464 {
2465  nsAutoLock mon(mPropIdsLock);
2466 
2467  // Clear all the old property IDs from this set, and remove the cache
2468  // entries on the old key.
2469  mPropIdsUsedInCacheKey.clear();
2470 
2471  if(mLengthCache && !mCachedLengthKey.IsEmpty()) {
2472  mLengthCache->RemoveCachedLength(mCachedLengthKey);
2473  mLengthCache->RemoveCachedNonNullLength(mCachedLengthKey);
2474  }
2475 
2476  // Try and avoid resizing the string a bunch of times.
2477  mCachedLengthKey.SetLength(2048);
2478 
2479  // Clear the key.
2480  mCachedLengthKey.Truncate();
2481 
2482  // Add Database GUID
2483  mCachedLengthKey.Append(mDatabaseGUID);
2484 
2485  // Add Base Table
2486  mCachedLengthKey.Append(mBaseTable);
2487 
2488  // Add Base Constraint Column
2489  mCachedLengthKey.Append(mBaseConstraintColumn);
2490 
2491  // Add Base Constraint Value
2492  mCachedLengthKey.AppendInt(mBaseConstraintValue);
2493 
2494  // Add Is Distinct
2495  mCachedLengthKey.AppendInt(mIsDistinct);
2496 
2497  // Add Distinct with Sortable
2498  mCachedLengthKey.AppendInt(mDistinctWithSortableValues);
2499 
2500  // Add Is Full Library
2501  mCachedLengthKey.AppendInt(mIsFullLibrary);
2502 
2503  // We'll save off the property ids so we can associate them with the
2504  // keys generated so we can invalidate cached lengths later.
2505 
2506  // Go through the filters and add them to the key.
2507  PRUint32 filterCount = mFilters.Length();
2508  for (PRUint32 index = 0; index < filterCount; index++) {
2509  const FilterSpec& refSpec = mFilters.ElementAt(index);
2510 
2511  mCachedLengthKey.Append(refSpec.property);
2512 
2513  PRUint32 propId = 0;
2514  if(NS_SUCCEEDED(mPropertyCache->GetPropertyDBID(refSpec.property,
2515  &propId))) {
2516  mPropIdsUsedInCacheKey.insert(propId);
2517  }
2518 
2519  mCachedLengthKey.AppendInt(refSpec.isSearch);
2520 
2521  PRUint32 valueCount = refSpec.values.Length();
2522  for(PRUint32 valueIndex = 0; valueIndex < valueCount; valueIndex++) {
2523  mCachedLengthKey.Append(refSpec.values.ElementAt(valueIndex));
2524  }
2525  }
2526 
2527  // Go through the properties we use to sort and add them to the key.
2528  PRUint32 sortCount = mSorts.Length();
2529  for (PRUint32 index = 0; index < sortCount; index++) {
2530  const SortSpec& sortSpec = mSorts.ElementAt(index);
2531  mCachedLengthKey.AppendInt(sortSpec.propertyId);
2532  mPropIdsUsedInCacheKey.insert(sortSpec.propertyId);
2533 
2534  mCachedLengthKey.AppendInt(sortSpec.ascending);
2535  mCachedLengthKey.AppendInt(sortSpec.secondary);
2536  }
2537 }
2538 
2540 
2542  sbILocalDatabaseGUIDArray* aArray) :
2543  mLibrary(aLibrary),
2544  mArray(aArray),
2545  mNextIndex(0),
2546  mPreviousLength(0)
2547 {
2548 }
2549 
2551 {
2552 }
2553 
2554 NS_IMETHODIMP
2555 sbGUIDArrayEnumerator::HasMoreElements(PRBool *_retval)
2556 {
2557  nsresult rv;
2558 
2559  PRUint32 length;
2560  rv = mArray->GetLength(&length);
2561  NS_ENSURE_SUCCESS(rv, rv);
2562 
2563  // Length changed, reset next index, next guid.
2564  // This happens when the data inside the guid array changes
2565  // because guids were added or removed from the result set.
2566  if (length != mPreviousLength) {
2567  mPreviousLength = length;
2568  mNextIndex = 0;
2569  mNextGUID.Truncate();
2570  }
2571 
2572  *_retval = mNextIndex < length;
2573 
2574  return NS_OK;
2575 }
2576 
2577 NS_IMETHODIMP
2578 sbGUIDArrayEnumerator::GetNext(nsISupports **_retval)
2579 {
2580  nsresult rv = NS_ERROR_UNEXPECTED;
2581 
2582  nsAutoString guid;
2583  rv = mArray->GetGuidByIndex(mNextIndex, guid);
2584  NS_ENSURE_SUCCESS(rv, rv);
2585 
2586  nsCOMPtr<sbIMediaItem> item;
2587  rv = mLibrary->GetMediaItem(guid, getter_AddRefs(item));
2588  NS_ENSURE_SUCCESS(rv, rv);
2589 
2590  nsCOMPtr<nsISupports> supports = do_QueryInterface(item, &rv);
2591  NS_ENSURE_SUCCESS(rv, rv);
2592 
2593  NS_ADDREF(*_retval = supports);
2594 
2595  mNextIndex++;
2596 
2597  return NS_OK;
2598 }
2599 
2601 
2603  mArray(aArray),
2604  mNextIndex(0)
2605 {
2606  NS_ASSERTION(aArray, "Null value passed to ctor");
2607 }
2608 
2610 {
2611 }
2612 
2613 NS_IMETHODIMP
2614 sbGUIDArrayStringEnumerator::HasMore(PRBool *_retval)
2615 {
2616  nsresult rv;
2617 
2618  PRUint32 length;
2619  rv = mArray->GetLength(&length);
2620  NS_ENSURE_SUCCESS(rv, rv);
2621 
2622  *_retval = mNextIndex < length;
2623  return NS_OK;
2624 }
2625 
2626 NS_IMETHODIMP
2627 sbGUIDArrayStringEnumerator::GetNext(nsAString& _retval)
2628 {
2629  nsresult rv;
2630 
2631  rv = mArray->GetGuidByIndex(mNextIndex, _retval);
2632  NS_ENSURE_SUCCESS(rv, rv);
2633 
2634  mNextIndex++;
2635 
2636  return NS_OK;
2637 }
return NS_OK
#define SONGBIRD_DATABASEQUERY_CONTRACTID
Definition: DatabaseQuery.h:63
[USER CODE SHOULD NOT REFERENCE THIS CLASS]
attribute sbILocalDatabaseGUIDArrayListener listener
onPageChanged aValue
Definition: FeedWriter.js:1395
NS_IMPL_ISUPPORTS1(sbDeviceCapabilitiesUtils, sbIDeviceCapabilitiesUtils) sbDeviceCapabilitiesUtils
PRUint64 nsString_ToUint64(const nsAString &str, nsresult *rv)
#define TRACE(args)
[USER CODE SHOULD NOT REFERENCE THIS CLASS]
#define SB_PROPERTY_ORDINAL
[USER CODE SHOULD NOT REFERENCE THIS CLASS]
const unsigned long SORT_NULL_LAST
Null values always last.
#define SB_MUTABLEPROPERTYARRAY_CONTRACTID
#define LOG(args)
nsresult do_GetProxyForObject(nsIEventTarget *aTarget, REFNSIID aIID, nsISupports *aObj, PRInt32 aProxyType, void **aProxyObject)
static PRBool SB_IsTopLevelProperty(PRUint32 aPropertyDBID)
nsresult sbInvokeOnMainThread2Async(T &aObject, MT aMethod, RT aFailureReturnValue, A1 aArg1, A2 aArg2)
#define SB_PROPERTYMANAGER_CONTRACTID
PRUint32 & offset
#define SB_PROPERTY_CREATED
static PRInt32 SB_GetPropertyId(const nsAString &aProperty, sbILocalDatabasePropertyCache *aPropertyCache)
const unsigned long SORT_NULL_SMALL
Causes null values to be infinitely small. This is the default.
Songbird Thread Utilities Definitions.
GstMessage * message
#define DEFAULT_FETCH_SIZE
const PR_UINT32_MAX
Definition: httpd.js:55
[USER CODE SHOULD NOT REFERENCE THIS CLASS]
StringArrayEnumerator prototype hasMore
[USER CODE SHOULD NOT REFERENCE THIS CLASS]
countRef value
Definition: FeedWriter.js:1423
NS_IMPL_THREADSAFE_ISUPPORTS2(sbLocalDatabaseGUIDArray, sbILocalDatabaseGUIDArray, nsISupportsWeakReference) sbLocalDatabaseGUIDArray
static void AppendInt(nsAString &str, PRInt64 val)
An object responsible for executing SQL queries on the database.
restoreHistoryPrecursor aCount
const unsigned long SORT_NULL_FIRST
Null values always first.
const unsigned long SORT_NULL_BIG
Causes null values to be infinitely large.
An interface to carry around arrays of nsIProperty instances. Users of this interface should only QI ...
_getSelectedPageStyle s i
A prepared database statement.
Songbird Database Object Definition.