vendor/Pods/YapDatabase/YapDatabase/Extensions/Views/YapDatabaseViewTransaction.m in motion-yapper-0.0.1 vs vendor/Pods/YapDatabase/YapDatabase/Extensions/Views/YapDatabaseViewTransaction.m in motion-yapper-0.0.2

- old
+ new

@@ -23,10 +23,14 @@ static const int ydbLogLevel = YDB_LOG_LEVEL_WARN; #else static const int ydbLogLevel = YDB_LOG_LEVEL_WARN; #endif +#define ExtKey_classVersion @"classVersion" +#define ExtKey_persistent @"persistent" +#define ExtKey_version_deprecated @"version" +#define ExtKey_versionTag @"versionTag" /** * The view is tasked with storing ordered arrays of keys. * In doing so, it splits the array into "pages" of keys, * and stores the pages in the database. @@ -34,10 +38,17 @@ * And only the contents of a single page need be read to fetch a single key. **/ #define YAP_DATABASE_VIEW_MAX_PAGE_SIZE 50 /** + * Declare that this class implements YapDatabaseExtensionTransaction_Hooks protocol. + * This is done privately, as the protocol is internal. +**/ +@interface YapDatabaseViewTransaction () <YapDatabaseExtensionTransaction_Hooks> +@end + +/** * ARCHITECTURE OVERVIEW: * * A YapDatabaseView allows one to store a ordered array of collection/key tuples. * Furthermore, groups are supported, which means there may be multiple ordered arrays of tuples, one per group. * @@ -73,10 +84,12 @@ @implementation YapDatabaseViewTransaction - (id)initWithViewConnection:(YapDatabaseViewConnection *)inViewConnection databaseTransaction:(YapDatabaseReadTransaction *)inDatabaseTransaction { + YDBLogAutoTrace(); + if ((self = [super init])) { viewConnection = inViewConnection; databaseTransaction = inDatabaseTransaction; @@ -102,74 +115,124 @@ * Return YES if completed successfully, or if already prepared. * Return NO if some kind of error occured. **/ - (BOOL)createIfNeeded { + YDBLogAutoTrace(); + + int classVersion = YAP_DATABASE_VIEW_CLASS_VERSION; + BOOL isPersistent = [self isPersistentView]; + + NSString *versionTag = viewConnection->view->versionTag; + + // Figure out what steps we need to take in order to register the view + BOOL needsCreateTables = NO; + BOOL oldIsPersistent = NO; + BOOL hasOldIsPersistent = NO; + + NSString *oldVersionTag = nil; + // Check classVersion (the internal version number of YapDatabaseView implementation) - int oldClassVersion = [self intValueForExtensionKey:@"classVersion"]; - int classVersion = YAP_DATABASE_VIEW_CLASS_VERSION; + int oldClassVersion = 0; + BOOL hasOldClassVersion = [self getIntValue:&oldClassVersion forExtensionKey:ExtKey_classVersion]; - if (oldClassVersion != classVersion) + if (!hasOldClassVersion) + { needsCreateTables = YES; + } + else if (oldClassVersion != classVersion) + { + [self dropTablesForOldClassVersion:oldClassVersion]; + needsCreateTables = YES; + } // Check persistence. // Need to properly transition from persistent to non-persistent, and vice-versa. - BOOL oldIsPersistent = NO; - BOOL hasOldIsPersistent = [self getBoolValue:&oldIsPersistent forExtensionKey:@"persistent"]; - - BOOL isPersistent = [self isPersistentView]; - - if (hasOldIsPersistent && (oldIsPersistent != isPersistent)) + if (!needsCreateTables || hasOldClassVersion) { - [[viewConnection->view class] - dropTablesForRegisteredName:[self registeredName] - withTransaction:(YapDatabaseReadWriteTransaction *)databaseTransaction]; + hasOldIsPersistent = [self getBoolValue:&oldIsPersistent forExtensionKey:ExtKey_persistent]; - needsCreateTables = YES; + if (hasOldIsPersistent && oldIsPersistent && !isPersistent) + { + [[viewConnection->view class] + dropTablesForRegisteredName:[self registeredName] + withTransaction:(YapDatabaseReadWriteTransaction *)databaseTransaction]; + } + + if (!hasOldIsPersistent || (oldIsPersistent != isPersistent)) + { + needsCreateTables = YES; + } + else if (!isPersistent) + { + // We always have to create & populate the tables for non-persistent views. + // Even when re-registering from previous app launch. + needsCreateTables = YES; + + oldVersionTag = [self stringValueForExtensionKey:ExtKey_versionTag]; + } } // Create or re-populate if needed if (needsCreateTables) { // First time registration - [self dropTablesForOldClassVersion:oldClassVersion]; - if (![self createTables]) return NO; if (![self populateView]) return NO; - [self setIntValue:classVersion forExtensionKey:@"classVersion"]; + if (!hasOldClassVersion || (oldClassVersion != classVersion)) { + [self setIntValue:classVersion forExtensionKey:ExtKey_classVersion]; + } - [self setBoolValue:isPersistent forExtensionKey:@"persistent"]; + if (!hasOldIsPersistent || (oldIsPersistent != isPersistent)) { + [self setBoolValue:isPersistent forExtensionKey:ExtKey_persistent]; + } - int userSuppliedConfigVersion = viewConnection->view->version; - [self setIntValue:userSuppliedConfigVersion forExtensionKey:@"version"]; + if (![oldVersionTag isEqualToString:versionTag]) { + [self setStringValue:versionTag forExtensionKey:ExtKey_versionTag]; + } } else { // Check user-supplied config version. // We may need to re-populate the database if the groupingBlock or sortingBlock changed. - int oldVersion = [self intValueForExtensionKey:@"version"]; - int newVersion = viewConnection->view->version; + oldVersionTag = [self stringValueForExtensionKey:ExtKey_versionTag]; - if (oldVersion != newVersion) + BOOL hasOldVersion_deprecated = NO; + if (oldVersionTag == nil) { - if (![self populateView]) return NO; + int oldVersion_deprecated = 0; + hasOldVersion_deprecated = [self getIntValue:&oldVersion_deprecated + forExtensionKey:ExtKey_version_deprecated]; - [self setIntValue:newVersion forExtensionKey:@"version"]; + if (hasOldVersion_deprecated) + { + oldVersionTag = [NSString stringWithFormat:@"%d", oldVersion_deprecated]; + } } - if (!hasOldIsPersistent) + if (![oldVersionTag isEqualToString:versionTag]) { - [self setBoolValue:isPersistent forExtensionKey:@"persistent"]; + if (![self populateView]) return NO; + + [self setStringValue:versionTag forExtensionKey:ExtKey_versionTag]; + + if (hasOldVersion_deprecated) + [self removeValueForExtensionKey:ExtKey_version_deprecated]; } + else if (hasOldVersion_deprecated) + { + [self removeValueForExtensionKey:ExtKey_version_deprecated]; + [self setStringValue:versionTag forExtensionKey:ExtKey_versionTag]; + } } return YES; } @@ -182,10 +245,12 @@ * Return YES if completed successfully, or if already prepared. * Return NO if some kind of error occured. **/ - (BOOL)prepareIfNeeded { + YDBLogAutoTrace(); + if (viewConnection->group_pagesMetadata_dict && viewConnection->pageKey_group_dict) { // Already prepared return YES; } @@ -426,10 +491,12 @@ return !error; } - (void)dropTablesForOldClassVersion:(int)oldClassVersion { + YDBLogAutoTrace(); + if (oldClassVersion == 1) { // In version 2, we switched from 'view_name_key' to 'view_name_map'. // The old table stored key->pageKey mappings. // The new table stores rowid->pageKey mappings. @@ -471,10 +538,12 @@ } } - (BOOL)createTables { + YDBLogAutoTrace(); + if ([self isPersistentView]) { sqlite3 *db = databaseTransaction->connection->db; NSString *mapTableName = [self mapTableName]; @@ -554,19 +623,24 @@ } } - (BOOL)populateView { + YDBLogAutoTrace(); + // Remove everything from the database [self removeAllRowids]; // Initialize ivars - viewConnection->group_pagesMetadata_dict = [[NSMutableDictionary alloc] init]; - viewConnection->pageKey_group_dict = [[NSMutableDictionary alloc] init]; + if (viewConnection->group_pagesMetadata_dict == nil) + viewConnection->group_pagesMetadata_dict = [[NSMutableDictionary alloc] init]; + if (viewConnection->pageKey_group_dict == nil) + viewConnection->pageKey_group_dict = [[NSMutableDictionary alloc] init]; + // Enumerate the existing rows in the database and populate the view __unsafe_unretained YapDatabaseView *view = viewConnection->view; BOOL groupingNeedsObject = view->groupingBlockType == YapDatabaseViewBlockTypeWithObject || @@ -583,41 +657,51 @@ BOOL needsObject = groupingNeedsObject || sortingNeedsObject; BOOL needsMetadata = groupingNeedsMetadata || sortingNeedsMetadata; NSString *(^getGroup)(NSString *collection, NSString *key, id object, id metadata); - getGroup = ^(NSString *collection, NSString *key, id object, id metadata){ - - if (view->groupingBlockType == YapDatabaseViewBlockTypeWithKey) - { + + if (view->groupingBlockType == YapDatabaseViewBlockTypeWithKey) + { + getGroup = ^(NSString *collection, NSString *key, id object, id metadata){ + __unsafe_unretained YapDatabaseViewGroupingWithKeyBlock groupingBlock = (YapDatabaseViewGroupingWithKeyBlock)view->groupingBlock; return groupingBlock(collection, key); - } - else if (view->groupingBlockType == YapDatabaseViewBlockTypeWithObject) - { + }; + } + else if (view->groupingBlockType == YapDatabaseViewBlockTypeWithObject) + { + getGroup = ^(NSString *collection, NSString *key, id object, id metadata){ + __unsafe_unretained YapDatabaseViewGroupingWithObjectBlock groupingBlock = (YapDatabaseViewGroupingWithObjectBlock)view->groupingBlock; return groupingBlock(collection, key, object); - } - else if (view->groupingBlockType == YapDatabaseViewBlockTypeWithMetadata) - { + }; + } + else if (view->groupingBlockType == YapDatabaseViewBlockTypeWithMetadata) + { + getGroup = ^(NSString *collection, NSString *key, id object, id metadata){ + __unsafe_unretained YapDatabaseViewGroupingWithMetadataBlock groupingBlock = (YapDatabaseViewGroupingWithMetadataBlock)view->groupingBlock; return groupingBlock(collection, key, metadata); - } - else - { + }; + } + else + { + getGroup = ^(NSString *collection, NSString *key, id object, id metadata){ + __unsafe_unretained YapDatabaseViewGroupingWithRowBlock groupingBlock = (YapDatabaseViewGroupingWithRowBlock)view->groupingBlock; return groupingBlock(collection, key, object, metadata); - } - }; + }; + } int flags = (YapDatabaseViewChangedObject | YapDatabaseViewChangedMetadata); if (needsObject && needsMetadata) { @@ -859,10 +943,59 @@ } return YES; } +- (void)repopulateView +{ + YDBLogAutoTrace(); + + // Code overview: + // + // We could simply run the usual algorithm. + // That is, enumerate over every item in the database, and run pretty much the same code as + // in the handleUpdateObject:forCollectionKey:withMetadata:rowid:. + // However, this causes a potential issue where the sortingBlock will be invoked with items that + // no longer exist in the given group. + // + // Instead we're going to find a way around this. + // That way the sortingBlock works in a manner we're used to. + // + // Here's the algorithm overview: + // + // - Insert remove ops for every row & group + // - Remove all items from the database tables + // - Flush the group_pagesMetadata_dict (and related ivars) + // - Set the reset flag (for internal notification creation) + // - And then run the normal populate routine, with one exceptione handled by the isRepopulate flag. + // + // The changeset mechanism will automatically consolidate all changes to the minimum. + + for (NSString *group in viewConnection->group_pagesMetadata_dict) + { + // We must add the changes in reverse order. + // Either that, or the change index of each item would have to be zero, + // because a YapDatabaseViewRowChange records the index at the moment the change happens. + + [self enumerateRowidsInGroup:group + withOptions:NSEnumerationReverse + usingBlock:^(int64_t rowid, NSUInteger index, BOOL *stop) + { + YapCollectionKey *collectionKey = [databaseTransaction collectionKeyForRowid:rowid]; + + [viewConnection->changes addObject: + [YapDatabaseViewRowChange deleteKey:collectionKey inGroup:group atIndex:index]]; + }]; + + [viewConnection->changes addObject:[YapDatabaseViewSectionChange deleteGroup:group]]; + } + + isRepopulate = YES; + [self populateView]; + isRepopulate = NO; +} + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// #pragma mark Accessors //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /** @@ -1227,11 +1360,11 @@ - (NSUInteger)indexForRowid:(int64_t)rowid inGroup:(NSString *)group withPageKey:(NSString *)pageKey { // Calculate the offset of the corresponding page within the group. NSUInteger pageOffset = 0; - NSMutableArray *pagesMetadataForGroup = [viewConnection->group_pagesMetadata_dict objectForKey:group]; + NSArray *pagesMetadataForGroup = [viewConnection->group_pagesMetadata_dict objectForKey:group]; for (YapDatabaseViewPageMetadata *pageMetadata in pagesMetadataForGroup) { if ([pageMetadata->pageKey isEqualToString:pageKey]) { @@ -1257,11 +1390,11 @@ return pageOffset + indexWithinPage; } - (BOOL)getRowid:(int64_t *)rowidPtr atIndex:(NSUInteger)index inGroup:(NSString *)group { - NSMutableArray *pagesMetadataForGroup = [viewConnection->group_pagesMetadata_dict objectForKey:group]; + NSArray *pagesMetadataForGroup = [viewConnection->group_pagesMetadata_dict objectForKey:group]; NSUInteger pageOffset = 0; for (YapDatabaseViewPageMetadata *pageMetadata in pagesMetadataForGroup) { if ((index < (pageOffset + pageMetadata->count)) && (pageMetadata->count > 0)) @@ -1291,11 +1424,11 @@ // if (count > 0) // return [self getRowid:rowidPtr atIndex:(count-1) inGroup:group]; // else // return nil; - NSMutableArray *pagesMetadataForGroup = [viewConnection->group_pagesMetadata_dict objectForKey:group]; + NSArray *pagesMetadataForGroup = [viewConnection->group_pagesMetadata_dict objectForKey:group]; __block int64_t rowid = 0; __block BOOL found = NO; [pagesMetadataForGroup enumerateObjectsWithOptions:NSEnumerationReverse @@ -1373,11 +1506,11 @@ [viewConnection->mapCache setObject:pageKey forKey:@(rowid)]; // Add change to log [viewConnection->changes addObject: - [YapDatabaseViewSectionChange insertGroup:group]]; + [YapDatabaseViewSectionChange insertGroup:group]]; [viewConnection->changes addObject: [YapDatabaseViewRowChange insertKey:collectionKey inGroup:group atIndex:0]]; [viewConnection->mutatedGroups addObject:group]; @@ -1625,66 +1758,58 @@ if (view->sortingBlockType == YapDatabaseViewBlockTypeWithKey) { __unsafe_unretained YapDatabaseViewSortingWithKeyBlock sortingBlock = (YapDatabaseViewSortingWithKeyBlock)view->sortingBlock; - NSString *anotherKey = nil; - NSString *anotherCollection = nil; - [databaseTransaction getKey:&anotherKey collection:&anotherCollection forRowid:anotherRowid]; + YapCollectionKey *another = [databaseTransaction collectionKeyForRowid:anotherRowid]; return sortingBlock(group, collectionKey.collection, collectionKey.key, - anotherCollection, anotherKey); + another.collection, another.key); } else if (view->sortingBlockType == YapDatabaseViewBlockTypeWithObject) { __unsafe_unretained YapDatabaseViewSortingWithObjectBlock sortingBlock = (YapDatabaseViewSortingWithObjectBlock)view->sortingBlock; - NSString *anotherKey = nil; - NSString *anotherCollection = nil; + YapCollectionKey *another = nil; id anotherObject = nil; - [databaseTransaction getKey:&anotherKey - collection:&anotherCollection - object:&anotherObject - forRowid:anotherRowid]; + [databaseTransaction getCollectionKey:&another + object:&anotherObject + forRowid:anotherRowid]; return sortingBlock(group, collectionKey.collection, collectionKey.key, object, - anotherCollection, anotherKey, anotherObject); + another.collection, another.key, anotherObject); } else if (view->sortingBlockType == YapDatabaseViewBlockTypeWithMetadata) { __unsafe_unretained YapDatabaseViewSortingWithMetadataBlock sortingBlock = (YapDatabaseViewSortingWithMetadataBlock)view->sortingBlock; - NSString *anotherKey = nil; - NSString *anotherCollection = nil; + YapCollectionKey *another = nil; id anotherMetadata = nil; - [databaseTransaction getKey:&anotherKey - collection:&anotherCollection - metadata:&anotherMetadata - forRowid:anotherRowid]; + [databaseTransaction getCollectionKey:&another + metadata:&anotherMetadata + forRowid:anotherRowid]; return sortingBlock(group, collectionKey.collection, collectionKey.key, metadata, - anotherCollection, anotherKey, anotherMetadata); + another.collection, another.key, anotherMetadata); } else { __unsafe_unretained YapDatabaseViewSortingWithRowBlock sortingBlock = (YapDatabaseViewSortingWithRowBlock)view->sortingBlock; - NSString *anotherKey = nil; - NSString *anotherCollection = nil; + YapCollectionKey *another = nil; id anotherObject = nil; id anotherMetadata = nil; - [databaseTransaction getKey:&anotherKey - collection:&anotherCollection - object:&anotherObject - metadata:&anotherMetadata - forRowid:anotherRowid]; + [databaseTransaction getCollectionKey:&another + object:&anotherObject + metadata:&anotherMetadata + forRowid:anotherRowid]; return sortingBlock(group, collectionKey.collection, collectionKey.key, object, metadata, - anotherCollection, anotherKey, anotherObject, anotherMetadata); + another.collection, another.key, anotherObject, anotherMetadata); } }; NSComparisonResult cmp; @@ -1948,12 +2073,14 @@ YDBLogVerbose(@"Removing collection(%@) key(%@) from page(%@) at index(%lu)", collectionKey.collection, collectionKey.key, page, (unsigned long)indexWithinPage); // Add change to log + NSUInteger indexWithinGroup = pageOffset + indexWithinPage; + [viewConnection->changes addObject: - [YapDatabaseViewRowChange deleteKey:collectionKey inGroup:group atIndex:(pageOffset + indexWithinPage)]]; + [YapDatabaseViewRowChange deleteKey:collectionKey inGroup:group atIndex:indexWithinGroup]]; [viewConnection->mutatedGroups addObject:group]; // Update page (by removing key from array) @@ -2063,11 +2190,11 @@ { [page removeRowidAtIndex:i]; numRemoved++; [viewConnection->changes addObject: - [YapDatabaseViewRowChange deleteKey:collectionKey inGroup:group atIndex:(pageOffset + i)]]; + [YapDatabaseViewRowChange deleteKey:collectionKey inGroup:group atIndex:(pageOffset + i)]]; } } [viewConnection->mutatedGroups addObject:group]; @@ -2144,14 +2271,16 @@ { [mapTableTransaction removeAllObjects]; [pageTableTransaction removeAllObjects]; [pageMetadataTableTransaction removeAllObjects]; } - + for (NSString *group in viewConnection->group_pagesMetadata_dict) { - [viewConnection->changes addObject:[YapDatabaseViewSectionChange resetGroup:group]]; + if (!isRepopulate) { + [viewConnection->changes addObject:[YapDatabaseViewSectionChange resetGroup:group]]; + } [viewConnection->mutatedGroups addObject:group]; } [viewConnection->group_pagesMetadata_dict removeAllObjects]; [viewConnection->pageKey_group_dict removeAllObjects]; @@ -2427,11 +2556,11 @@ * This method is only called if within a readwrite transaction. * * Extensions may implement it to perform any "cleanup" before the changeset is requested. * Remember, the changeset is requested before the commitTransaction method is invoked. **/ -- (void)preCommitReadWriteTransaction +- (void)prepareChangeset { YDBLogAutoTrace(); // During the readwrite transaction we do nothing to enforce the pageSize restriction. // Multiple modifications during a transaction make it non worthwhile. @@ -2512,11 +2641,11 @@ hasDirtyLink = YES; } else { NSString *group = [self groupForPageKey:pageKey]; - NSMutableArray *pagesMetadataForGroup = [viewConnection->group_pagesMetadata_dict objectForKey:group]; + NSArray *pagesMetadataForGroup = [viewConnection->group_pagesMetadata_dict objectForKey:group]; for (YapDatabaseViewPageMetadata *pm in pagesMetadataForGroup) { if ([pm->pageKey isEqualToString:pageKey]) { @@ -2863,12 +2992,12 @@ pageMetadata = [viewConnection->dirtyLinks objectForKey:pageKey]; if (pageMetadata == nil) { NSString *group = [self groupForPageKey:pageKey]; - NSMutableArray *pagesMetadataForGroup = - [viewConnection->group_pagesMetadata_dict objectForKey:group]; + NSArray *pagesMetadataForGroup = + [viewConnection->group_pagesMetadata_dict objectForKey:group]; for (YapDatabaseViewPageMetadata *pm in pagesMetadataForGroup) { if ([pm->pageKey isEqualToString:pageKey]) { @@ -2979,19 +3108,21 @@ /** * YapDatabase extension hook. * This method is invoked by a YapDatabaseReadWriteTransaction as a post-operation-hook. **/ - (void)handleInsertObject:(id)object - forKey:(NSString *)key - inCollection:(NSString *)collection + forCollectionKey:(YapCollectionKey *)collectionKey withMetadata:(id)metadata rowid:(int64_t)rowid { YDBLogAutoTrace(); __unsafe_unretained YapDatabaseView *view = viewConnection->view; + __unsafe_unretained NSString *collection = collectionKey.collection; + __unsafe_unretained NSString *key = collectionKey.key; + // Invoke the grouping block to find out if the object should be included in the view. NSString *group = nil; NSSet *allowedCollections = view->options.allowedCollections; @@ -3034,12 +3165,10 @@ else { // Add key to view. // This was an insert operation, so we know the key wasn't already in the view. - YapCollectionKey *collectionKey = [[YapCollectionKey alloc] initWithCollection:collection key:key]; - int flags = (YapDatabaseViewChangedObject | YapDatabaseViewChangedMetadata); [self insertRowid:rowid collectionKey:collectionKey object:object @@ -3053,22 +3182,21 @@ /** * YapDatabase extension hook. * This method is invoked by a YapDatabaseReadWriteTransaction as a post-operation-hook. **/ - (void)handleUpdateObject:(id)object - forKey:(NSString *)key - inCollection:(NSString *)collection + forCollectionKey:(YapCollectionKey *)collectionKey withMetadata:(id)metadata rowid:(int64_t)rowid { YDBLogAutoTrace(); - NSParameterAssert(key != nil); - NSParameterAssert(collection != nil); - __unsafe_unretained YapDatabaseView *view = viewConnection->view; + __unsafe_unretained NSString *collection = collectionKey.collection; + __unsafe_unretained NSString *key = collectionKey.key; + // Invoke the grouping block to find out if the object should be included in the view. NSString *group = nil; NSSet *allowedCollections = view->options.allowedCollections; @@ -3101,12 +3229,10 @@ (YapDatabaseViewGroupingWithRowBlock)view->groupingBlock; group = groupingBlock(collection, key, object, metadata); } } - - YapCollectionKey *collectionKey = [[YapCollectionKey alloc] initWithCollection:collection key:key]; if (group == nil) { // Remove key from view (if needed). // This was an update operation, so the key may have previously been in the view. @@ -3132,23 +3258,174 @@ /** * YapDatabase extension hook. * This method is invoked by a YapDatabaseReadWriteTransaction as a post-operation-hook. **/ -- (void)handleUpdateMetadata:(id)metadata - forKey:(NSString *)key - inCollection:(NSString *)collection - withRowid:(int64_t)rowid +- (void)handleReplaceObject:(id)object forCollectionKey:(YapCollectionKey *)collectionKey withRowid:(int64_t)rowid { YDBLogAutoTrace(); __unsafe_unretained YapDatabaseView *view = viewConnection->view; - YapCollectionKey *collectionKey = [[YapCollectionKey alloc] initWithCollection:collection key:key]; + __unsafe_unretained NSString *collection = collectionKey.collection; + __unsafe_unretained NSString *key = collectionKey.key; // Invoke the grouping block to find out if the object should be included in the view. + id metadata = nil; + NSString *group = nil; + + if (view->groupingBlockType == YapDatabaseViewBlockTypeWithKey || + view->groupingBlockType == YapDatabaseViewBlockTypeWithMetadata) + { + // Grouping is based on the key or metadata. + // Neither have changed, and thus the group hasn't changed. + + NSString *pageKey = [self pageKeyForRowid:rowid]; + group = [self groupForPageKey:pageKey]; + + if (group == nil) + { + // Nothing to do. + // The key wasn't previously in the view, and still isn't in the view. + lastHandledGroup = group; + return; + } + + if (view->sortingBlockType == YapDatabaseViewBlockTypeWithKey || + view->sortingBlockType == YapDatabaseViewBlockTypeWithMetadata) + { + // Nothing has moved because the group hasn't changed and + // nothing has changed that relates to sorting. + + int flags = YapDatabaseViewChangedObject; + NSUInteger existingIndex = [self indexForRowid:rowid inGroup:group withPageKey:pageKey]; + + [viewConnection->changes addObject: + [YapDatabaseViewRowChange updateKey:collectionKey changes:flags inGroup:group atIndex:existingIndex]]; + } + else + { + // Sorting is based on the object, which has changed. + // So the sort order may possibly have changed. + + // From previous if statement (above) we know: + // sortingBlockType is object or row (object+metadata) + + if (view->sortingBlockType == YapDatabaseViewBlockTypeWithRow) + { + // Need the metadata for the sorting block + metadata = [databaseTransaction metadataForCollectionKey:collectionKey withRowid:rowid]; + } + + int flags = YapDatabaseViewChangedObject; + + [self insertRowid:rowid + collectionKey:collectionKey + object:object + metadata:metadata + inGroup:group withChanges:flags isNew:NO]; + } + } + else + { + // Grouping is based on object or row (object+metadata). + // Invoke groupingBlock to see what the new group is. + + NSSet *allowedCollections = view->options.allowedCollections; + + if (!allowedCollections || [allowedCollections containsObject:collection]) + { + if (view->groupingBlockType == YapDatabaseViewBlockTypeWithObject) + { + __unsafe_unretained YapDatabaseViewGroupingWithObjectBlock groupingBlock = + (YapDatabaseViewGroupingWithObjectBlock)view->groupingBlock; + + group = groupingBlock(collection, key, object); + } + else + { + __unsafe_unretained YapDatabaseViewGroupingWithRowBlock groupingBlock = + (YapDatabaseViewGroupingWithRowBlock)view->groupingBlock; + + metadata = [databaseTransaction metadataForCollectionKey:collectionKey withRowid:rowid]; + group = groupingBlock(collection, key, object, metadata); + } + } + + if (group == nil) + { + // The key is not included in the view. + // Remove key from view (if needed). + + [self removeRowid:rowid collectionKey:collectionKey]; + } + else + { + if (view->sortingBlockType == YapDatabaseViewBlockTypeWithKey || + view->sortingBlockType == YapDatabaseViewBlockTypeWithMetadata) + { + // Sorting is based on the key or metadata, neither of which has changed. + // So if the group hasn't changed, then the sort order hasn't changed. + + NSString *existingPageKey = [self pageKeyForRowid:rowid]; + NSString *existingGroup = [self groupForPageKey:existingPageKey]; + + if ([group isEqualToString:existingGroup]) + { + // Nothing left to do. + // The group didn't change, and the sort order cannot change (because the object didn't change). + + int flags = YapDatabaseViewChangedObject; + NSUInteger existingIndex = [self indexForRowid:rowid inGroup:group withPageKey:existingPageKey]; + + [viewConnection->changes addObject: + [YapDatabaseViewRowChange updateKey:collectionKey + changes:flags + inGroup:group + atIndex:existingIndex]]; + + lastHandledGroup = group; + return; + } + } + + if (metadata == nil && (view->sortingBlockType == YapDatabaseViewBlockTypeWithMetadata || + view->sortingBlockType == YapDatabaseViewBlockTypeWithRow )) + { + // Need the metadata for the sorting block + metadata = [databaseTransaction metadataForCollectionKey:collectionKey withRowid:rowid]; + } + + int flags = YapDatabaseViewChangedObject; + + [self insertRowid:rowid + collectionKey:collectionKey + object:object + metadata:metadata + inGroup:group withChanges:flags isNew:NO]; + } + } + + lastHandledGroup = group; +} + +/** + * YapDatabase extension hook. + * This method is invoked by a YapDatabaseReadWriteTransaction as a post-operation-hook. +**/ +- (void)handleReplaceMetadata:(id)metadata forCollectionKey:(YapCollectionKey *)collectionKey withRowid:(int64_t)rowid +{ + YDBLogAutoTrace(); + + __unsafe_unretained YapDatabaseView *view = viewConnection->view; + + __unsafe_unretained NSString *collection = collectionKey.collection; + __unsafe_unretained NSString *key = collectionKey.key; + + // Invoke the grouping block to find out if the object should be included in the view. + id object = nil; NSString *group = nil; if (view->groupingBlockType == YapDatabaseViewBlockTypeWithKey || view->groupingBlockType == YapDatabaseViewBlockTypeWithObject) @@ -3188,11 +3465,11 @@ // sortingBlockType is metadata or objectAndMetadata if (view->sortingBlockType == YapDatabaseViewBlockTypeWithRow) { // Need the object for the sorting block - object = [databaseTransaction objectForKey:key inCollection:collection withRowid:rowid]; + object = [databaseTransaction objectForCollectionKey:collectionKey withRowid:rowid]; } int flags = YapDatabaseViewChangedMetadata; [self insertRowid:rowid @@ -3221,11 +3498,11 @@ else { __unsafe_unretained YapDatabaseViewGroupingWithRowBlock groupingBlock = (YapDatabaseViewGroupingWithRowBlock)view->groupingBlock; - object = [databaseTransaction objectForKey:key inCollection:collection withRowid:rowid]; + object = [databaseTransaction objectForCollectionKey:collectionKey withRowid:rowid]; group = groupingBlock(collection, key, object, metadata); } } if (group == nil) @@ -3267,11 +3544,11 @@ if (object == nil && (view->sortingBlockType == YapDatabaseViewBlockTypeWithObject || view->sortingBlockType == YapDatabaseViewBlockTypeWithRow )) { // Need the object for the sorting block - object = [databaseTransaction objectForKey:key inCollection:collection withRowid:rowid]; + object = [databaseTransaction objectForCollectionKey:collectionKey withRowid:rowid]; } int flags = YapDatabaseViewChangedMetadata; [self insertRowid:rowid @@ -3287,11 +3564,11 @@ /** * YapDatabase extension hook. * This method is invoked by a YapDatabaseReadWriteTransaction as a post-operation-hook. **/ -- (void)handleTouchObjectForKey:(NSString *)key inCollection:(NSString *)collection withRowid:(int64_t)rowid +- (void)handleTouchObjectForCollectionKey:(YapCollectionKey *)collectionKey withRowid:(int64_t)rowid { YDBLogAutoTrace(); // Almost the same as touchRowForKey:inCollection: @@ -3299,11 +3576,10 @@ if (pageKey) { NSString *group = [self groupForPageKey:pageKey]; NSUInteger index = [self indexForRowid:rowid inGroup:group withPageKey:pageKey]; - YapCollectionKey *collectionKey = [[YapCollectionKey alloc] initWithCollection:collection key:key]; int flags = (YapDatabaseViewChangedObject | YapDatabaseViewChangedMetadata); [viewConnection->changes addObject: [YapDatabaseViewRowChange updateKey:collectionKey changes:flags inGroup:group atIndex:index]]; } @@ -3311,11 +3587,11 @@ /** * YapDatabase extension hook. * This method is invoked by a YapDatabaseReadWriteTransaction as a post-operation-hook. **/ -- (void)handleTouchMetadataForKey:(NSString *)key inCollection:(NSString *)collection withRowid:(int64_t)rowid +- (void)handleTouchMetadataForCollectionKey:(YapCollectionKey *)collectionKey withRowid:(int64_t)rowid { YDBLogAutoTrace(); // Almost the same as touchMetadatForKey:inCollection: @@ -3330,11 +3606,10 @@ if (pageKey) { NSString *group = [self groupForPageKey:pageKey]; NSUInteger index = [self indexForRowid:rowid inGroup:group withPageKey:pageKey]; - YapCollectionKey *collectionKey = [[YapCollectionKey alloc] initWithCollection:collection key:key]; int flags = YapDatabaseViewChangedMetadata; [viewConnection->changes addObject: [YapDatabaseViewRowChange updateKey:collectionKey changes:flags inGroup:group atIndex:index]]; } @@ -3343,19 +3618,14 @@ /** * YapDatabase extension hook. * This method is invoked by a YapDatabaseReadWriteTransaction as a post-operation-hook. **/ -- (void)handleRemoveObjectForKey:(NSString *)key inCollection:(NSString *)collection withRowid:(int64_t)rowid +- (void)handleRemoveObjectForCollectionKey:(YapCollectionKey *)collectionKey withRowid:(int64_t)rowid { YDBLogAutoTrace(); - NSParameterAssert(key != nil); - NSParameterAssert(collection != nil); - - YapCollectionKey *collectionKey = [[YapCollectionKey alloc] initWithCollection:collection key:key]; - [self removeRowid:rowid collectionKey:collectionKey]; } /** * YapDatabase extension hook. @@ -3363,12 +3633,10 @@ **/ - (void)handleRemoveObjectsForKeys:(NSArray *)keys inCollection:(NSString *)collection withRowids:(NSArray *)rowids { YDBLogAutoTrace(); - NSParameterAssert(collection != nil); - NSUInteger count = [keys count]; NSMutableDictionary *keyMappings = [NSMutableDictionary dictionaryWithCapacity:count]; for (NSUInteger i = 0; i < count; i++) { @@ -3477,24 +3745,22 @@ inGroup:(NSString *)group { int64_t rowid = 0; if ([self getRowid:&rowid atIndex:index inGroup:group]) { - NSString *collection = nil; - NSString *key = nil; - BOOL found = [databaseTransaction getKey:&key collection:&collection forRowid:rowid]; - - if (collectionPtr) *collectionPtr = collection; - if (keyPtr) *keyPtr = key; - return found; + YapCollectionKey *ck = [databaseTransaction collectionKeyForRowid:rowid]; + if (ck) + { + if (collectionPtr) *collectionPtr = ck.collection; + if (keyPtr) *keyPtr = ck.key; + return YES; + } } - else - { - if (collectionPtr) *collectionPtr = nil; - if (keyPtr) *keyPtr = nil; - return NO; - } + + if (collectionPtr) *collectionPtr = nil; + if (keyPtr) *keyPtr = nil; + return NO; } - (BOOL)getFirstKey:(NSString **)keyPtr collection:(NSString **)collectionPtr inGroup:(NSString *)group { return [self getKey:keyPtr collection:collectionPtr atIndex:0 inGroup:group]; @@ -3503,24 +3769,22 @@ - (BOOL)getLastKey:(NSString **)keyPtr collection:(NSString **)collectionPtr inGroup:(NSString *)group { int64_t rowid = 0; if ([self getLastRowid:&rowid inGroup:group]) { - NSString *collection = nil; - NSString *key = nil; - BOOL found = [databaseTransaction getKey:&key collection:&collection forRowid:rowid]; - - if (collectionPtr) *collectionPtr = collection; - if (keyPtr) *keyPtr = key; - return found; + YapCollectionKey *ck = [databaseTransaction collectionKeyForRowid:rowid]; + if (ck) + { + if (collectionPtr) *collectionPtr = ck.collection; + if (keyPtr) *keyPtr = ck.key; + return YES; + } } - else - { - if (collectionPtr) *collectionPtr = nil; - if (keyPtr) *keyPtr = nil; - return NO; - } + + if (collectionPtr) *collectionPtr = nil; + if (keyPtr) *keyPtr = nil; + return NO; } - (NSString *)collectionAtIndex:(NSUInteger)index inGroup:(NSString *)group { NSString *collection = nil; @@ -3589,11 +3853,11 @@ group = [self groupForPageKey:pageKey]; // Calculate the offset of the corresponding page within the group. NSUInteger pageOffset = 0; - NSMutableArray *pagesMetadataForGroup = [viewConnection->group_pagesMetadata_dict objectForKey:group]; + NSArray *pagesMetadataForGroup = [viewConnection->group_pagesMetadata_dict objectForKey:group]; for (YapDatabaseViewPageMetadata *pageMetadata in pagesMetadataForGroup) { if ([pageMetadata->pageKey isEqualToString:pageKey]) { @@ -3643,11 +3907,11 @@ if (group == nil || block == NULL || invalidBlockType) { return NSMakeRange(NSNotFound, 0); } - NSMutableArray *pagesMetadataForGroup = [viewConnection->group_pagesMetadata_dict objectForKey:group]; + NSArray *pagesMetadataForGroup = [viewConnection->group_pagesMetadata_dict objectForKey:group]; NSUInteger count = 0; for (YapDatabaseViewPageMetadata *pageMetadata in pagesMetadataForGroup) { count += pageMetadata->count; @@ -3681,52 +3945,47 @@ if (blockType == YapDatabaseViewBlockTypeWithKey) { __unsafe_unretained YapDatabaseViewFindWithKeyBlock findBlock = (YapDatabaseViewFindWithKeyBlock)block; - NSString *key = nil; - NSString *collection = nil; - [databaseTransaction getKey:&key collection:&collection forRowid:rowid]; + YapCollectionKey *ck = [databaseTransaction collectionKeyForRowid:rowid]; - return findBlock(collection, key); + return findBlock(ck.collection, ck.key); } else if (blockType == YapDatabaseViewBlockTypeWithObject) { __unsafe_unretained YapDatabaseViewFindWithObjectBlock findBlock = (YapDatabaseViewFindWithObjectBlock)block; - NSString *key = nil; - NSString *collection = nil; + YapCollectionKey *ck = nil; id object = nil; - [databaseTransaction getKey:&key collection:&collection object:&object forRowid:rowid]; + [databaseTransaction getCollectionKey:&ck object:&object forRowid:rowid]; - return findBlock(collection, key, object); + return findBlock(ck.collection, ck.key, object); } else if (blockType == YapDatabaseViewBlockTypeWithMetadata) { __unsafe_unretained YapDatabaseViewFindWithMetadataBlock findBlock = (YapDatabaseViewFindWithMetadataBlock)block; - NSString *key = nil; - NSString *collection = nil; + YapCollectionKey *ck = nil; id metadata = nil; - [databaseTransaction getKey:&key collection:&collection metadata:&metadata forRowid:rowid]; + [databaseTransaction getCollectionKey:&ck metadata:&metadata forRowid:rowid]; - return findBlock(collection, key, metadata); + return findBlock(ck.collection, ck.key, metadata); } else { __unsafe_unretained YapDatabaseViewFindWithRowBlock findBlock = (YapDatabaseViewFindWithRowBlock)block; - NSString *key = nil; - NSString *collection = nil; + YapCollectionKey *ck = nil; id object = nil; id metadata = nil; - [databaseTransaction getKey:&key collection:&collection object:&object metadata:&metadata forRowid:rowid]; + [databaseTransaction getCollectionKey:&ck object:&object metadata:&metadata forRowid:rowid]; - return findBlock(collection, key, object, metadata); + return findBlock(ck.collection, ck.key, object, metadata); } }; NSUInteger loopCount = 0; @@ -3813,15 +4072,13 @@ { if (block == NULL) return; [self enumerateRowidsInGroup:group usingBlock:^(int64_t rowid, NSUInteger index, BOOL *stop) { - NSString *key = nil; - NSString *collection = nil; - [databaseTransaction getKey:&key collection:&collection forRowid:rowid]; + YapCollectionKey *ck = [databaseTransaction collectionKeyForRowid:rowid]; - block(collection, key, index, stop); + block(ck.collection, ck.key, index, stop); }]; } - (void)enumerateKeysInGroup:(NSString *)group withOptions:(NSEnumerationOptions)options @@ -3829,15 +4086,13 @@ { if (block == NULL) return; [self enumerateRowidsInGroup:group withOptions:options usingBlock:^(int64_t rowid, NSUInteger index, BOOL *stop) { - NSString *key = nil; - NSString *collection = nil; - [databaseTransaction getKey:&key collection:&collection forRowid:rowid]; + YapCollectionKey *ck = [databaseTransaction collectionKeyForRowid:rowid]; - block(collection, key, index, stop); + block(ck.collection, ck.key, index, stop); }]; } - (void)enumerateKeysInGroup:(NSString *)group withOptions:(NSEnumerationOptions)options @@ -3847,17 +4102,15 @@ if (block == NULL) return; [self enumerateRowidsInGroup:group withOptions:options range:range - usingBlock:^(int64_t rowid, NSUInteger index, BOOL *stop) { + usingBlock:^(int64_t rowid, NSUInteger index, BOOL *stop) + { + YapCollectionKey *ck = [databaseTransaction collectionKeyForRowid:rowid]; - NSString *key = nil; - NSString *collection = nil; - [databaseTransaction getKey:&key collection:&collection forRowid:rowid]; - - block(collection, key, index, stop); + block(ck.collection, ck.key, index, stop); }]; } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// #pragma mark Private API @@ -3871,11 +4124,11 @@ [viewConnection->mutatedGroups removeObject:group]; // mutation during enumeration protection __block BOOL stop = NO; NSUInteger pageOffset = 0; - NSMutableArray *pagesMetadataForGroup = [viewConnection->group_pagesMetadata_dict objectForKey:group]; + NSArray *pagesMetadataForGroup = [viewConnection->group_pagesMetadata_dict objectForKey:group]; for (YapDatabaseViewPageMetadata *pageMetadata in pagesMetadataForGroup) { YapDatabaseViewPage *page = [self pageForPageKey:pageMetadata->pageKey]; @@ -3916,15 +4169,15 @@ if (forwardEnumeration) index = 0; else index = [self numberOfKeysInGroup:group] - 1; - NSMutableArray *pagesMetadataForGroup = [viewConnection->group_pagesMetadata_dict objectForKey:group]; + NSArray *pagesMetadataForGroup = [viewConnection->group_pagesMetadata_dict objectForKey:group]; [pagesMetadataForGroup enumerateObjectsWithOptions:options - usingBlock:^(id pageMetadataObj, NSUInteger outerIdx, BOOL *outerStop){ - + usingBlock:^(id pageMetadataObj, NSUInteger outerIdx, BOOL *outerStop) + { __unsafe_unretained YapDatabaseViewPageMetadata *pageMetadata = (YapDatabaseViewPageMetadata *)pageMetadataObj; YapDatabaseViewPage *page = [self pageForPageKey:pageMetadata->pageKey]; @@ -3956,11 +4209,11 @@ { if (block == NULL) return; NSEnumerationOptions options = (inOptions & NSEnumerationReverse); // We only support NSEnumerationReverse - NSMutableArray *pagesMetadataForGroup = [viewConnection->group_pagesMetadata_dict objectForKey:group]; + NSArray *pagesMetadataForGroup = [viewConnection->group_pagesMetadata_dict objectForKey:group]; // Helper block to fetch the pageOffset for some page. NSUInteger (^pageOffsetForPageMetadata)(YapDatabaseViewPageMetadata *inPageMetadata); pageOffsetForPageMetadata = ^ NSUInteger (YapDatabaseViewPageMetadata *inPageMetadata){ @@ -3983,12 +4236,12 @@ __block BOOL stop = NO; __block BOOL startedRange = NO; __block NSUInteger keysLeft = range.length; [pagesMetadataForGroup enumerateObjectsWithOptions:options - usingBlock:^(id pageMetadataObj, NSUInteger pageIndex, BOOL *outerStop){ - + usingBlock:^(id pageMetadataObj, NSUInteger pageIndex, BOOL *outerStop) + { __unsafe_unretained YapDatabaseViewPageMetadata *pageMetadata = (YapDatabaseViewPageMetadata *)pageMetadataObj; NSUInteger pageOffset = pageOffsetForPageMetadata(pageMetadata); NSRange pageRange = NSMakeRange(pageOffset, pageMetadata->count); @@ -4199,10 +4452,86 @@ } } } } +/** + * This method allows you to change the groupingBlock and/or sortingBlock on-the-fly. + * + * Note: You must pass a different versionTag, or this method does nothing. +**/ +- (void)setGroupingBlock:(YapDatabaseViewGroupingBlock)inGroupingBlock + groupingBlockType:(YapDatabaseViewBlockType)inGroupingBlockType + sortingBlock:(YapDatabaseViewSortingBlock)inSortingBlock + sortingBlockType:(YapDatabaseViewBlockType)inSortingBlockType + versionTag:(NSString *)inVersionTag +{ + YDBLogAutoTrace(); + + NSAssert(inGroupingBlock != NULL, @"Invalid grouping block"); + NSAssert(inSortingBlock != NULL, @"Invalid grouping block"); + + NSAssert(inGroupingBlockType == YapDatabaseViewBlockTypeWithKey || + inGroupingBlockType == YapDatabaseViewBlockTypeWithObject || + inGroupingBlockType == YapDatabaseViewBlockTypeWithMetadata || + inGroupingBlockType == YapDatabaseViewBlockTypeWithRow, + @"Invalid grouping block type"); + + NSAssert(inSortingBlockType == YapDatabaseViewBlockTypeWithKey || + inSortingBlockType == YapDatabaseViewBlockTypeWithObject || + inSortingBlockType == YapDatabaseViewBlockTypeWithMetadata || + inSortingBlockType == YapDatabaseViewBlockTypeWithRow, + @"Invalid sorting block type"); + + if (!databaseTransaction->isReadWriteTransaction) + { + YDBLogWarn(@"%@ - Method only allowed in readWrite transaction", THIS_METHOD); + return; + } + + NSString *newVersionTag = inVersionTag ? [inVersionTag copy] : @""; + + __unsafe_unretained YapDatabaseView *view = viewConnection->view; + + if ([view->versionTag isEqualToString:newVersionTag]) + { + YDBLogWarn(@"%@ - versionTag didn't change, so not updating view", THIS_METHOD); + return; + } + + view->groupingBlock = inGroupingBlock; + view->groupingBlockType = inGroupingBlockType; + view->sortingBlock = inSortingBlock; + view->sortingBlockType = inSortingBlockType; + + view->versionTag = newVersionTag; + + [self repopulateView]; + [self setStringValue:newVersionTag forExtensionKey:ExtKey_versionTag]; + + // Notify any extensions dependent upon this one that we repopulated. + + NSString *registeredName = [self registeredName]; + NSDictionary *extensionDependencies = databaseTransaction->connection->extensionDependencies; + + [extensionDependencies enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop){ + + __unsafe_unretained NSString *extName = (NSString *)key; + __unsafe_unretained NSSet *extDependencies = (NSSet *)obj; + + if ([extDependencies containsObject:registeredName]) + { + YapDatabaseExtensionTransaction *extTransaction = [databaseTransaction ext:extName]; + + if ([extTransaction respondsToSelector:@selector(viewDidRepopulate:)]) + { + [(id <YapDatabaseViewDependency>)extTransaction viewDidRepopulate:registeredName]; + } + } + }]; +} + @end //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// #pragma mark - //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -4217,15 +4546,13 @@ // We could use getKey:collection:object:forRowid: at this point. // But in most cases the object is going to be in the objectCache. // So it's likely faster to fetch just the key first. // And if the cache misses then we're still using a fetch based on the rowid. - NSString *key = nil; - NSString *collection = nil; - [databaseTransaction getKey:&key collection:&collection forRowid:rowid]; + YapCollectionKey *ck = [databaseTransaction collectionKeyForRowid:rowid]; - return [databaseTransaction objectForKey:key inCollection:collection withRowid:rowid]; + return [databaseTransaction objectForCollectionKey:ck withRowid:rowid]; } else { return nil; } @@ -4245,15 +4572,13 @@ // We could use getKey:collection:object:forRowid: at this point. // But in most cases the object is going to be in the objectCache. // So it's likely faster to fetch just the key first. // And if the cache misses then we're still using a fetch based on the rowid. - NSString *key = nil; - NSString *collection = nil; - [databaseTransaction getKey:&key collection:&collection forRowid:rowid]; + YapCollectionKey *ck = [databaseTransaction collectionKeyForRowid:rowid]; - return [databaseTransaction objectForKey:key inCollection:collection withRowid:rowid]; + return [databaseTransaction objectForCollectionKey:ck withRowid:rowid]; } else { return nil; } @@ -4270,16 +4595,15 @@ { if (block == NULL) return; [self enumerateRowidsInGroup:group usingBlock:^(int64_t rowid, NSUInteger index, BOOL *stop) { - NSString *key = nil; - NSString *collection = nil; + YapCollectionKey *ck = nil; id metadata = nil; - [databaseTransaction getKey:&key collection:&collection metadata:&metadata forRowid:rowid]; + [databaseTransaction getCollectionKey:&ck metadata:&metadata forRowid:rowid]; - block(collection, key, metadata, index, stop); + block(ck.collection, ck.key, metadata, index, stop); }]; } - (void)enumerateKeysAndMetadataInGroup:(NSString *)group withOptions:(NSEnumerationOptions)options @@ -4288,18 +4612,17 @@ { if (block == NULL) return; [self enumerateRowidsInGroup:group withOptions:options - usingBlock:^(int64_t rowid, NSUInteger index, BOOL *stop) { - - NSString *key = nil; - NSString *collection = nil; + usingBlock:^(int64_t rowid, NSUInteger index, BOOL *stop) + { + YapCollectionKey *ck = nil; id metadata = nil; - [databaseTransaction getKey:&key collection:&collection metadata:&metadata forRowid:rowid]; + [databaseTransaction getCollectionKey:&ck metadata:&metadata forRowid:rowid]; - block(collection, key, metadata, index, stop); + block(ck.collection, ck.key, metadata, index, stop); }]; } - (void)enumerateKeysAndMetadataInGroup:(NSString *)group withOptions:(NSEnumerationOptions)options @@ -4310,18 +4633,17 @@ if (block == NULL) return; [self enumerateRowidsInGroup:group withOptions:options range:range - usingBlock:^(int64_t rowid, NSUInteger index, BOOL *stop) { - - NSString *key = nil; - NSString *collection = nil; + usingBlock:^(int64_t rowid, NSUInteger index, BOOL *stop) + { + YapCollectionKey *ck = nil; id metadata = nil; - [databaseTransaction getKey:&key collection:&collection metadata:&metadata forRowid:rowid]; + [databaseTransaction getCollectionKey:&ck metadata:&metadata forRowid:rowid]; - block(collection, key, metadata, index, stop); + block(ck.collection, ck.key, metadata, index, stop); }]; } /** * The following methods are equivalent to invoking the enumerateKeysInGroup:... methods, @@ -4334,16 +4656,15 @@ { if (block == NULL) return; [self enumerateRowidsInGroup:group usingBlock:^(int64_t rowid, NSUInteger index, BOOL *stop) { - NSString *key = nil; - NSString *collection = nil; + YapCollectionKey *ck = nil; id object = nil; - [databaseTransaction getKey:&key collection:&collection object:&object forRowid:rowid]; + [databaseTransaction getCollectionKey:&ck object:&object forRowid:rowid]; - block(collection, key, object, index, stop); + block(ck.collection, ck.key, object, index, stop); }]; } - (void)enumerateKeysAndObjectsInGroup:(NSString *)group withOptions:(NSEnumerationOptions)options @@ -4352,18 +4673,17 @@ { if (block == NULL) return; [self enumerateRowidsInGroup:group withOptions:options - usingBlock:^(int64_t rowid, NSUInteger index, BOOL *stop) { - - NSString *key = nil; - NSString *collection = nil; + usingBlock:^(int64_t rowid, NSUInteger index, BOOL *stop) + { + YapCollectionKey *ck = nil; id object = nil; - [databaseTransaction getKey:&key collection:&collection object:&object forRowid:rowid]; + [databaseTransaction getCollectionKey:&ck object:&object forRowid:rowid]; - block(collection, key, object, index, stop); + block(ck.collection, ck.key, object, index, stop); }]; } - (void)enumerateKeysAndObjectsInGroup:(NSString *)group withOptions:(NSEnumerationOptions)options @@ -4374,18 +4694,17 @@ if (block == NULL) return; [self enumerateRowidsInGroup:group withOptions:options range:range - usingBlock:^(int64_t rowid, NSUInteger index, BOOL *stop) { - - NSString *key = nil; - NSString *collection = nil; + usingBlock:^(int64_t rowid, NSUInteger index, BOOL *stop) + { + YapCollectionKey *ck = nil; id object = nil; - [databaseTransaction getKey:&key collection:&collection object:&object forRowid:rowid]; + [databaseTransaction getCollectionKey:&ck object:&object forRowid:rowid]; - block(collection, key, object, index, stop); + block(ck.collection, ck.key, object, index, stop); }]; } - (void)enumerateRowsInGroup:(NSString *)group usingBlock: @@ -4393,17 +4712,16 @@ { if (block == NULL) return; [self enumerateRowidsInGroup:group usingBlock:^(int64_t rowid, NSUInteger index, BOOL *stop) { - NSString *key = nil; - NSString *collection = nil; + YapCollectionKey *ck = nil; id object = nil; id metadata = nil; - [databaseTransaction getKey:&key collection:&collection object:&object metadata:&metadata forRowid:rowid]; + [databaseTransaction getCollectionKey:&ck object:&object metadata:&metadata forRowid:rowid]; - block(collection, key, object, metadata, index, stop); + block(ck.collection, ck.key, object, metadata, index, stop); }]; } - (void)enumerateRowsInGroup:(NSString *)group withOptions:(NSEnumerationOptions)options @@ -4412,19 +4730,18 @@ { if (block == NULL) return; [self enumerateRowidsInGroup:group withOptions:options - usingBlock:^(int64_t rowid, NSUInteger index, BOOL *stop) { - - NSString *key = nil; - NSString *collection = nil; + usingBlock:^(int64_t rowid, NSUInteger index, BOOL *stop) + { + YapCollectionKey *ck = nil; id object = nil; id metadata = nil; - [databaseTransaction getKey:&key collection:&collection object:&object metadata:&metadata forRowid:rowid]; - - block(collection, key, object, metadata, index, stop); + [databaseTransaction getCollectionKey:&ck object:&object metadata:&metadata forRowid:rowid]; + + block(ck.collection, ck.key, object, metadata, index, stop); }]; } - (void)enumerateRowsInGroup:(NSString *)group withOptions:(NSEnumerationOptions)options @@ -4435,19 +4752,18 @@ if (block == NULL) return; [self enumerateRowidsInGroup:group withOptions:options range:range - usingBlock:^(int64_t rowid, NSUInteger index, BOOL *stop) { - - NSString *key = nil; - NSString *collection = nil; + usingBlock:^(int64_t rowid, NSUInteger index, BOOL *stop) + { + YapCollectionKey *ck = nil; id object = nil; id metadata = nil; - [databaseTransaction getKey:&key collection:&collection object:&object metadata:&metadata forRowid:rowid]; + [databaseTransaction getCollectionKey:&ck object:&object metadata:&metadata forRowid:rowid]; - block(collection, key, object, metadata, index, stop); + block(ck.collection, ck.key, object, metadata, index, stop); }]; } @end @@ -4456,36 +4772,177 @@ //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @implementation YapDatabaseViewTransaction (Mappings) /** + * Performance boost. + * If the object isn't in the cache, having the rowid makes for a faster fetch from sqlite. +**/ +- (BOOL)getRowid:(int64_t *)rowidPtr + collectionKey:(YapCollectionKey **)collectionKeyPtr + forRow:(NSUInteger)row + inSection:(NSUInteger)section + withMappings:(YapDatabaseViewMappings *)mappings +{ + if (mappings) + { + NSString *group = nil; + NSUInteger index = 0; + + if ([mappings getGroup:&group index:&index forRow:row inSection:section]) + { + int64_t rowid = 0; + if ([self getRowid:&rowid atIndex:index inGroup:group]) + { + if (collectionKeyPtr) + { + YapCollectionKey *ck = [databaseTransaction collectionKeyForRowid:rowid]; + *collectionKeyPtr = ck; + } + + if (rowidPtr) *rowidPtr = rowid; + return YES; + } + } + } + + if (rowidPtr) *rowidPtr = 0; + if (collectionKeyPtr) *collectionKeyPtr = nil; + return NO; +} + +/** * Gets the key & collection at the given indexPath, assuming the given mappings are being used. * Returns NO if the indexPath is invalid, or the mappings aren't initialized. * Otherwise returns YES, and sets the key & collection ptr (both optional). **/ - (BOOL)getKey:(NSString **)keyPtr collection:(NSString **)collectionPtr atIndexPath:(NSIndexPath *)indexPath withMappings:(YapDatabaseViewMappings *)mappings { - if (indexPath && mappings) + if (indexPath == nil) { - NSString *group = nil; - NSUInteger index = 0; + if (keyPtr) *keyPtr = nil; + if (collectionPtr) *collectionPtr = nil; - if ([mappings getGroup:&group index:&index forIndexPath:indexPath]) - { - return [self getKey:keyPtr collection:collectionPtr atIndex:index inGroup:group]; - } + return NO; } - if (keyPtr) *keyPtr = nil; - if (collectionPtr) *collectionPtr = nil; - return NO; +#if TARGET_OS_IPHONE + NSUInteger section = indexPath.section; + NSUInteger row = indexPath.row; +#else + NSUInteger section = [indexPath indexAtPosition:0]; + NSUInteger row = [indexPath indexAtPosition:1]; +#endif + + YapCollectionKey *ck = nil; + BOOL result = [self getRowid:NULL + collectionKey:&ck + forRow:row + inSection:section + withMappings:mappings]; + + if (keyPtr) *keyPtr = ck.key; + if (collectionPtr) *collectionPtr = ck.collection; + + return result; } /** + * Gets the key & collection at the given row & section, assuming the given mappings are being used. + * Returns NO if the row or section is invalid, or the mappings aren't initialized. + * Otherwise returns YES, and sets the key & collection ptr (both optional). +**/ +- (BOOL)getKey:(NSString **)keyPtr + collection:(NSString **)collectionPtr + forRow:(NSUInteger)row + inSection:(NSUInteger)section + withMappings:(YapDatabaseViewMappings *)mappings +{ + YapCollectionKey *ck = nil; + BOOL result = [self getRowid:NULL + collectionKey:&ck + forRow:row + inSection:section + withMappings:mappings]; + + if (keyPtr) *keyPtr = ck.key; + if (collectionPtr) *collectionPtr = ck.collection; + + return result; +} + +/** + * Gets the object at the given indexPath, assuming the given mappings are being used. + * + * Equivalent to invoking: + * + * NSString *collection, *key; + * if ([[transaction ext:@"myView"] getKey:&key collection:&collection atIndexPath:indexPath withMappings:mappings]) { + * object = [transaction objectForKey:key inCollection:collection]; + * } +**/ +- (id)objectAtIndexPath:(NSIndexPath *)indexPath withMappings:(YapDatabaseViewMappings *)mappings +{ + if (indexPath == nil) + { + return nil; + } + +#if TARGET_OS_IPHONE + NSUInteger section = indexPath.section; + NSUInteger row = indexPath.row; +#else + NSUInteger section = [indexPath indexAtPosition:0]; + NSUInteger row = [indexPath indexAtPosition:1]; +#endif + + id object = nil; + + int64_t rowid = 0; + YapCollectionKey *ck = nil; + + if ([self getRowid:&rowid collectionKey:&ck forRow:row inSection:section withMappings:mappings]) + { + object = [databaseTransaction objectForCollectionKey:ck withRowid:rowid]; + } + + return object; +} + +/** + * Gets the object at the given indexPath, assuming the given mappings are being used. + * + * Equivalent to invoking: + * + * NSString *collection, *key; + * if ([[transaction ext:@"view"] getKey:&key + * collection:&collection + * forRow:row + * inSection:section + * withMappings:mappings]) { + * object = [transaction objectForKey:key inCollection:collection]; + * } +**/ +- (id)objectAtRow:(NSUInteger)row inSection:(NSUInteger)section withMappings:(YapDatabaseViewMappings *)mappings +{ + id object = nil; + + int64_t rowid = 0; + YapCollectionKey *ck = nil; + + if ([self getRowid:&rowid collectionKey:&ck forRow:row inSection:section withMappings:mappings]) + { + object = [databaseTransaction objectForCollectionKey:ck withRowid:rowid]; + } + + return object; +} + +/** * Fetches the indexPath for the given {collection, key} tuple, assuming the given mappings are being used. * Returns nil if the {collection, key} tuple isn't included in the view + mappings. **/ - (NSIndexPath *)indexPathForKey:(NSString *)key inCollection:(NSString *)collection @@ -4498,8 +4955,32 @@ { return [mappings indexPathForIndex:index inGroup:group]; } return nil; +} + +/** + * Fetches the row & section for the given {collection, key} tuple, assuming the given mappings are being used. + * Returns NO if the {collection, key} tuple isn't included in the view + mappings. + * Otherwise returns YES, and sets the row & section (both optional). +**/ +- (BOOL)getRow:(NSUInteger *)rowPtr + section:(NSUInteger *)sectionPtr + forKey:(NSString *)key + inCollection:(NSString *)collection + withMappings:(YapDatabaseViewMappings *)mappings +{ + NSString *group = nil; + NSUInteger index = 0; + + if ([self getGroup:&group index:&index forKey:key inCollection:collection]) + { + return [mappings getRow:rowPtr section:sectionPtr forIndex:index inGroup:group]; + } + + if (rowPtr) *rowPtr = 0; + if (sectionPtr) *sectionPtr = 0; + return NO; } @end