motion-yapper 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +20 -0
- data/.travis.yml +6 -0
- data/Gemfile +5 -0
- data/Gemfile.lock +63 -0
- data/README.md +16 -0
- data/Rakefile +20 -0
- data/app/app_delegate.rb +5 -0
- data/lib/yapper.rb +30 -0
- data/lib/yapper/attachment.rb +48 -0
- data/lib/yapper/bson.rb +20 -0
- data/lib/yapper/config.rb +18 -0
- data/lib/yapper/db.rb +151 -0
- data/lib/yapper/document.rb +54 -0
- data/lib/yapper/document/attachment.rb +26 -0
- data/lib/yapper/document/callbacks.rb +86 -0
- data/lib/yapper/document/persistance.rb +171 -0
- data/lib/yapper/document/relation.rb +84 -0
- data/lib/yapper/document/selection.rb +100 -0
- data/lib/yapper/extensions.rb +80 -0
- data/lib/yapper/log.rb +5 -0
- data/lib/yapper/sync.rb +134 -0
- data/lib/yapper/sync/data.rb +12 -0
- data/lib/yapper/sync/event.rb +194 -0
- data/lib/yapper/sync/queue.rb +164 -0
- data/lib/yapper/timestamps.rb +16 -0
- data/lib/yapper/version.rb +3 -0
- data/lib/yapper/yapper.rb +2 -0
- data/spec/helpers/time_helper.rb +3 -0
- data/spec/integration/all_spec.rb +40 -0
- data/spec/integration/callback_spec.rb +87 -0
- data/spec/integration/db_spec.rb +40 -0
- data/spec/integration/find_spec.rb +51 -0
- data/spec/integration/persistance_spec.rb +118 -0
- data/spec/integration/relation_spec.rb +174 -0
- data/spec/integration/sync_spec.rb +42 -0
- data/spec/integration/timestamps_spec.rb +34 -0
- data/spec/integration/types_spec.rb +23 -0
- data/spec/integration/when_spec.rb +29 -0
- data/spec/integration/where_spec.rb +132 -0
- data/vendor/Podfile.lock +24 -0
- data/vendor/Pods/AFNetworking/AFNetworking/AFHTTPClient.h +641 -0
- data/vendor/Pods/AFNetworking/AFNetworking/AFHTTPClient.m +1396 -0
- data/vendor/Pods/AFNetworking/AFNetworking/AFHTTPRequestOperation.h +133 -0
- data/vendor/Pods/AFNetworking/AFNetworking/AFHTTPRequestOperation.m +327 -0
- data/vendor/Pods/AFNetworking/AFNetworking/AFImageRequestOperation.h +113 -0
- data/vendor/Pods/AFNetworking/AFNetworking/AFImageRequestOperation.m +321 -0
- data/vendor/Pods/AFNetworking/AFNetworking/AFJSONRequestOperation.h +71 -0
- data/vendor/Pods/AFNetworking/AFNetworking/AFJSONRequestOperation.m +150 -0
- data/vendor/Pods/AFNetworking/AFNetworking/AFNetworkActivityIndicatorManager.h +75 -0
- data/vendor/Pods/AFNetworking/AFNetworking/AFNetworkActivityIndicatorManager.m +157 -0
- data/vendor/Pods/AFNetworking/AFNetworking/AFNetworking.h +43 -0
- data/vendor/Pods/AFNetworking/AFNetworking/AFPropertyListRequestOperation.h +68 -0
- data/vendor/Pods/AFNetworking/AFNetworking/AFPropertyListRequestOperation.m +143 -0
- data/vendor/Pods/AFNetworking/AFNetworking/AFURLConnectionOperation.h +370 -0
- data/vendor/Pods/AFNetworking/AFNetworking/AFURLConnectionOperation.m +848 -0
- data/vendor/Pods/AFNetworking/AFNetworking/AFXMLRequestOperation.h +89 -0
- data/vendor/Pods/AFNetworking/AFNetworking/AFXMLRequestOperation.m +167 -0
- data/vendor/Pods/AFNetworking/AFNetworking/UIImageView+AFNetworking.h +78 -0
- data/vendor/Pods/AFNetworking/AFNetworking/UIImageView+AFNetworking.m +191 -0
- data/vendor/Pods/AFNetworking/LICENSE +19 -0
- data/vendor/Pods/AFNetworking/README.md +208 -0
- data/vendor/Pods/BuildHeaders/AFNetworking/AFHTTPClient.h +641 -0
- data/vendor/Pods/BuildHeaders/AFNetworking/AFHTTPRequestOperation.h +133 -0
- data/vendor/Pods/BuildHeaders/AFNetworking/AFImageRequestOperation.h +113 -0
- data/vendor/Pods/BuildHeaders/AFNetworking/AFJSONRequestOperation.h +71 -0
- data/vendor/Pods/BuildHeaders/AFNetworking/AFNetworkActivityIndicatorManager.h +75 -0
- data/vendor/Pods/BuildHeaders/AFNetworking/AFNetworking.h +43 -0
- data/vendor/Pods/BuildHeaders/AFNetworking/AFPropertyListRequestOperation.h +68 -0
- data/vendor/Pods/BuildHeaders/AFNetworking/AFURLConnectionOperation.h +370 -0
- data/vendor/Pods/BuildHeaders/AFNetworking/AFXMLRequestOperation.h +89 -0
- data/vendor/Pods/BuildHeaders/AFNetworking/UIImageView+AFNetworking.h +78 -0
- data/vendor/Pods/BuildHeaders/CocoaLumberjack/ContextFilterLogFormatter.h +65 -0
- data/vendor/Pods/BuildHeaders/CocoaLumberjack/DDASLLogger.h +41 -0
- data/vendor/Pods/BuildHeaders/CocoaLumberjack/DDAbstractDatabaseLogger.h +102 -0
- data/vendor/Pods/BuildHeaders/CocoaLumberjack/DDFileLogger.h +334 -0
- data/vendor/Pods/BuildHeaders/CocoaLumberjack/DDLog.h +601 -0
- data/vendor/Pods/BuildHeaders/CocoaLumberjack/DDTTYLogger.h +167 -0
- data/vendor/Pods/BuildHeaders/CocoaLumberjack/DispatchQueueLogFormatter.h +116 -0
- data/vendor/Pods/BuildHeaders/NSData+MD5Digest/NSData+MD5Digest.h +18 -0
- data/vendor/Pods/BuildHeaders/Reachability/Reachability.h +109 -0
- data/vendor/Pods/BuildHeaders/YapDatabase/YapCache.h +90 -0
- data/vendor/Pods/BuildHeaders/YapDatabase/YapCollectionKey.h +20 -0
- data/vendor/Pods/BuildHeaders/YapDatabase/YapDatabase.h +547 -0
- data/vendor/Pods/BuildHeaders/YapDatabase/YapDatabaseConnection.h +447 -0
- data/vendor/Pods/BuildHeaders/YapDatabase/YapDatabaseConnectionState.h +29 -0
- data/vendor/Pods/BuildHeaders/YapDatabase/YapDatabaseDefaults.h +37 -0
- data/vendor/Pods/BuildHeaders/YapDatabase/YapDatabaseExtension.h +15 -0
- data/vendor/Pods/BuildHeaders/YapDatabase/YapDatabaseExtensionConnection.h +11 -0
- data/vendor/Pods/BuildHeaders/YapDatabase/YapDatabaseExtensionPrivate.h +440 -0
- data/vendor/Pods/BuildHeaders/YapDatabase/YapDatabaseExtensionTransaction.h +11 -0
- data/vendor/Pods/BuildHeaders/YapDatabase/YapDatabaseFilteredView.h +108 -0
- data/vendor/Pods/BuildHeaders/YapDatabase/YapDatabaseFilteredViewConnection.h +12 -0
- data/vendor/Pods/BuildHeaders/YapDatabase/YapDatabaseFilteredViewPrivate.h +19 -0
- data/vendor/Pods/BuildHeaders/YapDatabase/YapDatabaseFilteredViewTransaction.h +39 -0
- data/vendor/Pods/BuildHeaders/YapDatabase/YapDatabaseFullTextSearch.h +89 -0
- data/vendor/Pods/BuildHeaders/YapDatabase/YapDatabaseFullTextSearchConnection.h +32 -0
- data/vendor/Pods/BuildHeaders/YapDatabase/YapDatabaseFullTextSearchPrivate.h +69 -0
- data/vendor/Pods/BuildHeaders/YapDatabase/YapDatabaseFullTextSearchSnippetOptions.h +79 -0
- data/vendor/Pods/BuildHeaders/YapDatabase/YapDatabaseFullTextSearchTransaction.h +68 -0
- data/vendor/Pods/BuildHeaders/YapDatabase/YapDatabaseLogging.h +158 -0
- data/vendor/Pods/BuildHeaders/YapDatabase/YapDatabaseManager.h +17 -0
- data/vendor/Pods/BuildHeaders/YapDatabase/YapDatabasePrivate.h +424 -0
- data/vendor/Pods/BuildHeaders/YapDatabase/YapDatabaseQuery.h +42 -0
- data/vendor/Pods/BuildHeaders/YapDatabase/YapDatabaseSecondaryIndex.h +100 -0
- data/vendor/Pods/BuildHeaders/YapDatabase/YapDatabaseSecondaryIndexConnection.h +33 -0
- data/vendor/Pods/BuildHeaders/YapDatabase/YapDatabaseSecondaryIndexPrivate.h +73 -0
- data/vendor/Pods/BuildHeaders/YapDatabase/YapDatabaseSecondaryIndexSetup.h +33 -0
- data/vendor/Pods/BuildHeaders/YapDatabase/YapDatabaseSecondaryIndexTransaction.h +58 -0
- data/vendor/Pods/BuildHeaders/YapDatabase/YapDatabaseStatement.h +13 -0
- data/vendor/Pods/BuildHeaders/YapDatabase/YapDatabaseString.h +121 -0
- data/vendor/Pods/BuildHeaders/YapDatabase/YapDatabaseTransaction.h +541 -0
- data/vendor/Pods/BuildHeaders/YapDatabase/YapDatabaseView.h +186 -0
- data/vendor/Pods/BuildHeaders/YapDatabase/YapDatabaseViewChange.h +272 -0
- data/vendor/Pods/BuildHeaders/YapDatabase/YapDatabaseViewChangePrivate.h +94 -0
- data/vendor/Pods/BuildHeaders/YapDatabase/YapDatabaseViewConnection.h +115 -0
- data/vendor/Pods/BuildHeaders/YapDatabase/YapDatabaseViewMappings.h +825 -0
- data/vendor/Pods/BuildHeaders/YapDatabase/YapDatabaseViewMappingsPrivate.h +72 -0
- data/vendor/Pods/BuildHeaders/YapDatabase/YapDatabaseViewOptions.h +56 -0
- data/vendor/Pods/BuildHeaders/YapDatabase/YapDatabaseViewPage.h +36 -0
- data/vendor/Pods/BuildHeaders/YapDatabase/YapDatabaseViewPageMetadata.h +27 -0
- data/vendor/Pods/BuildHeaders/YapDatabase/YapDatabaseViewPrivate.h +153 -0
- data/vendor/Pods/BuildHeaders/YapDatabase/YapDatabaseViewRangeOptions.h +330 -0
- data/vendor/Pods/BuildHeaders/YapDatabase/YapDatabaseViewRangeOptionsPrivate.h +17 -0
- data/vendor/Pods/BuildHeaders/YapDatabase/YapDatabaseViewTransaction.h +447 -0
- data/vendor/Pods/BuildHeaders/YapDatabase/YapMemoryTable.h +74 -0
- data/vendor/Pods/BuildHeaders/YapDatabase/YapNull.h +17 -0
- data/vendor/Pods/BuildHeaders/YapDatabase/YapSet.h +41 -0
- data/vendor/Pods/BuildHeaders/YapDatabase/YapTouch.h +15 -0
- data/vendor/Pods/CocoaLumberjack/LICENSE.txt +18 -0
- data/vendor/Pods/CocoaLumberjack/Lumberjack/DDASLLogger.h +41 -0
- data/vendor/Pods/CocoaLumberjack/Lumberjack/DDASLLogger.m +99 -0
- data/vendor/Pods/CocoaLumberjack/Lumberjack/DDAbstractDatabaseLogger.h +102 -0
- data/vendor/Pods/CocoaLumberjack/Lumberjack/DDAbstractDatabaseLogger.m +727 -0
- data/vendor/Pods/CocoaLumberjack/Lumberjack/DDFileLogger.h +334 -0
- data/vendor/Pods/CocoaLumberjack/Lumberjack/DDFileLogger.m +1353 -0
- data/vendor/Pods/CocoaLumberjack/Lumberjack/DDLog.h +601 -0
- data/vendor/Pods/CocoaLumberjack/Lumberjack/DDLog.m +1083 -0
- data/vendor/Pods/CocoaLumberjack/Lumberjack/DDTTYLogger.h +167 -0
- data/vendor/Pods/CocoaLumberjack/Lumberjack/DDTTYLogger.m +1479 -0
- data/vendor/Pods/CocoaLumberjack/Lumberjack/Extensions/ContextFilterLogFormatter.h +65 -0
- data/vendor/Pods/CocoaLumberjack/Lumberjack/Extensions/ContextFilterLogFormatter.m +191 -0
- data/vendor/Pods/CocoaLumberjack/Lumberjack/Extensions/DispatchQueueLogFormatter.h +116 -0
- data/vendor/Pods/CocoaLumberjack/Lumberjack/Extensions/DispatchQueueLogFormatter.m +251 -0
- data/vendor/Pods/CocoaLumberjack/Lumberjack/Extensions/README.txt +7 -0
- data/vendor/Pods/CocoaLumberjack/README.markdown +37 -0
- data/vendor/Pods/Headers/AFNetworking/AFHTTPClient.h +641 -0
- data/vendor/Pods/Headers/AFNetworking/AFHTTPRequestOperation.h +133 -0
- data/vendor/Pods/Headers/AFNetworking/AFImageRequestOperation.h +113 -0
- data/vendor/Pods/Headers/AFNetworking/AFJSONRequestOperation.h +71 -0
- data/vendor/Pods/Headers/AFNetworking/AFNetworkActivityIndicatorManager.h +75 -0
- data/vendor/Pods/Headers/AFNetworking/AFNetworking.h +43 -0
- data/vendor/Pods/Headers/AFNetworking/AFPropertyListRequestOperation.h +68 -0
- data/vendor/Pods/Headers/AFNetworking/AFURLConnectionOperation.h +370 -0
- data/vendor/Pods/Headers/AFNetworking/AFXMLRequestOperation.h +89 -0
- data/vendor/Pods/Headers/AFNetworking/UIImageView+AFNetworking.h +78 -0
- data/vendor/Pods/Headers/CocoaLumberjack/ContextFilterLogFormatter.h +65 -0
- data/vendor/Pods/Headers/CocoaLumberjack/DDASLLogger.h +41 -0
- data/vendor/Pods/Headers/CocoaLumberjack/DDAbstractDatabaseLogger.h +102 -0
- data/vendor/Pods/Headers/CocoaLumberjack/DDFileLogger.h +334 -0
- data/vendor/Pods/Headers/CocoaLumberjack/DDLog.h +601 -0
- data/vendor/Pods/Headers/CocoaLumberjack/DDTTYLogger.h +167 -0
- data/vendor/Pods/Headers/CocoaLumberjack/DispatchQueueLogFormatter.h +116 -0
- data/vendor/Pods/Headers/NSData+MD5Digest/NSData+MD5Digest.h +18 -0
- data/vendor/Pods/Headers/Reachability/Reachability.h +109 -0
- data/vendor/Pods/Headers/YapDatabase/YapCache.h +90 -0
- data/vendor/Pods/Headers/YapDatabase/YapCollectionKey.h +20 -0
- data/vendor/Pods/Headers/YapDatabase/YapDatabase.h +547 -0
- data/vendor/Pods/Headers/YapDatabase/YapDatabaseConnection.h +447 -0
- data/vendor/Pods/Headers/YapDatabase/YapDatabaseConnectionState.h +29 -0
- data/vendor/Pods/Headers/YapDatabase/YapDatabaseDefaults.h +37 -0
- data/vendor/Pods/Headers/YapDatabase/YapDatabaseExtension.h +15 -0
- data/vendor/Pods/Headers/YapDatabase/YapDatabaseExtensionConnection.h +11 -0
- data/vendor/Pods/Headers/YapDatabase/YapDatabaseExtensionPrivate.h +440 -0
- data/vendor/Pods/Headers/YapDatabase/YapDatabaseExtensionTransaction.h +11 -0
- data/vendor/Pods/Headers/YapDatabase/YapDatabaseFilteredView.h +108 -0
- data/vendor/Pods/Headers/YapDatabase/YapDatabaseFilteredViewConnection.h +12 -0
- data/vendor/Pods/Headers/YapDatabase/YapDatabaseFilteredViewPrivate.h +19 -0
- data/vendor/Pods/Headers/YapDatabase/YapDatabaseFilteredViewTransaction.h +39 -0
- data/vendor/Pods/Headers/YapDatabase/YapDatabaseFullTextSearch.h +89 -0
- data/vendor/Pods/Headers/YapDatabase/YapDatabaseFullTextSearchConnection.h +32 -0
- data/vendor/Pods/Headers/YapDatabase/YapDatabaseFullTextSearchPrivate.h +69 -0
- data/vendor/Pods/Headers/YapDatabase/YapDatabaseFullTextSearchSnippetOptions.h +79 -0
- data/vendor/Pods/Headers/YapDatabase/YapDatabaseFullTextSearchTransaction.h +68 -0
- data/vendor/Pods/Headers/YapDatabase/YapDatabaseLogging.h +158 -0
- data/vendor/Pods/Headers/YapDatabase/YapDatabaseManager.h +17 -0
- data/vendor/Pods/Headers/YapDatabase/YapDatabasePrivate.h +424 -0
- data/vendor/Pods/Headers/YapDatabase/YapDatabaseQuery.h +42 -0
- data/vendor/Pods/Headers/YapDatabase/YapDatabaseSecondaryIndex.h +100 -0
- data/vendor/Pods/Headers/YapDatabase/YapDatabaseSecondaryIndexConnection.h +33 -0
- data/vendor/Pods/Headers/YapDatabase/YapDatabaseSecondaryIndexPrivate.h +73 -0
- data/vendor/Pods/Headers/YapDatabase/YapDatabaseSecondaryIndexSetup.h +33 -0
- data/vendor/Pods/Headers/YapDatabase/YapDatabaseSecondaryIndexTransaction.h +58 -0
- data/vendor/Pods/Headers/YapDatabase/YapDatabaseStatement.h +13 -0
- data/vendor/Pods/Headers/YapDatabase/YapDatabaseString.h +121 -0
- data/vendor/Pods/Headers/YapDatabase/YapDatabaseTransaction.h +541 -0
- data/vendor/Pods/Headers/YapDatabase/YapDatabaseView.h +186 -0
- data/vendor/Pods/Headers/YapDatabase/YapDatabaseViewChange.h +272 -0
- data/vendor/Pods/Headers/YapDatabase/YapDatabaseViewChangePrivate.h +94 -0
- data/vendor/Pods/Headers/YapDatabase/YapDatabaseViewConnection.h +115 -0
- data/vendor/Pods/Headers/YapDatabase/YapDatabaseViewMappings.h +825 -0
- data/vendor/Pods/Headers/YapDatabase/YapDatabaseViewMappingsPrivate.h +72 -0
- data/vendor/Pods/Headers/YapDatabase/YapDatabaseViewOptions.h +56 -0
- data/vendor/Pods/Headers/YapDatabase/YapDatabaseViewPage.h +36 -0
- data/vendor/Pods/Headers/YapDatabase/YapDatabaseViewPageMetadata.h +27 -0
- data/vendor/Pods/Headers/YapDatabase/YapDatabaseViewPrivate.h +153 -0
- data/vendor/Pods/Headers/YapDatabase/YapDatabaseViewRangeOptions.h +330 -0
- data/vendor/Pods/Headers/YapDatabase/YapDatabaseViewRangeOptionsPrivate.h +17 -0
- data/vendor/Pods/Headers/YapDatabase/YapDatabaseViewTransaction.h +447 -0
- data/vendor/Pods/Headers/YapDatabase/YapMemoryTable.h +74 -0
- data/vendor/Pods/Headers/YapDatabase/YapNull.h +17 -0
- data/vendor/Pods/Headers/YapDatabase/YapSet.h +41 -0
- data/vendor/Pods/Headers/YapDatabase/YapTouch.h +15 -0
- data/vendor/Pods/Headers/____Pods-AFNetworking-prefix.h +17 -0
- data/vendor/Pods/Headers/____Pods-CocoaLumberjack-prefix.h +5 -0
- data/vendor/Pods/Headers/____Pods-NSData+MD5Digest-prefix.h +5 -0
- data/vendor/Pods/Headers/____Pods-Reachability-prefix.h +5 -0
- data/vendor/Pods/Headers/____Pods-YapDatabase-prefix.h +5 -0
- data/vendor/Pods/Headers/____Pods-environment.h +38 -0
- data/vendor/Pods/Manifest.lock +24 -0
- data/vendor/Pods/NSData+MD5Digest/NSData+MD5Digest/NSData+MD5Digest.h +18 -0
- data/vendor/Pods/NSData+MD5Digest/NSData+MD5Digest/NSData+MD5Digest.m +39 -0
- data/vendor/Pods/NSData+MD5Digest/README.md +11 -0
- data/vendor/Pods/Pods-AFNetworking-Private.xcconfig +5 -0
- data/vendor/Pods/Pods-AFNetworking-dummy.m +5 -0
- data/vendor/Pods/Pods-AFNetworking-prefix.pch +17 -0
- data/vendor/Pods/Pods-AFNetworking.xcconfig +1 -0
- data/vendor/Pods/Pods-Acknowledgements.markdown +96 -0
- data/vendor/Pods/Pods-Acknowledgements.plist +142 -0
- data/vendor/Pods/Pods-CocoaLumberjack-Private.xcconfig +5 -0
- data/vendor/Pods/Pods-CocoaLumberjack-dummy.m +5 -0
- data/vendor/Pods/Pods-CocoaLumberjack-prefix.pch +5 -0
- data/vendor/Pods/Pods-CocoaLumberjack.xcconfig +0 -0
- data/vendor/Pods/Pods-NSData+MD5Digest-Private.xcconfig +5 -0
- data/vendor/Pods/Pods-NSData+MD5Digest-dummy.m +5 -0
- data/vendor/Pods/Pods-NSData+MD5Digest-prefix.pch +5 -0
- data/vendor/Pods/Pods-NSData+MD5Digest.xcconfig +0 -0
- data/vendor/Pods/Pods-Reachability-Private.xcconfig +5 -0
- data/vendor/Pods/Pods-Reachability-dummy.m +5 -0
- data/vendor/Pods/Pods-Reachability-prefix.pch +5 -0
- data/vendor/Pods/Pods-Reachability.xcconfig +1 -0
- data/vendor/Pods/Pods-YapDatabase-Private.xcconfig +5 -0
- data/vendor/Pods/Pods-YapDatabase-dummy.m +5 -0
- data/vendor/Pods/Pods-YapDatabase-prefix.pch +5 -0
- data/vendor/Pods/Pods-YapDatabase.xcconfig +1 -0
- data/vendor/Pods/Pods-dummy.m +5 -0
- data/vendor/Pods/Pods-environment.h +38 -0
- data/vendor/Pods/Pods-resources.sh +68 -0
- data/vendor/Pods/Pods.bridgesupport +5096 -0
- data/vendor/Pods/Pods.xcconfig +5 -0
- data/vendor/Pods/Pods.xcodeproj/project.pbxproj +5536 -0
- data/vendor/Pods/Reachability/LICENCE.txt +24 -0
- data/vendor/Pods/Reachability/README.md +65 -0
- data/vendor/Pods/Reachability/Reachability.h +109 -0
- data/vendor/Pods/Reachability/Reachability.m +527 -0
- data/vendor/Pods/YapDatabase/LICENSE.txt +18 -0
- data/vendor/Pods/YapDatabase/README.md +30 -0
- data/vendor/Pods/YapDatabase/YapDatabase/Extensions/FilteredViews/Internal/YapDatabaseFilteredViewPrivate.h +19 -0
- data/vendor/Pods/YapDatabase/YapDatabase/Extensions/FilteredViews/YapDatabaseFilteredView.h +108 -0
- data/vendor/Pods/YapDatabase/YapDatabase/Extensions/FilteredViews/YapDatabaseFilteredView.m +175 -0
- data/vendor/Pods/YapDatabase/YapDatabase/Extensions/FilteredViews/YapDatabaseFilteredViewConnection.h +12 -0
- data/vendor/Pods/YapDatabase/YapDatabase/Extensions/FilteredViews/YapDatabaseFilteredViewConnection.m +41 -0
- data/vendor/Pods/YapDatabase/YapDatabase/Extensions/FilteredViews/YapDatabaseFilteredViewTransaction.h +39 -0
- data/vendor/Pods/YapDatabase/YapDatabase/Extensions/FilteredViews/YapDatabaseFilteredViewTransaction.m +966 -0
- data/vendor/Pods/YapDatabase/YapDatabase/Extensions/FullTextSearch/Internal/YapDatabaseFullTextSearchPrivate.h +69 -0
- data/vendor/Pods/YapDatabase/YapDatabase/Extensions/FullTextSearch/YapDatabaseFullTextSearch.h +89 -0
- data/vendor/Pods/YapDatabase/YapDatabase/Extensions/FullTextSearch/YapDatabaseFullTextSearch.m +146 -0
- data/vendor/Pods/YapDatabase/YapDatabase/Extensions/FullTextSearch/YapDatabaseFullTextSearchConnection.h +32 -0
- data/vendor/Pods/YapDatabase/YapDatabase/Extensions/FullTextSearch/YapDatabaseFullTextSearchConnection.m +298 -0
- data/vendor/Pods/YapDatabase/YapDatabase/Extensions/FullTextSearch/YapDatabaseFullTextSearchSnippetOptions.h +79 -0
- data/vendor/Pods/YapDatabase/YapDatabase/Extensions/FullTextSearch/YapDatabaseFullTextSearchSnippetOptions.m +95 -0
- data/vendor/Pods/YapDatabase/YapDatabase/Extensions/FullTextSearch/YapDatabaseFullTextSearchTransaction.h +68 -0
- data/vendor/Pods/YapDatabase/YapDatabase/Extensions/FullTextSearch/YapDatabaseFullTextSearchTransaction.m +1352 -0
- data/vendor/Pods/YapDatabase/YapDatabase/Extensions/Protocol/Internal/YapDatabaseExtensionPrivate.h +440 -0
- data/vendor/Pods/YapDatabase/YapDatabase/Extensions/Protocol/YapDatabaseExtension.h +15 -0
- data/vendor/Pods/YapDatabase/YapDatabase/Extensions/Protocol/YapDatabaseExtension.m +58 -0
- data/vendor/Pods/YapDatabase/YapDatabase/Extensions/Protocol/YapDatabaseExtensionConnection.h +11 -0
- data/vendor/Pods/YapDatabase/YapDatabase/Extensions/Protocol/YapDatabaseExtensionConnection.m +46 -0
- data/vendor/Pods/YapDatabase/YapDatabase/Extensions/Protocol/YapDatabaseExtensionTransaction.h +11 -0
- data/vendor/Pods/YapDatabase/YapDatabase/Extensions/Protocol/YapDatabaseExtensionTransaction.m +180 -0
- data/vendor/Pods/YapDatabase/YapDatabase/Extensions/SecondaryIndex/Internal/YapDatabaseSecondaryIndexPrivate.h +73 -0
- data/vendor/Pods/YapDatabase/YapDatabase/Extensions/SecondaryIndex/YapDatabaseSecondaryIndex.h +100 -0
- data/vendor/Pods/YapDatabase/YapDatabase/Extensions/SecondaryIndex/YapDatabaseSecondaryIndex.m +149 -0
- data/vendor/Pods/YapDatabase/YapDatabase/Extensions/SecondaryIndex/YapDatabaseSecondaryIndexConnection.h +33 -0
- data/vendor/Pods/YapDatabase/YapDatabase/Extensions/SecondaryIndex/YapDatabaseSecondaryIndexConnection.m +330 -0
- data/vendor/Pods/YapDatabase/YapDatabase/Extensions/SecondaryIndex/YapDatabaseSecondaryIndexSetup.h +33 -0
- data/vendor/Pods/YapDatabase/YapDatabase/Extensions/SecondaryIndex/YapDatabaseSecondaryIndexSetup.m +184 -0
- data/vendor/Pods/YapDatabase/YapDatabase/Extensions/SecondaryIndex/YapDatabaseSecondaryIndexTransaction.h +58 -0
- data/vendor/Pods/YapDatabase/YapDatabase/Extensions/SecondaryIndex/YapDatabaseSecondaryIndexTransaction.m +1166 -0
- data/vendor/Pods/YapDatabase/YapDatabase/Extensions/Views/Internal/YapDatabaseViewChangePrivate.h +94 -0
- data/vendor/Pods/YapDatabase/YapDatabase/Extensions/Views/Internal/YapDatabaseViewMappingsPrivate.h +72 -0
- data/vendor/Pods/YapDatabase/YapDatabase/Extensions/Views/Internal/YapDatabaseViewPage.h +36 -0
- data/vendor/Pods/YapDatabase/YapDatabase/Extensions/Views/Internal/YapDatabaseViewPage.mm +296 -0
- data/vendor/Pods/YapDatabase/YapDatabase/Extensions/Views/Internal/YapDatabaseViewPageMetadata.h +27 -0
- data/vendor/Pods/YapDatabase/YapDatabase/Extensions/Views/Internal/YapDatabaseViewPageMetadata.m +28 -0
- data/vendor/Pods/YapDatabase/YapDatabase/Extensions/Views/Internal/YapDatabaseViewPrivate.h +153 -0
- data/vendor/Pods/YapDatabase/YapDatabase/Extensions/Views/Internal/YapDatabaseViewRangeOptionsPrivate.h +17 -0
- data/vendor/Pods/YapDatabase/YapDatabase/Extensions/Views/Utilities/YapDatabaseViewChange.h +272 -0
- data/vendor/Pods/YapDatabase/YapDatabase/Extensions/Views/Utilities/YapDatabaseViewChange.m +2494 -0
- data/vendor/Pods/YapDatabase/YapDatabase/Extensions/Views/Utilities/YapDatabaseViewMappings.h +825 -0
- data/vendor/Pods/YapDatabase/YapDatabase/Extensions/Views/Utilities/YapDatabaseViewMappings.m +1567 -0
- data/vendor/Pods/YapDatabase/YapDatabase/Extensions/Views/Utilities/YapDatabaseViewRangeOptions.h +330 -0
- data/vendor/Pods/YapDatabase/YapDatabase/Extensions/Views/Utilities/YapDatabaseViewRangeOptions.m +141 -0
- data/vendor/Pods/YapDatabase/YapDatabase/Extensions/Views/YapDatabaseView.h +186 -0
- data/vendor/Pods/YapDatabase/YapDatabase/Extensions/Views/YapDatabaseView.m +191 -0
- data/vendor/Pods/YapDatabase/YapDatabase/Extensions/Views/YapDatabaseViewConnection.h +115 -0
- data/vendor/Pods/YapDatabase/YapDatabase/Extensions/Views/YapDatabaseViewConnection.m +897 -0
- data/vendor/Pods/YapDatabase/YapDatabase/Extensions/Views/YapDatabaseViewOptions.h +56 -0
- data/vendor/Pods/YapDatabase/YapDatabase/Extensions/Views/YapDatabaseViewOptions.m +27 -0
- data/vendor/Pods/YapDatabase/YapDatabase/Extensions/Views/YapDatabaseViewTransaction.h +447 -0
- data/vendor/Pods/YapDatabase/YapDatabase/Extensions/Views/YapDatabaseViewTransaction.m +4505 -0
- data/vendor/Pods/YapDatabase/YapDatabase/Internal/YapCache.h +90 -0
- data/vendor/Pods/YapDatabase/YapDatabase/Internal/YapCache.m +453 -0
- data/vendor/Pods/YapDatabase/YapDatabase/Internal/YapDatabaseConnectionState.h +29 -0
- data/vendor/Pods/YapDatabase/YapDatabase/Internal/YapDatabaseConnectionState.m +48 -0
- data/vendor/Pods/YapDatabase/YapDatabase/Internal/YapDatabaseDefaults.h +37 -0
- data/vendor/Pods/YapDatabase/YapDatabase/Internal/YapDatabaseDefaults.m +83 -0
- data/vendor/Pods/YapDatabase/YapDatabase/Internal/YapDatabaseLogging.h +158 -0
- data/vendor/Pods/YapDatabase/YapDatabase/Internal/YapDatabaseLogging.m +73 -0
- data/vendor/Pods/YapDatabase/YapDatabase/Internal/YapDatabaseManager.h +17 -0
- data/vendor/Pods/YapDatabase/YapDatabase/Internal/YapDatabaseManager.m +56 -0
- data/vendor/Pods/YapDatabase/YapDatabase/Internal/YapDatabasePrivate.h +424 -0
- data/vendor/Pods/YapDatabase/YapDatabase/Internal/YapDatabaseStatement.h +13 -0
- data/vendor/Pods/YapDatabase/YapDatabase/Internal/YapDatabaseStatement.m +26 -0
- data/vendor/Pods/YapDatabase/YapDatabase/Internal/YapDatabaseString.h +121 -0
- data/vendor/Pods/YapDatabase/YapDatabase/Internal/YapMemoryTable.h +74 -0
- data/vendor/Pods/YapDatabase/YapDatabase/Internal/YapMemoryTable.m +603 -0
- data/vendor/Pods/YapDatabase/YapDatabase/Internal/YapNull.h +17 -0
- data/vendor/Pods/YapDatabase/YapDatabase/Internal/YapNull.m +31 -0
- data/vendor/Pods/YapDatabase/YapDatabase/Internal/YapTouch.h +15 -0
- data/vendor/Pods/YapDatabase/YapDatabase/Internal/YapTouch.m +31 -0
- data/vendor/Pods/YapDatabase/YapDatabase/Utilities/YapCollectionKey.h +20 -0
- data/vendor/Pods/YapDatabase/YapDatabase/Utilities/YapCollectionKey.m +194 -0
- data/vendor/Pods/YapDatabase/YapDatabase/Utilities/YapDatabaseQuery.h +42 -0
- data/vendor/Pods/YapDatabase/YapDatabase/Utilities/YapDatabaseQuery.m +96 -0
- data/vendor/Pods/YapDatabase/YapDatabase/Utilities/YapSet.h +41 -0
- data/vendor/Pods/YapDatabase/YapDatabase/Utilities/YapSet.m +82 -0
- data/vendor/Pods/YapDatabase/YapDatabase/YapDatabase.h +547 -0
- data/vendor/Pods/YapDatabase/YapDatabase/YapDatabase.m +2022 -0
- data/vendor/Pods/YapDatabase/YapDatabase/YapDatabaseConnection.h +447 -0
- data/vendor/Pods/YapDatabase/YapDatabase/YapDatabaseConnection.m +3874 -0
- data/vendor/Pods/YapDatabase/YapDatabase/YapDatabaseTransaction.h +541 -0
- data/vendor/Pods/YapDatabase/YapDatabase/YapDatabaseTransaction.m +5282 -0
- data/vendor/YapDatabaseRubyMotion/YapDatabaseRubyMotion.bridgesupport +16 -0
- data/vendor/YapDatabaseRubyMotion/YapDatabaseRubyMotion.h +13 -0
- data/vendor/YapDatabaseRubyMotion/YapDatabaseRubyMotion.m +20 -0
- data/yapper.gemspec +24 -0
- metadata +458 -0
|
@@ -0,0 +1,4505 @@
|
|
|
1
|
+
#import "YapDatabaseViewTransaction.h"
|
|
2
|
+
#import "YapDatabaseViewPrivate.h"
|
|
3
|
+
#import "YapDatabaseViewPage.h"
|
|
4
|
+
#import "YapDatabaseViewPageMetadata.h"
|
|
5
|
+
#import "YapDatabaseViewChange.h"
|
|
6
|
+
#import "YapDatabaseViewChangePrivate.h"
|
|
7
|
+
#import "YapDatabaseExtensionPrivate.h"
|
|
8
|
+
#import "YapDatabasePrivate.h"
|
|
9
|
+
#import "YapCache.h"
|
|
10
|
+
#import "YapCollectionKey.h"
|
|
11
|
+
#import "YapDatabaseString.h"
|
|
12
|
+
#import "YapDatabaseLogging.h"
|
|
13
|
+
|
|
14
|
+
#if ! __has_feature(objc_arc)
|
|
15
|
+
#warning This file must be compiled with ARC. Use -fobjc-arc flag (or convert project to ARC).
|
|
16
|
+
#endif
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Define log level for this file: OFF, ERROR, WARN, INFO, VERBOSE
|
|
20
|
+
* See YapDatabaseLogging.h for more information.
|
|
21
|
+
**/
|
|
22
|
+
#if DEBUG
|
|
23
|
+
static const int ydbLogLevel = YDB_LOG_LEVEL_WARN;
|
|
24
|
+
#else
|
|
25
|
+
static const int ydbLogLevel = YDB_LOG_LEVEL_WARN;
|
|
26
|
+
#endif
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* The view is tasked with storing ordered arrays of keys.
|
|
31
|
+
* In doing so, it splits the array into "pages" of keys,
|
|
32
|
+
* and stores the pages in the database.
|
|
33
|
+
* This reduces disk IO, as only the contents of a single page are written for a single change.
|
|
34
|
+
* And only the contents of a single page need be read to fetch a single key.
|
|
35
|
+
**/
|
|
36
|
+
#define YAP_DATABASE_VIEW_MAX_PAGE_SIZE 50
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* ARCHITECTURE OVERVIEW:
|
|
40
|
+
*
|
|
41
|
+
* A YapDatabaseView allows one to store a ordered array of collection/key tuples.
|
|
42
|
+
* Furthermore, groups are supported, which means there may be multiple ordered arrays of tuples, one per group.
|
|
43
|
+
*
|
|
44
|
+
* Conceptually this is a very simple concept.
|
|
45
|
+
* But obviously there are memory and performance requirements that add complexity.
|
|
46
|
+
*
|
|
47
|
+
* The view creates two database tables:
|
|
48
|
+
*
|
|
49
|
+
* view_name_key:
|
|
50
|
+
* - collection (string) : from the database table
|
|
51
|
+
* - key (string) : from the database table
|
|
52
|
+
* - pageKey (string) : the primary key in the page table
|
|
53
|
+
*
|
|
54
|
+
* view_name_page:
|
|
55
|
+
* - pageKey (string, primary key) : a uuid
|
|
56
|
+
* - data (blob) : an array of collection/key tuples (the page)
|
|
57
|
+
* - metadata (blob) : a YapDatabaseViewPageMetadata object
|
|
58
|
+
*
|
|
59
|
+
* For both tables "name" is replaced by the registered name of the view.
|
|
60
|
+
*
|
|
61
|
+
* Thus, given a key, we can quickly identify if the key exists in the view (via the key table).
|
|
62
|
+
* And if so we can use the associated pageKey to figure out the group and index of the key.
|
|
63
|
+
*
|
|
64
|
+
* When we open the view, we read all the metadata objects from the page table into memory.
|
|
65
|
+
* We use the metadata to create the two primary data structures:
|
|
66
|
+
*
|
|
67
|
+
* - group_pagesMetadata_dict (NSMutableDictionary) : key(group), value(array of YapDatabaseViewPageMetadata objects)
|
|
68
|
+
* - pageKey_group_dict (NSMutableDictionary) : key(pageKey), value(group)
|
|
69
|
+
*
|
|
70
|
+
* Given a group, we can use the group_pages_dict to find the associated array of pages (and metadata for each page).
|
|
71
|
+
* Given a pageKey, we can use the pageKey_group_dict to quickly find the associated group.
|
|
72
|
+
**/
|
|
73
|
+
@implementation YapDatabaseViewTransaction
|
|
74
|
+
|
|
75
|
+
- (id)initWithViewConnection:(YapDatabaseViewConnection *)inViewConnection
|
|
76
|
+
databaseTransaction:(YapDatabaseReadTransaction *)inDatabaseTransaction
|
|
77
|
+
{
|
|
78
|
+
if ((self = [super init]))
|
|
79
|
+
{
|
|
80
|
+
viewConnection = inViewConnection;
|
|
81
|
+
databaseTransaction = inDatabaseTransaction;
|
|
82
|
+
|
|
83
|
+
if (viewConnection->view->options.isPersistent == NO)
|
|
84
|
+
{
|
|
85
|
+
mapTableTransaction = [databaseTransaction memoryTableTransaction:[self mapTableName]];
|
|
86
|
+
pageTableTransaction = [databaseTransaction memoryTableTransaction:[self pageTableName]];
|
|
87
|
+
pageMetadataTableTransaction = [databaseTransaction memoryTableTransaction:[self pageMetadataTableName]];
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
}
|
|
91
|
+
return self;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
95
|
+
#pragma mark Extension Lifecycle
|
|
96
|
+
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* This method is called to create any necessary tables,
|
|
100
|
+
* as well as populate the view by enumerating over the existing rows in the database.
|
|
101
|
+
*
|
|
102
|
+
* Return YES if completed successfully, or if already prepared.
|
|
103
|
+
* Return NO if some kind of error occured.
|
|
104
|
+
**/
|
|
105
|
+
- (BOOL)createIfNeeded
|
|
106
|
+
{
|
|
107
|
+
BOOL needsCreateTables = NO;
|
|
108
|
+
|
|
109
|
+
// Check classVersion (the internal version number of YapDatabaseView implementation)
|
|
110
|
+
|
|
111
|
+
int oldClassVersion = [self intValueForExtensionKey:@"classVersion"];
|
|
112
|
+
int classVersion = YAP_DATABASE_VIEW_CLASS_VERSION;
|
|
113
|
+
|
|
114
|
+
if (oldClassVersion != classVersion)
|
|
115
|
+
needsCreateTables = YES;
|
|
116
|
+
|
|
117
|
+
// Check persistence.
|
|
118
|
+
// Need to properly transition from persistent to non-persistent, and vice-versa.
|
|
119
|
+
|
|
120
|
+
BOOL oldIsPersistent = NO;
|
|
121
|
+
BOOL hasOldIsPersistent = [self getBoolValue:&oldIsPersistent forExtensionKey:@"persistent"];
|
|
122
|
+
|
|
123
|
+
BOOL isPersistent = [self isPersistentView];
|
|
124
|
+
|
|
125
|
+
if (hasOldIsPersistent && (oldIsPersistent != isPersistent))
|
|
126
|
+
{
|
|
127
|
+
[[viewConnection->view class]
|
|
128
|
+
dropTablesForRegisteredName:[self registeredName]
|
|
129
|
+
withTransaction:(YapDatabaseReadWriteTransaction *)databaseTransaction];
|
|
130
|
+
|
|
131
|
+
needsCreateTables = YES;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// Create or re-populate if needed
|
|
135
|
+
|
|
136
|
+
if (needsCreateTables)
|
|
137
|
+
{
|
|
138
|
+
// First time registration
|
|
139
|
+
|
|
140
|
+
[self dropTablesForOldClassVersion:oldClassVersion];
|
|
141
|
+
|
|
142
|
+
if (![self createTables]) return NO;
|
|
143
|
+
if (![self populateView]) return NO;
|
|
144
|
+
|
|
145
|
+
[self setIntValue:classVersion forExtensionKey:@"classVersion"];
|
|
146
|
+
|
|
147
|
+
[self setBoolValue:isPersistent forExtensionKey:@"persistent"];
|
|
148
|
+
|
|
149
|
+
int userSuppliedConfigVersion = viewConnection->view->version;
|
|
150
|
+
[self setIntValue:userSuppliedConfigVersion forExtensionKey:@"version"];
|
|
151
|
+
}
|
|
152
|
+
else
|
|
153
|
+
{
|
|
154
|
+
// Check user-supplied config version.
|
|
155
|
+
// We may need to re-populate the database if the groupingBlock or sortingBlock changed.
|
|
156
|
+
|
|
157
|
+
int oldVersion = [self intValueForExtensionKey:@"version"];
|
|
158
|
+
int newVersion = viewConnection->view->version;
|
|
159
|
+
|
|
160
|
+
if (oldVersion != newVersion)
|
|
161
|
+
{
|
|
162
|
+
if (![self populateView]) return NO;
|
|
163
|
+
|
|
164
|
+
[self setIntValue:newVersion forExtensionKey:@"version"];
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
if (!hasOldIsPersistent)
|
|
168
|
+
{
|
|
169
|
+
[self setBoolValue:isPersistent forExtensionKey:@"persistent"];
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
return YES;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* This method is called to prepare the transaction for use.
|
|
178
|
+
*
|
|
179
|
+
* Remember, an extension transaction is a very short lived object.
|
|
180
|
+
* Thus it stores the majority of its state within the extension connection (the parent).
|
|
181
|
+
*
|
|
182
|
+
* Return YES if completed successfully, or if already prepared.
|
|
183
|
+
* Return NO if some kind of error occured.
|
|
184
|
+
**/
|
|
185
|
+
- (BOOL)prepareIfNeeded
|
|
186
|
+
{
|
|
187
|
+
if (viewConnection->group_pagesMetadata_dict && viewConnection->pageKey_group_dict)
|
|
188
|
+
{
|
|
189
|
+
// Already prepared
|
|
190
|
+
return YES;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
// Enumerate over the page rows in the database, and populate our data structure.
|
|
194
|
+
// Each row has the following information:
|
|
195
|
+
//
|
|
196
|
+
// - group
|
|
197
|
+
// - pageKey
|
|
198
|
+
// - prevPageKey
|
|
199
|
+
//
|
|
200
|
+
// From this information we need to piece together the group_pagesMetadata_dict:
|
|
201
|
+
// - dict.key = group
|
|
202
|
+
// - dict.value = properly ordered array of YapDatabaseViewKeyPageMetadata objects
|
|
203
|
+
//
|
|
204
|
+
// To piece together the proper page order we make a temporary dictionary with each link in the linked-list.
|
|
205
|
+
// For example:
|
|
206
|
+
//
|
|
207
|
+
// pageC.prevPage = pageB => B -> C
|
|
208
|
+
// pageB.prevPage = pageA => A -> B
|
|
209
|
+
// pageA.prevPage = nil => NSNull -> A
|
|
210
|
+
//
|
|
211
|
+
// After the enumeration of all rows is complete, we can simply walk the linked list from the first page.
|
|
212
|
+
|
|
213
|
+
NSMutableDictionary *groupPageDict = [[NSMutableDictionary alloc] init];
|
|
214
|
+
NSMutableDictionary *groupOrderDict = [[NSMutableDictionary alloc] init];
|
|
215
|
+
|
|
216
|
+
__block BOOL error = NO;
|
|
217
|
+
|
|
218
|
+
if ([self isPersistentView])
|
|
219
|
+
{
|
|
220
|
+
sqlite3 *db = databaseTransaction->connection->db;
|
|
221
|
+
|
|
222
|
+
NSString *string = [NSString stringWithFormat:
|
|
223
|
+
@"SELECT \"pageKey\", \"group\", \"prevPageKey\", \"count\" FROM \"%@\";", [self pageTableName]];
|
|
224
|
+
|
|
225
|
+
|
|
226
|
+
sqlite3_stmt *statement = NULL;
|
|
227
|
+
|
|
228
|
+
int status = sqlite3_prepare_v2(db, [string UTF8String], -1, &statement, NULL);
|
|
229
|
+
if (status != SQLITE_OK)
|
|
230
|
+
{
|
|
231
|
+
YDBLogError(@"%@ (%@): Cannot create 'enumerate_stmt': %d %s",
|
|
232
|
+
THIS_METHOD, [self registeredName], status, sqlite3_errmsg(db));
|
|
233
|
+
return NO;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
unsigned int stepCount = 0;
|
|
237
|
+
|
|
238
|
+
while (sqlite3_step(statement) == SQLITE_ROW)
|
|
239
|
+
{
|
|
240
|
+
stepCount++;
|
|
241
|
+
|
|
242
|
+
const unsigned char *text0 = sqlite3_column_text(statement, 0);
|
|
243
|
+
int textSize0 = sqlite3_column_bytes(statement, 0);
|
|
244
|
+
|
|
245
|
+
const unsigned char *text1 = sqlite3_column_text(statement, 1);
|
|
246
|
+
int textSize1 = sqlite3_column_bytes(statement, 1);
|
|
247
|
+
|
|
248
|
+
const unsigned char *text2 = sqlite3_column_text(statement, 2);
|
|
249
|
+
int textSize2 = sqlite3_column_bytes(statement, 2);
|
|
250
|
+
|
|
251
|
+
int count = sqlite3_column_int(statement, 3);
|
|
252
|
+
|
|
253
|
+
NSString *pageKey = [[NSString alloc] initWithBytes:text0 length:textSize0 encoding:NSUTF8StringEncoding];
|
|
254
|
+
NSString *group = [[NSString alloc] initWithBytes:text1 length:textSize1 encoding:NSUTF8StringEncoding];
|
|
255
|
+
|
|
256
|
+
NSString *prevPageKey = nil;
|
|
257
|
+
if (textSize2 > 0)
|
|
258
|
+
prevPageKey = [[NSString alloc] initWithBytes:text2 length:textSize2 encoding:NSUTF8StringEncoding];
|
|
259
|
+
|
|
260
|
+
if (count >= 0)
|
|
261
|
+
{
|
|
262
|
+
YapDatabaseViewPageMetadata *pageMetadata = [[YapDatabaseViewPageMetadata alloc] init];
|
|
263
|
+
pageMetadata->pageKey = pageKey;
|
|
264
|
+
pageMetadata->group = group;
|
|
265
|
+
pageMetadata->prevPageKey = prevPageKey;
|
|
266
|
+
pageMetadata->count = (NSUInteger)count;
|
|
267
|
+
|
|
268
|
+
NSMutableDictionary *pageDict = [groupPageDict objectForKey:group];
|
|
269
|
+
if (pageDict == nil)
|
|
270
|
+
{
|
|
271
|
+
pageDict = [[NSMutableDictionary alloc] init];
|
|
272
|
+
[groupPageDict setObject:pageDict forKey:group];
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
NSMutableDictionary *orderDict = [groupOrderDict objectForKey:group];
|
|
276
|
+
if (orderDict == nil)
|
|
277
|
+
{
|
|
278
|
+
orderDict = [[NSMutableDictionary alloc] init];
|
|
279
|
+
[groupOrderDict setObject:orderDict forKey:group];
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
[pageDict setObject:pageMetadata forKey:pageKey];
|
|
283
|
+
|
|
284
|
+
if (prevPageKey)
|
|
285
|
+
[orderDict setObject:pageKey forKey:prevPageKey];
|
|
286
|
+
else
|
|
287
|
+
[orderDict setObject:pageKey forKey:[NSNull null]];
|
|
288
|
+
}
|
|
289
|
+
else
|
|
290
|
+
{
|
|
291
|
+
YDBLogWarn(@"%@ (%@): Encountered invalid count: %d", THIS_METHOD, [self registeredName], count);
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
YDBLogVerbose(@"Processing %u items from %@...", stepCount, [self pageTableName]);
|
|
296
|
+
|
|
297
|
+
YDBLogVerbose(@"groupPageDict: %@", groupPageDict);
|
|
298
|
+
YDBLogVerbose(@"groupOrderDict: %@", groupOrderDict);
|
|
299
|
+
|
|
300
|
+
if ((status != SQLITE_OK) && (status != SQLITE_DONE))
|
|
301
|
+
{
|
|
302
|
+
error = YES;
|
|
303
|
+
YDBLogError(@"%@ (%@): Error enumerating page table: %d %s",
|
|
304
|
+
THIS_METHOD, [self registeredName], status, sqlite3_errmsg(db));
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
sqlite3_finalize(statement);
|
|
308
|
+
}
|
|
309
|
+
else // if (isNonPersistentView)
|
|
310
|
+
{
|
|
311
|
+
[pageMetadataTableTransaction enumerateKeysAndObjectsWithBlock:^(id key, id obj, BOOL *stop) {
|
|
312
|
+
|
|
313
|
+
YapDatabaseViewPageMetadata *pageMetadata = [(YapDatabaseViewPageMetadata *)obj copy];
|
|
314
|
+
|
|
315
|
+
NSMutableDictionary *pageDict = [groupPageDict objectForKey:pageMetadata->group];
|
|
316
|
+
if (pageDict == nil)
|
|
317
|
+
{
|
|
318
|
+
pageDict = [[NSMutableDictionary alloc] init];
|
|
319
|
+
[groupPageDict setObject:pageDict forKey:pageMetadata->group];
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
NSMutableDictionary *orderDict = [groupOrderDict objectForKey:pageMetadata->group];
|
|
323
|
+
if (orderDict == nil)
|
|
324
|
+
{
|
|
325
|
+
orderDict = [[NSMutableDictionary alloc] init];
|
|
326
|
+
[groupOrderDict setObject:orderDict forKey:pageMetadata->group];
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
[pageDict setObject:pageMetadata forKey:pageMetadata->pageKey];
|
|
330
|
+
|
|
331
|
+
if (pageMetadata->prevPageKey)
|
|
332
|
+
[orderDict setObject:pageMetadata->pageKey forKey:pageMetadata->prevPageKey];
|
|
333
|
+
else
|
|
334
|
+
[orderDict setObject:pageMetadata->pageKey forKey:[NSNull null]];
|
|
335
|
+
}];
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
// Now that we have all the metadata about each page,
|
|
339
|
+
// it's time to piece them together in the proper order.
|
|
340
|
+
|
|
341
|
+
if (!error)
|
|
342
|
+
{
|
|
343
|
+
// Initialize ivars in viewConnection.
|
|
344
|
+
// We try not to do this before we know the table exists.
|
|
345
|
+
|
|
346
|
+
viewConnection->group_pagesMetadata_dict = [[NSMutableDictionary alloc] init];
|
|
347
|
+
viewConnection->pageKey_group_dict = [[NSMutableDictionary alloc] init];
|
|
348
|
+
|
|
349
|
+
// Enumerate over each group
|
|
350
|
+
|
|
351
|
+
[groupOrderDict enumerateKeysAndObjectsUsingBlock:^(id _group, id _orderDict, BOOL *stop) {
|
|
352
|
+
|
|
353
|
+
__unsafe_unretained NSString *group = (NSString *)_group;
|
|
354
|
+
__unsafe_unretained NSMutableDictionary *orderDict = (NSMutableDictionary *)_orderDict;
|
|
355
|
+
|
|
356
|
+
NSMutableDictionary *pageDict = [groupPageDict objectForKey:group];
|
|
357
|
+
|
|
358
|
+
// Walk the linked-list to stitch together the pages for this section.
|
|
359
|
+
//
|
|
360
|
+
// NSNull -> firstPageKey
|
|
361
|
+
// firstPageKey -> secondPageKey
|
|
362
|
+
// ...
|
|
363
|
+
// secondToLastPageKey -> lastPageKey
|
|
364
|
+
//
|
|
365
|
+
// And from the keys, we can get the actual pageMetadata using the pageDict.
|
|
366
|
+
|
|
367
|
+
NSMutableArray *pagesForGroup = [[NSMutableArray alloc] initWithCapacity:[pageDict count]];
|
|
368
|
+
[viewConnection->group_pagesMetadata_dict setObject:pagesForGroup forKey:group];
|
|
369
|
+
|
|
370
|
+
NSString *pageKey = [orderDict objectForKey:[NSNull null]];
|
|
371
|
+
while (pageKey)
|
|
372
|
+
{
|
|
373
|
+
[viewConnection->pageKey_group_dict setObject:group forKey:pageKey];
|
|
374
|
+
|
|
375
|
+
YapDatabaseViewPageMetadata *pageMetadata = [pageDict objectForKey:pageKey];
|
|
376
|
+
if (pageMetadata == nil)
|
|
377
|
+
{
|
|
378
|
+
YDBLogError(@"%@ (%@): Invalid key ordering detected in group(%@)",
|
|
379
|
+
THIS_METHOD, [self registeredName], group);
|
|
380
|
+
|
|
381
|
+
error = YES;
|
|
382
|
+
break;
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
[pagesForGroup addObject:pageMetadata];
|
|
386
|
+
pageKey = [orderDict objectForKey:pageKey];
|
|
387
|
+
|
|
388
|
+
if ([pagesForGroup count] > [orderDict count])
|
|
389
|
+
{
|
|
390
|
+
YDBLogError(@"%@ (%@): Circular key ordering detected in group(%@)",
|
|
391
|
+
THIS_METHOD, [self registeredName], group);
|
|
392
|
+
|
|
393
|
+
error = YES;
|
|
394
|
+
break;
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
// Validate data for this section
|
|
399
|
+
|
|
400
|
+
if (!error && ([pagesForGroup count] != [orderDict count]))
|
|
401
|
+
{
|
|
402
|
+
YDBLogError(@"%@ (%@): Missing key page(s) in group(%@)",
|
|
403
|
+
THIS_METHOD, [self registeredName], group);
|
|
404
|
+
|
|
405
|
+
error = YES;
|
|
406
|
+
}
|
|
407
|
+
}];
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
// Validate data
|
|
411
|
+
|
|
412
|
+
if (error)
|
|
413
|
+
{
|
|
414
|
+
// If there was an error opening the view, we need to reset the ivars to nil.
|
|
415
|
+
// These are checked at the beginning of this method as a shortcut.
|
|
416
|
+
|
|
417
|
+
viewConnection->group_pagesMetadata_dict = nil;
|
|
418
|
+
viewConnection->pageKey_group_dict = nil;
|
|
419
|
+
}
|
|
420
|
+
else
|
|
421
|
+
{
|
|
422
|
+
YDBLogVerbose(@"viewConnection->group_pagesMetadata_dict: %@", viewConnection->group_pagesMetadata_dict);
|
|
423
|
+
YDBLogVerbose(@"viewConnection->pageKey_group_dict: %@", viewConnection->pageKey_group_dict);
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
return !error;
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
- (void)dropTablesForOldClassVersion:(int)oldClassVersion
|
|
430
|
+
{
|
|
431
|
+
if (oldClassVersion == 1)
|
|
432
|
+
{
|
|
433
|
+
// In version 2, we switched from 'view_name_key' to 'view_name_map'.
|
|
434
|
+
// The old table stored key->pageKey mappings.
|
|
435
|
+
// The new table stores rowid->pageKey mappings.
|
|
436
|
+
//
|
|
437
|
+
// So we can drop the old table.
|
|
438
|
+
|
|
439
|
+
sqlite3 *db = databaseTransaction->connection->db;
|
|
440
|
+
|
|
441
|
+
NSString *keyTableName = [NSString stringWithFormat:@"view_%@_key", [self registeredName]];
|
|
442
|
+
|
|
443
|
+
NSString *dropKeyTable = [NSString stringWithFormat:@"DROP TABLE IF EXISTS \"%@\";", keyTableName];
|
|
444
|
+
|
|
445
|
+
int status = sqlite3_exec(db, [dropKeyTable UTF8String], NULL, NULL, NULL);
|
|
446
|
+
if (status != SQLITE_OK)
|
|
447
|
+
{
|
|
448
|
+
YDBLogError(@"%@ - Failed dropping key table (%@): %d %s",
|
|
449
|
+
THIS_METHOD, keyTableName, status, sqlite3_errmsg(db));
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
if (oldClassVersion == 1 || oldClassVersion == 2)
|
|
454
|
+
{
|
|
455
|
+
// In version 3, we changed the columns of the 'view_name_page' table.
|
|
456
|
+
// The old table stored all metadata in a blob.
|
|
457
|
+
// The new table stores each metadata item in its own column.
|
|
458
|
+
//
|
|
459
|
+
// This new layout reduces the amount of data we have to write to the table.
|
|
460
|
+
|
|
461
|
+
sqlite3 *db = databaseTransaction->connection->db;
|
|
462
|
+
|
|
463
|
+
NSString *dropPageTable = [NSString stringWithFormat:@"DROP TABLE IF EXISTS \"%@\";", [self pageTableName]];
|
|
464
|
+
|
|
465
|
+
int status = sqlite3_exec(db, [dropPageTable UTF8String], NULL, NULL, NULL);
|
|
466
|
+
if (status != SQLITE_OK)
|
|
467
|
+
{
|
|
468
|
+
YDBLogError(@"%@ - Failed dropping old page table (%@): %d %s",
|
|
469
|
+
THIS_METHOD, dropPageTable, status, sqlite3_errmsg(db));
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
- (BOOL)createTables
|
|
475
|
+
{
|
|
476
|
+
if ([self isPersistentView])
|
|
477
|
+
{
|
|
478
|
+
sqlite3 *db = databaseTransaction->connection->db;
|
|
479
|
+
|
|
480
|
+
NSString *mapTableName = [self mapTableName];
|
|
481
|
+
NSString *pageTableName = [self pageTableName];
|
|
482
|
+
|
|
483
|
+
YDBLogVerbose(@"Creating view tables for registeredName(%@): %@, %@",
|
|
484
|
+
[self registeredName], mapTableName, pageTableName);
|
|
485
|
+
|
|
486
|
+
NSString *createMapTable = [NSString stringWithFormat:
|
|
487
|
+
@"CREATE TABLE IF NOT EXISTS \"%@\""
|
|
488
|
+
@" (\"rowid\" INTEGER PRIMARY KEY,"
|
|
489
|
+
@" \"pageKey\" CHAR NOT NULL"
|
|
490
|
+
@" );", mapTableName];
|
|
491
|
+
|
|
492
|
+
NSString *createPageTable = [NSString stringWithFormat:
|
|
493
|
+
@"CREATE TABLE IF NOT EXISTS \"%@\""
|
|
494
|
+
@" (\"pageKey\" CHAR NOT NULL PRIMARY KEY,"
|
|
495
|
+
@" \"group\" CHAR NOT NULL,"
|
|
496
|
+
@" \"prevPageKey\" CHAR,"
|
|
497
|
+
@" \"count\" INTEGER,"
|
|
498
|
+
@" \"data\" BLOB"
|
|
499
|
+
@" );", pageTableName];
|
|
500
|
+
|
|
501
|
+
int status;
|
|
502
|
+
|
|
503
|
+
status = sqlite3_exec(db, [createMapTable UTF8String], NULL, NULL, NULL);
|
|
504
|
+
if (status != SQLITE_OK)
|
|
505
|
+
{
|
|
506
|
+
YDBLogError(@"%@ - Failed creating map table (%@): %d %s",
|
|
507
|
+
THIS_METHOD, mapTableName, status, sqlite3_errmsg(db));
|
|
508
|
+
return NO;
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
status = sqlite3_exec(db, [createPageTable UTF8String], NULL, NULL, NULL);
|
|
512
|
+
if (status != SQLITE_OK)
|
|
513
|
+
{
|
|
514
|
+
YDBLogError(@"%@ - Failed creating page table (%@): %d %s",
|
|
515
|
+
THIS_METHOD, pageTableName, status, sqlite3_errmsg(db));
|
|
516
|
+
return NO;
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
return YES;
|
|
520
|
+
}
|
|
521
|
+
else // if (isNonPersistentView)
|
|
522
|
+
{
|
|
523
|
+
NSString *mapTableName = [self mapTableName];
|
|
524
|
+
NSString *pageTableName = [self pageTableName];
|
|
525
|
+
NSString *pageMetadataTableName = [self pageMetadataTableName];
|
|
526
|
+
|
|
527
|
+
YapMemoryTable *mapTable = [[YapMemoryTable alloc] initWithKeyClass:[NSNumber class]];
|
|
528
|
+
YapMemoryTable *pageTable = [[YapMemoryTable alloc] initWithKeyClass:[NSString class]];
|
|
529
|
+
YapMemoryTable *pageMetadataTable = [[YapMemoryTable alloc] initWithKeyClass:[NSString class]];
|
|
530
|
+
|
|
531
|
+
if (![databaseTransaction->connection registerTable:mapTable withName:mapTableName])
|
|
532
|
+
{
|
|
533
|
+
YDBLogError(@"%@ - Failed registering map table", THIS_METHOD);
|
|
534
|
+
return NO;
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
if (![databaseTransaction->connection registerTable:pageTable withName:pageTableName])
|
|
538
|
+
{
|
|
539
|
+
YDBLogError(@"%@ - Failed registering page table", THIS_METHOD);
|
|
540
|
+
return NO;
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
if (![databaseTransaction->connection registerTable:pageMetadataTable withName:pageMetadataTableName])
|
|
544
|
+
{
|
|
545
|
+
YDBLogError(@"%@ - Failed registering pageMetadata table", THIS_METHOD);
|
|
546
|
+
return NO;
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
mapTableTransaction = [databaseTransaction memoryTableTransaction:mapTableName];
|
|
550
|
+
pageTableTransaction = [databaseTransaction memoryTableTransaction:pageTableName];
|
|
551
|
+
pageMetadataTableTransaction = [databaseTransaction memoryTableTransaction:pageMetadataTableName];
|
|
552
|
+
|
|
553
|
+
return YES;
|
|
554
|
+
}
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
- (BOOL)populateView
|
|
558
|
+
{
|
|
559
|
+
// Remove everything from the database
|
|
560
|
+
|
|
561
|
+
[self removeAllRowids];
|
|
562
|
+
|
|
563
|
+
// Initialize ivars
|
|
564
|
+
|
|
565
|
+
viewConnection->group_pagesMetadata_dict = [[NSMutableDictionary alloc] init];
|
|
566
|
+
viewConnection->pageKey_group_dict = [[NSMutableDictionary alloc] init];
|
|
567
|
+
|
|
568
|
+
// Enumerate the existing rows in the database and populate the view
|
|
569
|
+
|
|
570
|
+
__unsafe_unretained YapDatabaseView *view = viewConnection->view;
|
|
571
|
+
|
|
572
|
+
BOOL groupingNeedsObject = view->groupingBlockType == YapDatabaseViewBlockTypeWithObject ||
|
|
573
|
+
view->groupingBlockType == YapDatabaseViewBlockTypeWithRow;
|
|
574
|
+
|
|
575
|
+
BOOL groupingNeedsMetadata = view->groupingBlockType == YapDatabaseViewBlockTypeWithMetadata ||
|
|
576
|
+
view->groupingBlockType == YapDatabaseViewBlockTypeWithRow;
|
|
577
|
+
|
|
578
|
+
BOOL sortingNeedsObject = view->sortingBlockType == YapDatabaseViewBlockTypeWithObject ||
|
|
579
|
+
view->sortingBlockType == YapDatabaseViewBlockTypeWithRow;
|
|
580
|
+
|
|
581
|
+
BOOL sortingNeedsMetadata = view->sortingBlockType == YapDatabaseViewBlockTypeWithMetadata ||
|
|
582
|
+
view->sortingBlockType == YapDatabaseViewBlockTypeWithRow;
|
|
583
|
+
|
|
584
|
+
BOOL needsObject = groupingNeedsObject || sortingNeedsObject;
|
|
585
|
+
BOOL needsMetadata = groupingNeedsMetadata || sortingNeedsMetadata;
|
|
586
|
+
|
|
587
|
+
NSString *(^getGroup)(NSString *collection, NSString *key, id object, id metadata);
|
|
588
|
+
getGroup = ^(NSString *collection, NSString *key, id object, id metadata){
|
|
589
|
+
|
|
590
|
+
if (view->groupingBlockType == YapDatabaseViewBlockTypeWithKey)
|
|
591
|
+
{
|
|
592
|
+
__unsafe_unretained YapDatabaseViewGroupingWithKeyBlock groupingBlock =
|
|
593
|
+
(YapDatabaseViewGroupingWithKeyBlock)view->groupingBlock;
|
|
594
|
+
|
|
595
|
+
return groupingBlock(collection, key);
|
|
596
|
+
}
|
|
597
|
+
else if (view->groupingBlockType == YapDatabaseViewBlockTypeWithObject)
|
|
598
|
+
{
|
|
599
|
+
__unsafe_unretained YapDatabaseViewGroupingWithObjectBlock groupingBlock =
|
|
600
|
+
(YapDatabaseViewGroupingWithObjectBlock)view->groupingBlock;
|
|
601
|
+
|
|
602
|
+
return groupingBlock(collection, key, object);
|
|
603
|
+
}
|
|
604
|
+
else if (view->groupingBlockType == YapDatabaseViewBlockTypeWithMetadata)
|
|
605
|
+
{
|
|
606
|
+
__unsafe_unretained YapDatabaseViewGroupingWithMetadataBlock groupingBlock =
|
|
607
|
+
(YapDatabaseViewGroupingWithMetadataBlock)view->groupingBlock;
|
|
608
|
+
|
|
609
|
+
return groupingBlock(collection, key, metadata);
|
|
610
|
+
}
|
|
611
|
+
else
|
|
612
|
+
{
|
|
613
|
+
__unsafe_unretained YapDatabaseViewGroupingWithRowBlock groupingBlock =
|
|
614
|
+
(YapDatabaseViewGroupingWithRowBlock)view->groupingBlock;
|
|
615
|
+
|
|
616
|
+
return groupingBlock(collection, key, object, metadata);
|
|
617
|
+
}
|
|
618
|
+
};
|
|
619
|
+
|
|
620
|
+
int flags = (YapDatabaseViewChangedObject | YapDatabaseViewChangedMetadata);
|
|
621
|
+
|
|
622
|
+
if (needsObject && needsMetadata)
|
|
623
|
+
{
|
|
624
|
+
if (groupingNeedsObject || groupingNeedsMetadata)
|
|
625
|
+
{
|
|
626
|
+
void (^block)(int64_t rowid, NSString *collection, NSString *key, id object, id metadata, BOOL *stop);
|
|
627
|
+
block = ^(int64_t rowid, NSString *collection, NSString *key, id object, id metadata, BOOL *stop){
|
|
628
|
+
|
|
629
|
+
NSString *group = getGroup(collection, key, object, metadata);
|
|
630
|
+
if (group)
|
|
631
|
+
{
|
|
632
|
+
YapCollectionKey *collectionKey = [[YapCollectionKey alloc] initWithCollection:collection key:key];
|
|
633
|
+
|
|
634
|
+
[self insertRowid:rowid
|
|
635
|
+
collectionKey:collectionKey
|
|
636
|
+
object:object
|
|
637
|
+
metadata:metadata
|
|
638
|
+
inGroup:group withChanges:flags isNew:YES];
|
|
639
|
+
}
|
|
640
|
+
};
|
|
641
|
+
|
|
642
|
+
NSSet *allowedCollections = viewConnection->view->options.allowedCollections;
|
|
643
|
+
if (allowedCollections)
|
|
644
|
+
{
|
|
645
|
+
[databaseTransaction _enumerateRowsInCollections:[allowedCollections allObjects] usingBlock:block];
|
|
646
|
+
}
|
|
647
|
+
else
|
|
648
|
+
{
|
|
649
|
+
[databaseTransaction _enumerateRowsInAllCollectionsUsingBlock:block];
|
|
650
|
+
}
|
|
651
|
+
}
|
|
652
|
+
else
|
|
653
|
+
{
|
|
654
|
+
// Optimization: Grouping doesn't require the object or metadata.
|
|
655
|
+
// So we can skip the deserialization step for any rows not in the view.
|
|
656
|
+
|
|
657
|
+
__block NSString *group = nil;
|
|
658
|
+
|
|
659
|
+
BOOL (^filter)(int64_t rowid, NSString *collection, NSString *key);
|
|
660
|
+
filter = ^BOOL(int64_t rowid, NSString *collection, NSString *key) {
|
|
661
|
+
|
|
662
|
+
group = getGroup(collection, key, nil, nil);
|
|
663
|
+
return (group != nil);
|
|
664
|
+
};
|
|
665
|
+
|
|
666
|
+
void (^block)(int64_t rowid, NSString *collection, NSString *key, id object, id metadata, BOOL *stop);
|
|
667
|
+
block = ^(int64_t rowid, NSString *collection, NSString *key, id object, id metadata, BOOL *stop){
|
|
668
|
+
|
|
669
|
+
YapCollectionKey *collectionKey = [[YapCollectionKey alloc] initWithCollection:collection key:key];
|
|
670
|
+
|
|
671
|
+
[self insertRowid:rowid
|
|
672
|
+
collectionKey:collectionKey
|
|
673
|
+
object:object
|
|
674
|
+
metadata:metadata
|
|
675
|
+
inGroup:group withChanges:flags isNew:YES];
|
|
676
|
+
};
|
|
677
|
+
|
|
678
|
+
NSSet *allowedCollections = viewConnection->view->options.allowedCollections;
|
|
679
|
+
if (allowedCollections)
|
|
680
|
+
{
|
|
681
|
+
[databaseTransaction _enumerateRowsInCollections:[allowedCollections allObjects]
|
|
682
|
+
usingBlock:block
|
|
683
|
+
withFilter:filter];
|
|
684
|
+
}
|
|
685
|
+
else
|
|
686
|
+
{
|
|
687
|
+
[databaseTransaction _enumerateRowsInAllCollectionsUsingBlock:block withFilter:filter];
|
|
688
|
+
}
|
|
689
|
+
}
|
|
690
|
+
}
|
|
691
|
+
else if (needsObject && !needsMetadata)
|
|
692
|
+
{
|
|
693
|
+
if (groupingNeedsObject)
|
|
694
|
+
{
|
|
695
|
+
void (^block)(int64_t rowid, NSString *collection, NSString *key, id object, BOOL *stop);
|
|
696
|
+
block = ^(int64_t rowid, NSString *collection, NSString *key, id object, BOOL *stop){
|
|
697
|
+
|
|
698
|
+
NSString *group = getGroup(collection, key, object, nil);
|
|
699
|
+
if (group)
|
|
700
|
+
{
|
|
701
|
+
YapCollectionKey *collectionKey = [[YapCollectionKey alloc] initWithCollection:collection key:key];
|
|
702
|
+
|
|
703
|
+
[self insertRowid:rowid
|
|
704
|
+
collectionKey:collectionKey
|
|
705
|
+
object:object
|
|
706
|
+
metadata:nil
|
|
707
|
+
inGroup:group withChanges:flags isNew:YES];
|
|
708
|
+
}
|
|
709
|
+
};
|
|
710
|
+
|
|
711
|
+
NSSet *allowedCollections = viewConnection->view->options.allowedCollections;
|
|
712
|
+
if (allowedCollections)
|
|
713
|
+
{
|
|
714
|
+
[databaseTransaction _enumerateKeysAndObjectsInCollections:[allowedCollections allObjects]
|
|
715
|
+
usingBlock:block];
|
|
716
|
+
}
|
|
717
|
+
else
|
|
718
|
+
{
|
|
719
|
+
[databaseTransaction _enumerateKeysAndObjectsInAllCollectionsUsingBlock:block];
|
|
720
|
+
}
|
|
721
|
+
}
|
|
722
|
+
else
|
|
723
|
+
{
|
|
724
|
+
// Optimization: Grouping doesn't require the object.
|
|
725
|
+
// So we can skip the deserialization step for any rows not in the view.
|
|
726
|
+
|
|
727
|
+
__block NSString *group = nil;
|
|
728
|
+
|
|
729
|
+
BOOL (^filter)(int64_t rowid, NSString *collection, NSString *key);
|
|
730
|
+
filter = ^BOOL(int64_t rowid, NSString *collection, NSString *key) {
|
|
731
|
+
|
|
732
|
+
group = getGroup(collection, key, nil, nil);
|
|
733
|
+
return (group != nil);
|
|
734
|
+
};
|
|
735
|
+
|
|
736
|
+
void (^block)(int64_t rowid, NSString *collection, NSString *key, id object, BOOL *stop);
|
|
737
|
+
block = ^(int64_t rowid, NSString *collection, NSString *key, id object, BOOL *stop){
|
|
738
|
+
|
|
739
|
+
YapCollectionKey *collectionKey = [[YapCollectionKey alloc] initWithCollection:collection key:key];
|
|
740
|
+
|
|
741
|
+
[self insertRowid:rowid
|
|
742
|
+
collectionKey:collectionKey
|
|
743
|
+
object:object
|
|
744
|
+
metadata:nil
|
|
745
|
+
inGroup:group withChanges:flags isNew:YES];
|
|
746
|
+
};
|
|
747
|
+
|
|
748
|
+
NSSet *allowedCollections = viewConnection->view->options.allowedCollections;
|
|
749
|
+
if (allowedCollections)
|
|
750
|
+
{
|
|
751
|
+
[databaseTransaction _enumerateKeysAndObjectsInCollections:[allowedCollections allObjects]
|
|
752
|
+
usingBlock:block
|
|
753
|
+
withFilter:filter];
|
|
754
|
+
}
|
|
755
|
+
else
|
|
756
|
+
{
|
|
757
|
+
[databaseTransaction _enumerateKeysAndObjectsInAllCollectionsUsingBlock:block withFilter:filter];
|
|
758
|
+
}
|
|
759
|
+
}
|
|
760
|
+
}
|
|
761
|
+
else if (!needsObject && needsMetadata)
|
|
762
|
+
{
|
|
763
|
+
if (groupingNeedsMetadata)
|
|
764
|
+
{
|
|
765
|
+
void (^block)(int64_t rowid, NSString *collection, NSString *key, id metadata, BOOL *stop);
|
|
766
|
+
block = ^(int64_t rowid, NSString *collection, NSString *key, id metadata, BOOL *stop){
|
|
767
|
+
|
|
768
|
+
NSString *group = getGroup(collection, key, nil, metadata);
|
|
769
|
+
if (group)
|
|
770
|
+
{
|
|
771
|
+
YapCollectionKey *collectionKey = [[YapCollectionKey alloc] initWithCollection:collection key:key];
|
|
772
|
+
|
|
773
|
+
[self insertRowid:rowid
|
|
774
|
+
collectionKey:collectionKey
|
|
775
|
+
object:nil
|
|
776
|
+
metadata:metadata
|
|
777
|
+
inGroup:group withChanges:flags isNew:YES];
|
|
778
|
+
}
|
|
779
|
+
};
|
|
780
|
+
|
|
781
|
+
|
|
782
|
+
NSSet *allowedCollections = viewConnection->view->options.allowedCollections;
|
|
783
|
+
if (allowedCollections)
|
|
784
|
+
{
|
|
785
|
+
[databaseTransaction _enumerateKeysAndMetadataInCollections:[allowedCollections allObjects]
|
|
786
|
+
usingBlock:block];
|
|
787
|
+
}
|
|
788
|
+
else
|
|
789
|
+
{
|
|
790
|
+
[databaseTransaction _enumerateKeysAndMetadataInAllCollectionsUsingBlock:block];
|
|
791
|
+
}
|
|
792
|
+
}
|
|
793
|
+
else
|
|
794
|
+
{
|
|
795
|
+
// Optimization: Grouping doesn't require the metadata.
|
|
796
|
+
// So we can skip the deserialization step for any rows not in the view.
|
|
797
|
+
|
|
798
|
+
__block NSString *group = nil;
|
|
799
|
+
|
|
800
|
+
BOOL (^filter)(int64_t rowid, NSString *collection, NSString *key);
|
|
801
|
+
filter = ^BOOL(int64_t rowid, NSString *collection, NSString *key){
|
|
802
|
+
|
|
803
|
+
group = getGroup(collection, key, nil, nil);
|
|
804
|
+
return (group != nil);
|
|
805
|
+
};
|
|
806
|
+
|
|
807
|
+
void (^block)(int64_t rowid, NSString *collection, NSString *key, id metadata, BOOL *stop);
|
|
808
|
+
block = ^(int64_t rowid, NSString *collection, NSString *key, id metadata, BOOL *stop){
|
|
809
|
+
|
|
810
|
+
YapCollectionKey *collectionKey = [[YapCollectionKey alloc] initWithCollection:collection key:key];
|
|
811
|
+
|
|
812
|
+
[self insertRowid:rowid
|
|
813
|
+
collectionKey:collectionKey
|
|
814
|
+
object:nil
|
|
815
|
+
metadata:metadata
|
|
816
|
+
inGroup:group withChanges:flags isNew:YES];
|
|
817
|
+
};
|
|
818
|
+
|
|
819
|
+
NSSet *allowedCollections = viewConnection->view->options.allowedCollections;
|
|
820
|
+
if (allowedCollections)
|
|
821
|
+
{
|
|
822
|
+
[databaseTransaction _enumerateKeysAndMetadataInCollections:[allowedCollections allObjects]
|
|
823
|
+
usingBlock:block
|
|
824
|
+
withFilter:filter];
|
|
825
|
+
}
|
|
826
|
+
else
|
|
827
|
+
{
|
|
828
|
+
[databaseTransaction _enumerateKeysAndMetadataInAllCollectionsUsingBlock:block withFilter:filter];
|
|
829
|
+
}
|
|
830
|
+
}
|
|
831
|
+
}
|
|
832
|
+
else // if (!needsObject && !needsMetadata)
|
|
833
|
+
{
|
|
834
|
+
void (^block)(int64_t rowid, NSString *collection, NSString *key, BOOL *stop);
|
|
835
|
+
block = ^(int64_t rowid, NSString *collection, NSString *key, BOOL *stop){
|
|
836
|
+
|
|
837
|
+
NSString *group = getGroup(collection, key, nil, nil);
|
|
838
|
+
if (group)
|
|
839
|
+
{
|
|
840
|
+
YapCollectionKey *collectionKey = [[YapCollectionKey alloc] initWithCollection:collection key:key];
|
|
841
|
+
|
|
842
|
+
[self insertRowid:rowid
|
|
843
|
+
collectionKey:collectionKey
|
|
844
|
+
object:nil
|
|
845
|
+
metadata:nil
|
|
846
|
+
inGroup:group withChanges:flags isNew:YES];
|
|
847
|
+
}
|
|
848
|
+
};
|
|
849
|
+
|
|
850
|
+
NSSet *allowedCollections = viewConnection->view->options.allowedCollections;
|
|
851
|
+
if (allowedCollections)
|
|
852
|
+
{
|
|
853
|
+
[databaseTransaction _enumerateKeysInCollections:[allowedCollections allObjects] usingBlock:block];
|
|
854
|
+
}
|
|
855
|
+
else
|
|
856
|
+
{
|
|
857
|
+
[databaseTransaction _enumerateKeysInAllCollectionsUsingBlock:block];
|
|
858
|
+
}
|
|
859
|
+
}
|
|
860
|
+
|
|
861
|
+
return YES;
|
|
862
|
+
}
|
|
863
|
+
|
|
864
|
+
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
865
|
+
#pragma mark Accessors
|
|
866
|
+
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
867
|
+
|
|
868
|
+
/**
|
|
869
|
+
* Required override method from YapDatabaseExtensionTransaction.
|
|
870
|
+
**/
|
|
871
|
+
- (YapDatabaseReadTransaction *)databaseTransaction
|
|
872
|
+
{
|
|
873
|
+
return databaseTransaction;
|
|
874
|
+
}
|
|
875
|
+
|
|
876
|
+
/**
|
|
877
|
+
* Required override method from YapDatabaseExtensionTransaction.
|
|
878
|
+
**/
|
|
879
|
+
- (YapDatabaseExtensionConnection *)extensionConnection
|
|
880
|
+
{
|
|
881
|
+
return viewConnection;
|
|
882
|
+
}
|
|
883
|
+
|
|
884
|
+
- (NSString *)registeredName
|
|
885
|
+
{
|
|
886
|
+
return [viewConnection->view registeredName];
|
|
887
|
+
}
|
|
888
|
+
|
|
889
|
+
- (NSString *)mapTableName
|
|
890
|
+
{
|
|
891
|
+
return [viewConnection->view mapTableName];
|
|
892
|
+
}
|
|
893
|
+
|
|
894
|
+
- (NSString *)pageTableName
|
|
895
|
+
{
|
|
896
|
+
return [viewConnection->view pageTableName];
|
|
897
|
+
}
|
|
898
|
+
|
|
899
|
+
- (NSString *)pageMetadataTableName
|
|
900
|
+
{
|
|
901
|
+
return [viewConnection->view pageMetadataTableName];
|
|
902
|
+
}
|
|
903
|
+
|
|
904
|
+
- (BOOL)isPersistentView
|
|
905
|
+
{
|
|
906
|
+
return viewConnection->view->options.isPersistent;
|
|
907
|
+
}
|
|
908
|
+
|
|
909
|
+
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
910
|
+
#pragma mark Serialization & Deserialization
|
|
911
|
+
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
912
|
+
|
|
913
|
+
- (NSData *)serializePage:(YapDatabaseViewPage *)page
|
|
914
|
+
{
|
|
915
|
+
return [page serialize];
|
|
916
|
+
}
|
|
917
|
+
|
|
918
|
+
- (YapDatabaseViewPage *)deserializePage:(NSData *)data
|
|
919
|
+
{
|
|
920
|
+
YapDatabaseViewPage *page = [[YapDatabaseViewPage alloc] init];
|
|
921
|
+
[page deserialize:data];
|
|
922
|
+
|
|
923
|
+
return page;
|
|
924
|
+
}
|
|
925
|
+
|
|
926
|
+
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
927
|
+
#pragma mark Utilities
|
|
928
|
+
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
929
|
+
|
|
930
|
+
- (NSString *)generatePageKey
|
|
931
|
+
{
|
|
932
|
+
NSString *key = nil;
|
|
933
|
+
|
|
934
|
+
CFUUIDRef uuid = CFUUIDCreate(NULL);
|
|
935
|
+
if (uuid)
|
|
936
|
+
{
|
|
937
|
+
key = (__bridge_transfer NSString *)CFUUIDCreateString(NULL, uuid);
|
|
938
|
+
CFRelease(uuid);
|
|
939
|
+
}
|
|
940
|
+
|
|
941
|
+
return key;
|
|
942
|
+
}
|
|
943
|
+
|
|
944
|
+
/**
|
|
945
|
+
* If the given rowid is in the view, returns the associated pageKey.
|
|
946
|
+
*
|
|
947
|
+
* This method will use the cache(s) if possible.
|
|
948
|
+
* Otherwise it will lookup the value in the map table.
|
|
949
|
+
**/
|
|
950
|
+
- (NSString *)pageKeyForRowid:(int64_t)rowid
|
|
951
|
+
{
|
|
952
|
+
NSString *pageKey = nil;
|
|
953
|
+
NSNumber *rowidNumber = @(rowid);
|
|
954
|
+
|
|
955
|
+
// Check dirty cache & clean cache
|
|
956
|
+
|
|
957
|
+
pageKey = [viewConnection->dirtyMaps objectForKey:rowidNumber];
|
|
958
|
+
if (pageKey)
|
|
959
|
+
{
|
|
960
|
+
if ((__bridge void *)pageKey == (__bridge void *)[NSNull null])
|
|
961
|
+
return nil;
|
|
962
|
+
else
|
|
963
|
+
return pageKey;
|
|
964
|
+
}
|
|
965
|
+
|
|
966
|
+
pageKey = [viewConnection->mapCache objectForKey:rowidNumber];
|
|
967
|
+
if (pageKey)
|
|
968
|
+
{
|
|
969
|
+
if ((__bridge void *)pageKey == (__bridge void *)[NSNull null])
|
|
970
|
+
return nil;
|
|
971
|
+
else
|
|
972
|
+
return pageKey;
|
|
973
|
+
}
|
|
974
|
+
|
|
975
|
+
// Otherwise pull from the database
|
|
976
|
+
|
|
977
|
+
if ([self isPersistentView])
|
|
978
|
+
{
|
|
979
|
+
sqlite3_stmt *statement = [viewConnection mapTable_getPageKeyForRowidStatement];
|
|
980
|
+
if (statement == NULL)
|
|
981
|
+
return nil;
|
|
982
|
+
|
|
983
|
+
// SELECT "pageKey" FROM "mapTableName" WHERE "rowid" = ? ;
|
|
984
|
+
|
|
985
|
+
sqlite3_bind_int64(statement, 1, rowid);
|
|
986
|
+
|
|
987
|
+
int status = sqlite3_step(statement);
|
|
988
|
+
if (status == SQLITE_ROW)
|
|
989
|
+
{
|
|
990
|
+
const unsigned char *text = sqlite3_column_text(statement, 0);
|
|
991
|
+
int textSize = sqlite3_column_bytes(statement, 0);
|
|
992
|
+
|
|
993
|
+
pageKey = [[NSString alloc] initWithBytes:text length:textSize encoding:NSUTF8StringEncoding];
|
|
994
|
+
}
|
|
995
|
+
else if (status == SQLITE_ERROR)
|
|
996
|
+
{
|
|
997
|
+
YDBLogError(@"%@ (%@): Error executing statement: %d %s",
|
|
998
|
+
THIS_METHOD, [self registeredName],
|
|
999
|
+
status, sqlite3_errmsg(databaseTransaction->connection->db));
|
|
1000
|
+
}
|
|
1001
|
+
|
|
1002
|
+
sqlite3_clear_bindings(statement);
|
|
1003
|
+
sqlite3_reset(statement);
|
|
1004
|
+
}
|
|
1005
|
+
else // if (isNonPersistentView)
|
|
1006
|
+
{
|
|
1007
|
+
pageKey = [mapTableTransaction objectForKey:rowidNumber];
|
|
1008
|
+
}
|
|
1009
|
+
|
|
1010
|
+
if (pageKey)
|
|
1011
|
+
[viewConnection->mapCache setObject:pageKey forKey:rowidNumber];
|
|
1012
|
+
else
|
|
1013
|
+
[viewConnection->mapCache setObject:[NSNull null] forKey:rowidNumber];
|
|
1014
|
+
|
|
1015
|
+
return pageKey;
|
|
1016
|
+
}
|
|
1017
|
+
|
|
1018
|
+
/**
|
|
1019
|
+
* This method looks up a whole bunch of pageKeys using only a few queries.
|
|
1020
|
+
*
|
|
1021
|
+
* @param input
|
|
1022
|
+
* A dictionary of the form: @{
|
|
1023
|
+
* @(rowid) = collectionKey, ...
|
|
1024
|
+
* }
|
|
1025
|
+
*
|
|
1026
|
+
* @return A dictionary of the form: @{
|
|
1027
|
+
* pageKey = @{ @(rowid) = collectionKey, ... }
|
|
1028
|
+
* }
|
|
1029
|
+
**/
|
|
1030
|
+
- (NSDictionary *)pageKeysForRowids:(NSArray *)rowids withKeyMappings:(NSDictionary *)keyMappings
|
|
1031
|
+
{
|
|
1032
|
+
NSUInteger count = [rowids count];
|
|
1033
|
+
if (count == 0)
|
|
1034
|
+
{
|
|
1035
|
+
return [NSDictionary dictionary];
|
|
1036
|
+
}
|
|
1037
|
+
|
|
1038
|
+
NSMutableDictionary *result = [NSMutableDictionary dictionaryWithCapacity:count];
|
|
1039
|
+
|
|
1040
|
+
if ([self isPersistentView])
|
|
1041
|
+
{
|
|
1042
|
+
sqlite3 *db = databaseTransaction->connection->db;
|
|
1043
|
+
|
|
1044
|
+
// Sqlite has an upper bound on the number of host parameters that may be used in a single query.
|
|
1045
|
+
// We need to watch out for this in case a large array of keys is passed.
|
|
1046
|
+
|
|
1047
|
+
NSUInteger maxHostParams = (NSUInteger) sqlite3_limit(db, SQLITE_LIMIT_VARIABLE_NUMBER, -1);
|
|
1048
|
+
|
|
1049
|
+
NSUInteger offset = 0;
|
|
1050
|
+
do
|
|
1051
|
+
{
|
|
1052
|
+
NSUInteger left = count - offset;
|
|
1053
|
+
NSUInteger numHostParams = MIN(left, maxHostParams);
|
|
1054
|
+
|
|
1055
|
+
// SELECT "rowid", "pageKey" FROM "mapTableName" WHERE "rowid" IN (?, ?, ...);
|
|
1056
|
+
|
|
1057
|
+
NSUInteger capacity = 50 + (numHostParams * 3);
|
|
1058
|
+
NSMutableString *query = [NSMutableString stringWithCapacity:capacity];
|
|
1059
|
+
|
|
1060
|
+
[query appendFormat:@"SELECT \"rowid\", \"pageKey\" FROM \"%@\" WHERE \"rowid\" IN (", [self mapTableName]];
|
|
1061
|
+
|
|
1062
|
+
for (NSUInteger i = 0; i < numHostParams; i++)
|
|
1063
|
+
{
|
|
1064
|
+
if (i == 0)
|
|
1065
|
+
[query appendFormat:@"?"];
|
|
1066
|
+
else
|
|
1067
|
+
[query appendFormat:@", ?"];
|
|
1068
|
+
}
|
|
1069
|
+
|
|
1070
|
+
[query appendString:@");"];
|
|
1071
|
+
|
|
1072
|
+
sqlite3_stmt *statement;
|
|
1073
|
+
int status;
|
|
1074
|
+
|
|
1075
|
+
status = sqlite3_prepare_v2(db, [query UTF8String], -1, &statement, NULL);
|
|
1076
|
+
if (status != SQLITE_OK)
|
|
1077
|
+
{
|
|
1078
|
+
YDBLogError(@"%@ (%@): Error creating statement\n"
|
|
1079
|
+
@" - status(%d), errmsg: %s\n"
|
|
1080
|
+
@" - query: %@",
|
|
1081
|
+
THIS_METHOD, [self registeredName], status, sqlite3_errmsg(db), query);
|
|
1082
|
+
return nil;
|
|
1083
|
+
}
|
|
1084
|
+
|
|
1085
|
+
for (NSUInteger i = 0; i < numHostParams; i++)
|
|
1086
|
+
{
|
|
1087
|
+
int64_t rowid = [[rowids objectAtIndex:(offset + i)] longLongValue];
|
|
1088
|
+
|
|
1089
|
+
sqlite3_bind_int64(statement, (int)(i + 1), rowid);
|
|
1090
|
+
}
|
|
1091
|
+
|
|
1092
|
+
while ((status = sqlite3_step(statement)) == SQLITE_ROW)
|
|
1093
|
+
{
|
|
1094
|
+
// Extract rowid & pageKey from row
|
|
1095
|
+
|
|
1096
|
+
int64_t rowid = sqlite3_column_int64(statement, 0);
|
|
1097
|
+
|
|
1098
|
+
const unsigned char *text = sqlite3_column_text(statement, 1);
|
|
1099
|
+
int textSize = sqlite3_column_bytes(statement, 1);
|
|
1100
|
+
|
|
1101
|
+
NSString *pageKey = [[NSString alloc] initWithBytes:text length:textSize encoding:NSUTF8StringEncoding];
|
|
1102
|
+
|
|
1103
|
+
// Add to result dictionary
|
|
1104
|
+
|
|
1105
|
+
NSMutableDictionary *subKeyMappings = [result objectForKey:pageKey];
|
|
1106
|
+
if (subKeyMappings == nil)
|
|
1107
|
+
{
|
|
1108
|
+
subKeyMappings = [NSMutableDictionary dictionaryWithCapacity:1];
|
|
1109
|
+
[result setObject:subKeyMappings forKey:pageKey];
|
|
1110
|
+
}
|
|
1111
|
+
|
|
1112
|
+
NSNumber *number = @(rowid);
|
|
1113
|
+
YapCollectionKey *collectionKey = [keyMappings objectForKey:number];
|
|
1114
|
+
|
|
1115
|
+
[subKeyMappings setObject:collectionKey forKey:number];
|
|
1116
|
+
}
|
|
1117
|
+
|
|
1118
|
+
if (status != SQLITE_DONE)
|
|
1119
|
+
{
|
|
1120
|
+
YDBLogError(@"%@ (%@): Error executing statement: %d %s",
|
|
1121
|
+
THIS_METHOD, [self registeredName], status, sqlite3_errmsg(db));
|
|
1122
|
+
return nil;
|
|
1123
|
+
}
|
|
1124
|
+
|
|
1125
|
+
|
|
1126
|
+
offset += numHostParams;
|
|
1127
|
+
|
|
1128
|
+
} while (offset < count);
|
|
1129
|
+
|
|
1130
|
+
}
|
|
1131
|
+
else // if (isNonPersistentView)
|
|
1132
|
+
{
|
|
1133
|
+
[mapTableTransaction accessWithBlock:^{ @autoreleasepool {
|
|
1134
|
+
|
|
1135
|
+
for (NSNumber *rowidNumber in rowids)
|
|
1136
|
+
{
|
|
1137
|
+
NSString *pageKey = [mapTableTransaction objectForKey:rowidNumber];
|
|
1138
|
+
if (pageKey)
|
|
1139
|
+
{
|
|
1140
|
+
NSMutableDictionary *subKeyMappings = [result objectForKey:pageKey];
|
|
1141
|
+
if (subKeyMappings == nil)
|
|
1142
|
+
{
|
|
1143
|
+
subKeyMappings = [NSMutableDictionary dictionaryWithCapacity:1];
|
|
1144
|
+
[result setObject:subKeyMappings forKey:pageKey];
|
|
1145
|
+
}
|
|
1146
|
+
|
|
1147
|
+
NSString *key = [keyMappings objectForKey:rowidNumber];
|
|
1148
|
+
|
|
1149
|
+
[subKeyMappings setObject:key forKey:rowidNumber];
|
|
1150
|
+
}
|
|
1151
|
+
}
|
|
1152
|
+
}}];
|
|
1153
|
+
}
|
|
1154
|
+
|
|
1155
|
+
return result;
|
|
1156
|
+
}
|
|
1157
|
+
|
|
1158
|
+
/**
|
|
1159
|
+
* Fetches the page for the given pageKey.
|
|
1160
|
+
*
|
|
1161
|
+
* This method will use the cache(s) if possible.
|
|
1162
|
+
* Otherwise it will load the data from the page table and deserialize it.
|
|
1163
|
+
**/
|
|
1164
|
+
- (YapDatabaseViewPage *)pageForPageKey:(NSString *)pageKey
|
|
1165
|
+
{
|
|
1166
|
+
YapDatabaseViewPage *page = nil;
|
|
1167
|
+
|
|
1168
|
+
// Check dirty cache & clean cache
|
|
1169
|
+
|
|
1170
|
+
page = [viewConnection->dirtyPages objectForKey:pageKey];
|
|
1171
|
+
if (page) return page;
|
|
1172
|
+
|
|
1173
|
+
page = [viewConnection->pageCache objectForKey:pageKey];
|
|
1174
|
+
if (page) return page;
|
|
1175
|
+
|
|
1176
|
+
// Otherwise pull from the database
|
|
1177
|
+
|
|
1178
|
+
if ([self isPersistentView])
|
|
1179
|
+
{
|
|
1180
|
+
sqlite3_stmt *statement = [viewConnection pageTable_getDataForPageKeyStatement];
|
|
1181
|
+
if (statement == NULL)
|
|
1182
|
+
return nil;
|
|
1183
|
+
|
|
1184
|
+
// SELECT data FROM 'pageTableName' WHERE pageKey = ? ;
|
|
1185
|
+
|
|
1186
|
+
YapDatabaseString _pageKey; MakeYapDatabaseString(&_pageKey, pageKey);
|
|
1187
|
+
sqlite3_bind_text(statement, 1, _pageKey.str, _pageKey.length, SQLITE_STATIC);
|
|
1188
|
+
|
|
1189
|
+
int status = sqlite3_step(statement);
|
|
1190
|
+
if (status == SQLITE_ROW)
|
|
1191
|
+
{
|
|
1192
|
+
const void *blob = sqlite3_column_blob(statement, 0);
|
|
1193
|
+
int blobSize = sqlite3_column_bytes(statement, 0);
|
|
1194
|
+
|
|
1195
|
+
NSData *data = [[NSData alloc] initWithBytesNoCopy:(void *)blob length:blobSize freeWhenDone:NO];
|
|
1196
|
+
|
|
1197
|
+
page = [self deserializePage:data];
|
|
1198
|
+
}
|
|
1199
|
+
else if (status == SQLITE_ERROR)
|
|
1200
|
+
{
|
|
1201
|
+
YDBLogError(@"%@ (%@): Error executing statement: %d %s",
|
|
1202
|
+
THIS_METHOD, [self registeredName],
|
|
1203
|
+
status, sqlite3_errmsg(databaseTransaction->connection->db));
|
|
1204
|
+
}
|
|
1205
|
+
|
|
1206
|
+
sqlite3_clear_bindings(statement);
|
|
1207
|
+
sqlite3_reset(statement);
|
|
1208
|
+
FreeYapDatabaseString(&_pageKey);
|
|
1209
|
+
}
|
|
1210
|
+
else // if (isNonPersistentView)
|
|
1211
|
+
{
|
|
1212
|
+
page = [[pageTableTransaction objectForKey:pageKey] copy];
|
|
1213
|
+
}
|
|
1214
|
+
|
|
1215
|
+
// Store in cache if found
|
|
1216
|
+
if (page)
|
|
1217
|
+
[viewConnection->pageCache setObject:page forKey:pageKey];
|
|
1218
|
+
|
|
1219
|
+
return page;
|
|
1220
|
+
}
|
|
1221
|
+
|
|
1222
|
+
- (NSString *)groupForPageKey:(NSString *)pageKey
|
|
1223
|
+
{
|
|
1224
|
+
return [viewConnection->pageKey_group_dict objectForKey:pageKey];
|
|
1225
|
+
}
|
|
1226
|
+
|
|
1227
|
+
- (NSUInteger)indexForRowid:(int64_t)rowid inGroup:(NSString *)group withPageKey:(NSString *)pageKey
|
|
1228
|
+
{
|
|
1229
|
+
// Calculate the offset of the corresponding page within the group.
|
|
1230
|
+
|
|
1231
|
+
NSUInteger pageOffset = 0;
|
|
1232
|
+
NSMutableArray *pagesMetadataForGroup = [viewConnection->group_pagesMetadata_dict objectForKey:group];
|
|
1233
|
+
|
|
1234
|
+
for (YapDatabaseViewPageMetadata *pageMetadata in pagesMetadataForGroup)
|
|
1235
|
+
{
|
|
1236
|
+
if ([pageMetadata->pageKey isEqualToString:pageKey])
|
|
1237
|
+
{
|
|
1238
|
+
break;
|
|
1239
|
+
}
|
|
1240
|
+
|
|
1241
|
+
pageOffset += pageMetadata->count;
|
|
1242
|
+
}
|
|
1243
|
+
|
|
1244
|
+
// Fetch the actual page (ordered array of rowid's)
|
|
1245
|
+
|
|
1246
|
+
YapDatabaseViewPage *page = [self pageForPageKey:pageKey];
|
|
1247
|
+
|
|
1248
|
+
// Find the exact index of the rowid within the page
|
|
1249
|
+
|
|
1250
|
+
NSUInteger indexWithinPage = 0;
|
|
1251
|
+
BOOL found = [page getIndex:&indexWithinPage ofRowid:rowid];
|
|
1252
|
+
|
|
1253
|
+
NSAssert(found, @"Missing rowid in page");
|
|
1254
|
+
|
|
1255
|
+
// Return the full index of the rowid within the group
|
|
1256
|
+
|
|
1257
|
+
return pageOffset + indexWithinPage;
|
|
1258
|
+
}
|
|
1259
|
+
|
|
1260
|
+
- (BOOL)getRowid:(int64_t *)rowidPtr atIndex:(NSUInteger)index inGroup:(NSString *)group
|
|
1261
|
+
{
|
|
1262
|
+
NSMutableArray *pagesMetadataForGroup = [viewConnection->group_pagesMetadata_dict objectForKey:group];
|
|
1263
|
+
NSUInteger pageOffset = 0;
|
|
1264
|
+
|
|
1265
|
+
for (YapDatabaseViewPageMetadata *pageMetadata in pagesMetadataForGroup)
|
|
1266
|
+
{
|
|
1267
|
+
if ((index < (pageOffset + pageMetadata->count)) && (pageMetadata->count > 0))
|
|
1268
|
+
{
|
|
1269
|
+
YapDatabaseViewPage *page = [self pageForPageKey:pageMetadata->pageKey];
|
|
1270
|
+
|
|
1271
|
+
int64_t rowid = [page rowidAtIndex:(index - pageOffset)];
|
|
1272
|
+
|
|
1273
|
+
if (rowidPtr) *rowidPtr = rowid;
|
|
1274
|
+
return YES;
|
|
1275
|
+
}
|
|
1276
|
+
else
|
|
1277
|
+
{
|
|
1278
|
+
pageOffset += pageMetadata->count;
|
|
1279
|
+
}
|
|
1280
|
+
}
|
|
1281
|
+
|
|
1282
|
+
if (rowidPtr) *rowidPtr = 0;
|
|
1283
|
+
return NO;
|
|
1284
|
+
}
|
|
1285
|
+
|
|
1286
|
+
- (BOOL)getLastRowid:(int64_t *)rowidPtr inGroup:(NSString *)group
|
|
1287
|
+
{
|
|
1288
|
+
// We can actually do something a little faster than this:
|
|
1289
|
+
//
|
|
1290
|
+
// NSUInteger count = [self numberOfKeysInGroup:group];
|
|
1291
|
+
// if (count > 0)
|
|
1292
|
+
// return [self getRowid:rowidPtr atIndex:(count-1) inGroup:group];
|
|
1293
|
+
// else
|
|
1294
|
+
// return nil;
|
|
1295
|
+
|
|
1296
|
+
NSMutableArray *pagesMetadataForGroup = [viewConnection->group_pagesMetadata_dict objectForKey:group];
|
|
1297
|
+
|
|
1298
|
+
__block int64_t rowid = 0;
|
|
1299
|
+
__block BOOL found = NO;
|
|
1300
|
+
|
|
1301
|
+
[pagesMetadataForGroup enumerateObjectsWithOptions:NSEnumerationReverse
|
|
1302
|
+
usingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
|
|
1303
|
+
|
|
1304
|
+
__unsafe_unretained YapDatabaseViewPageMetadata *pageMetadata = (YapDatabaseViewPageMetadata *)obj;
|
|
1305
|
+
|
|
1306
|
+
if (pageMetadata->count > 0)
|
|
1307
|
+
{
|
|
1308
|
+
YapDatabaseViewPage *lastPage = [self pageForPageKey:pageMetadata->pageKey];
|
|
1309
|
+
|
|
1310
|
+
rowid = [lastPage rowidAtIndex:(pageMetadata->count - 1)];
|
|
1311
|
+
found = YES;
|
|
1312
|
+
*stop = YES;
|
|
1313
|
+
}
|
|
1314
|
+
}];
|
|
1315
|
+
|
|
1316
|
+
if (rowidPtr) *rowidPtr = rowid;
|
|
1317
|
+
return found;
|
|
1318
|
+
}
|
|
1319
|
+
|
|
1320
|
+
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
1321
|
+
#pragma mark Logic
|
|
1322
|
+
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
1323
|
+
|
|
1324
|
+
/**
|
|
1325
|
+
* Creates a new group and inserts the given row.
|
|
1326
|
+
* Important: The group MUST NOT already exist.
|
|
1327
|
+
**/
|
|
1328
|
+
- (void)insertRowid:(int64_t)rowid collectionKey:(YapCollectionKey *)collectionKey inNewGroup:(NSString *)group
|
|
1329
|
+
{
|
|
1330
|
+
YDBLogAutoTrace();
|
|
1331
|
+
|
|
1332
|
+
NSParameterAssert(collectionKey != nil);
|
|
1333
|
+
NSParameterAssert(group != nil);
|
|
1334
|
+
|
|
1335
|
+
// First object added to group.
|
|
1336
|
+
|
|
1337
|
+
NSString *pageKey = [self generatePageKey];
|
|
1338
|
+
|
|
1339
|
+
YDBLogVerbose(@"Inserting key(%@) collection(%@) in new group(%@) with page(%@)",
|
|
1340
|
+
collectionKey.key, collectionKey.collection, group, pageKey);
|
|
1341
|
+
|
|
1342
|
+
// Create page
|
|
1343
|
+
|
|
1344
|
+
YapDatabaseViewPage *page =
|
|
1345
|
+
[[YapDatabaseViewPage alloc] initWithCapacity:YAP_DATABASE_VIEW_MAX_PAGE_SIZE];
|
|
1346
|
+
[page addRowid:rowid];
|
|
1347
|
+
|
|
1348
|
+
// Create pageMetadata
|
|
1349
|
+
|
|
1350
|
+
YapDatabaseViewPageMetadata *pageMetadata = [[YapDatabaseViewPageMetadata alloc] init];
|
|
1351
|
+
pageMetadata->pageKey = pageKey;
|
|
1352
|
+
pageMetadata->prevPageKey = nil;
|
|
1353
|
+
pageMetadata->group = group;
|
|
1354
|
+
pageMetadata->count = 1;
|
|
1355
|
+
pageMetadata->isNew = YES;
|
|
1356
|
+
|
|
1357
|
+
// Add page and pageMetadata to in-memory structures
|
|
1358
|
+
|
|
1359
|
+
NSMutableArray *pagesMetadataForGroup = [[NSMutableArray alloc] initWithCapacity:1];
|
|
1360
|
+
[pagesMetadataForGroup addObject:pageMetadata];
|
|
1361
|
+
|
|
1362
|
+
[viewConnection->group_pagesMetadata_dict setObject:pagesMetadataForGroup forKey:group];
|
|
1363
|
+
[viewConnection->pageKey_group_dict setObject:group forKey:pageKey];
|
|
1364
|
+
|
|
1365
|
+
// Mark page as dirty
|
|
1366
|
+
|
|
1367
|
+
[viewConnection->dirtyPages setObject:page forKey:pageKey];
|
|
1368
|
+
[viewConnection->pageCache setObject:page forKey:pageKey];
|
|
1369
|
+
|
|
1370
|
+
// Mark rowid for insertion
|
|
1371
|
+
|
|
1372
|
+
[viewConnection->dirtyMaps setObject:pageKey forKey:@(rowid)];
|
|
1373
|
+
[viewConnection->mapCache setObject:pageKey forKey:@(rowid)];
|
|
1374
|
+
|
|
1375
|
+
// Add change to log
|
|
1376
|
+
|
|
1377
|
+
[viewConnection->changes addObject:
|
|
1378
|
+
[YapDatabaseViewSectionChange insertGroup:group]];
|
|
1379
|
+
|
|
1380
|
+
[viewConnection->changes addObject:
|
|
1381
|
+
[YapDatabaseViewRowChange insertKey:collectionKey inGroup:group atIndex:0]];
|
|
1382
|
+
|
|
1383
|
+
[viewConnection->mutatedGroups addObject:group];
|
|
1384
|
+
}
|
|
1385
|
+
|
|
1386
|
+
/**
|
|
1387
|
+
* Inserts the given rowid into an existing group.
|
|
1388
|
+
* Important: The group MUST already exist.
|
|
1389
|
+
**/
|
|
1390
|
+
- (void)insertRowid:(int64_t)rowid collectionKey:(YapCollectionKey *)collectionKey
|
|
1391
|
+
inGroup:(NSString *)group
|
|
1392
|
+
atIndex:(NSUInteger)index
|
|
1393
|
+
withExistingPageKey:(NSString *)existingPageKey
|
|
1394
|
+
{
|
|
1395
|
+
YDBLogAutoTrace();
|
|
1396
|
+
|
|
1397
|
+
NSParameterAssert(collectionKey != nil);
|
|
1398
|
+
NSParameterAssert(group != nil);
|
|
1399
|
+
|
|
1400
|
+
// Find pageMetadata, pageKey and page
|
|
1401
|
+
|
|
1402
|
+
YapDatabaseViewPageMetadata *pageMetadata = nil;
|
|
1403
|
+
|
|
1404
|
+
NSMutableArray *pagesMetadataForGroup = [viewConnection->group_pagesMetadata_dict objectForKey:group];
|
|
1405
|
+
|
|
1406
|
+
NSUInteger pageOffset = 0;
|
|
1407
|
+
NSUInteger pageIndex = 0;
|
|
1408
|
+
|
|
1409
|
+
NSUInteger lastPageIndex = [pagesMetadataForGroup count] - 1;
|
|
1410
|
+
|
|
1411
|
+
for (YapDatabaseViewPageMetadata *pm in pagesMetadataForGroup)
|
|
1412
|
+
{
|
|
1413
|
+
// Edge case: key is being inserted at the very end
|
|
1414
|
+
|
|
1415
|
+
if ((index < (pageOffset + pm->count)) || (pageIndex == lastPageIndex))
|
|
1416
|
+
{
|
|
1417
|
+
pageMetadata = pm;
|
|
1418
|
+
break;
|
|
1419
|
+
}
|
|
1420
|
+
else if (index == (pageOffset + pm->count))
|
|
1421
|
+
{
|
|
1422
|
+
// Optimization:
|
|
1423
|
+
// The insertion index is in-between two pages.
|
|
1424
|
+
// So it could go at the end of this page, or the beginning of the next page.
|
|
1425
|
+
//
|
|
1426
|
+
// We always place the key in the next page, unless:
|
|
1427
|
+
// - this page has room AND
|
|
1428
|
+
// - the next page is already full
|
|
1429
|
+
//
|
|
1430
|
+
// Related method: splitOversizedPage:
|
|
1431
|
+
|
|
1432
|
+
NSUInteger maxPageSize = YAP_DATABASE_VIEW_MAX_PAGE_SIZE;
|
|
1433
|
+
|
|
1434
|
+
if (pm->count < maxPageSize)
|
|
1435
|
+
{
|
|
1436
|
+
YapDatabaseViewPageMetadata *nextpm = [pagesMetadataForGroup objectAtIndex:(pageIndex+1)];
|
|
1437
|
+
if (nextpm->count >= maxPageSize)
|
|
1438
|
+
{
|
|
1439
|
+
pageMetadata = pm;
|
|
1440
|
+
break;
|
|
1441
|
+
}
|
|
1442
|
+
}
|
|
1443
|
+
}
|
|
1444
|
+
|
|
1445
|
+
pageIndex++;
|
|
1446
|
+
pageOffset += pm->count;
|
|
1447
|
+
}
|
|
1448
|
+
|
|
1449
|
+
NSAssert(pageMetadata != nil, @"Missing pageMetadata in group(%@)", group);
|
|
1450
|
+
|
|
1451
|
+
NSString *pageKey = pageMetadata->pageKey;
|
|
1452
|
+
YapDatabaseViewPage *page = [self pageForPageKey:pageKey];
|
|
1453
|
+
|
|
1454
|
+
YDBLogVerbose(@"Inserting key(%@) collection(%@) in group(%@) at index(%lu) with page(%@) pageOffset(%lu)",
|
|
1455
|
+
collectionKey.key, collectionKey.collection, group,
|
|
1456
|
+
(unsigned long)index, pageKey, (unsigned long)(index - pageOffset));
|
|
1457
|
+
|
|
1458
|
+
// Update page (insert rowid)
|
|
1459
|
+
|
|
1460
|
+
[page insertRowid:rowid atIndex:(index - pageOffset)];
|
|
1461
|
+
|
|
1462
|
+
// Update pageMetadata (increment count)
|
|
1463
|
+
|
|
1464
|
+
pageMetadata->count = [page count];
|
|
1465
|
+
|
|
1466
|
+
// Mark page as dirty
|
|
1467
|
+
|
|
1468
|
+
[viewConnection->dirtyPages setObject:page forKey:pageKey];
|
|
1469
|
+
[viewConnection->pageCache setObject:page forKey:pageKey];
|
|
1470
|
+
|
|
1471
|
+
// Mark key for insertion (if needed - may have already been in group)
|
|
1472
|
+
|
|
1473
|
+
if (![pageKey isEqualToString:existingPageKey])
|
|
1474
|
+
{
|
|
1475
|
+
[viewConnection->dirtyMaps setObject:pageKey forKey:@(rowid)];
|
|
1476
|
+
[viewConnection->mapCache setObject:pageKey forKey:@(rowid)];
|
|
1477
|
+
}
|
|
1478
|
+
|
|
1479
|
+
// Add change to log
|
|
1480
|
+
|
|
1481
|
+
[viewConnection->changes addObject:
|
|
1482
|
+
[YapDatabaseViewRowChange insertKey:collectionKey inGroup:group atIndex:index]];
|
|
1483
|
+
|
|
1484
|
+
[viewConnection->mutatedGroups addObject:group];
|
|
1485
|
+
|
|
1486
|
+
// During a transaction we allow pages to grow in size beyond the max page size.
|
|
1487
|
+
// This increases efficiency, as we can allow multiple changes to be written,
|
|
1488
|
+
// and then only perform the "cleanup" task of splitting the oversized page into multiple pages only once.
|
|
1489
|
+
//
|
|
1490
|
+
// However, we do want to avoid allowing a single page to grow infinitely large.
|
|
1491
|
+
// So we use triggers to ensure pages don't get too big.
|
|
1492
|
+
|
|
1493
|
+
NSUInteger trigger = YAP_DATABASE_VIEW_MAX_PAGE_SIZE * 32;
|
|
1494
|
+
NSUInteger target = YAP_DATABASE_VIEW_MAX_PAGE_SIZE * 16;
|
|
1495
|
+
|
|
1496
|
+
if ([page count] > trigger)
|
|
1497
|
+
{
|
|
1498
|
+
[self splitOversizedPage:page withPageKey:pageKey toSize:target];
|
|
1499
|
+
}
|
|
1500
|
+
}
|
|
1501
|
+
|
|
1502
|
+
/**
|
|
1503
|
+
* Use this method after it has been determined that the key should be inserted into the given group.
|
|
1504
|
+
* The object and metadata parameters must be properly set (if needed by the sorting block).
|
|
1505
|
+
*
|
|
1506
|
+
* This method will use the configured sorting block to find the proper index for the key.
|
|
1507
|
+
* It will attempt to optimize this operation as best as possible using a variety of techniques.
|
|
1508
|
+
**/
|
|
1509
|
+
- (void)insertRowid:(int64_t)rowid
|
|
1510
|
+
collectionKey:(YapCollectionKey *)collectionKey
|
|
1511
|
+
object:(id)object
|
|
1512
|
+
metadata:(id)metadata
|
|
1513
|
+
inGroup:(NSString *)group
|
|
1514
|
+
withChanges:(int)flags
|
|
1515
|
+
isNew:(BOOL)isGuaranteedNew
|
|
1516
|
+
{
|
|
1517
|
+
YDBLogAutoTrace();
|
|
1518
|
+
|
|
1519
|
+
__unsafe_unretained YapDatabaseView *view = viewConnection->view;
|
|
1520
|
+
|
|
1521
|
+
// Fetch the pages associated with the group.
|
|
1522
|
+
|
|
1523
|
+
NSMutableArray *pagesMetadataForGroup = [viewConnection->group_pagesMetadata_dict objectForKey:group];
|
|
1524
|
+
|
|
1525
|
+
// Is the key already in the group?
|
|
1526
|
+
// If so:
|
|
1527
|
+
// - its index within the group may or may not have changed.
|
|
1528
|
+
// - we can use its existing position as an optimization during sorting.
|
|
1529
|
+
|
|
1530
|
+
BOOL tryExistingIndexInGroup = NO;
|
|
1531
|
+
NSUInteger existingIndexInGroup = NSNotFound;
|
|
1532
|
+
|
|
1533
|
+
NSString *existingPageKey = isGuaranteedNew ? nil : [self pageKeyForRowid:rowid];
|
|
1534
|
+
if (existingPageKey)
|
|
1535
|
+
{
|
|
1536
|
+
// The key is already in the view.
|
|
1537
|
+
// Has it changed groups?
|
|
1538
|
+
|
|
1539
|
+
NSString *existingGroup = [self groupForPageKey:existingPageKey];
|
|
1540
|
+
|
|
1541
|
+
if ([group isEqualToString:existingGroup])
|
|
1542
|
+
{
|
|
1543
|
+
// The key is already in the group.
|
|
1544
|
+
//
|
|
1545
|
+
// Find out what its current index is.
|
|
1546
|
+
|
|
1547
|
+
existingIndexInGroup = [self indexForRowid:rowid inGroup:group withPageKey:existingPageKey];
|
|
1548
|
+
|
|
1549
|
+
if (view->sortingBlockType == YapDatabaseViewBlockTypeWithKey)
|
|
1550
|
+
{
|
|
1551
|
+
// Sorting is based entirely on the key, which hasn't changed.
|
|
1552
|
+
// Thus the position within the view hasn't changed.
|
|
1553
|
+
|
|
1554
|
+
[viewConnection->changes addObject:
|
|
1555
|
+
[YapDatabaseViewRowChange updateKey:collectionKey
|
|
1556
|
+
changes:flags
|
|
1557
|
+
inGroup:group
|
|
1558
|
+
atIndex:existingIndexInGroup]];
|
|
1559
|
+
return;
|
|
1560
|
+
}
|
|
1561
|
+
else
|
|
1562
|
+
{
|
|
1563
|
+
// Possible optimization:
|
|
1564
|
+
// Object or metadata was updated, but doesn't affect the position of the row within the view.
|
|
1565
|
+
tryExistingIndexInGroup = YES;
|
|
1566
|
+
}
|
|
1567
|
+
}
|
|
1568
|
+
else
|
|
1569
|
+
{
|
|
1570
|
+
[self removeRowid:rowid collectionKey:collectionKey withPageKey:existingPageKey inGroup:existingGroup];
|
|
1571
|
+
|
|
1572
|
+
// Don't forget to reset the existingPageKey ivar!
|
|
1573
|
+
// Or else 'insertKey:inGroup:atIndex:withExistingPageKey:' will be given an invalid existingPageKey.
|
|
1574
|
+
existingPageKey = nil;
|
|
1575
|
+
}
|
|
1576
|
+
}
|
|
1577
|
+
|
|
1578
|
+
// Is this a new group ?
|
|
1579
|
+
|
|
1580
|
+
if (pagesMetadataForGroup == nil)
|
|
1581
|
+
{
|
|
1582
|
+
// First object added to group.
|
|
1583
|
+
|
|
1584
|
+
[self insertRowid:rowid collectionKey:collectionKey inNewGroup:group];
|
|
1585
|
+
|
|
1586
|
+
return;
|
|
1587
|
+
}
|
|
1588
|
+
|
|
1589
|
+
// Need to determine the location within the existing group.
|
|
1590
|
+
|
|
1591
|
+
// Calculate how many keys are in the group.
|
|
1592
|
+
|
|
1593
|
+
NSUInteger count = 0;
|
|
1594
|
+
|
|
1595
|
+
for (YapDatabaseViewPageMetadata *pageMetadata in pagesMetadataForGroup)
|
|
1596
|
+
{
|
|
1597
|
+
count += pageMetadata->count;
|
|
1598
|
+
}
|
|
1599
|
+
|
|
1600
|
+
// Create a block to do a single sorting comparison between the object to be inserted,
|
|
1601
|
+
// and some other object within the group at a given index.
|
|
1602
|
+
//
|
|
1603
|
+
// This block will be invoked repeatedly as we calculate the insertion index.
|
|
1604
|
+
|
|
1605
|
+
NSComparisonResult (^compare)(NSUInteger) = ^NSComparisonResult (NSUInteger index){
|
|
1606
|
+
|
|
1607
|
+
int64_t anotherRowid = 0;
|
|
1608
|
+
|
|
1609
|
+
NSUInteger pageOffset = 0;
|
|
1610
|
+
for (YapDatabaseViewPageMetadata *pageMetadata in pagesMetadataForGroup)
|
|
1611
|
+
{
|
|
1612
|
+
if ((index < (pageOffset + pageMetadata->count)) && (pageMetadata->count > 0))
|
|
1613
|
+
{
|
|
1614
|
+
YapDatabaseViewPage *page = [self pageForPageKey:pageMetadata->pageKey];
|
|
1615
|
+
|
|
1616
|
+
anotherRowid = [page rowidAtIndex:(index - pageOffset)];
|
|
1617
|
+
break;
|
|
1618
|
+
}
|
|
1619
|
+
else
|
|
1620
|
+
{
|
|
1621
|
+
pageOffset += pageMetadata->count;
|
|
1622
|
+
}
|
|
1623
|
+
}
|
|
1624
|
+
|
|
1625
|
+
if (view->sortingBlockType == YapDatabaseViewBlockTypeWithKey)
|
|
1626
|
+
{
|
|
1627
|
+
__unsafe_unretained YapDatabaseViewSortingWithKeyBlock sortingBlock =
|
|
1628
|
+
(YapDatabaseViewSortingWithKeyBlock)view->sortingBlock;
|
|
1629
|
+
|
|
1630
|
+
NSString *anotherKey = nil;
|
|
1631
|
+
NSString *anotherCollection = nil;
|
|
1632
|
+
[databaseTransaction getKey:&anotherKey collection:&anotherCollection forRowid:anotherRowid];
|
|
1633
|
+
|
|
1634
|
+
return sortingBlock(group, collectionKey.collection, collectionKey.key,
|
|
1635
|
+
anotherCollection, anotherKey);
|
|
1636
|
+
}
|
|
1637
|
+
else if (view->sortingBlockType == YapDatabaseViewBlockTypeWithObject)
|
|
1638
|
+
{
|
|
1639
|
+
__unsafe_unretained YapDatabaseViewSortingWithObjectBlock sortingBlock =
|
|
1640
|
+
(YapDatabaseViewSortingWithObjectBlock)view->sortingBlock;
|
|
1641
|
+
|
|
1642
|
+
NSString *anotherKey = nil;
|
|
1643
|
+
NSString *anotherCollection = nil;
|
|
1644
|
+
id anotherObject = nil;
|
|
1645
|
+
[databaseTransaction getKey:&anotherKey
|
|
1646
|
+
collection:&anotherCollection
|
|
1647
|
+
object:&anotherObject
|
|
1648
|
+
forRowid:anotherRowid];
|
|
1649
|
+
|
|
1650
|
+
return sortingBlock(group, collectionKey.collection, collectionKey.key, object,
|
|
1651
|
+
anotherCollection, anotherKey, anotherObject);
|
|
1652
|
+
}
|
|
1653
|
+
else if (view->sortingBlockType == YapDatabaseViewBlockTypeWithMetadata)
|
|
1654
|
+
{
|
|
1655
|
+
__unsafe_unretained YapDatabaseViewSortingWithMetadataBlock sortingBlock =
|
|
1656
|
+
(YapDatabaseViewSortingWithMetadataBlock)view->sortingBlock;
|
|
1657
|
+
|
|
1658
|
+
NSString *anotherKey = nil;
|
|
1659
|
+
NSString *anotherCollection = nil;
|
|
1660
|
+
id anotherMetadata = nil;
|
|
1661
|
+
[databaseTransaction getKey:&anotherKey
|
|
1662
|
+
collection:&anotherCollection
|
|
1663
|
+
metadata:&anotherMetadata
|
|
1664
|
+
forRowid:anotherRowid];
|
|
1665
|
+
|
|
1666
|
+
return sortingBlock(group, collectionKey.collection, collectionKey.key, metadata,
|
|
1667
|
+
anotherCollection, anotherKey, anotherMetadata);
|
|
1668
|
+
}
|
|
1669
|
+
else
|
|
1670
|
+
{
|
|
1671
|
+
__unsafe_unretained YapDatabaseViewSortingWithRowBlock sortingBlock =
|
|
1672
|
+
(YapDatabaseViewSortingWithRowBlock)view->sortingBlock;
|
|
1673
|
+
|
|
1674
|
+
NSString *anotherKey = nil;
|
|
1675
|
+
NSString *anotherCollection = nil;
|
|
1676
|
+
id anotherObject = nil;
|
|
1677
|
+
id anotherMetadata = nil;
|
|
1678
|
+
[databaseTransaction getKey:&anotherKey
|
|
1679
|
+
collection:&anotherCollection
|
|
1680
|
+
object:&anotherObject
|
|
1681
|
+
metadata:&anotherMetadata
|
|
1682
|
+
forRowid:anotherRowid];
|
|
1683
|
+
|
|
1684
|
+
return sortingBlock(group, collectionKey.collection, collectionKey.key, object, metadata,
|
|
1685
|
+
anotherCollection, anotherKey, anotherObject, anotherMetadata);
|
|
1686
|
+
}
|
|
1687
|
+
};
|
|
1688
|
+
|
|
1689
|
+
NSComparisonResult cmp;
|
|
1690
|
+
|
|
1691
|
+
// Optimization 1:
|
|
1692
|
+
//
|
|
1693
|
+
// If the key is already in the group, check to see if its index is the same as before.
|
|
1694
|
+
// This handles the common case where an object is updated without changing its position within the view.
|
|
1695
|
+
|
|
1696
|
+
if (tryExistingIndexInGroup)
|
|
1697
|
+
{
|
|
1698
|
+
// Edge case: existing key is the only key in the group
|
|
1699
|
+
//
|
|
1700
|
+
// (existingIndex == 0) && (count == 1)
|
|
1701
|
+
|
|
1702
|
+
BOOL useExistingIndexInGroup = YES;
|
|
1703
|
+
|
|
1704
|
+
if (existingIndexInGroup > 0)
|
|
1705
|
+
{
|
|
1706
|
+
cmp = compare(existingIndexInGroup - 1); // compare vs prev
|
|
1707
|
+
|
|
1708
|
+
useExistingIndexInGroup = (cmp != NSOrderedAscending); // object >= prev
|
|
1709
|
+
}
|
|
1710
|
+
|
|
1711
|
+
if ((existingIndexInGroup + 1) < count && useExistingIndexInGroup)
|
|
1712
|
+
{
|
|
1713
|
+
cmp = compare(existingIndexInGroup + 1); // compare vs next
|
|
1714
|
+
|
|
1715
|
+
useExistingIndexInGroup = (cmp != NSOrderedDescending); // object <= next
|
|
1716
|
+
}
|
|
1717
|
+
|
|
1718
|
+
if (useExistingIndexInGroup)
|
|
1719
|
+
{
|
|
1720
|
+
// The key doesn't change position.
|
|
1721
|
+
|
|
1722
|
+
YDBLogVerbose(@"Updated key(%@) in group(%@) maintains current index", collectionKey.key, group);
|
|
1723
|
+
|
|
1724
|
+
[viewConnection->changes addObject:
|
|
1725
|
+
[YapDatabaseViewRowChange updateKey:collectionKey
|
|
1726
|
+
changes:flags
|
|
1727
|
+
inGroup:group
|
|
1728
|
+
atIndex:existingIndexInGroup]];
|
|
1729
|
+
return;
|
|
1730
|
+
}
|
|
1731
|
+
else
|
|
1732
|
+
{
|
|
1733
|
+
// The key has changed position.
|
|
1734
|
+
// Remove it from previous position (and don't forget to decrement count).
|
|
1735
|
+
|
|
1736
|
+
[self removeRowid:rowid collectionKey:collectionKey withPageKey:existingPageKey inGroup:group];
|
|
1737
|
+
count--;
|
|
1738
|
+
|
|
1739
|
+
// Don't forget to reset the existingPageKey ivar!
|
|
1740
|
+
// Or else 'insertKey:inGroup:atIndex:withExistingPageKey:' will be given an invalid existingPageKey.
|
|
1741
|
+
existingPageKey = nil;
|
|
1742
|
+
}
|
|
1743
|
+
}
|
|
1744
|
+
|
|
1745
|
+
// Optimization 2:
|
|
1746
|
+
//
|
|
1747
|
+
// A very common operation is to insert objects at the beginning or end of the array.
|
|
1748
|
+
// We attempt to notice this trend and optimize around it.
|
|
1749
|
+
|
|
1750
|
+
if (viewConnection->lastInsertWasAtFirstIndex && (count > 1))
|
|
1751
|
+
{
|
|
1752
|
+
cmp = compare(0);
|
|
1753
|
+
|
|
1754
|
+
if (cmp == NSOrderedAscending) // object < first
|
|
1755
|
+
{
|
|
1756
|
+
YDBLogVerbose(@"Insert key(%@) collection(%@) in group(%@) at beginning (optimization)",
|
|
1757
|
+
collectionKey.key, collectionKey.collection, group);
|
|
1758
|
+
|
|
1759
|
+
[self insertRowid:rowid collectionKey:collectionKey
|
|
1760
|
+
inGroup:group
|
|
1761
|
+
atIndex:0
|
|
1762
|
+
withExistingPageKey:existingPageKey];
|
|
1763
|
+
return;
|
|
1764
|
+
}
|
|
1765
|
+
}
|
|
1766
|
+
|
|
1767
|
+
if (viewConnection->lastInsertWasAtLastIndex && (count > 1))
|
|
1768
|
+
{
|
|
1769
|
+
cmp = compare(count - 1);
|
|
1770
|
+
|
|
1771
|
+
if (cmp != NSOrderedAscending) // object >= last
|
|
1772
|
+
{
|
|
1773
|
+
YDBLogVerbose(@"Insert key(%@) collection(%@) in group(%@) at end (optimization)",
|
|
1774
|
+
collectionKey.key, collectionKey.collection, group);
|
|
1775
|
+
|
|
1776
|
+
[self insertRowid:rowid collectionKey:collectionKey
|
|
1777
|
+
inGroup:group
|
|
1778
|
+
atIndex:count
|
|
1779
|
+
withExistingPageKey:existingPageKey];
|
|
1780
|
+
return;
|
|
1781
|
+
}
|
|
1782
|
+
}
|
|
1783
|
+
|
|
1784
|
+
// Otherwise:
|
|
1785
|
+
//
|
|
1786
|
+
// Binary search operation.
|
|
1787
|
+
//
|
|
1788
|
+
// This particular algorithm accounts for cases where the objects are not unique.
|
|
1789
|
+
// That is, if some objects are NSOrderedSame, then the algorithm returns the largest index possible
|
|
1790
|
+
// (within the region where elements are "equal").
|
|
1791
|
+
|
|
1792
|
+
NSUInteger loopCount = 0;
|
|
1793
|
+
|
|
1794
|
+
NSUInteger min = 0;
|
|
1795
|
+
NSUInteger max = count;
|
|
1796
|
+
|
|
1797
|
+
while (min < max)
|
|
1798
|
+
{
|
|
1799
|
+
NSUInteger mid = (min + max) / 2;
|
|
1800
|
+
|
|
1801
|
+
cmp = compare(mid);
|
|
1802
|
+
|
|
1803
|
+
if (cmp == NSOrderedAscending)
|
|
1804
|
+
max = mid;
|
|
1805
|
+
else
|
|
1806
|
+
min = mid + 1;
|
|
1807
|
+
|
|
1808
|
+
loopCount++;
|
|
1809
|
+
}
|
|
1810
|
+
|
|
1811
|
+
YDBLogVerbose(@"Insert key(%@) collection(%@) in group(%@) took %lu comparisons",
|
|
1812
|
+
collectionKey.key, collectionKey.collection, group, (unsigned long)loopCount);
|
|
1813
|
+
|
|
1814
|
+
[self insertRowid:rowid collectionKey:collectionKey inGroup:group atIndex:min withExistingPageKey:existingPageKey];
|
|
1815
|
+
|
|
1816
|
+
viewConnection->lastInsertWasAtFirstIndex = (min == 0);
|
|
1817
|
+
viewConnection->lastInsertWasAtLastIndex = (min == count);
|
|
1818
|
+
}
|
|
1819
|
+
|
|
1820
|
+
/**
|
|
1821
|
+
* Use this method when the index (within the group) is already known.
|
|
1822
|
+
**/
|
|
1823
|
+
- (void)removeRowid:(int64_t)rowid
|
|
1824
|
+
collectionKey:(YapCollectionKey *)collectionKey
|
|
1825
|
+
atIndex:(NSUInteger)index
|
|
1826
|
+
inGroup:(NSString *)group
|
|
1827
|
+
{
|
|
1828
|
+
YDBLogAutoTrace();
|
|
1829
|
+
|
|
1830
|
+
NSParameterAssert(collectionKey != nil);
|
|
1831
|
+
NSParameterAssert(group != nil);
|
|
1832
|
+
|
|
1833
|
+
// Fetch page
|
|
1834
|
+
|
|
1835
|
+
YapDatabaseViewPage *page = nil;
|
|
1836
|
+
YapDatabaseViewPageMetadata *pageMetadata = nil;
|
|
1837
|
+
|
|
1838
|
+
NSUInteger pageOffset = 0;
|
|
1839
|
+
NSMutableArray *pagesMetadataForGroup = [viewConnection->group_pagesMetadata_dict objectForKey:group];
|
|
1840
|
+
|
|
1841
|
+
for (YapDatabaseViewPageMetadata *pm in pagesMetadataForGroup)
|
|
1842
|
+
{
|
|
1843
|
+
if ((index < (pageOffset + pm->count)) && (pm->count > 0))
|
|
1844
|
+
{
|
|
1845
|
+
pageMetadata = pm;
|
|
1846
|
+
page = [self pageForPageKey:pm->pageKey];
|
|
1847
|
+
|
|
1848
|
+
break;
|
|
1849
|
+
}
|
|
1850
|
+
else
|
|
1851
|
+
{
|
|
1852
|
+
pageOffset += pm->count;
|
|
1853
|
+
}
|
|
1854
|
+
}
|
|
1855
|
+
|
|
1856
|
+
if (page == nil)
|
|
1857
|
+
{
|
|
1858
|
+
YDBLogError(@"%@ (%@): Unable to remove rowid at groupIndex(%lu) in group(%@)",
|
|
1859
|
+
THIS_METHOD, [self registeredName], (unsigned long)index, group);
|
|
1860
|
+
return;
|
|
1861
|
+
}
|
|
1862
|
+
|
|
1863
|
+
// Verify specified rowid matches specified index
|
|
1864
|
+
|
|
1865
|
+
NSUInteger indexWithinPage = index - pageOffset;
|
|
1866
|
+
|
|
1867
|
+
NSAssert(rowid == [page rowidAtIndex:indexWithinPage], @"Rowid mismatch");
|
|
1868
|
+
|
|
1869
|
+
YDBLogVerbose(@"Removing collection(%@) key(%@) from page(%@) at pageIndex(%lu)",
|
|
1870
|
+
collectionKey.collection, collectionKey.key, page, (unsigned long)indexWithinPage);
|
|
1871
|
+
|
|
1872
|
+
// Add change to log
|
|
1873
|
+
|
|
1874
|
+
[viewConnection->changes addObject:
|
|
1875
|
+
[YapDatabaseViewRowChange deleteKey:collectionKey inGroup:group atIndex:index]];
|
|
1876
|
+
|
|
1877
|
+
[viewConnection->mutatedGroups addObject:group];
|
|
1878
|
+
|
|
1879
|
+
// Update page (by removing rowid from array)
|
|
1880
|
+
|
|
1881
|
+
[page removeRowidAtIndex:indexWithinPage];
|
|
1882
|
+
|
|
1883
|
+
// Update page metadata (by decrementing count)
|
|
1884
|
+
|
|
1885
|
+
pageMetadata->count = [page count];
|
|
1886
|
+
|
|
1887
|
+
// Mark page as dirty
|
|
1888
|
+
|
|
1889
|
+
YDBLogVerbose(@"Dirty page(%@)", pageMetadata->pageKey);
|
|
1890
|
+
|
|
1891
|
+
[viewConnection->dirtyPages setObject:page forKey:pageMetadata->pageKey];
|
|
1892
|
+
[viewConnection->pageCache setObject:page forKey:pageMetadata->pageKey];
|
|
1893
|
+
|
|
1894
|
+
// Mark key for deletion
|
|
1895
|
+
|
|
1896
|
+
[viewConnection->dirtyMaps setObject:[NSNull null] forKey:@(rowid)];
|
|
1897
|
+
[viewConnection->mapCache removeObjectForKey:@(rowid)];
|
|
1898
|
+
}
|
|
1899
|
+
|
|
1900
|
+
/**
|
|
1901
|
+
* Use this method (instead of removeKey:) when the pageKey and group are already known.
|
|
1902
|
+
**/
|
|
1903
|
+
- (void)removeRowid:(int64_t)rowid
|
|
1904
|
+
collectionKey:(YapCollectionKey *)collectionKey
|
|
1905
|
+
withPageKey:(NSString *)pageKey
|
|
1906
|
+
inGroup:(NSString *)group
|
|
1907
|
+
{
|
|
1908
|
+
YDBLogAutoTrace();
|
|
1909
|
+
|
|
1910
|
+
NSParameterAssert(collectionKey != nil);
|
|
1911
|
+
NSParameterAssert(pageKey != nil);
|
|
1912
|
+
NSParameterAssert(group != nil);
|
|
1913
|
+
|
|
1914
|
+
// Fetch page & pageMetadata
|
|
1915
|
+
|
|
1916
|
+
YapDatabaseViewPage *page = [self pageForPageKey:pageKey];
|
|
1917
|
+
|
|
1918
|
+
YapDatabaseViewPageMetadata *pageMetadata = nil;
|
|
1919
|
+
NSUInteger pageOffset = 0;
|
|
1920
|
+
|
|
1921
|
+
NSMutableArray *pagesMetadataForGroup = [viewConnection->group_pagesMetadata_dict objectForKey:group];
|
|
1922
|
+
|
|
1923
|
+
for (YapDatabaseViewPageMetadata *pm in pagesMetadataForGroup)
|
|
1924
|
+
{
|
|
1925
|
+
if ([pm->pageKey isEqualToString:pageKey])
|
|
1926
|
+
{
|
|
1927
|
+
pageMetadata = pm;
|
|
1928
|
+
break;
|
|
1929
|
+
}
|
|
1930
|
+
|
|
1931
|
+
pageOffset += pm->count;
|
|
1932
|
+
}
|
|
1933
|
+
|
|
1934
|
+
NSAssert(pageMetadata != nil, @"Missing pageMetadata in group(%@) withPageKey(%@)", group, pageKey);
|
|
1935
|
+
|
|
1936
|
+
// Find index within page
|
|
1937
|
+
|
|
1938
|
+
NSUInteger indexWithinPage = 0;
|
|
1939
|
+
BOOL found = [page getIndex:&indexWithinPage ofRowid:rowid];
|
|
1940
|
+
|
|
1941
|
+
if (!found)
|
|
1942
|
+
{
|
|
1943
|
+
YDBLogError(@"%@ (%@): collection(%@) key(%@) expected to be in page(%@), but is missing",
|
|
1944
|
+
THIS_METHOD, [self registeredName], collectionKey.collection, collectionKey.key, pageKey);
|
|
1945
|
+
return;
|
|
1946
|
+
}
|
|
1947
|
+
|
|
1948
|
+
YDBLogVerbose(@"Removing collection(%@) key(%@) from page(%@) at index(%lu)",
|
|
1949
|
+
collectionKey.collection, collectionKey.key, page, (unsigned long)indexWithinPage);
|
|
1950
|
+
|
|
1951
|
+
// Add change to log
|
|
1952
|
+
|
|
1953
|
+
[viewConnection->changes addObject:
|
|
1954
|
+
[YapDatabaseViewRowChange deleteKey:collectionKey inGroup:group atIndex:(pageOffset + indexWithinPage)]];
|
|
1955
|
+
|
|
1956
|
+
[viewConnection->mutatedGroups addObject:group];
|
|
1957
|
+
|
|
1958
|
+
// Update page (by removing key from array)
|
|
1959
|
+
|
|
1960
|
+
[page removeRowidAtIndex:indexWithinPage];
|
|
1961
|
+
|
|
1962
|
+
// Update page metadata (by decrementing count)
|
|
1963
|
+
|
|
1964
|
+
pageMetadata->count = [page count];
|
|
1965
|
+
|
|
1966
|
+
// Mark page as dirty
|
|
1967
|
+
|
|
1968
|
+
YDBLogVerbose(@"Dirty page(%@)", pageKey);
|
|
1969
|
+
|
|
1970
|
+
[viewConnection->dirtyPages setObject:page forKey:pageKey];
|
|
1971
|
+
[viewConnection->pageCache setObject:page forKey:pageKey];
|
|
1972
|
+
|
|
1973
|
+
// Mark key for deletion
|
|
1974
|
+
|
|
1975
|
+
[viewConnection->dirtyMaps setObject:[NSNull null] forKey:@(rowid)];
|
|
1976
|
+
[viewConnection->mapCache removeObjectForKey:@(rowid)];
|
|
1977
|
+
}
|
|
1978
|
+
|
|
1979
|
+
/**
|
|
1980
|
+
* Use this method when you don't know if the collection/key exists in the view.
|
|
1981
|
+
**/
|
|
1982
|
+
- (void)removeRowid:(int64_t)rowid collectionKey:(YapCollectionKey *)collectionKey
|
|
1983
|
+
{
|
|
1984
|
+
YDBLogAutoTrace();
|
|
1985
|
+
|
|
1986
|
+
// Find out if collection/key is in view
|
|
1987
|
+
|
|
1988
|
+
NSString *pageKey = [self pageKeyForRowid:rowid];
|
|
1989
|
+
if (pageKey)
|
|
1990
|
+
{
|
|
1991
|
+
[self removeRowid:rowid collectionKey:collectionKey withPageKey:pageKey inGroup:[self groupForPageKey:pageKey]];
|
|
1992
|
+
}
|
|
1993
|
+
}
|
|
1994
|
+
|
|
1995
|
+
/**
|
|
1996
|
+
* Use this method to remove 1 or more keys from a given pageKey & group.
|
|
1997
|
+
*
|
|
1998
|
+
* The dictionary is to be of the form:
|
|
1999
|
+
* @{
|
|
2000
|
+
* @(rowid) = collectionKey,
|
|
2001
|
+
* }
|
|
2002
|
+
**/
|
|
2003
|
+
- (void)removeRowidsWithKeyMappings:(NSDictionary *)keyMappings pageKey:(NSString *)pageKey inGroup:(NSString *)group
|
|
2004
|
+
{
|
|
2005
|
+
YDBLogAutoTrace();
|
|
2006
|
+
|
|
2007
|
+
NSUInteger count = [keyMappings count];
|
|
2008
|
+
|
|
2009
|
+
if (count == 0) return;
|
|
2010
|
+
if (count == 1)
|
|
2011
|
+
{
|
|
2012
|
+
for (NSNumber *number in keyMappings)
|
|
2013
|
+
{
|
|
2014
|
+
int64_t rowid = [number longLongValue];
|
|
2015
|
+
YapCollectionKey *collectionKey = [keyMappings objectForKey:number];
|
|
2016
|
+
|
|
2017
|
+
[self removeRowid:rowid collectionKey:collectionKey withPageKey:pageKey inGroup:group];
|
|
2018
|
+
}
|
|
2019
|
+
return;
|
|
2020
|
+
}
|
|
2021
|
+
|
|
2022
|
+
NSParameterAssert(pageKey != nil);
|
|
2023
|
+
NSParameterAssert(group != nil);
|
|
2024
|
+
|
|
2025
|
+
// Fetch page & pageMetadata
|
|
2026
|
+
|
|
2027
|
+
YapDatabaseViewPage *page = [self pageForPageKey:pageKey];
|
|
2028
|
+
|
|
2029
|
+
YapDatabaseViewPageMetadata *pageMetadata = nil;
|
|
2030
|
+
NSUInteger pageOffset = 0;
|
|
2031
|
+
|
|
2032
|
+
NSMutableArray *pagesMetadataForGroup = [viewConnection->group_pagesMetadata_dict objectForKey:group];
|
|
2033
|
+
|
|
2034
|
+
for (YapDatabaseViewPageMetadata *pm in pagesMetadataForGroup)
|
|
2035
|
+
{
|
|
2036
|
+
if ([pm->pageKey isEqualToString:pageKey])
|
|
2037
|
+
{
|
|
2038
|
+
pageMetadata = pm;
|
|
2039
|
+
break;
|
|
2040
|
+
}
|
|
2041
|
+
|
|
2042
|
+
pageOffset += pm->count;
|
|
2043
|
+
}
|
|
2044
|
+
|
|
2045
|
+
NSAssert(pageMetadata != nil, @"Missing pageMetadata in group(%@) withPageKey(%@)", group, pageKey);
|
|
2046
|
+
|
|
2047
|
+
// Find matching indexes within page.
|
|
2048
|
+
// And add changes to log.
|
|
2049
|
+
// Notes:
|
|
2050
|
+
//
|
|
2051
|
+
// - We must add the changes in reverse order,
|
|
2052
|
+
// just as if we were deleting them from the array one-at-a-time.
|
|
2053
|
+
|
|
2054
|
+
NSUInteger numRemoved = 0;
|
|
2055
|
+
|
|
2056
|
+
for (NSUInteger iPlusOne = [page count]; iPlusOne > 0; iPlusOne--)
|
|
2057
|
+
{
|
|
2058
|
+
NSUInteger i = iPlusOne - 1;
|
|
2059
|
+
int64_t rowid = [page rowidAtIndex:i];
|
|
2060
|
+
|
|
2061
|
+
YapCollectionKey *collectionKey = [keyMappings objectForKey:@(rowid)];
|
|
2062
|
+
if (collectionKey)
|
|
2063
|
+
{
|
|
2064
|
+
[page removeRowidAtIndex:i];
|
|
2065
|
+
numRemoved++;
|
|
2066
|
+
|
|
2067
|
+
[viewConnection->changes addObject:
|
|
2068
|
+
[YapDatabaseViewRowChange deleteKey:collectionKey inGroup:group atIndex:(pageOffset + i)]];
|
|
2069
|
+
}
|
|
2070
|
+
}
|
|
2071
|
+
|
|
2072
|
+
[viewConnection->mutatedGroups addObject:group];
|
|
2073
|
+
|
|
2074
|
+
YDBLogVerbose(@"Removed %lu key(s) from page(%@)", (unsigned long)numRemoved, page);
|
|
2075
|
+
|
|
2076
|
+
if (numRemoved != count)
|
|
2077
|
+
{
|
|
2078
|
+
YDBLogWarn(@"%@ (%@): Expected to remove %lu, but only found %lu in page(%@)",
|
|
2079
|
+
THIS_METHOD, [self registeredName], (unsigned long)count, (unsigned long)numRemoved, pageKey);
|
|
2080
|
+
}
|
|
2081
|
+
|
|
2082
|
+
// Update page metadata (by decrementing count)
|
|
2083
|
+
|
|
2084
|
+
pageMetadata->count = [page count];
|
|
2085
|
+
|
|
2086
|
+
// Mark page as dirty
|
|
2087
|
+
|
|
2088
|
+
YDBLogVerbose(@"Dirty page(%@)", pageKey);
|
|
2089
|
+
|
|
2090
|
+
[viewConnection->dirtyPages setObject:page forKey:pageKey];
|
|
2091
|
+
[viewConnection->pageCache setObject:page forKey:pageKey];
|
|
2092
|
+
|
|
2093
|
+
// Mark rowid mappings for deletion
|
|
2094
|
+
|
|
2095
|
+
for (NSNumber *number in keyMappings)
|
|
2096
|
+
{
|
|
2097
|
+
[viewConnection->dirtyMaps setObject:[NSNull null] forKey:number];
|
|
2098
|
+
[viewConnection->mapCache removeObjectForKey:number];
|
|
2099
|
+
}
|
|
2100
|
+
}
|
|
2101
|
+
|
|
2102
|
+
- (void)removeAllRowids
|
|
2103
|
+
{
|
|
2104
|
+
YDBLogAutoTrace();
|
|
2105
|
+
|
|
2106
|
+
if ([self isPersistentView])
|
|
2107
|
+
{
|
|
2108
|
+
sqlite3_stmt *mapStatement = [viewConnection mapTable_removeAllStatement];
|
|
2109
|
+
sqlite3_stmt *pageStatement = [viewConnection pageTable_removeAllStatement];
|
|
2110
|
+
|
|
2111
|
+
if (mapStatement == NULL || pageStatement == NULL)
|
|
2112
|
+
return;
|
|
2113
|
+
|
|
2114
|
+
int status;
|
|
2115
|
+
|
|
2116
|
+
// DELETE FROM "mapTableName";
|
|
2117
|
+
|
|
2118
|
+
YDBLogVerbose(@"DELETE FROM '%@';", [self mapTableName]);
|
|
2119
|
+
|
|
2120
|
+
status = sqlite3_step(mapStatement);
|
|
2121
|
+
if (status != SQLITE_DONE)
|
|
2122
|
+
{
|
|
2123
|
+
YDBLogError(@"%@ (%@): Error in mapStatement: %d %s",
|
|
2124
|
+
THIS_METHOD, [self registeredName],
|
|
2125
|
+
status, sqlite3_errmsg(databaseTransaction->connection->db));
|
|
2126
|
+
}
|
|
2127
|
+
|
|
2128
|
+
// DELETE FROM 'pageTableName';
|
|
2129
|
+
|
|
2130
|
+
YDBLogVerbose(@"DELETE FROM '%@';", [self pageTableName]);
|
|
2131
|
+
|
|
2132
|
+
status = sqlite3_step(pageStatement);
|
|
2133
|
+
if (status != SQLITE_DONE)
|
|
2134
|
+
{
|
|
2135
|
+
YDBLogError(@"%@ (%@): Error in pageStatement: %d %s",
|
|
2136
|
+
THIS_METHOD, [self registeredName],
|
|
2137
|
+
status, sqlite3_errmsg(databaseTransaction->connection->db));
|
|
2138
|
+
}
|
|
2139
|
+
|
|
2140
|
+
sqlite3_reset(mapStatement);
|
|
2141
|
+
sqlite3_reset(pageStatement);
|
|
2142
|
+
}
|
|
2143
|
+
else // if (isNonPersistentView)
|
|
2144
|
+
{
|
|
2145
|
+
[mapTableTransaction removeAllObjects];
|
|
2146
|
+
[pageTableTransaction removeAllObjects];
|
|
2147
|
+
[pageMetadataTableTransaction removeAllObjects];
|
|
2148
|
+
}
|
|
2149
|
+
|
|
2150
|
+
for (NSString *group in viewConnection->group_pagesMetadata_dict)
|
|
2151
|
+
{
|
|
2152
|
+
[viewConnection->changes addObject:[YapDatabaseViewSectionChange resetGroup:group]];
|
|
2153
|
+
[viewConnection->mutatedGroups addObject:group];
|
|
2154
|
+
}
|
|
2155
|
+
|
|
2156
|
+
[viewConnection->group_pagesMetadata_dict removeAllObjects];
|
|
2157
|
+
[viewConnection->pageKey_group_dict removeAllObjects];
|
|
2158
|
+
|
|
2159
|
+
[viewConnection->mapCache removeAllObjects];
|
|
2160
|
+
[viewConnection->pageCache removeAllObjects];
|
|
2161
|
+
|
|
2162
|
+
[viewConnection->dirtyMaps removeAllObjects];
|
|
2163
|
+
[viewConnection->dirtyPages removeAllObjects];
|
|
2164
|
+
[viewConnection->dirtyLinks removeAllObjects];
|
|
2165
|
+
|
|
2166
|
+
viewConnection->reset = YES;
|
|
2167
|
+
}
|
|
2168
|
+
|
|
2169
|
+
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
2170
|
+
#pragma mark Cleanup & Commit
|
|
2171
|
+
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
2172
|
+
|
|
2173
|
+
- (void)splitOversizedPage:(YapDatabaseViewPage *)page withPageKey:(NSString *)pageKey toSize:(NSUInteger)maxPageSize
|
|
2174
|
+
{
|
|
2175
|
+
YDBLogAutoTrace();
|
|
2176
|
+
|
|
2177
|
+
// Find associated pageMetadata
|
|
2178
|
+
|
|
2179
|
+
NSString *group = [self groupForPageKey:pageKey];
|
|
2180
|
+
NSMutableArray *pagesMetadataForGroup = [viewConnection->group_pagesMetadata_dict objectForKey:group];
|
|
2181
|
+
|
|
2182
|
+
YapDatabaseViewPageMetadata *pageMetadata = nil;
|
|
2183
|
+
for (YapDatabaseViewPageMetadata *pm in pagesMetadataForGroup)
|
|
2184
|
+
{
|
|
2185
|
+
if ([pm->pageKey isEqualToString:pageKey])
|
|
2186
|
+
{
|
|
2187
|
+
pageMetadata = pm;
|
|
2188
|
+
break;
|
|
2189
|
+
}
|
|
2190
|
+
}
|
|
2191
|
+
|
|
2192
|
+
// Split the page as many times as needed to make it fit the designated maxPageSize
|
|
2193
|
+
|
|
2194
|
+
while (pageMetadata->count > maxPageSize)
|
|
2195
|
+
{
|
|
2196
|
+
// Get the current pageIndex.
|
|
2197
|
+
// This may change during iterations of the while loop.
|
|
2198
|
+
|
|
2199
|
+
NSUInteger pageIndex = [pagesMetadataForGroup indexOfObjectIdenticalTo:pageMetadata];
|
|
2200
|
+
|
|
2201
|
+
// Check to see if there's room in the previous page
|
|
2202
|
+
|
|
2203
|
+
if (pageIndex > 0)
|
|
2204
|
+
{
|
|
2205
|
+
YapDatabaseViewPageMetadata *prevPageMetadata = [pagesMetadataForGroup objectAtIndex:(pageIndex - 1)];
|
|
2206
|
+
|
|
2207
|
+
if (prevPageMetadata->count < maxPageSize)
|
|
2208
|
+
{
|
|
2209
|
+
// Move objects from beginning of page to end of previous page
|
|
2210
|
+
|
|
2211
|
+
YapDatabaseViewPage *prevPage = [self pageForPageKey:prevPageMetadata->pageKey];
|
|
2212
|
+
|
|
2213
|
+
NSUInteger excessInPage = pageMetadata->count - maxPageSize;
|
|
2214
|
+
NSUInteger spaceInPrevPage = maxPageSize - prevPageMetadata->count;
|
|
2215
|
+
|
|
2216
|
+
NSUInteger numToMove = MIN(excessInPage, spaceInPrevPage);
|
|
2217
|
+
|
|
2218
|
+
NSRange pageRange = NSMakeRange(0, numToMove); // beginning range
|
|
2219
|
+
NSRange prevPageRange = NSMakeRange([prevPage count], numToMove); // end range
|
|
2220
|
+
|
|
2221
|
+
[prevPage appendRange:pageRange ofPage:page];
|
|
2222
|
+
[page removeRange:pageRange];
|
|
2223
|
+
|
|
2224
|
+
// Update counts
|
|
2225
|
+
|
|
2226
|
+
pageMetadata->count = [page count];
|
|
2227
|
+
prevPageMetadata->count = [prevPage count];
|
|
2228
|
+
|
|
2229
|
+
// Mark prevPage as dirty.
|
|
2230
|
+
// The page is already marked as dirty.
|
|
2231
|
+
|
|
2232
|
+
[viewConnection->dirtyPages setObject:prevPage forKey:prevPageMetadata->pageKey];
|
|
2233
|
+
[viewConnection->pageCache setObject:prevPage forKey:prevPageMetadata->pageKey];
|
|
2234
|
+
|
|
2235
|
+
// Mark rowid mappings as dirty
|
|
2236
|
+
|
|
2237
|
+
[prevPage enumerateRowidsWithOptions:0
|
|
2238
|
+
range:prevPageRange
|
|
2239
|
+
usingBlock:^(int64_t rowid, NSUInteger index, BOOL *stop) {
|
|
2240
|
+
|
|
2241
|
+
NSNumber *number = @(rowid);
|
|
2242
|
+
|
|
2243
|
+
[viewConnection->dirtyMaps setObject:prevPageMetadata->pageKey forKey:number];
|
|
2244
|
+
[viewConnection->mapCache setObject:prevPageMetadata->pageKey forKey:number];
|
|
2245
|
+
}];
|
|
2246
|
+
|
|
2247
|
+
continue;
|
|
2248
|
+
}
|
|
2249
|
+
}
|
|
2250
|
+
|
|
2251
|
+
// Check to see if there's room in the next page
|
|
2252
|
+
|
|
2253
|
+
if ((pageIndex + 1) < [pagesMetadataForGroup count])
|
|
2254
|
+
{
|
|
2255
|
+
YapDatabaseViewPageMetadata *nextPageMetadata = [pagesMetadataForGroup objectAtIndex:(pageIndex + 1)];
|
|
2256
|
+
|
|
2257
|
+
if (nextPageMetadata->count < maxPageSize)
|
|
2258
|
+
{
|
|
2259
|
+
// Move objects from end of page to beginning of next page
|
|
2260
|
+
|
|
2261
|
+
YapDatabaseViewPage *nextPage = [self pageForPageKey:nextPageMetadata->pageKey];
|
|
2262
|
+
|
|
2263
|
+
NSUInteger excessInPage = pageMetadata->count - maxPageSize;
|
|
2264
|
+
NSUInteger spaceInNextPage = maxPageSize - nextPageMetadata->count;
|
|
2265
|
+
|
|
2266
|
+
NSUInteger numToMove = MIN(excessInPage, spaceInNextPage);
|
|
2267
|
+
|
|
2268
|
+
NSRange pageRange = NSMakeRange([page count] - numToMove, numToMove); // end range
|
|
2269
|
+
NSRange nextPageRange = NSMakeRange(0, numToMove); // beginning range
|
|
2270
|
+
|
|
2271
|
+
[nextPage prependRange:pageRange ofPage:page];
|
|
2272
|
+
[page removeRange:pageRange];
|
|
2273
|
+
|
|
2274
|
+
// Update counts
|
|
2275
|
+
|
|
2276
|
+
pageMetadata->count = [page count];
|
|
2277
|
+
nextPageMetadata->count = [nextPage count];
|
|
2278
|
+
|
|
2279
|
+
// Mark nextPage as dirty.
|
|
2280
|
+
// The page is already marked as dirty.
|
|
2281
|
+
|
|
2282
|
+
[viewConnection->dirtyPages setObject:nextPage forKey:nextPageMetadata->pageKey];
|
|
2283
|
+
[viewConnection->pageCache setObject:nextPage forKey:nextPageMetadata->pageKey];
|
|
2284
|
+
|
|
2285
|
+
// Mark rowid mappings as dirty
|
|
2286
|
+
|
|
2287
|
+
[nextPage enumerateRowidsWithOptions:0
|
|
2288
|
+
range:nextPageRange
|
|
2289
|
+
usingBlock:^(int64_t rowid, NSUInteger index, BOOL *stop) {
|
|
2290
|
+
|
|
2291
|
+
NSNumber *number = @(rowid);
|
|
2292
|
+
|
|
2293
|
+
[viewConnection->dirtyMaps setObject:nextPageMetadata->pageKey forKey:number];
|
|
2294
|
+
[viewConnection->mapCache setObject:nextPageMetadata->pageKey forKey:number];
|
|
2295
|
+
}];
|
|
2296
|
+
|
|
2297
|
+
continue;
|
|
2298
|
+
}
|
|
2299
|
+
}
|
|
2300
|
+
|
|
2301
|
+
// Create new page and pageMetadata.
|
|
2302
|
+
// Insert into array.
|
|
2303
|
+
|
|
2304
|
+
NSUInteger excessInPage = pageMetadata->count - maxPageSize;
|
|
2305
|
+
NSUInteger numToMove = MIN(excessInPage, maxPageSize);
|
|
2306
|
+
|
|
2307
|
+
NSString *newPageKey = [self generatePageKey];
|
|
2308
|
+
YapDatabaseViewPage *newPage = [[YapDatabaseViewPage alloc] initWithCapacity:numToMove];
|
|
2309
|
+
|
|
2310
|
+
// Create new pageMetadata
|
|
2311
|
+
|
|
2312
|
+
YapDatabaseViewPageMetadata *newPageMetadata = [[YapDatabaseViewPageMetadata alloc] init];
|
|
2313
|
+
newPageMetadata->pageKey = newPageKey;
|
|
2314
|
+
newPageMetadata->prevPageKey = pageMetadata->pageKey;
|
|
2315
|
+
newPageMetadata->group = pageMetadata->group;
|
|
2316
|
+
newPageMetadata->isNew = YES;
|
|
2317
|
+
|
|
2318
|
+
// Insert new pageMetadata into array
|
|
2319
|
+
|
|
2320
|
+
[pagesMetadataForGroup insertObject:newPageMetadata atIndex:(pageIndex + 1)];
|
|
2321
|
+
|
|
2322
|
+
[viewConnection->pageKey_group_dict setObject:newPageMetadata->group
|
|
2323
|
+
forKey:newPageMetadata->pageKey];
|
|
2324
|
+
|
|
2325
|
+
// Update linked-list (if needed)
|
|
2326
|
+
|
|
2327
|
+
if ((pageIndex + 2) < [pagesMetadataForGroup count])
|
|
2328
|
+
{
|
|
2329
|
+
YapDatabaseViewPageMetadata *nextPageMetadata = [pagesMetadataForGroup objectAtIndex:(pageIndex + 2)];
|
|
2330
|
+
nextPageMetadata->prevPageKey = newPageKey;
|
|
2331
|
+
|
|
2332
|
+
[viewConnection->dirtyLinks setObject:nextPageMetadata forKey:nextPageMetadata->pageKey];
|
|
2333
|
+
}
|
|
2334
|
+
|
|
2335
|
+
// Move objects from end of page to beginning of new page
|
|
2336
|
+
|
|
2337
|
+
NSRange pageRange = NSMakeRange([page count] - numToMove, numToMove); // end range
|
|
2338
|
+
|
|
2339
|
+
[newPage appendRange:pageRange ofPage:page];
|
|
2340
|
+
[page removeRange:pageRange];
|
|
2341
|
+
|
|
2342
|
+
// Update counts
|
|
2343
|
+
|
|
2344
|
+
pageMetadata->count = [page count];
|
|
2345
|
+
newPageMetadata->count = [newPage count];
|
|
2346
|
+
|
|
2347
|
+
// Mark newPage as dirty.
|
|
2348
|
+
// The page is already marked as dirty.
|
|
2349
|
+
|
|
2350
|
+
[viewConnection->dirtyPages setObject:newPage forKey:newPageKey];
|
|
2351
|
+
[viewConnection->pageCache setObject:newPage forKey:newPageKey];
|
|
2352
|
+
|
|
2353
|
+
// Mark rowid mappings as dirty
|
|
2354
|
+
|
|
2355
|
+
[newPage enumerateRowidsUsingBlock:^(int64_t rowid, NSUInteger idx, BOOL *stop) {
|
|
2356
|
+
|
|
2357
|
+
NSNumber *number = @(rowid);
|
|
2358
|
+
|
|
2359
|
+
[viewConnection->dirtyMaps setObject:newPageKey forKey:number];
|
|
2360
|
+
[viewConnection->mapCache setObject:newPageKey forKey:number];
|
|
2361
|
+
}];
|
|
2362
|
+
|
|
2363
|
+
} // end while (pageMetadata->count > maxPageSize)
|
|
2364
|
+
}
|
|
2365
|
+
|
|
2366
|
+
- (void)dropEmptyPage:(YapDatabaseViewPage *)page withPageKey:(NSString *)pageKey
|
|
2367
|
+
{
|
|
2368
|
+
YDBLogAutoTrace();
|
|
2369
|
+
|
|
2370
|
+
// Find associated pageMetadata
|
|
2371
|
+
|
|
2372
|
+
NSString *group = [self groupForPageKey:pageKey];
|
|
2373
|
+
NSMutableArray *pagesMetadataForGroup = [viewConnection->group_pagesMetadata_dict objectForKey:group];
|
|
2374
|
+
|
|
2375
|
+
YapDatabaseViewPageMetadata *pageMetadata = nil;
|
|
2376
|
+
NSUInteger pageIndex = 0;
|
|
2377
|
+
|
|
2378
|
+
for (YapDatabaseViewPageMetadata *pm in pagesMetadataForGroup)
|
|
2379
|
+
{
|
|
2380
|
+
if ([pm->pageKey isEqualToString:pageKey])
|
|
2381
|
+
{
|
|
2382
|
+
pageMetadata = pm;
|
|
2383
|
+
break;
|
|
2384
|
+
}
|
|
2385
|
+
|
|
2386
|
+
pageIndex++;
|
|
2387
|
+
}
|
|
2388
|
+
|
|
2389
|
+
NSAssert(pageMetadata != nil, @"Missing pageMetadata in group(%@)", group);
|
|
2390
|
+
|
|
2391
|
+
// Update linked list (if needed)
|
|
2392
|
+
|
|
2393
|
+
if ((pageIndex + 1) < [pagesMetadataForGroup count])
|
|
2394
|
+
{
|
|
2395
|
+
YapDatabaseViewPageMetadata *nextPageMetadata = [pagesMetadataForGroup objectAtIndex:(pageIndex + 1)];
|
|
2396
|
+
nextPageMetadata->prevPageKey = pageMetadata->prevPageKey;
|
|
2397
|
+
|
|
2398
|
+
[viewConnection->dirtyLinks setObject:nextPageMetadata forKey:nextPageMetadata->pageKey];
|
|
2399
|
+
}
|
|
2400
|
+
|
|
2401
|
+
// Drop page
|
|
2402
|
+
|
|
2403
|
+
[pagesMetadataForGroup removeObjectAtIndex:pageIndex];
|
|
2404
|
+
[viewConnection->pageKey_group_dict removeObjectForKey:pageMetadata->pageKey];
|
|
2405
|
+
|
|
2406
|
+
// Mark page as dropped
|
|
2407
|
+
|
|
2408
|
+
[viewConnection->dirtyPages setObject:[NSNull null] forKey:pageMetadata->pageKey];
|
|
2409
|
+
[viewConnection->pageCache removeObjectForKey:pageMetadata->pageKey];
|
|
2410
|
+
|
|
2411
|
+
[viewConnection->dirtyLinks removeObjectForKey:pageMetadata->pageKey];
|
|
2412
|
+
|
|
2413
|
+
// Maybe drop group
|
|
2414
|
+
|
|
2415
|
+
if ([pagesMetadataForGroup count] == 0)
|
|
2416
|
+
{
|
|
2417
|
+
YDBLogVerbose(@"Dropping empty group(%@)", pageMetadata->group);
|
|
2418
|
+
|
|
2419
|
+
[viewConnection->changes addObject:
|
|
2420
|
+
[YapDatabaseViewSectionChange deleteGroup:pageMetadata->group]];
|
|
2421
|
+
|
|
2422
|
+
[viewConnection->group_pagesMetadata_dict removeObjectForKey:pageMetadata->group];
|
|
2423
|
+
}
|
|
2424
|
+
}
|
|
2425
|
+
|
|
2426
|
+
/**
|
|
2427
|
+
* This method is only called if within a readwrite transaction.
|
|
2428
|
+
*
|
|
2429
|
+
* Extensions may implement it to perform any "cleanup" before the changeset is requested.
|
|
2430
|
+
* Remember, the changeset is requested before the commitTransaction method is invoked.
|
|
2431
|
+
**/
|
|
2432
|
+
- (void)preCommitReadWriteTransaction
|
|
2433
|
+
{
|
|
2434
|
+
YDBLogAutoTrace();
|
|
2435
|
+
|
|
2436
|
+
// During the readwrite transaction we do nothing to enforce the pageSize restriction.
|
|
2437
|
+
// Multiple modifications during a transaction make it non worthwhile.
|
|
2438
|
+
//
|
|
2439
|
+
// Instead we wait til the transaction has completed
|
|
2440
|
+
// and then we can perform all such cleanup in a single step.
|
|
2441
|
+
|
|
2442
|
+
NSUInteger maxPageSize = YAP_DATABASE_VIEW_MAX_PAGE_SIZE;
|
|
2443
|
+
|
|
2444
|
+
// Get all the dirty pageMetadata objects.
|
|
2445
|
+
// We snapshot the items so we can make modifications as we enumerate.
|
|
2446
|
+
|
|
2447
|
+
NSArray *pageKeys = [viewConnection->dirtyPages allKeys];
|
|
2448
|
+
|
|
2449
|
+
// Step 1 is to "expand" the oversized pages.
|
|
2450
|
+
//
|
|
2451
|
+
// This means either splitting them in 2,
|
|
2452
|
+
// or allowing items to spill over into a neighboring page (that has room).
|
|
2453
|
+
|
|
2454
|
+
for (NSString *pageKey in pageKeys)
|
|
2455
|
+
{
|
|
2456
|
+
YapDatabaseViewPage *page = [viewConnection->dirtyPages objectForKey:pageKey];
|
|
2457
|
+
|
|
2458
|
+
if ([page count] > maxPageSize)
|
|
2459
|
+
{
|
|
2460
|
+
[self splitOversizedPage:page withPageKey:pageKey toSize:maxPageSize];
|
|
2461
|
+
}
|
|
2462
|
+
}
|
|
2463
|
+
|
|
2464
|
+
// Step 2 is to "collapse" undersized pages.
|
|
2465
|
+
//
|
|
2466
|
+
// This means dropping empty pages,
|
|
2467
|
+
// and maybe combining a page with a neighboring page (that has room).
|
|
2468
|
+
//
|
|
2469
|
+
// Note: We do this after "expansion" to allow undersized pages to first accomodate overflow.
|
|
2470
|
+
|
|
2471
|
+
for (NSString *pageKey in pageKeys)
|
|
2472
|
+
{
|
|
2473
|
+
YapDatabaseViewPage *page = [viewConnection->dirtyPages objectForKey:pageKey];
|
|
2474
|
+
|
|
2475
|
+
if ([page count] == 0)
|
|
2476
|
+
{
|
|
2477
|
+
[self dropEmptyPage:page withPageKey:pageKey];
|
|
2478
|
+
}
|
|
2479
|
+
}
|
|
2480
|
+
}
|
|
2481
|
+
|
|
2482
|
+
- (void)commitTransaction
|
|
2483
|
+
{
|
|
2484
|
+
YDBLogAutoTrace();
|
|
2485
|
+
|
|
2486
|
+
// During the transaction we stored all changes in the "dirty" dictionaries.
|
|
2487
|
+
// This allows the view to make multiple changes to a page, yet only write it once.
|
|
2488
|
+
|
|
2489
|
+
YDBLogVerbose(@"viewConnection->dirtyPages: %@", viewConnection->dirtyPages);
|
|
2490
|
+
YDBLogVerbose(@"viewConnection->dirtyLinks: %@", viewConnection->dirtyLinks);
|
|
2491
|
+
YDBLogVerbose(@"viewConnection->dirtyMaps: %@", viewConnection->dirtyMaps);
|
|
2492
|
+
|
|
2493
|
+
if ([self isPersistentView])
|
|
2494
|
+
{
|
|
2495
|
+
// Persistent View: Step 1 of 3
|
|
2496
|
+
//
|
|
2497
|
+
// Write dirty pages to table (along with associated dirty metadata)
|
|
2498
|
+
|
|
2499
|
+
[viewConnection->dirtyPages enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) {
|
|
2500
|
+
|
|
2501
|
+
__unsafe_unretained NSString *pageKey = (NSString *)key;
|
|
2502
|
+
__unsafe_unretained YapDatabaseViewPage *page = (YapDatabaseViewPage *)obj;
|
|
2503
|
+
|
|
2504
|
+
BOOL needsInsert = NO;
|
|
2505
|
+
BOOL hasDirtyLink = NO;
|
|
2506
|
+
|
|
2507
|
+
YapDatabaseViewPageMetadata *pageMetadata = nil;
|
|
2508
|
+
|
|
2509
|
+
pageMetadata = [viewConnection->dirtyLinks objectForKey:pageKey];
|
|
2510
|
+
if (pageMetadata)
|
|
2511
|
+
{
|
|
2512
|
+
hasDirtyLink = YES;
|
|
2513
|
+
}
|
|
2514
|
+
else
|
|
2515
|
+
{
|
|
2516
|
+
NSString *group = [self groupForPageKey:pageKey];
|
|
2517
|
+
NSMutableArray *pagesMetadataForGroup = [viewConnection->group_pagesMetadata_dict objectForKey:group];
|
|
2518
|
+
|
|
2519
|
+
for (YapDatabaseViewPageMetadata *pm in pagesMetadataForGroup)
|
|
2520
|
+
{
|
|
2521
|
+
if ([pm->pageKey isEqualToString:pageKey])
|
|
2522
|
+
{
|
|
2523
|
+
pageMetadata = pm;
|
|
2524
|
+
break;
|
|
2525
|
+
}
|
|
2526
|
+
}
|
|
2527
|
+
}
|
|
2528
|
+
|
|
2529
|
+
if (pageMetadata && pageMetadata->isNew)
|
|
2530
|
+
{
|
|
2531
|
+
needsInsert = YES;
|
|
2532
|
+
pageMetadata->isNew = NO; // Clear flag
|
|
2533
|
+
}
|
|
2534
|
+
|
|
2535
|
+
if ((id)page == (id)[NSNull null])
|
|
2536
|
+
{
|
|
2537
|
+
sqlite3_stmt *statement = [viewConnection pageTable_removeForPageKeyStatement];
|
|
2538
|
+
if (statement == NULL) {
|
|
2539
|
+
*stop = YES;
|
|
2540
|
+
return;//from block
|
|
2541
|
+
}
|
|
2542
|
+
|
|
2543
|
+
// DELETE FROM "pageTableName" WHERE "pageKey" = ?;
|
|
2544
|
+
|
|
2545
|
+
YDBLogVerbose(@"DELETE FROM '%@' WHERE 'pageKey' = ?;\n"
|
|
2546
|
+
@" - pageKey: %@", [self pageTableName], pageKey);
|
|
2547
|
+
|
|
2548
|
+
YapDatabaseString _pageKey; MakeYapDatabaseString(&_pageKey, pageKey);
|
|
2549
|
+
sqlite3_bind_text(statement, 1, _pageKey.str, _pageKey.length, SQLITE_STATIC);
|
|
2550
|
+
|
|
2551
|
+
int status = sqlite3_step(statement);
|
|
2552
|
+
if (status != SQLITE_DONE)
|
|
2553
|
+
{
|
|
2554
|
+
YDBLogError(@"%@ (%@): Error executing statement[1a]: %d %s",
|
|
2555
|
+
THIS_METHOD, [self registeredName],
|
|
2556
|
+
status, sqlite3_errmsg(databaseTransaction->connection->db));
|
|
2557
|
+
}
|
|
2558
|
+
|
|
2559
|
+
sqlite3_clear_bindings(statement);
|
|
2560
|
+
sqlite3_reset(statement);
|
|
2561
|
+
FreeYapDatabaseString(&_pageKey);
|
|
2562
|
+
}
|
|
2563
|
+
else if (needsInsert)
|
|
2564
|
+
{
|
|
2565
|
+
sqlite3_stmt *statement = [viewConnection pageTable_insertForPageKeyStatement];
|
|
2566
|
+
if (statement == NULL) {
|
|
2567
|
+
*stop = YES;
|
|
2568
|
+
return;//from block
|
|
2569
|
+
}
|
|
2570
|
+
|
|
2571
|
+
// INSERT INTO "pageTableName"
|
|
2572
|
+
// ("pageKey", "group", "prevPageKey", "count", "data") VALUES (?, ?, ?, ?, ?);
|
|
2573
|
+
|
|
2574
|
+
YDBLogVerbose(@"INSERT INTO '%@'"
|
|
2575
|
+
@" ('pageKey', 'group', 'prevPageKey', 'count', 'data') VALUES (?,?,?,?,?);\n"
|
|
2576
|
+
@" - pageKey : %@\n"
|
|
2577
|
+
@" - group : %@\n"
|
|
2578
|
+
@" - prePageKey: %@\n"
|
|
2579
|
+
@" - count : %d", [self pageTableName], pageKey,
|
|
2580
|
+
pageMetadata->group, pageMetadata->prevPageKey, (int)pageMetadata->count);
|
|
2581
|
+
|
|
2582
|
+
YapDatabaseString _pageKey; MakeYapDatabaseString(&_pageKey, pageKey);
|
|
2583
|
+
sqlite3_bind_text(statement, 1, _pageKey.str, _pageKey.length, SQLITE_STATIC);
|
|
2584
|
+
|
|
2585
|
+
YapDatabaseString _group; MakeYapDatabaseString(&_group, pageMetadata->group);
|
|
2586
|
+
sqlite3_bind_text(statement, 2, _group.str, _group.length, SQLITE_STATIC);
|
|
2587
|
+
|
|
2588
|
+
YapDatabaseString _prevPageKey; MakeYapDatabaseString(&_prevPageKey, pageMetadata->prevPageKey);
|
|
2589
|
+
if (pageMetadata->prevPageKey) {
|
|
2590
|
+
sqlite3_bind_text(statement, 3, _prevPageKey.str, _prevPageKey.length, SQLITE_STATIC);
|
|
2591
|
+
}
|
|
2592
|
+
|
|
2593
|
+
sqlite3_bind_int(statement, 4, (int)(pageMetadata->count));
|
|
2594
|
+
|
|
2595
|
+
__attribute__((objc_precise_lifetime)) NSData *rawData = [self serializePage:page];
|
|
2596
|
+
sqlite3_bind_blob(statement, 5, rawData.bytes, (int)rawData.length, SQLITE_STATIC);
|
|
2597
|
+
|
|
2598
|
+
int status = sqlite3_step(statement);
|
|
2599
|
+
if (status != SQLITE_DONE)
|
|
2600
|
+
{
|
|
2601
|
+
YDBLogError(@"%@ (%@): Error executing statement[1b]: %d %s",
|
|
2602
|
+
THIS_METHOD, [self registeredName],
|
|
2603
|
+
status, sqlite3_errmsg(databaseTransaction->connection->db));
|
|
2604
|
+
}
|
|
2605
|
+
|
|
2606
|
+
sqlite3_clear_bindings(statement);
|
|
2607
|
+
sqlite3_reset(statement);
|
|
2608
|
+
FreeYapDatabaseString(&_prevPageKey);
|
|
2609
|
+
FreeYapDatabaseString(&_group);
|
|
2610
|
+
FreeYapDatabaseString(&_pageKey);
|
|
2611
|
+
}
|
|
2612
|
+
else if (hasDirtyLink)
|
|
2613
|
+
{
|
|
2614
|
+
sqlite3_stmt *statement = [viewConnection pageTable_updateAllForPageKeyStatement];
|
|
2615
|
+
if (statement == NULL) {
|
|
2616
|
+
*stop = YES;
|
|
2617
|
+
return;//from block
|
|
2618
|
+
}
|
|
2619
|
+
|
|
2620
|
+
// UPDATE "pageTableName" SET "prevPageKey" = ?, "count" = ?, "data" = ? WHERE "pageKey" = ?;
|
|
2621
|
+
|
|
2622
|
+
YDBLogVerbose(@"UPDATE '%@' SET 'prevPageKey' = ?, 'count' = ?, 'data' = ? WHERE 'pageKey' = ?;\n"
|
|
2623
|
+
@" - pageKey : %@\n"
|
|
2624
|
+
@" - prevPageKey: %@\n"
|
|
2625
|
+
@" - count : %d", [self pageTableName], pageKey,
|
|
2626
|
+
pageMetadata->prevPageKey, (int)pageMetadata->count);
|
|
2627
|
+
|
|
2628
|
+
YapDatabaseString _prevPageKey; MakeYapDatabaseString(&_prevPageKey, pageMetadata->prevPageKey);
|
|
2629
|
+
if (pageMetadata->prevPageKey) {
|
|
2630
|
+
sqlite3_bind_text(statement, 1, _prevPageKey.str, _prevPageKey.length, SQLITE_STATIC);
|
|
2631
|
+
}
|
|
2632
|
+
|
|
2633
|
+
sqlite3_bind_int(statement, 2, (int)(pageMetadata->count));
|
|
2634
|
+
|
|
2635
|
+
__attribute__((objc_precise_lifetime)) NSData *rawData = [self serializePage:page];
|
|
2636
|
+
sqlite3_bind_blob(statement, 3, rawData.bytes, (int)rawData.length, SQLITE_STATIC);
|
|
2637
|
+
|
|
2638
|
+
YapDatabaseString _pageKey; MakeYapDatabaseString(&_pageKey, pageKey);
|
|
2639
|
+
sqlite3_bind_text(statement, 4, _pageKey.str, _pageKey.length, SQLITE_STATIC);
|
|
2640
|
+
|
|
2641
|
+
int status = sqlite3_step(statement);
|
|
2642
|
+
if (status != SQLITE_DONE)
|
|
2643
|
+
{
|
|
2644
|
+
YDBLogError(@"%@ (%@): Error executing statement[1c]: %d %s",
|
|
2645
|
+
THIS_METHOD, [self registeredName],
|
|
2646
|
+
status, sqlite3_errmsg(databaseTransaction->connection->db));
|
|
2647
|
+
}
|
|
2648
|
+
|
|
2649
|
+
sqlite3_clear_bindings(statement);
|
|
2650
|
+
sqlite3_reset(statement);
|
|
2651
|
+
FreeYapDatabaseString(&_prevPageKey);
|
|
2652
|
+
FreeYapDatabaseString(&_pageKey);
|
|
2653
|
+
}
|
|
2654
|
+
else
|
|
2655
|
+
{
|
|
2656
|
+
sqlite3_stmt *statement = [viewConnection pageTable_updatePageForPageKeyStatement];
|
|
2657
|
+
if (statement == NULL) {
|
|
2658
|
+
*stop = YES;
|
|
2659
|
+
return;//from block
|
|
2660
|
+
}
|
|
2661
|
+
|
|
2662
|
+
// UPDATE "pageTableName" SET "count" = ?, "data" = ? WHERE "pageKey" = ?;
|
|
2663
|
+
|
|
2664
|
+
YDBLogVerbose(@"UPDATE '%@' SET 'count' = ?, 'data' = ? WHERE 'pageKey' = ?;\n"
|
|
2665
|
+
@" - pageKey: %@\n"
|
|
2666
|
+
@" - count : %d", [self pageTableName], pageKey, (int)(pageMetadata->count));
|
|
2667
|
+
|
|
2668
|
+
sqlite3_bind_int(statement, 1, (int)[page count]);
|
|
2669
|
+
|
|
2670
|
+
__attribute__((objc_precise_lifetime)) NSData *rawData = [self serializePage:page];
|
|
2671
|
+
sqlite3_bind_blob(statement, 2, rawData.bytes, (int)rawData.length, SQLITE_STATIC);
|
|
2672
|
+
|
|
2673
|
+
YapDatabaseString _pageKey; MakeYapDatabaseString(&_pageKey, pageKey);
|
|
2674
|
+
sqlite3_bind_text(statement, 3, _pageKey.str, _pageKey.length, SQLITE_STATIC);
|
|
2675
|
+
|
|
2676
|
+
int status = sqlite3_step(statement);
|
|
2677
|
+
if (status != SQLITE_DONE)
|
|
2678
|
+
{
|
|
2679
|
+
YDBLogError(@"%@ (%@): Error executing statement[1d]: %d %s",
|
|
2680
|
+
THIS_METHOD, [self registeredName],
|
|
2681
|
+
status, sqlite3_errmsg(databaseTransaction->connection->db));
|
|
2682
|
+
}
|
|
2683
|
+
|
|
2684
|
+
sqlite3_clear_bindings(statement);
|
|
2685
|
+
sqlite3_reset(statement);
|
|
2686
|
+
FreeYapDatabaseString(&_pageKey);
|
|
2687
|
+
}
|
|
2688
|
+
}];
|
|
2689
|
+
|
|
2690
|
+
// Persistent View: Step 2 of 3
|
|
2691
|
+
//
|
|
2692
|
+
// Write dirty prevPageKey values to table (those not also associated with dirty pages).
|
|
2693
|
+
// This happens when only the prevPageKey pointer is changed.
|
|
2694
|
+
|
|
2695
|
+
[viewConnection->dirtyLinks enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) {
|
|
2696
|
+
|
|
2697
|
+
NSString *pageKey = (NSString *)key;
|
|
2698
|
+
YapDatabaseViewPageMetadata *pageMetadata = (YapDatabaseViewPageMetadata *)obj;
|
|
2699
|
+
|
|
2700
|
+
if ([viewConnection->dirtyPages objectForKey:pageKey])
|
|
2701
|
+
{
|
|
2702
|
+
// Both the page and metadata were dirty, so we wrote them both to disk at the same time.
|
|
2703
|
+
// No need to write the metadata again.
|
|
2704
|
+
|
|
2705
|
+
return;//continue;
|
|
2706
|
+
}
|
|
2707
|
+
|
|
2708
|
+
sqlite3_stmt *statement = [viewConnection pageTable_updateLinkForPageKeyStatement];
|
|
2709
|
+
if (statement == NULL) {
|
|
2710
|
+
*stop = YES;
|
|
2711
|
+
return;//from block
|
|
2712
|
+
}
|
|
2713
|
+
|
|
2714
|
+
// UPDATE "pageTableName" SET "prevPageKey" = ? WHERE "pageKey" = ?;
|
|
2715
|
+
|
|
2716
|
+
YDBLogVerbose(@"UPDATE '%@' SET 'prevPageKey' = ? WHERE 'pageKey' = ?;\n"
|
|
2717
|
+
@" - pageKey : %@\n"
|
|
2718
|
+
@" - prevPageKey: %@", [self pageTableName], pageKey, pageMetadata->prevPageKey);
|
|
2719
|
+
|
|
2720
|
+
YapDatabaseString _prevPageKey; MakeYapDatabaseString(&_prevPageKey, pageMetadata->prevPageKey);
|
|
2721
|
+
if (pageMetadata->prevPageKey) {
|
|
2722
|
+
sqlite3_bind_text(statement, 1, _prevPageKey.str, _prevPageKey.length, SQLITE_STATIC);
|
|
2723
|
+
}
|
|
2724
|
+
|
|
2725
|
+
YapDatabaseString _pageKey; MakeYapDatabaseString(&_pageKey, pageKey);
|
|
2726
|
+
sqlite3_bind_text(statement, 2, _pageKey.str, _pageKey.length, SQLITE_STATIC);
|
|
2727
|
+
|
|
2728
|
+
int status = sqlite3_step(statement);
|
|
2729
|
+
if (status != SQLITE_DONE)
|
|
2730
|
+
{
|
|
2731
|
+
YDBLogError(@"%@ (%@): Error executing statement[2]: %d %s",
|
|
2732
|
+
THIS_METHOD, [self registeredName],
|
|
2733
|
+
status, sqlite3_errmsg(databaseTransaction->connection->db));
|
|
2734
|
+
}
|
|
2735
|
+
|
|
2736
|
+
sqlite3_clear_bindings(statement);
|
|
2737
|
+
sqlite3_reset(statement);
|
|
2738
|
+
FreeYapDatabaseString(&_prevPageKey);
|
|
2739
|
+
FreeYapDatabaseString(&_pageKey);
|
|
2740
|
+
}];
|
|
2741
|
+
|
|
2742
|
+
// Persistent View: Step 3 of 3
|
|
2743
|
+
//
|
|
2744
|
+
// Update the dirty rowid -> pageKey mappings.
|
|
2745
|
+
|
|
2746
|
+
[viewConnection->dirtyMaps enumerateKeysAndObjectsUsingBlock:^(id rowIdObj, id pageKeyObj, BOOL *stop) {
|
|
2747
|
+
|
|
2748
|
+
int64_t rowid = [(NSNumber *)rowIdObj longLongValue];
|
|
2749
|
+
__unsafe_unretained NSString *pageKey = (NSString *)pageKeyObj;
|
|
2750
|
+
|
|
2751
|
+
if ((id)pageKey == (id)[NSNull null])
|
|
2752
|
+
{
|
|
2753
|
+
sqlite3_stmt *statement = [viewConnection mapTable_removeForRowidStatement];
|
|
2754
|
+
if (statement == NULL)
|
|
2755
|
+
{
|
|
2756
|
+
*stop = YES;
|
|
2757
|
+
return;//continue;
|
|
2758
|
+
}
|
|
2759
|
+
|
|
2760
|
+
// DELETE FROM "mapTableName" WHERE "rowid" = ?;
|
|
2761
|
+
|
|
2762
|
+
YDBLogVerbose(@"DELETE FROM '%@' WHERE 'rowid' = ?;\n"
|
|
2763
|
+
@" - rowid : %lld\n", [self mapTableName], rowid);
|
|
2764
|
+
|
|
2765
|
+
sqlite3_bind_int64(statement, 1, rowid);
|
|
2766
|
+
|
|
2767
|
+
int status = sqlite3_step(statement);
|
|
2768
|
+
if (status != SQLITE_DONE)
|
|
2769
|
+
{
|
|
2770
|
+
YDBLogError(@"%@ (%@): Error executing statement[3a]: %d %s",
|
|
2771
|
+
THIS_METHOD, [self registeredName],
|
|
2772
|
+
status, sqlite3_errmsg(databaseTransaction->connection->db));
|
|
2773
|
+
}
|
|
2774
|
+
|
|
2775
|
+
sqlite3_clear_bindings(statement);
|
|
2776
|
+
sqlite3_reset(statement);
|
|
2777
|
+
}
|
|
2778
|
+
else
|
|
2779
|
+
{
|
|
2780
|
+
sqlite3_stmt *statement = [viewConnection mapTable_setPageKeyForRowidStatement];
|
|
2781
|
+
if (statement == NULL)
|
|
2782
|
+
{
|
|
2783
|
+
*stop = YES;
|
|
2784
|
+
return;//continue;
|
|
2785
|
+
}
|
|
2786
|
+
|
|
2787
|
+
// INSERT OR REPLACE INTO "mapTableName" ("rowid", "pageKey") VALUES (?, ?);
|
|
2788
|
+
|
|
2789
|
+
YDBLogVerbose(@"INSERT OR REPLACE INTO '%@' ('rowid', 'pageKey') VALUES (?, ?);\n"
|
|
2790
|
+
@" - rowid : %lld\n"
|
|
2791
|
+
@" - pageKey: %@", [self mapTableName], rowid, pageKey);
|
|
2792
|
+
|
|
2793
|
+
sqlite3_bind_int64(statement, 1, rowid);
|
|
2794
|
+
|
|
2795
|
+
YapDatabaseString _pageKey; MakeYapDatabaseString(&_pageKey, pageKey);
|
|
2796
|
+
sqlite3_bind_text(statement, 2, _pageKey.str, _pageKey.length, SQLITE_STATIC);
|
|
2797
|
+
|
|
2798
|
+
int status = sqlite3_step(statement);
|
|
2799
|
+
if (status != SQLITE_DONE)
|
|
2800
|
+
{
|
|
2801
|
+
YDBLogError(@"%@ (%@): Error executing statement[3b]: %d %s",
|
|
2802
|
+
THIS_METHOD, [self registeredName],
|
|
2803
|
+
status, sqlite3_errmsg(databaseTransaction->connection->db));
|
|
2804
|
+
}
|
|
2805
|
+
|
|
2806
|
+
sqlite3_clear_bindings(statement);
|
|
2807
|
+
sqlite3_reset(statement);
|
|
2808
|
+
FreeYapDatabaseString(&_pageKey);
|
|
2809
|
+
}
|
|
2810
|
+
}];
|
|
2811
|
+
}
|
|
2812
|
+
else // if (isNonPersistentView)
|
|
2813
|
+
{
|
|
2814
|
+
// Memory View: Step 1 of 3
|
|
2815
|
+
//
|
|
2816
|
+
// Write dirty pages to table
|
|
2817
|
+
|
|
2818
|
+
BOOL hasDirtyPages = ([viewConnection->dirtyPages count] > 0);
|
|
2819
|
+
BOOL hasDirtyLinks = ([viewConnection->dirtyLinks count] > 0);
|
|
2820
|
+
BOOL hasDirtyMaps = ([viewConnection->dirtyMaps count] > 0);
|
|
2821
|
+
|
|
2822
|
+
if (hasDirtyPages)
|
|
2823
|
+
{
|
|
2824
|
+
[pageTableTransaction modifyWithBlock:^{ @autoreleasepool {
|
|
2825
|
+
|
|
2826
|
+
[viewConnection->dirtyPages enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) {
|
|
2827
|
+
|
|
2828
|
+
__unsafe_unretained NSString *pageKey = (NSString *)key;
|
|
2829
|
+
__unsafe_unretained YapDatabaseViewPage *page = (YapDatabaseViewPage *)obj;
|
|
2830
|
+
|
|
2831
|
+
if ((id)page == (id)[NSNull null])
|
|
2832
|
+
{
|
|
2833
|
+
[pageTableTransaction removeObjectForKey:pageKey];
|
|
2834
|
+
}
|
|
2835
|
+
else
|
|
2836
|
+
{
|
|
2837
|
+
[pageTableTransaction setObject:[page copy] forKey:pageKey];
|
|
2838
|
+
}
|
|
2839
|
+
}];
|
|
2840
|
+
}}];
|
|
2841
|
+
}
|
|
2842
|
+
// Memory View: Step 2 of 3
|
|
2843
|
+
//
|
|
2844
|
+
// Write dirty pageMetadata to table.
|
|
2845
|
+
// This includes anything referenced by dirtyPages or dirtyLinks.
|
|
2846
|
+
|
|
2847
|
+
if (hasDirtyPages || hasDirtyLinks)
|
|
2848
|
+
{
|
|
2849
|
+
[pageMetadataTableTransaction modifyWithBlock:^{ @autoreleasepool {
|
|
2850
|
+
|
|
2851
|
+
[viewConnection->dirtyPages enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) {
|
|
2852
|
+
|
|
2853
|
+
__unsafe_unretained NSString *pageKey = (NSString *)key;
|
|
2854
|
+
__unsafe_unretained YapDatabaseViewPage *page = (YapDatabaseViewPage *)obj;
|
|
2855
|
+
|
|
2856
|
+
if ((id)page == (id)[NSNull null])
|
|
2857
|
+
{
|
|
2858
|
+
[pageMetadataTableTransaction removeObjectForKey:pageKey];
|
|
2859
|
+
}
|
|
2860
|
+
else
|
|
2861
|
+
{
|
|
2862
|
+
YapDatabaseViewPageMetadata *pageMetadata = nil;
|
|
2863
|
+
|
|
2864
|
+
pageMetadata = [viewConnection->dirtyLinks objectForKey:pageKey];
|
|
2865
|
+
if (pageMetadata == nil)
|
|
2866
|
+
{
|
|
2867
|
+
NSString *group = [self groupForPageKey:pageKey];
|
|
2868
|
+
NSMutableArray *pagesMetadataForGroup =
|
|
2869
|
+
[viewConnection->group_pagesMetadata_dict objectForKey:group];
|
|
2870
|
+
|
|
2871
|
+
for (YapDatabaseViewPageMetadata *pm in pagesMetadataForGroup)
|
|
2872
|
+
{
|
|
2873
|
+
if ([pm->pageKey isEqualToString:pageKey])
|
|
2874
|
+
{
|
|
2875
|
+
pageMetadata = pm;
|
|
2876
|
+
break;
|
|
2877
|
+
}
|
|
2878
|
+
}
|
|
2879
|
+
}
|
|
2880
|
+
|
|
2881
|
+
if (pageMetadata)
|
|
2882
|
+
{
|
|
2883
|
+
if (pageMetadata->isNew)
|
|
2884
|
+
pageMetadata->isNew = NO; // Clear flag
|
|
2885
|
+
|
|
2886
|
+
[pageMetadataTableTransaction setObject:[pageMetadata copy] forKey:pageKey];
|
|
2887
|
+
}
|
|
2888
|
+
}
|
|
2889
|
+
}];
|
|
2890
|
+
|
|
2891
|
+
[viewConnection->dirtyLinks enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) {
|
|
2892
|
+
|
|
2893
|
+
__unsafe_unretained NSString *pageKey = (NSString *)key;
|
|
2894
|
+
__unsafe_unretained YapDatabaseViewPageMetadata *pageMetadata = (YapDatabaseViewPageMetadata *)obj;
|
|
2895
|
+
|
|
2896
|
+
if ([viewConnection->dirtyPages objectForKey:pageKey])
|
|
2897
|
+
{
|
|
2898
|
+
// Both the page and metadata were dirty, so we wrote them both to disk at the same time.
|
|
2899
|
+
// No need to write the metadata again.
|
|
2900
|
+
|
|
2901
|
+
return;//continue;
|
|
2902
|
+
}
|
|
2903
|
+
|
|
2904
|
+
[pageMetadataTableTransaction setObject:[pageMetadata copy] forKey:pageKey];
|
|
2905
|
+
}];
|
|
2906
|
+
}}];
|
|
2907
|
+
}
|
|
2908
|
+
|
|
2909
|
+
// Memory View: Step 3 of 3
|
|
2910
|
+
//
|
|
2911
|
+
// Update the dirty rowid -> pageKey mappings.
|
|
2912
|
+
|
|
2913
|
+
if (hasDirtyMaps)
|
|
2914
|
+
{
|
|
2915
|
+
[mapTableTransaction modifyWithBlock:^{ @autoreleasepool {
|
|
2916
|
+
|
|
2917
|
+
[viewConnection->dirtyMaps enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) {
|
|
2918
|
+
|
|
2919
|
+
__unsafe_unretained NSNumber *rowidNumber = (NSNumber *)key;
|
|
2920
|
+
__unsafe_unretained NSString *pageKey = (NSString *)obj;
|
|
2921
|
+
|
|
2922
|
+
if ((id)pageKey == (id)[NSNull null])
|
|
2923
|
+
{
|
|
2924
|
+
[mapTableTransaction removeObjectForKey:rowidNumber];
|
|
2925
|
+
}
|
|
2926
|
+
else
|
|
2927
|
+
{
|
|
2928
|
+
[mapTableTransaction setObject:pageKey forKey:rowidNumber];
|
|
2929
|
+
}
|
|
2930
|
+
}];
|
|
2931
|
+
}}];
|
|
2932
|
+
}
|
|
2933
|
+
|
|
2934
|
+
if (hasDirtyPages || viewConnection->reset)
|
|
2935
|
+
{
|
|
2936
|
+
[pageTableTransaction commit];
|
|
2937
|
+
}
|
|
2938
|
+
if (hasDirtyPages || hasDirtyLinks || viewConnection->reset)
|
|
2939
|
+
{
|
|
2940
|
+
[pageMetadataTableTransaction commit];
|
|
2941
|
+
}
|
|
2942
|
+
if (hasDirtyMaps || viewConnection->reset)
|
|
2943
|
+
{
|
|
2944
|
+
[mapTableTransaction commit];
|
|
2945
|
+
}
|
|
2946
|
+
}
|
|
2947
|
+
|
|
2948
|
+
// Commit is complete.
|
|
2949
|
+
// Cleanup time.
|
|
2950
|
+
|
|
2951
|
+
[viewConnection postCommitCleanup];
|
|
2952
|
+
|
|
2953
|
+
// An extensionTransaction is only valid within the scope of its encompassing databaseTransaction.
|
|
2954
|
+
// I imagine this may occasionally be misunderstood, and developers may attempt to store the extension in an ivar,
|
|
2955
|
+
// and then use it outside the context of the database transaction block.
|
|
2956
|
+
// Thus, this code is here as a safety net to ensure that such accidental misuse doesn't do any damage.
|
|
2957
|
+
|
|
2958
|
+
viewConnection = nil; // Do not remove !
|
|
2959
|
+
databaseTransaction = nil; // Do not remove !
|
|
2960
|
+
}
|
|
2961
|
+
|
|
2962
|
+
- (void)rollbackTransaction
|
|
2963
|
+
{
|
|
2964
|
+
[viewConnection postRollbackCleanup];
|
|
2965
|
+
|
|
2966
|
+
// An extensionTransaction is only valid within the scope of its encompassing databaseTransaction.
|
|
2967
|
+
// I imagine this may occasionally be misunderstood, and developers may attempt to store the extension in an ivar,
|
|
2968
|
+
// and then use it outside the context of the database transaction block.
|
|
2969
|
+
// Thus, this code is here as a safety net to ensure that such accidental misuse doesn't do any damage.
|
|
2970
|
+
|
|
2971
|
+
viewConnection = nil; // Do not remove !
|
|
2972
|
+
databaseTransaction = nil; // Do not remove !
|
|
2973
|
+
}
|
|
2974
|
+
|
|
2975
|
+
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
2976
|
+
#pragma mark YapDatabaseExtensionTransaction_Hooks
|
|
2977
|
+
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
2978
|
+
|
|
2979
|
+
/**
|
|
2980
|
+
* YapDatabase extension hook.
|
|
2981
|
+
* This method is invoked by a YapDatabaseReadWriteTransaction as a post-operation-hook.
|
|
2982
|
+
**/
|
|
2983
|
+
- (void)handleInsertObject:(id)object
|
|
2984
|
+
forKey:(NSString *)key
|
|
2985
|
+
inCollection:(NSString *)collection
|
|
2986
|
+
withMetadata:(id)metadata
|
|
2987
|
+
rowid:(int64_t)rowid
|
|
2988
|
+
{
|
|
2989
|
+
YDBLogAutoTrace();
|
|
2990
|
+
|
|
2991
|
+
__unsafe_unretained YapDatabaseView *view = viewConnection->view;
|
|
2992
|
+
|
|
2993
|
+
// Invoke the grouping block to find out if the object should be included in the view.
|
|
2994
|
+
|
|
2995
|
+
NSString *group = nil;
|
|
2996
|
+
NSSet *allowedCollections = view->options.allowedCollections;
|
|
2997
|
+
|
|
2998
|
+
if (!allowedCollections || [allowedCollections containsObject:collection])
|
|
2999
|
+
{
|
|
3000
|
+
if (view->groupingBlockType == YapDatabaseViewBlockTypeWithKey)
|
|
3001
|
+
{
|
|
3002
|
+
__unsafe_unretained YapDatabaseViewGroupingWithKeyBlock groupingBlock =
|
|
3003
|
+
(YapDatabaseViewGroupingWithKeyBlock)view->groupingBlock;
|
|
3004
|
+
|
|
3005
|
+
group = groupingBlock(collection, key);
|
|
3006
|
+
}
|
|
3007
|
+
else if (view->groupingBlockType == YapDatabaseViewBlockTypeWithObject)
|
|
3008
|
+
{
|
|
3009
|
+
__unsafe_unretained YapDatabaseViewGroupingWithObjectBlock groupingBlock =
|
|
3010
|
+
(YapDatabaseViewGroupingWithObjectBlock)view->groupingBlock;
|
|
3011
|
+
|
|
3012
|
+
group = groupingBlock(collection, key, object);
|
|
3013
|
+
}
|
|
3014
|
+
else if (view->groupingBlockType == YapDatabaseViewBlockTypeWithMetadata)
|
|
3015
|
+
{
|
|
3016
|
+
__unsafe_unretained YapDatabaseViewGroupingWithMetadataBlock groupingBlock =
|
|
3017
|
+
(YapDatabaseViewGroupingWithMetadataBlock)view->groupingBlock;
|
|
3018
|
+
|
|
3019
|
+
group = groupingBlock(collection, key, metadata);
|
|
3020
|
+
}
|
|
3021
|
+
else
|
|
3022
|
+
{
|
|
3023
|
+
__unsafe_unretained YapDatabaseViewGroupingWithRowBlock groupingBlock =
|
|
3024
|
+
(YapDatabaseViewGroupingWithRowBlock)view->groupingBlock;
|
|
3025
|
+
|
|
3026
|
+
group = groupingBlock(collection, key, object, metadata);
|
|
3027
|
+
}
|
|
3028
|
+
}
|
|
3029
|
+
|
|
3030
|
+
if (group == nil)
|
|
3031
|
+
{
|
|
3032
|
+
// This was an insert operation, so we know the key wasn't already in the view.
|
|
3033
|
+
}
|
|
3034
|
+
else
|
|
3035
|
+
{
|
|
3036
|
+
// Add key to view.
|
|
3037
|
+
// This was an insert operation, so we know the key wasn't already in the view.
|
|
3038
|
+
|
|
3039
|
+
YapCollectionKey *collectionKey = [[YapCollectionKey alloc] initWithCollection:collection key:key];
|
|
3040
|
+
|
|
3041
|
+
int flags = (YapDatabaseViewChangedObject | YapDatabaseViewChangedMetadata);
|
|
3042
|
+
|
|
3043
|
+
[self insertRowid:rowid
|
|
3044
|
+
collectionKey:collectionKey
|
|
3045
|
+
object:object
|
|
3046
|
+
metadata:metadata
|
|
3047
|
+
inGroup:group withChanges:flags isNew:YES];
|
|
3048
|
+
}
|
|
3049
|
+
|
|
3050
|
+
lastHandledGroup = group;
|
|
3051
|
+
}
|
|
3052
|
+
|
|
3053
|
+
/**
|
|
3054
|
+
* YapDatabase extension hook.
|
|
3055
|
+
* This method is invoked by a YapDatabaseReadWriteTransaction as a post-operation-hook.
|
|
3056
|
+
**/
|
|
3057
|
+
- (void)handleUpdateObject:(id)object
|
|
3058
|
+
forKey:(NSString *)key
|
|
3059
|
+
inCollection:(NSString *)collection
|
|
3060
|
+
withMetadata:(id)metadata
|
|
3061
|
+
rowid:(int64_t)rowid
|
|
3062
|
+
{
|
|
3063
|
+
YDBLogAutoTrace();
|
|
3064
|
+
|
|
3065
|
+
NSParameterAssert(key != nil);
|
|
3066
|
+
NSParameterAssert(collection != nil);
|
|
3067
|
+
|
|
3068
|
+
__unsafe_unretained YapDatabaseView *view = viewConnection->view;
|
|
3069
|
+
|
|
3070
|
+
// Invoke the grouping block to find out if the object should be included in the view.
|
|
3071
|
+
|
|
3072
|
+
NSString *group = nil;
|
|
3073
|
+
NSSet *allowedCollections = view->options.allowedCollections;
|
|
3074
|
+
|
|
3075
|
+
if (!allowedCollections || [allowedCollections containsObject:collection])
|
|
3076
|
+
{
|
|
3077
|
+
if (view->groupingBlockType == YapDatabaseViewBlockTypeWithKey)
|
|
3078
|
+
{
|
|
3079
|
+
__unsafe_unretained YapDatabaseViewGroupingWithKeyBlock groupingBlock =
|
|
3080
|
+
(YapDatabaseViewGroupingWithKeyBlock)view->groupingBlock;
|
|
3081
|
+
|
|
3082
|
+
group = groupingBlock(collection, key);
|
|
3083
|
+
}
|
|
3084
|
+
else if (view->groupingBlockType == YapDatabaseViewBlockTypeWithObject)
|
|
3085
|
+
{
|
|
3086
|
+
__unsafe_unretained YapDatabaseViewGroupingWithObjectBlock groupingBlock =
|
|
3087
|
+
(YapDatabaseViewGroupingWithObjectBlock)view->groupingBlock;
|
|
3088
|
+
|
|
3089
|
+
group = groupingBlock(collection, key, object);
|
|
3090
|
+
}
|
|
3091
|
+
else if (view->groupingBlockType == YapDatabaseViewBlockTypeWithMetadata)
|
|
3092
|
+
{
|
|
3093
|
+
__unsafe_unretained YapDatabaseViewGroupingWithMetadataBlock groupingBlock =
|
|
3094
|
+
(YapDatabaseViewGroupingWithMetadataBlock)view->groupingBlock;
|
|
3095
|
+
|
|
3096
|
+
group = groupingBlock(collection, key, metadata);
|
|
3097
|
+
}
|
|
3098
|
+
else
|
|
3099
|
+
{
|
|
3100
|
+
__unsafe_unretained YapDatabaseViewGroupingWithRowBlock groupingBlock =
|
|
3101
|
+
(YapDatabaseViewGroupingWithRowBlock)view->groupingBlock;
|
|
3102
|
+
|
|
3103
|
+
group = groupingBlock(collection, key, object, metadata);
|
|
3104
|
+
}
|
|
3105
|
+
}
|
|
3106
|
+
|
|
3107
|
+
YapCollectionKey *collectionKey = [[YapCollectionKey alloc] initWithCollection:collection key:key];
|
|
3108
|
+
|
|
3109
|
+
if (group == nil)
|
|
3110
|
+
{
|
|
3111
|
+
// Remove key from view (if needed).
|
|
3112
|
+
// This was an update operation, so the key may have previously been in the view.
|
|
3113
|
+
|
|
3114
|
+
[self removeRowid:rowid collectionKey:collectionKey];
|
|
3115
|
+
}
|
|
3116
|
+
else
|
|
3117
|
+
{
|
|
3118
|
+
// Add key to view (or update position).
|
|
3119
|
+
// This was an update operation, so the key may have previously been in the view.
|
|
3120
|
+
|
|
3121
|
+
int flags = (YapDatabaseViewChangedObject | YapDatabaseViewChangedMetadata);
|
|
3122
|
+
|
|
3123
|
+
[self insertRowid:rowid
|
|
3124
|
+
collectionKey:collectionKey
|
|
3125
|
+
object:object
|
|
3126
|
+
metadata:metadata
|
|
3127
|
+
inGroup:group withChanges:flags isNew:NO];
|
|
3128
|
+
}
|
|
3129
|
+
|
|
3130
|
+
lastHandledGroup = group;
|
|
3131
|
+
}
|
|
3132
|
+
|
|
3133
|
+
/**
|
|
3134
|
+
* YapDatabase extension hook.
|
|
3135
|
+
* This method is invoked by a YapDatabaseReadWriteTransaction as a post-operation-hook.
|
|
3136
|
+
**/
|
|
3137
|
+
- (void)handleUpdateMetadata:(id)metadata
|
|
3138
|
+
forKey:(NSString *)key
|
|
3139
|
+
inCollection:(NSString *)collection
|
|
3140
|
+
withRowid:(int64_t)rowid
|
|
3141
|
+
{
|
|
3142
|
+
YDBLogAutoTrace();
|
|
3143
|
+
|
|
3144
|
+
__unsafe_unretained YapDatabaseView *view = viewConnection->view;
|
|
3145
|
+
|
|
3146
|
+
YapCollectionKey *collectionKey = [[YapCollectionKey alloc] initWithCollection:collection key:key];
|
|
3147
|
+
|
|
3148
|
+
// Invoke the grouping block to find out if the object should be included in the view.
|
|
3149
|
+
|
|
3150
|
+
id object = nil;
|
|
3151
|
+
NSString *group = nil;
|
|
3152
|
+
|
|
3153
|
+
if (view->groupingBlockType == YapDatabaseViewBlockTypeWithKey ||
|
|
3154
|
+
view->groupingBlockType == YapDatabaseViewBlockTypeWithObject)
|
|
3155
|
+
{
|
|
3156
|
+
// Grouping is based on the key or object.
|
|
3157
|
+
// Neither have changed, and thus the group hasn't changed.
|
|
3158
|
+
|
|
3159
|
+
NSString *pageKey = [self pageKeyForRowid:rowid];
|
|
3160
|
+
group = [self groupForPageKey:pageKey];
|
|
3161
|
+
|
|
3162
|
+
if (group == nil)
|
|
3163
|
+
{
|
|
3164
|
+
// Nothing to do.
|
|
3165
|
+
// The key wasn't previously in the view, and still isn't in the view.
|
|
3166
|
+
lastHandledGroup = group;
|
|
3167
|
+
return;
|
|
3168
|
+
}
|
|
3169
|
+
|
|
3170
|
+
if (view->sortingBlockType == YapDatabaseViewBlockTypeWithKey ||
|
|
3171
|
+
view->sortingBlockType == YapDatabaseViewBlockTypeWithObject)
|
|
3172
|
+
{
|
|
3173
|
+
// Nothing has moved because the group hasn't changed and
|
|
3174
|
+
// nothing has changed that relates to sorting.
|
|
3175
|
+
|
|
3176
|
+
int flags = YapDatabaseViewChangedMetadata;
|
|
3177
|
+
NSUInteger existingIndex = [self indexForRowid:rowid inGroup:group withPageKey:pageKey];
|
|
3178
|
+
|
|
3179
|
+
[viewConnection->changes addObject:
|
|
3180
|
+
[YapDatabaseViewRowChange updateKey:collectionKey changes:flags inGroup:group atIndex:existingIndex]];
|
|
3181
|
+
}
|
|
3182
|
+
else
|
|
3183
|
+
{
|
|
3184
|
+
// Sorting is based on the metadata, which has changed.
|
|
3185
|
+
// So the sort order may possibly have changed.
|
|
3186
|
+
|
|
3187
|
+
// From previous if statement (above) we know:
|
|
3188
|
+
// sortingBlockType is metadata or objectAndMetadata
|
|
3189
|
+
|
|
3190
|
+
if (view->sortingBlockType == YapDatabaseViewBlockTypeWithRow)
|
|
3191
|
+
{
|
|
3192
|
+
// Need the object for the sorting block
|
|
3193
|
+
object = [databaseTransaction objectForKey:key inCollection:collection withRowid:rowid];
|
|
3194
|
+
}
|
|
3195
|
+
|
|
3196
|
+
int flags = YapDatabaseViewChangedMetadata;
|
|
3197
|
+
|
|
3198
|
+
[self insertRowid:rowid
|
|
3199
|
+
collectionKey:collectionKey
|
|
3200
|
+
object:object
|
|
3201
|
+
metadata:metadata
|
|
3202
|
+
inGroup:group withChanges:flags isNew:NO];
|
|
3203
|
+
}
|
|
3204
|
+
}
|
|
3205
|
+
else
|
|
3206
|
+
{
|
|
3207
|
+
// Grouping is based on metadata or objectAndMetadata.
|
|
3208
|
+
// Invoke groupingBlock to see what the new group is.
|
|
3209
|
+
|
|
3210
|
+
NSSet *allowedCollections = view->options.allowedCollections;
|
|
3211
|
+
|
|
3212
|
+
if (!allowedCollections || [allowedCollections containsObject:collection])
|
|
3213
|
+
{
|
|
3214
|
+
if (view->groupingBlockType == YapDatabaseViewBlockTypeWithMetadata)
|
|
3215
|
+
{
|
|
3216
|
+
__unsafe_unretained YapDatabaseViewGroupingWithMetadataBlock groupingBlock =
|
|
3217
|
+
(YapDatabaseViewGroupingWithMetadataBlock)view->groupingBlock;
|
|
3218
|
+
|
|
3219
|
+
group = groupingBlock(collection, key, metadata);
|
|
3220
|
+
}
|
|
3221
|
+
else
|
|
3222
|
+
{
|
|
3223
|
+
__unsafe_unretained YapDatabaseViewGroupingWithRowBlock groupingBlock =
|
|
3224
|
+
(YapDatabaseViewGroupingWithRowBlock)view->groupingBlock;
|
|
3225
|
+
|
|
3226
|
+
object = [databaseTransaction objectForKey:key inCollection:collection withRowid:rowid];
|
|
3227
|
+
group = groupingBlock(collection, key, object, metadata);
|
|
3228
|
+
}
|
|
3229
|
+
}
|
|
3230
|
+
|
|
3231
|
+
if (group == nil)
|
|
3232
|
+
{
|
|
3233
|
+
// The key is not included in the view.
|
|
3234
|
+
// Remove key from view (if needed).
|
|
3235
|
+
|
|
3236
|
+
[self removeRowid:rowid collectionKey:collectionKey];
|
|
3237
|
+
}
|
|
3238
|
+
else
|
|
3239
|
+
{
|
|
3240
|
+
if (view->sortingBlockType == YapDatabaseViewBlockTypeWithKey ||
|
|
3241
|
+
view->sortingBlockType == YapDatabaseViewBlockTypeWithObject)
|
|
3242
|
+
{
|
|
3243
|
+
// Sorting is based on the key or object, neither of which has changed.
|
|
3244
|
+
// So if the group hasn't changed, then the sort order hasn't changed.
|
|
3245
|
+
|
|
3246
|
+
NSString *existingPageKey = [self pageKeyForRowid:rowid];
|
|
3247
|
+
NSString *existingGroup = [self groupForPageKey:existingPageKey];
|
|
3248
|
+
|
|
3249
|
+
if ([group isEqualToString:existingGroup])
|
|
3250
|
+
{
|
|
3251
|
+
// Nothing left to do.
|
|
3252
|
+
// The group didn't change, and the sort order cannot change (because the object didn't change).
|
|
3253
|
+
|
|
3254
|
+
int flags = YapDatabaseViewChangedMetadata;
|
|
3255
|
+
NSUInteger existingIndex = [self indexForRowid:rowid inGroup:group withPageKey:existingPageKey];
|
|
3256
|
+
|
|
3257
|
+
[viewConnection->changes addObject:
|
|
3258
|
+
[YapDatabaseViewRowChange updateKey:collectionKey
|
|
3259
|
+
changes:flags
|
|
3260
|
+
inGroup:group
|
|
3261
|
+
atIndex:existingIndex]];
|
|
3262
|
+
|
|
3263
|
+
lastHandledGroup = group;
|
|
3264
|
+
return;
|
|
3265
|
+
}
|
|
3266
|
+
}
|
|
3267
|
+
|
|
3268
|
+
if (object == nil && (view->sortingBlockType == YapDatabaseViewBlockTypeWithObject ||
|
|
3269
|
+
view->sortingBlockType == YapDatabaseViewBlockTypeWithRow ))
|
|
3270
|
+
{
|
|
3271
|
+
// Need the object for the sorting block
|
|
3272
|
+
object = [databaseTransaction objectForKey:key inCollection:collection withRowid:rowid];
|
|
3273
|
+
}
|
|
3274
|
+
|
|
3275
|
+
int flags = YapDatabaseViewChangedMetadata;
|
|
3276
|
+
|
|
3277
|
+
[self insertRowid:rowid
|
|
3278
|
+
collectionKey:collectionKey
|
|
3279
|
+
object:object
|
|
3280
|
+
metadata:metadata
|
|
3281
|
+
inGroup:group withChanges:flags isNew:NO];
|
|
3282
|
+
}
|
|
3283
|
+
}
|
|
3284
|
+
|
|
3285
|
+
lastHandledGroup = group;
|
|
3286
|
+
}
|
|
3287
|
+
|
|
3288
|
+
/**
|
|
3289
|
+
* YapDatabase extension hook.
|
|
3290
|
+
* This method is invoked by a YapDatabaseReadWriteTransaction as a post-operation-hook.
|
|
3291
|
+
**/
|
|
3292
|
+
- (void)handleTouchObjectForKey:(NSString *)key inCollection:(NSString *)collection withRowid:(int64_t)rowid
|
|
3293
|
+
{
|
|
3294
|
+
YDBLogAutoTrace();
|
|
3295
|
+
|
|
3296
|
+
// Almost the same as touchRowForKey:inCollection:
|
|
3297
|
+
|
|
3298
|
+
NSString *pageKey = [self pageKeyForRowid:rowid];
|
|
3299
|
+
if (pageKey)
|
|
3300
|
+
{
|
|
3301
|
+
NSString *group = [self groupForPageKey:pageKey];
|
|
3302
|
+
NSUInteger index = [self indexForRowid:rowid inGroup:group withPageKey:pageKey];
|
|
3303
|
+
|
|
3304
|
+
YapCollectionKey *collectionKey = [[YapCollectionKey alloc] initWithCollection:collection key:key];
|
|
3305
|
+
int flags = (YapDatabaseViewChangedObject | YapDatabaseViewChangedMetadata);
|
|
3306
|
+
|
|
3307
|
+
[viewConnection->changes addObject:
|
|
3308
|
+
[YapDatabaseViewRowChange updateKey:collectionKey changes:flags inGroup:group atIndex:index]];
|
|
3309
|
+
}
|
|
3310
|
+
}
|
|
3311
|
+
|
|
3312
|
+
/**
|
|
3313
|
+
* YapDatabase extension hook.
|
|
3314
|
+
* This method is invoked by a YapDatabaseReadWriteTransaction as a post-operation-hook.
|
|
3315
|
+
**/
|
|
3316
|
+
- (void)handleTouchMetadataForKey:(NSString *)key inCollection:(NSString *)collection withRowid:(int64_t)rowid
|
|
3317
|
+
{
|
|
3318
|
+
YDBLogAutoTrace();
|
|
3319
|
+
|
|
3320
|
+
// Almost the same as touchMetadatForKey:inCollection:
|
|
3321
|
+
|
|
3322
|
+
__unsafe_unretained YapDatabaseView *view = viewConnection->view;
|
|
3323
|
+
|
|
3324
|
+
if (view->groupingBlockType == YapDatabaseViewBlockTypeWithMetadata ||
|
|
3325
|
+
view->groupingBlockType == YapDatabaseViewBlockTypeWithRow ||
|
|
3326
|
+
view->sortingBlockType == YapDatabaseViewBlockTypeWithMetadata ||
|
|
3327
|
+
view->sortingBlockType == YapDatabaseViewBlockTypeWithRow )
|
|
3328
|
+
{
|
|
3329
|
+
NSString *pageKey = [self pageKeyForRowid:rowid];
|
|
3330
|
+
if (pageKey)
|
|
3331
|
+
{
|
|
3332
|
+
NSString *group = [self groupForPageKey:pageKey];
|
|
3333
|
+
NSUInteger index = [self indexForRowid:rowid inGroup:group withPageKey:pageKey];
|
|
3334
|
+
|
|
3335
|
+
YapCollectionKey *collectionKey = [[YapCollectionKey alloc] initWithCollection:collection key:key];
|
|
3336
|
+
int flags = YapDatabaseViewChangedMetadata;
|
|
3337
|
+
|
|
3338
|
+
[viewConnection->changes addObject:
|
|
3339
|
+
[YapDatabaseViewRowChange updateKey:collectionKey changes:flags inGroup:group atIndex:index]];
|
|
3340
|
+
}
|
|
3341
|
+
}
|
|
3342
|
+
}
|
|
3343
|
+
|
|
3344
|
+
/**
|
|
3345
|
+
* YapDatabase extension hook.
|
|
3346
|
+
* This method is invoked by a YapDatabaseReadWriteTransaction as a post-operation-hook.
|
|
3347
|
+
**/
|
|
3348
|
+
- (void)handleRemoveObjectForKey:(NSString *)key inCollection:(NSString *)collection withRowid:(int64_t)rowid
|
|
3349
|
+
{
|
|
3350
|
+
YDBLogAutoTrace();
|
|
3351
|
+
|
|
3352
|
+
NSParameterAssert(key != nil);
|
|
3353
|
+
NSParameterAssert(collection != nil);
|
|
3354
|
+
|
|
3355
|
+
YapCollectionKey *collectionKey = [[YapCollectionKey alloc] initWithCollection:collection key:key];
|
|
3356
|
+
|
|
3357
|
+
[self removeRowid:rowid collectionKey:collectionKey];
|
|
3358
|
+
}
|
|
3359
|
+
|
|
3360
|
+
/**
|
|
3361
|
+
* YapDatabase extension hook.
|
|
3362
|
+
* This method is invoked by a YapDatabaseReadWriteTransaction as a post-operation-hook.
|
|
3363
|
+
**/
|
|
3364
|
+
- (void)handleRemoveObjectsForKeys:(NSArray *)keys inCollection:(NSString *)collection withRowids:(NSArray *)rowids
|
|
3365
|
+
{
|
|
3366
|
+
YDBLogAutoTrace();
|
|
3367
|
+
|
|
3368
|
+
NSParameterAssert(collection != nil);
|
|
3369
|
+
|
|
3370
|
+
NSUInteger count = [keys count];
|
|
3371
|
+
NSMutableDictionary *keyMappings = [NSMutableDictionary dictionaryWithCapacity:count];
|
|
3372
|
+
|
|
3373
|
+
for (NSUInteger i = 0; i < count; i++)
|
|
3374
|
+
{
|
|
3375
|
+
NSNumber *rowid = [rowids objectAtIndex:i];
|
|
3376
|
+
NSString *key = [keys objectAtIndex:i];
|
|
3377
|
+
|
|
3378
|
+
YapCollectionKey *collectionKey = [[YapCollectionKey alloc] initWithCollection:collection key:key];
|
|
3379
|
+
|
|
3380
|
+
[keyMappings setObject:collectionKey forKey:rowid];
|
|
3381
|
+
}
|
|
3382
|
+
|
|
3383
|
+
NSDictionary *output = [self pageKeysForRowids:rowids withKeyMappings:keyMappings];
|
|
3384
|
+
|
|
3385
|
+
// output.key = pageKey
|
|
3386
|
+
// output.value = NSDictionary with keyMappings for page
|
|
3387
|
+
|
|
3388
|
+
[output enumerateKeysAndObjectsUsingBlock:^(id pageKeyObj, id dictObj, BOOL *stop) {
|
|
3389
|
+
|
|
3390
|
+
__unsafe_unretained NSString *pageKey = (NSString *)pageKeyObj;
|
|
3391
|
+
__unsafe_unretained NSDictionary *keyMappingsForPage = (NSDictionary *)dictObj;
|
|
3392
|
+
|
|
3393
|
+
[self removeRowidsWithKeyMappings:keyMappingsForPage pageKey:pageKey inGroup:[self groupForPageKey:pageKey]];
|
|
3394
|
+
}];
|
|
3395
|
+
}
|
|
3396
|
+
|
|
3397
|
+
/**
|
|
3398
|
+
* YapDatabase extension hook.
|
|
3399
|
+
* This method is invoked by a YapDatabaseReadWriteTransaction as a post-operation-hook.
|
|
3400
|
+
**/
|
|
3401
|
+
- (void)handleRemoveAllObjectsInAllCollections
|
|
3402
|
+
{
|
|
3403
|
+
YDBLogAutoTrace();
|
|
3404
|
+
|
|
3405
|
+
[self removeAllRowids];
|
|
3406
|
+
}
|
|
3407
|
+
|
|
3408
|
+
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
3409
|
+
#pragma mark Public API - Groups
|
|
3410
|
+
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
3411
|
+
|
|
3412
|
+
- (NSUInteger)numberOfGroups
|
|
3413
|
+
{
|
|
3414
|
+
return [viewConnection->group_pagesMetadata_dict count];
|
|
3415
|
+
}
|
|
3416
|
+
|
|
3417
|
+
- (NSArray *)allGroups
|
|
3418
|
+
{
|
|
3419
|
+
return [viewConnection->group_pagesMetadata_dict allKeys];
|
|
3420
|
+
}
|
|
3421
|
+
|
|
3422
|
+
/**
|
|
3423
|
+
* Returns YES if there are any keys in the given group.
|
|
3424
|
+
* This is equivalent to ([viewTransaction numberOfKeysInGroup:group] > 0)
|
|
3425
|
+
**/
|
|
3426
|
+
- (BOOL)hasGroup:(NSString *)group
|
|
3427
|
+
{
|
|
3428
|
+
// Note: We don't remove pages or groups until preCommitReadWriteTransaction.
|
|
3429
|
+
// This allows us to recycle pages whenever possible, which reduces disk IO during the commit.
|
|
3430
|
+
|
|
3431
|
+
NSMutableArray *pagesMetadataForGroup = [viewConnection->group_pagesMetadata_dict objectForKey:group];
|
|
3432
|
+
|
|
3433
|
+
for (YapDatabaseViewPageMetadata *pageMetadata in pagesMetadataForGroup)
|
|
3434
|
+
{
|
|
3435
|
+
if (pageMetadata->count > 0)
|
|
3436
|
+
return YES;
|
|
3437
|
+
}
|
|
3438
|
+
|
|
3439
|
+
return NO;
|
|
3440
|
+
}
|
|
3441
|
+
|
|
3442
|
+
- (NSUInteger)numberOfKeysInGroup:(NSString *)group
|
|
3443
|
+
{
|
|
3444
|
+
NSMutableArray *pagesMetadataForGroup = [viewConnection->group_pagesMetadata_dict objectForKey:group];
|
|
3445
|
+
NSUInteger count = 0;
|
|
3446
|
+
|
|
3447
|
+
for (YapDatabaseViewPageMetadata *pageMetadata in pagesMetadataForGroup)
|
|
3448
|
+
{
|
|
3449
|
+
count += pageMetadata->count;
|
|
3450
|
+
}
|
|
3451
|
+
|
|
3452
|
+
return count;
|
|
3453
|
+
}
|
|
3454
|
+
|
|
3455
|
+
- (NSUInteger)numberOfKeysInAllGroups
|
|
3456
|
+
{
|
|
3457
|
+
NSUInteger count = 0;
|
|
3458
|
+
|
|
3459
|
+
for (NSMutableArray *pagesMetadataForGroup in [viewConnection->group_pagesMetadata_dict objectEnumerator])
|
|
3460
|
+
{
|
|
3461
|
+
for (YapDatabaseViewPageMetadata *pageMetadata in pagesMetadataForGroup)
|
|
3462
|
+
{
|
|
3463
|
+
count += pageMetadata->count;
|
|
3464
|
+
}
|
|
3465
|
+
}
|
|
3466
|
+
|
|
3467
|
+
return count;
|
|
3468
|
+
}
|
|
3469
|
+
|
|
3470
|
+
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
3471
|
+
#pragma mark Public API - Fetching
|
|
3472
|
+
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
3473
|
+
|
|
3474
|
+
- (BOOL)getKey:(NSString **)keyPtr
|
|
3475
|
+
collection:(NSString **)collectionPtr
|
|
3476
|
+
atIndex:(NSUInteger)index
|
|
3477
|
+
inGroup:(NSString *)group
|
|
3478
|
+
{
|
|
3479
|
+
int64_t rowid = 0;
|
|
3480
|
+
if ([self getRowid:&rowid atIndex:index inGroup:group])
|
|
3481
|
+
{
|
|
3482
|
+
NSString *collection = nil;
|
|
3483
|
+
NSString *key = nil;
|
|
3484
|
+
BOOL found = [databaseTransaction getKey:&key collection:&collection forRowid:rowid];
|
|
3485
|
+
|
|
3486
|
+
if (collectionPtr) *collectionPtr = collection;
|
|
3487
|
+
if (keyPtr) *keyPtr = key;
|
|
3488
|
+
return found;
|
|
3489
|
+
}
|
|
3490
|
+
else
|
|
3491
|
+
{
|
|
3492
|
+
if (collectionPtr) *collectionPtr = nil;
|
|
3493
|
+
if (keyPtr) *keyPtr = nil;
|
|
3494
|
+
return NO;
|
|
3495
|
+
}
|
|
3496
|
+
}
|
|
3497
|
+
|
|
3498
|
+
- (BOOL)getFirstKey:(NSString **)keyPtr collection:(NSString **)collectionPtr inGroup:(NSString *)group
|
|
3499
|
+
{
|
|
3500
|
+
return [self getKey:keyPtr collection:collectionPtr atIndex:0 inGroup:group];
|
|
3501
|
+
}
|
|
3502
|
+
|
|
3503
|
+
- (BOOL)getLastKey:(NSString **)keyPtr collection:(NSString **)collectionPtr inGroup:(NSString *)group
|
|
3504
|
+
{
|
|
3505
|
+
int64_t rowid = 0;
|
|
3506
|
+
if ([self getLastRowid:&rowid inGroup:group])
|
|
3507
|
+
{
|
|
3508
|
+
NSString *collection = nil;
|
|
3509
|
+
NSString *key = nil;
|
|
3510
|
+
BOOL found = [databaseTransaction getKey:&key collection:&collection forRowid:rowid];
|
|
3511
|
+
|
|
3512
|
+
if (collectionPtr) *collectionPtr = collection;
|
|
3513
|
+
if (keyPtr) *keyPtr = key;
|
|
3514
|
+
return found;
|
|
3515
|
+
}
|
|
3516
|
+
else
|
|
3517
|
+
{
|
|
3518
|
+
if (collectionPtr) *collectionPtr = nil;
|
|
3519
|
+
if (keyPtr) *keyPtr = nil;
|
|
3520
|
+
return NO;
|
|
3521
|
+
}
|
|
3522
|
+
}
|
|
3523
|
+
|
|
3524
|
+
- (NSString *)collectionAtIndex:(NSUInteger)index inGroup:(NSString *)group
|
|
3525
|
+
{
|
|
3526
|
+
NSString *collection = nil;
|
|
3527
|
+
[self getKey:NULL collection:&collection atIndex:index inGroup:group];
|
|
3528
|
+
|
|
3529
|
+
return collection;
|
|
3530
|
+
}
|
|
3531
|
+
|
|
3532
|
+
- (NSString *)keyAtIndex:(NSUInteger)index inGroup:(NSString *)group
|
|
3533
|
+
{
|
|
3534
|
+
NSString *key = nil;
|
|
3535
|
+
[self getKey:&key collection:NULL atIndex:index inGroup:group];
|
|
3536
|
+
|
|
3537
|
+
return key;
|
|
3538
|
+
}
|
|
3539
|
+
|
|
3540
|
+
- (NSString *)groupForKey:(NSString *)key inCollection:(NSString *)collection
|
|
3541
|
+
{
|
|
3542
|
+
if (key == nil)
|
|
3543
|
+
return nil;
|
|
3544
|
+
|
|
3545
|
+
if (collection == nil)
|
|
3546
|
+
collection = @"";
|
|
3547
|
+
|
|
3548
|
+
int64_t rowid;
|
|
3549
|
+
if ([databaseTransaction getRowid:&rowid forKey:key inCollection:collection])
|
|
3550
|
+
{
|
|
3551
|
+
return [self groupForPageKey:[self pageKeyForRowid:rowid]];
|
|
3552
|
+
}
|
|
3553
|
+
|
|
3554
|
+
return nil;
|
|
3555
|
+
}
|
|
3556
|
+
|
|
3557
|
+
- (BOOL)getGroup:(NSString **)groupPtr
|
|
3558
|
+
index:(NSUInteger *)indexPtr
|
|
3559
|
+
forKey:(NSString *)key
|
|
3560
|
+
inCollection:(NSString *)collection
|
|
3561
|
+
{
|
|
3562
|
+
if (key == nil)
|
|
3563
|
+
{
|
|
3564
|
+
if (groupPtr) *groupPtr = nil;
|
|
3565
|
+
if (indexPtr) *indexPtr = 0;
|
|
3566
|
+
|
|
3567
|
+
return NO;
|
|
3568
|
+
}
|
|
3569
|
+
|
|
3570
|
+
if (collection == nil)
|
|
3571
|
+
collection = @"";
|
|
3572
|
+
|
|
3573
|
+
BOOL found = NO;
|
|
3574
|
+
NSString *group = nil;
|
|
3575
|
+
NSUInteger index = 0;
|
|
3576
|
+
|
|
3577
|
+
int64_t rowid = 0;
|
|
3578
|
+
if ([databaseTransaction getRowid:&rowid forKey:key inCollection:collection])
|
|
3579
|
+
{
|
|
3580
|
+
// Query the database to see if the given key is in the view.
|
|
3581
|
+
// If it is, the query will return the corresponding page the key is in.
|
|
3582
|
+
|
|
3583
|
+
NSString *pageKey = [self pageKeyForRowid:rowid];
|
|
3584
|
+
if (pageKey)
|
|
3585
|
+
{
|
|
3586
|
+
// Now that we have the pageKey, fetch the corresponding group.
|
|
3587
|
+
// This is done using an in-memory cache.
|
|
3588
|
+
|
|
3589
|
+
group = [self groupForPageKey:pageKey];
|
|
3590
|
+
|
|
3591
|
+
// Calculate the offset of the corresponding page within the group.
|
|
3592
|
+
|
|
3593
|
+
NSUInteger pageOffset = 0;
|
|
3594
|
+
NSMutableArray *pagesMetadataForGroup = [viewConnection->group_pagesMetadata_dict objectForKey:group];
|
|
3595
|
+
|
|
3596
|
+
for (YapDatabaseViewPageMetadata *pageMetadata in pagesMetadataForGroup)
|
|
3597
|
+
{
|
|
3598
|
+
if ([pageMetadata->pageKey isEqualToString:pageKey])
|
|
3599
|
+
{
|
|
3600
|
+
break;
|
|
3601
|
+
}
|
|
3602
|
+
|
|
3603
|
+
pageOffset += pageMetadata->count;
|
|
3604
|
+
}
|
|
3605
|
+
|
|
3606
|
+
// Fetch the actual page (ordered array of keys)
|
|
3607
|
+
|
|
3608
|
+
YapDatabaseViewPage *page = [self pageForPageKey:pageKey];
|
|
3609
|
+
|
|
3610
|
+
// And find the exact index of the key within the page
|
|
3611
|
+
|
|
3612
|
+
NSUInteger indexWithinPage = 0;
|
|
3613
|
+
if ([page getIndex:&indexWithinPage ofRowid:rowid])
|
|
3614
|
+
{
|
|
3615
|
+
index = pageOffset + indexWithinPage;
|
|
3616
|
+
found = YES;
|
|
3617
|
+
}
|
|
3618
|
+
}
|
|
3619
|
+
}
|
|
3620
|
+
|
|
3621
|
+
if (groupPtr) *groupPtr = group;
|
|
3622
|
+
if (indexPtr) *indexPtr = index;
|
|
3623
|
+
|
|
3624
|
+
return found;
|
|
3625
|
+
}
|
|
3626
|
+
|
|
3627
|
+
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
3628
|
+
#pragma mark Public API - Finding
|
|
3629
|
+
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
3630
|
+
|
|
3631
|
+
/**
|
|
3632
|
+
* See header file for extensive documentation for this method.
|
|
3633
|
+
**/
|
|
3634
|
+
- (NSRange)findRangeInGroup:(NSString *)group
|
|
3635
|
+
usingBlock:(YapDatabaseViewFindBlock)block
|
|
3636
|
+
blockType:(YapDatabaseViewBlockType)blockType
|
|
3637
|
+
{
|
|
3638
|
+
BOOL invalidBlockType = blockType != YapDatabaseViewBlockTypeWithKey &&
|
|
3639
|
+
blockType != YapDatabaseViewBlockTypeWithObject &&
|
|
3640
|
+
blockType != YapDatabaseViewBlockTypeWithMetadata &&
|
|
3641
|
+
blockType != YapDatabaseViewBlockTypeWithRow;
|
|
3642
|
+
|
|
3643
|
+
if (group == nil || block == NULL || invalidBlockType)
|
|
3644
|
+
{
|
|
3645
|
+
return NSMakeRange(NSNotFound, 0);
|
|
3646
|
+
}
|
|
3647
|
+
|
|
3648
|
+
NSMutableArray *pagesMetadataForGroup = [viewConnection->group_pagesMetadata_dict objectForKey:group];
|
|
3649
|
+
NSUInteger count = 0;
|
|
3650
|
+
|
|
3651
|
+
for (YapDatabaseViewPageMetadata *pageMetadata in pagesMetadataForGroup)
|
|
3652
|
+
{
|
|
3653
|
+
count += pageMetadata->count;
|
|
3654
|
+
}
|
|
3655
|
+
|
|
3656
|
+
if (count == 0)
|
|
3657
|
+
{
|
|
3658
|
+
return NSMakeRange(NSNotFound, 0);
|
|
3659
|
+
}
|
|
3660
|
+
|
|
3661
|
+
NSComparisonResult (^compare)(NSUInteger) = ^NSComparisonResult (NSUInteger index){
|
|
3662
|
+
|
|
3663
|
+
int64_t rowid = 0;
|
|
3664
|
+
|
|
3665
|
+
NSUInteger pageOffset = 0;
|
|
3666
|
+
for (YapDatabaseViewPageMetadata *pageMetadata in pagesMetadataForGroup)
|
|
3667
|
+
{
|
|
3668
|
+
if ((index < (pageOffset + pageMetadata->count)) && (pageMetadata->count > 0))
|
|
3669
|
+
{
|
|
3670
|
+
YapDatabaseViewPage *page = [self pageForPageKey:pageMetadata->pageKey];
|
|
3671
|
+
|
|
3672
|
+
rowid = [page rowidAtIndex:(index - pageOffset)];
|
|
3673
|
+
break;
|
|
3674
|
+
}
|
|
3675
|
+
else
|
|
3676
|
+
{
|
|
3677
|
+
pageOffset += pageMetadata->count;
|
|
3678
|
+
}
|
|
3679
|
+
}
|
|
3680
|
+
|
|
3681
|
+
if (blockType == YapDatabaseViewBlockTypeWithKey)
|
|
3682
|
+
{
|
|
3683
|
+
__unsafe_unretained YapDatabaseViewFindWithKeyBlock findBlock =
|
|
3684
|
+
(YapDatabaseViewFindWithKeyBlock)block;
|
|
3685
|
+
|
|
3686
|
+
NSString *key = nil;
|
|
3687
|
+
NSString *collection = nil;
|
|
3688
|
+
[databaseTransaction getKey:&key collection:&collection forRowid:rowid];
|
|
3689
|
+
|
|
3690
|
+
return findBlock(collection, key);
|
|
3691
|
+
}
|
|
3692
|
+
else if (blockType == YapDatabaseViewBlockTypeWithObject)
|
|
3693
|
+
{
|
|
3694
|
+
__unsafe_unretained YapDatabaseViewFindWithObjectBlock findBlock =
|
|
3695
|
+
(YapDatabaseViewFindWithObjectBlock)block;
|
|
3696
|
+
|
|
3697
|
+
NSString *key = nil;
|
|
3698
|
+
NSString *collection = nil;
|
|
3699
|
+
id object = nil;
|
|
3700
|
+
[databaseTransaction getKey:&key collection:&collection object:&object forRowid:rowid];
|
|
3701
|
+
|
|
3702
|
+
return findBlock(collection, key, object);
|
|
3703
|
+
}
|
|
3704
|
+
else if (blockType == YapDatabaseViewBlockTypeWithMetadata)
|
|
3705
|
+
{
|
|
3706
|
+
__unsafe_unretained YapDatabaseViewFindWithMetadataBlock findBlock =
|
|
3707
|
+
(YapDatabaseViewFindWithMetadataBlock)block;
|
|
3708
|
+
|
|
3709
|
+
NSString *key = nil;
|
|
3710
|
+
NSString *collection = nil;
|
|
3711
|
+
id metadata = nil;
|
|
3712
|
+
[databaseTransaction getKey:&key collection:&collection metadata:&metadata forRowid:rowid];
|
|
3713
|
+
|
|
3714
|
+
return findBlock(collection, key, metadata);
|
|
3715
|
+
}
|
|
3716
|
+
else
|
|
3717
|
+
{
|
|
3718
|
+
__unsafe_unretained YapDatabaseViewFindWithRowBlock findBlock =
|
|
3719
|
+
(YapDatabaseViewFindWithRowBlock)block;
|
|
3720
|
+
|
|
3721
|
+
NSString *key = nil;
|
|
3722
|
+
NSString *collection = nil;
|
|
3723
|
+
id object = nil;
|
|
3724
|
+
id metadata = nil;
|
|
3725
|
+
[databaseTransaction getKey:&key collection:&collection object:&object metadata:&metadata forRowid:rowid];
|
|
3726
|
+
|
|
3727
|
+
return findBlock(collection, key, object, metadata);
|
|
3728
|
+
}
|
|
3729
|
+
};
|
|
3730
|
+
|
|
3731
|
+
NSUInteger loopCount = 0;
|
|
3732
|
+
|
|
3733
|
+
// Find first match (first to return NSOrderedSame)
|
|
3734
|
+
|
|
3735
|
+
NSUInteger mMin = 0;
|
|
3736
|
+
NSUInteger mMax = count;
|
|
3737
|
+
NSUInteger mMid;
|
|
3738
|
+
|
|
3739
|
+
BOOL found = NO;
|
|
3740
|
+
|
|
3741
|
+
while (mMin < mMax && !found)
|
|
3742
|
+
{
|
|
3743
|
+
mMid = (mMin + mMax) / 2;
|
|
3744
|
+
|
|
3745
|
+
NSComparisonResult cmp = compare(mMid);
|
|
3746
|
+
|
|
3747
|
+
if (cmp == NSOrderedDescending) // Descending => value is greater than desired range
|
|
3748
|
+
mMax = mMid;
|
|
3749
|
+
else if (cmp == NSOrderedAscending) // Ascending => value is less than desired range
|
|
3750
|
+
mMin = mMid + 1;
|
|
3751
|
+
else
|
|
3752
|
+
found = YES;
|
|
3753
|
+
|
|
3754
|
+
loopCount++;
|
|
3755
|
+
}
|
|
3756
|
+
|
|
3757
|
+
if (!found)
|
|
3758
|
+
{
|
|
3759
|
+
return NSMakeRange(NSNotFound, 0);
|
|
3760
|
+
}
|
|
3761
|
+
|
|
3762
|
+
// Find start of range
|
|
3763
|
+
|
|
3764
|
+
NSUInteger sMin = mMin;
|
|
3765
|
+
NSUInteger sMax = mMid;
|
|
3766
|
+
NSUInteger sMid;
|
|
3767
|
+
|
|
3768
|
+
while (sMin < sMax)
|
|
3769
|
+
{
|
|
3770
|
+
sMid = (sMin + sMax) / 2;
|
|
3771
|
+
|
|
3772
|
+
NSComparisonResult cmp = compare(sMid);
|
|
3773
|
+
|
|
3774
|
+
if (cmp == NSOrderedAscending) // Ascending => value is less than desired range
|
|
3775
|
+
sMin = sMid + 1;
|
|
3776
|
+
else
|
|
3777
|
+
sMax = sMid;
|
|
3778
|
+
|
|
3779
|
+
loopCount++;
|
|
3780
|
+
}
|
|
3781
|
+
|
|
3782
|
+
// Find end of range
|
|
3783
|
+
|
|
3784
|
+
NSUInteger eMin = mMid;
|
|
3785
|
+
NSUInteger eMax = mMax;
|
|
3786
|
+
NSUInteger eMid;
|
|
3787
|
+
|
|
3788
|
+
while (eMin < eMax)
|
|
3789
|
+
{
|
|
3790
|
+
eMid = (eMin + eMax) / 2;
|
|
3791
|
+
|
|
3792
|
+
NSComparisonResult cmp = compare(eMid);
|
|
3793
|
+
|
|
3794
|
+
if (cmp == NSOrderedDescending) // Descending => value is greater than desired range
|
|
3795
|
+
eMax = eMid;
|
|
3796
|
+
else
|
|
3797
|
+
eMin = eMid + 1;
|
|
3798
|
+
|
|
3799
|
+
loopCount++;
|
|
3800
|
+
}
|
|
3801
|
+
|
|
3802
|
+
YDBLogVerbose(@"Find range in group(%@) took %lu comparisons", group, (unsigned long)loopCount);
|
|
3803
|
+
|
|
3804
|
+
return NSMakeRange(sMin, (eMax - sMin));
|
|
3805
|
+
}
|
|
3806
|
+
|
|
3807
|
+
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
3808
|
+
#pragma mark Public API - Enumerating
|
|
3809
|
+
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
3810
|
+
|
|
3811
|
+
- (void)enumerateKeysInGroup:(NSString *)group
|
|
3812
|
+
usingBlock:(void (^)(NSString *collection, NSString *key, NSUInteger index, BOOL *stop))block
|
|
3813
|
+
{
|
|
3814
|
+
if (block == NULL) return;
|
|
3815
|
+
|
|
3816
|
+
[self enumerateRowidsInGroup:group usingBlock:^(int64_t rowid, NSUInteger index, BOOL *stop) {
|
|
3817
|
+
|
|
3818
|
+
NSString *key = nil;
|
|
3819
|
+
NSString *collection = nil;
|
|
3820
|
+
[databaseTransaction getKey:&key collection:&collection forRowid:rowid];
|
|
3821
|
+
|
|
3822
|
+
block(collection, key, index, stop);
|
|
3823
|
+
}];
|
|
3824
|
+
}
|
|
3825
|
+
|
|
3826
|
+
- (void)enumerateKeysInGroup:(NSString *)group
|
|
3827
|
+
withOptions:(NSEnumerationOptions)options
|
|
3828
|
+
usingBlock:(void (^)(NSString *collection, NSString *key, NSUInteger index, BOOL *stop))block
|
|
3829
|
+
{
|
|
3830
|
+
if (block == NULL) return;
|
|
3831
|
+
|
|
3832
|
+
[self enumerateRowidsInGroup:group withOptions:options usingBlock:^(int64_t rowid, NSUInteger index, BOOL *stop) {
|
|
3833
|
+
|
|
3834
|
+
NSString *key = nil;
|
|
3835
|
+
NSString *collection = nil;
|
|
3836
|
+
[databaseTransaction getKey:&key collection:&collection forRowid:rowid];
|
|
3837
|
+
|
|
3838
|
+
block(collection, key, index, stop);
|
|
3839
|
+
}];
|
|
3840
|
+
}
|
|
3841
|
+
|
|
3842
|
+
- (void)enumerateKeysInGroup:(NSString *)group
|
|
3843
|
+
withOptions:(NSEnumerationOptions)options
|
|
3844
|
+
range:(NSRange)range
|
|
3845
|
+
usingBlock:(void (^)(NSString *collection, NSString *key, NSUInteger index, BOOL *stop))block
|
|
3846
|
+
{
|
|
3847
|
+
if (block == NULL) return;
|
|
3848
|
+
|
|
3849
|
+
[self enumerateRowidsInGroup:group
|
|
3850
|
+
withOptions:options
|
|
3851
|
+
range:range
|
|
3852
|
+
usingBlock:^(int64_t rowid, NSUInteger index, BOOL *stop) {
|
|
3853
|
+
|
|
3854
|
+
NSString *key = nil;
|
|
3855
|
+
NSString *collection = nil;
|
|
3856
|
+
[databaseTransaction getKey:&key collection:&collection forRowid:rowid];
|
|
3857
|
+
|
|
3858
|
+
block(collection, key, index, stop);
|
|
3859
|
+
}];
|
|
3860
|
+
}
|
|
3861
|
+
|
|
3862
|
+
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
3863
|
+
#pragma mark Private API
|
|
3864
|
+
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
3865
|
+
|
|
3866
|
+
- (void)enumerateRowidsInGroup:(NSString *)group
|
|
3867
|
+
usingBlock:(void (^)(int64_t rowid, NSUInteger index, BOOL *stop))block
|
|
3868
|
+
{
|
|
3869
|
+
if (block == NULL) return;
|
|
3870
|
+
|
|
3871
|
+
[viewConnection->mutatedGroups removeObject:group]; // mutation during enumeration protection
|
|
3872
|
+
|
|
3873
|
+
__block BOOL stop = NO;
|
|
3874
|
+
|
|
3875
|
+
NSUInteger pageOffset = 0;
|
|
3876
|
+
NSMutableArray *pagesMetadataForGroup = [viewConnection->group_pagesMetadata_dict objectForKey:group];
|
|
3877
|
+
|
|
3878
|
+
for (YapDatabaseViewPageMetadata *pageMetadata in pagesMetadataForGroup)
|
|
3879
|
+
{
|
|
3880
|
+
YapDatabaseViewPage *page = [self pageForPageKey:pageMetadata->pageKey];
|
|
3881
|
+
|
|
3882
|
+
__block NSUInteger index = pageOffset;
|
|
3883
|
+
[page enumerateRowidsUsingBlock:^(int64_t rowid, NSUInteger idx, BOOL *innerStop) {
|
|
3884
|
+
|
|
3885
|
+
block(rowid, index, &stop);
|
|
3886
|
+
|
|
3887
|
+
index++;
|
|
3888
|
+
if (stop || [viewConnection->mutatedGroups containsObject:group]) *innerStop = YES;
|
|
3889
|
+
}];
|
|
3890
|
+
|
|
3891
|
+
if (stop || [viewConnection->mutatedGroups containsObject:group]) break;
|
|
3892
|
+
|
|
3893
|
+
pageOffset += pageMetadata->count;
|
|
3894
|
+
}
|
|
3895
|
+
|
|
3896
|
+
if (!stop && [viewConnection->mutatedGroups containsObject:group])
|
|
3897
|
+
{
|
|
3898
|
+
@throw [self mutationDuringEnumerationException:group];
|
|
3899
|
+
}
|
|
3900
|
+
}
|
|
3901
|
+
|
|
3902
|
+
- (void)enumerateRowidsInGroup:(NSString *)group
|
|
3903
|
+
withOptions:(NSEnumerationOptions)inOptions
|
|
3904
|
+
usingBlock:(void (^)(int64_t rowid, NSUInteger index, BOOL *stop))block
|
|
3905
|
+
{
|
|
3906
|
+
if (block == NULL) return;
|
|
3907
|
+
|
|
3908
|
+
NSEnumerationOptions options = (inOptions & NSEnumerationReverse); // We only support NSEnumerationReverse
|
|
3909
|
+
BOOL forwardEnumeration = (options != NSEnumerationReverse);
|
|
3910
|
+
|
|
3911
|
+
[viewConnection->mutatedGroups removeObject:group]; // mutation during enumeration protection
|
|
3912
|
+
|
|
3913
|
+
__block BOOL stop = NO;
|
|
3914
|
+
__block NSUInteger index;
|
|
3915
|
+
|
|
3916
|
+
if (forwardEnumeration)
|
|
3917
|
+
index = 0;
|
|
3918
|
+
else
|
|
3919
|
+
index = [self numberOfKeysInGroup:group] - 1;
|
|
3920
|
+
|
|
3921
|
+
NSMutableArray *pagesMetadataForGroup = [viewConnection->group_pagesMetadata_dict objectForKey:group];
|
|
3922
|
+
|
|
3923
|
+
[pagesMetadataForGroup enumerateObjectsWithOptions:options
|
|
3924
|
+
usingBlock:^(id pageMetadataObj, NSUInteger outerIdx, BOOL *outerStop){
|
|
3925
|
+
|
|
3926
|
+
__unsafe_unretained YapDatabaseViewPageMetadata *pageMetadata =
|
|
3927
|
+
(YapDatabaseViewPageMetadata *)pageMetadataObj;
|
|
3928
|
+
|
|
3929
|
+
YapDatabaseViewPage *page = [self pageForPageKey:pageMetadata->pageKey];
|
|
3930
|
+
|
|
3931
|
+
[page enumerateRowidsWithOptions:options usingBlock:^(int64_t rowid, NSUInteger innerIdx, BOOL *innerStop) {
|
|
3932
|
+
|
|
3933
|
+
block(rowid, index, &stop);
|
|
3934
|
+
|
|
3935
|
+
if (forwardEnumeration)
|
|
3936
|
+
index++;
|
|
3937
|
+
else
|
|
3938
|
+
index--;
|
|
3939
|
+
|
|
3940
|
+
if (stop || [viewConnection->mutatedGroups containsObject:group]) *innerStop = YES;
|
|
3941
|
+
}];
|
|
3942
|
+
|
|
3943
|
+
if (stop || [viewConnection->mutatedGroups containsObject:group]) *outerStop = YES;
|
|
3944
|
+
}];
|
|
3945
|
+
|
|
3946
|
+
if (!stop && [viewConnection->mutatedGroups containsObject:group])
|
|
3947
|
+
{
|
|
3948
|
+
@throw [self mutationDuringEnumerationException:group];
|
|
3949
|
+
}
|
|
3950
|
+
}
|
|
3951
|
+
|
|
3952
|
+
- (void)enumerateRowidsInGroup:(NSString *)group
|
|
3953
|
+
withOptions:(NSEnumerationOptions)inOptions
|
|
3954
|
+
range:(NSRange)range
|
|
3955
|
+
usingBlock:(void (^)(int64_t rowid, NSUInteger index, BOOL *stop))block
|
|
3956
|
+
{
|
|
3957
|
+
if (block == NULL) return;
|
|
3958
|
+
|
|
3959
|
+
NSEnumerationOptions options = (inOptions & NSEnumerationReverse); // We only support NSEnumerationReverse
|
|
3960
|
+
|
|
3961
|
+
NSMutableArray *pagesMetadataForGroup = [viewConnection->group_pagesMetadata_dict objectForKey:group];
|
|
3962
|
+
|
|
3963
|
+
// Helper block to fetch the pageOffset for some page.
|
|
3964
|
+
|
|
3965
|
+
NSUInteger (^pageOffsetForPageMetadata)(YapDatabaseViewPageMetadata *inPageMetadata);
|
|
3966
|
+
pageOffsetForPageMetadata = ^ NSUInteger (YapDatabaseViewPageMetadata *inPageMetadata){
|
|
3967
|
+
|
|
3968
|
+
NSUInteger pageOffset = 0;
|
|
3969
|
+
|
|
3970
|
+
for (YapDatabaseViewPageMetadata *pageMetadata in pagesMetadataForGroup)
|
|
3971
|
+
{
|
|
3972
|
+
if (pageMetadata == inPageMetadata)
|
|
3973
|
+
return pageOffset;
|
|
3974
|
+
else
|
|
3975
|
+
pageOffset += pageMetadata->count;
|
|
3976
|
+
}
|
|
3977
|
+
|
|
3978
|
+
return pageOffset;
|
|
3979
|
+
};
|
|
3980
|
+
|
|
3981
|
+
[viewConnection->mutatedGroups removeObject:group]; // mutation during enumeration protection
|
|
3982
|
+
|
|
3983
|
+
__block BOOL stop = NO;
|
|
3984
|
+
__block BOOL startedRange = NO;
|
|
3985
|
+
__block NSUInteger keysLeft = range.length;
|
|
3986
|
+
|
|
3987
|
+
[pagesMetadataForGroup enumerateObjectsWithOptions:options
|
|
3988
|
+
usingBlock:^(id pageMetadataObj, NSUInteger pageIndex, BOOL *outerStop){
|
|
3989
|
+
|
|
3990
|
+
__unsafe_unretained YapDatabaseViewPageMetadata *pageMetadata =
|
|
3991
|
+
(YapDatabaseViewPageMetadata *)pageMetadataObj;
|
|
3992
|
+
|
|
3993
|
+
NSUInteger pageOffset = pageOffsetForPageMetadata(pageMetadata);
|
|
3994
|
+
NSRange pageRange = NSMakeRange(pageOffset, pageMetadata->count);
|
|
3995
|
+
NSRange keysRange = NSIntersectionRange(pageRange, range);
|
|
3996
|
+
|
|
3997
|
+
if (keysRange.length > 0)
|
|
3998
|
+
{
|
|
3999
|
+
startedRange = YES;
|
|
4000
|
+
YapDatabaseViewPage *page = [self pageForPageKey:pageMetadata->pageKey];
|
|
4001
|
+
|
|
4002
|
+
// Enumerate the subset
|
|
4003
|
+
|
|
4004
|
+
NSRange subsetRange = NSMakeRange(keysRange.location-pageOffset, keysRange.length);
|
|
4005
|
+
|
|
4006
|
+
[page enumerateRowidsWithOptions:options
|
|
4007
|
+
range:subsetRange
|
|
4008
|
+
usingBlock:^(int64_t rowid, NSUInteger idx, BOOL *innerStop) {
|
|
4009
|
+
|
|
4010
|
+
block(rowid, pageOffset+idx, &stop);
|
|
4011
|
+
|
|
4012
|
+
if (stop || [viewConnection->mutatedGroups containsObject:group]) *innerStop = YES;
|
|
4013
|
+
}];
|
|
4014
|
+
|
|
4015
|
+
keysLeft -= keysRange.length;
|
|
4016
|
+
|
|
4017
|
+
if (stop || [viewConnection->mutatedGroups containsObject:group]) *outerStop = YES;
|
|
4018
|
+
}
|
|
4019
|
+
else if (startedRange)
|
|
4020
|
+
{
|
|
4021
|
+
// We've completed the range
|
|
4022
|
+
*outerStop = YES;
|
|
4023
|
+
}
|
|
4024
|
+
|
|
4025
|
+
}];
|
|
4026
|
+
|
|
4027
|
+
if (!stop && [viewConnection->mutatedGroups containsObject:group])
|
|
4028
|
+
{
|
|
4029
|
+
@throw [self mutationDuringEnumerationException:group];
|
|
4030
|
+
}
|
|
4031
|
+
|
|
4032
|
+
if (!stop && keysLeft > 0)
|
|
4033
|
+
{
|
|
4034
|
+
YDBLogWarn(@"%@: Range out of bounds: range(%lu, %lu) >= numberOfKeys(%lu) in group %@", THIS_METHOD,
|
|
4035
|
+
(unsigned long)range.location, (unsigned long)range.length,
|
|
4036
|
+
(unsigned long)[self numberOfKeysInGroup:group], group);
|
|
4037
|
+
}
|
|
4038
|
+
}
|
|
4039
|
+
|
|
4040
|
+
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
4041
|
+
#pragma mark Exceptions
|
|
4042
|
+
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
4043
|
+
|
|
4044
|
+
- (NSException *)mutationDuringEnumerationException:(NSString *)group
|
|
4045
|
+
{
|
|
4046
|
+
NSString *reason = [NSString stringWithFormat:
|
|
4047
|
+
@"View <RegisteredName=%@, Group=%@> was mutated while being enumerated.", [self registeredName], group];
|
|
4048
|
+
|
|
4049
|
+
NSDictionary *userInfo = @{ NSLocalizedRecoverySuggestionErrorKey:
|
|
4050
|
+
@"If you modify the database during enumeration you must either"
|
|
4051
|
+
@" (A) ensure you don't mutate the group you're enumerating OR"
|
|
4052
|
+
@" (B) set the 'stop' parameter of the enumeration block to YES (*stop = YES;). "
|
|
4053
|
+
@"If you're enumerating in order to remove items from the database,"
|
|
4054
|
+
@" and you're enumerating in order (forwards or backwards)"
|
|
4055
|
+
@" then you may also consider looping and using firstKeyInGroup / lastKeyInGroup."};
|
|
4056
|
+
|
|
4057
|
+
return [NSException exceptionWithName:@"YapDatabaseException" reason:reason userInfo:userInfo];
|
|
4058
|
+
}
|
|
4059
|
+
|
|
4060
|
+
@end
|
|
4061
|
+
|
|
4062
|
+
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
4063
|
+
#pragma mark -
|
|
4064
|
+
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
4065
|
+
|
|
4066
|
+
@implementation YapDatabaseViewTransaction (ReadWrite)
|
|
4067
|
+
|
|
4068
|
+
/**
|
|
4069
|
+
* "Touching" a object allows you to mark an item in the view as "updated",
|
|
4070
|
+
* even if the object itself wasn't directly updated.
|
|
4071
|
+
*
|
|
4072
|
+
* This is most often useful when a view is being used by a tableView,
|
|
4073
|
+
* but the tableView cells are also dependent upon another object in the database.
|
|
4074
|
+
*
|
|
4075
|
+
* For example:
|
|
4076
|
+
*
|
|
4077
|
+
* You have a view which includes the departments in the company, sorted by name.
|
|
4078
|
+
* But as part of the cell that's displayed for the department,
|
|
4079
|
+
* you also display the number of employees in the department.
|
|
4080
|
+
* The employee count comes from elsewhere.
|
|
4081
|
+
* That is, the employee count isn't a property of the department object itself.
|
|
4082
|
+
* Perhaps you get the count from another view,
|
|
4083
|
+
* or perhaps the count is simply the number of keys in a particular collection.
|
|
4084
|
+
* Either way, when you add or remove an employee, you want to ensure that the view marks the
|
|
4085
|
+
* affected department as updated so that the corresponding cell will properly redraw itself.
|
|
4086
|
+
*
|
|
4087
|
+
* So the idea is to mark certain items as updated so that the changeset
|
|
4088
|
+
* for the view will properly reflect a change to the corresponding index.
|
|
4089
|
+
*
|
|
4090
|
+
* "Touching" an item has very minimal overhead.
|
|
4091
|
+
* It doesn't cause the groupingBlock or sortingBlock to be invoked,
|
|
4092
|
+
* and it doesn't cause any writes to the database.
|
|
4093
|
+
*
|
|
4094
|
+
* You can touch
|
|
4095
|
+
* - just the object
|
|
4096
|
+
* - just the metadata
|
|
4097
|
+
* - or both object and metadata (the row)
|
|
4098
|
+
*
|
|
4099
|
+
* If you mark just the object as changed,
|
|
4100
|
+
* and neither the groupingBlock nor sortingBlock depend upon the object,
|
|
4101
|
+
* then the view doesn't reflect any change.
|
|
4102
|
+
*
|
|
4103
|
+
* If you mark just the metadata as changed,
|
|
4104
|
+
* and neither the groupingBlock nor sortingBlock depend upon the metadata,
|
|
4105
|
+
* then the view doesn't relect any change.
|
|
4106
|
+
*
|
|
4107
|
+
* In all other cases, the view will properly reflect a corresponding change in the notification that's posted.
|
|
4108
|
+
**/
|
|
4109
|
+
|
|
4110
|
+
- (void)touchRowForKey:(NSString *)key inCollection:(NSString *)collection
|
|
4111
|
+
{
|
|
4112
|
+
if (!databaseTransaction->isReadWriteTransaction)
|
|
4113
|
+
{
|
|
4114
|
+
YDBLogWarn(@"%@ - Method only allowed in readWrite transaction", THIS_METHOD);
|
|
4115
|
+
return;
|
|
4116
|
+
}
|
|
4117
|
+
|
|
4118
|
+
int64_t rowid = 0;
|
|
4119
|
+
if ([databaseTransaction getRowid:&rowid forKey:key inCollection:collection])
|
|
4120
|
+
{
|
|
4121
|
+
NSString *pageKey = [self pageKeyForRowid:rowid];
|
|
4122
|
+
if (pageKey)
|
|
4123
|
+
{
|
|
4124
|
+
NSString *group = [self groupForPageKey:pageKey];
|
|
4125
|
+
NSUInteger index = [self indexForRowid:rowid inGroup:group withPageKey:pageKey];
|
|
4126
|
+
|
|
4127
|
+
YapCollectionKey *collectionKey = [[YapCollectionKey alloc] initWithCollection:collection key:key];
|
|
4128
|
+
int flags = (YapDatabaseViewChangedObject | YapDatabaseViewChangedMetadata);
|
|
4129
|
+
|
|
4130
|
+
[viewConnection->changes addObject:
|
|
4131
|
+
[YapDatabaseViewRowChange updateKey:collectionKey changes:flags inGroup:group atIndex:index]];
|
|
4132
|
+
}
|
|
4133
|
+
}
|
|
4134
|
+
}
|
|
4135
|
+
|
|
4136
|
+
- (void)touchObjectForKey:(NSString *)key inCollection:(NSString *)collection
|
|
4137
|
+
{
|
|
4138
|
+
if (!databaseTransaction->isReadWriteTransaction)
|
|
4139
|
+
{
|
|
4140
|
+
YDBLogWarn(@"%@ - Method only allowed in readWrite transaction", THIS_METHOD);
|
|
4141
|
+
return;
|
|
4142
|
+
}
|
|
4143
|
+
|
|
4144
|
+
__unsafe_unretained YapDatabaseView *view = viewConnection->view;
|
|
4145
|
+
|
|
4146
|
+
if (view->groupingBlockType == YapDatabaseViewBlockTypeWithObject ||
|
|
4147
|
+
view->groupingBlockType == YapDatabaseViewBlockTypeWithRow ||
|
|
4148
|
+
view->sortingBlockType == YapDatabaseViewBlockTypeWithObject ||
|
|
4149
|
+
view->sortingBlockType == YapDatabaseViewBlockTypeWithRow )
|
|
4150
|
+
{
|
|
4151
|
+
int64_t rowid = 0;
|
|
4152
|
+
if ([databaseTransaction getRowid:&rowid forKey:key inCollection:collection])
|
|
4153
|
+
{
|
|
4154
|
+
NSString *pageKey = [self pageKeyForRowid:rowid];
|
|
4155
|
+
if (pageKey)
|
|
4156
|
+
{
|
|
4157
|
+
NSString *group = [self groupForPageKey:pageKey];
|
|
4158
|
+
NSUInteger index = [self indexForRowid:rowid inGroup:group withPageKey:pageKey];
|
|
4159
|
+
|
|
4160
|
+
YapCollectionKey *collectionKey = [[YapCollectionKey alloc] initWithCollection:collection key:key];
|
|
4161
|
+
int flags = YapDatabaseViewChangedObject;
|
|
4162
|
+
|
|
4163
|
+
[viewConnection->changes addObject:
|
|
4164
|
+
[YapDatabaseViewRowChange updateKey:collectionKey changes:flags inGroup:group atIndex:index]];
|
|
4165
|
+
}
|
|
4166
|
+
}
|
|
4167
|
+
}
|
|
4168
|
+
}
|
|
4169
|
+
|
|
4170
|
+
- (void)touchMetadataForKey:(NSString *)key inCollection:(NSString *)collection
|
|
4171
|
+
{
|
|
4172
|
+
if (!databaseTransaction->isReadWriteTransaction)
|
|
4173
|
+
{
|
|
4174
|
+
YDBLogWarn(@"%@ - Method only allowed in readWrite transaction", THIS_METHOD);
|
|
4175
|
+
return;
|
|
4176
|
+
}
|
|
4177
|
+
|
|
4178
|
+
__unsafe_unretained YapDatabaseView *view = viewConnection->view;
|
|
4179
|
+
|
|
4180
|
+
if (view->groupingBlockType == YapDatabaseViewBlockTypeWithMetadata ||
|
|
4181
|
+
view->groupingBlockType == YapDatabaseViewBlockTypeWithRow ||
|
|
4182
|
+
view->sortingBlockType == YapDatabaseViewBlockTypeWithMetadata ||
|
|
4183
|
+
view->sortingBlockType == YapDatabaseViewBlockTypeWithRow )
|
|
4184
|
+
{
|
|
4185
|
+
int64_t rowid = 0;
|
|
4186
|
+
if ([databaseTransaction getRowid:&rowid forKey:key inCollection:collection])
|
|
4187
|
+
{
|
|
4188
|
+
NSString *pageKey = [self pageKeyForRowid:rowid];
|
|
4189
|
+
if (pageKey)
|
|
4190
|
+
{
|
|
4191
|
+
NSString *group = [self groupForPageKey:pageKey];
|
|
4192
|
+
NSUInteger index = [self indexForRowid:rowid inGroup:group withPageKey:pageKey];
|
|
4193
|
+
|
|
4194
|
+
YapCollectionKey *collectionKey = [[YapCollectionKey alloc] initWithCollection:collection key:key];
|
|
4195
|
+
int flags = YapDatabaseViewChangedMetadata;
|
|
4196
|
+
|
|
4197
|
+
[viewConnection->changes addObject:
|
|
4198
|
+
[YapDatabaseViewRowChange updateKey:collectionKey changes:flags inGroup:group atIndex:index]];
|
|
4199
|
+
}
|
|
4200
|
+
}
|
|
4201
|
+
}
|
|
4202
|
+
}
|
|
4203
|
+
|
|
4204
|
+
@end
|
|
4205
|
+
|
|
4206
|
+
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
4207
|
+
#pragma mark -
|
|
4208
|
+
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
4209
|
+
|
|
4210
|
+
@implementation YapDatabaseViewTransaction (Convenience)
|
|
4211
|
+
|
|
4212
|
+
- (id)objectAtIndex:(NSUInteger)index inGroup:(NSString *)group
|
|
4213
|
+
{
|
|
4214
|
+
int64_t rowid = 0;
|
|
4215
|
+
if ([self getRowid:&rowid atIndex:index inGroup:group])
|
|
4216
|
+
{
|
|
4217
|
+
// We could use getKey:collection:object:forRowid: at this point.
|
|
4218
|
+
// But in most cases the object is going to be in the objectCache.
|
|
4219
|
+
// So it's likely faster to fetch just the key first.
|
|
4220
|
+
// And if the cache misses then we're still using a fetch based on the rowid.
|
|
4221
|
+
|
|
4222
|
+
NSString *key = nil;
|
|
4223
|
+
NSString *collection = nil;
|
|
4224
|
+
[databaseTransaction getKey:&key collection:&collection forRowid:rowid];
|
|
4225
|
+
|
|
4226
|
+
return [databaseTransaction objectForKey:key inCollection:collection withRowid:rowid];
|
|
4227
|
+
}
|
|
4228
|
+
else
|
|
4229
|
+
{
|
|
4230
|
+
return nil;
|
|
4231
|
+
}
|
|
4232
|
+
}
|
|
4233
|
+
|
|
4234
|
+
|
|
4235
|
+
- (id)firstObjectInGroup:(NSString *)group
|
|
4236
|
+
{
|
|
4237
|
+
return [self objectAtIndex:0 inGroup:group];
|
|
4238
|
+
}
|
|
4239
|
+
|
|
4240
|
+
- (id)lastObjectInGroup:(NSString *)group
|
|
4241
|
+
{
|
|
4242
|
+
int64_t rowid = 0;
|
|
4243
|
+
if ([self getLastRowid:&rowid inGroup:group])
|
|
4244
|
+
{
|
|
4245
|
+
// We could use getKey:collection:object:forRowid: at this point.
|
|
4246
|
+
// But in most cases the object is going to be in the objectCache.
|
|
4247
|
+
// So it's likely faster to fetch just the key first.
|
|
4248
|
+
// And if the cache misses then we're still using a fetch based on the rowid.
|
|
4249
|
+
|
|
4250
|
+
NSString *key = nil;
|
|
4251
|
+
NSString *collection = nil;
|
|
4252
|
+
[databaseTransaction getKey:&key collection:&collection forRowid:rowid];
|
|
4253
|
+
|
|
4254
|
+
return [databaseTransaction objectForKey:key inCollection:collection withRowid:rowid];
|
|
4255
|
+
}
|
|
4256
|
+
else
|
|
4257
|
+
{
|
|
4258
|
+
return nil;
|
|
4259
|
+
}
|
|
4260
|
+
}
|
|
4261
|
+
|
|
4262
|
+
/**
|
|
4263
|
+
* The following methods are equivalent to invoking the enumerateKeysInGroup:... methods,
|
|
4264
|
+
* and then fetching the metadata within your own block.
|
|
4265
|
+
**/
|
|
4266
|
+
|
|
4267
|
+
- (void)enumerateKeysAndMetadataInGroup:(NSString *)group
|
|
4268
|
+
usingBlock:
|
|
4269
|
+
(void (^)(NSString *collection, NSString *key, id metadata, NSUInteger index, BOOL *stop))block
|
|
4270
|
+
{
|
|
4271
|
+
if (block == NULL) return;
|
|
4272
|
+
|
|
4273
|
+
[self enumerateRowidsInGroup:group usingBlock:^(int64_t rowid, NSUInteger index, BOOL *stop) {
|
|
4274
|
+
|
|
4275
|
+
NSString *key = nil;
|
|
4276
|
+
NSString *collection = nil;
|
|
4277
|
+
id metadata = nil;
|
|
4278
|
+
[databaseTransaction getKey:&key collection:&collection metadata:&metadata forRowid:rowid];
|
|
4279
|
+
|
|
4280
|
+
block(collection, key, metadata, index, stop);
|
|
4281
|
+
}];
|
|
4282
|
+
}
|
|
4283
|
+
|
|
4284
|
+
- (void)enumerateKeysAndMetadataInGroup:(NSString *)group
|
|
4285
|
+
withOptions:(NSEnumerationOptions)options
|
|
4286
|
+
usingBlock:
|
|
4287
|
+
(void (^)(NSString *collection, NSString *key, id metadata, NSUInteger index, BOOL *stop))block
|
|
4288
|
+
{
|
|
4289
|
+
if (block == NULL) return;
|
|
4290
|
+
|
|
4291
|
+
[self enumerateRowidsInGroup:group
|
|
4292
|
+
withOptions:options
|
|
4293
|
+
usingBlock:^(int64_t rowid, NSUInteger index, BOOL *stop) {
|
|
4294
|
+
|
|
4295
|
+
NSString *key = nil;
|
|
4296
|
+
NSString *collection = nil;
|
|
4297
|
+
id metadata = nil;
|
|
4298
|
+
[databaseTransaction getKey:&key collection:&collection metadata:&metadata forRowid:rowid];
|
|
4299
|
+
|
|
4300
|
+
block(collection, key, metadata, index, stop);
|
|
4301
|
+
}];
|
|
4302
|
+
}
|
|
4303
|
+
|
|
4304
|
+
- (void)enumerateKeysAndMetadataInGroup:(NSString *)group
|
|
4305
|
+
withOptions:(NSEnumerationOptions)options
|
|
4306
|
+
range:(NSRange)range
|
|
4307
|
+
usingBlock:
|
|
4308
|
+
(void (^)(NSString *collection, NSString *key, id metadata, NSUInteger index, BOOL *stop))block
|
|
4309
|
+
{
|
|
4310
|
+
if (block == NULL) return;
|
|
4311
|
+
|
|
4312
|
+
[self enumerateRowidsInGroup:group
|
|
4313
|
+
withOptions:options
|
|
4314
|
+
range:range
|
|
4315
|
+
usingBlock:^(int64_t rowid, NSUInteger index, BOOL *stop) {
|
|
4316
|
+
|
|
4317
|
+
NSString *key = nil;
|
|
4318
|
+
NSString *collection = nil;
|
|
4319
|
+
id metadata = nil;
|
|
4320
|
+
[databaseTransaction getKey:&key collection:&collection metadata:&metadata forRowid:rowid];
|
|
4321
|
+
|
|
4322
|
+
block(collection, key, metadata, index, stop);
|
|
4323
|
+
}];
|
|
4324
|
+
}
|
|
4325
|
+
|
|
4326
|
+
/**
|
|
4327
|
+
* The following methods are equivalent to invoking the enumerateKeysInGroup:... methods,
|
|
4328
|
+
* and then fetching the object within your own block.
|
|
4329
|
+
**/
|
|
4330
|
+
|
|
4331
|
+
- (void)enumerateKeysAndObjectsInGroup:(NSString *)group
|
|
4332
|
+
usingBlock:
|
|
4333
|
+
(void (^)(NSString *collection, NSString *key, id object, NSUInteger index, BOOL *stop))block
|
|
4334
|
+
{
|
|
4335
|
+
if (block == NULL) return;
|
|
4336
|
+
|
|
4337
|
+
[self enumerateRowidsInGroup:group usingBlock:^(int64_t rowid, NSUInteger index, BOOL *stop) {
|
|
4338
|
+
|
|
4339
|
+
NSString *key = nil;
|
|
4340
|
+
NSString *collection = nil;
|
|
4341
|
+
id object = nil;
|
|
4342
|
+
[databaseTransaction getKey:&key collection:&collection object:&object forRowid:rowid];
|
|
4343
|
+
|
|
4344
|
+
block(collection, key, object, index, stop);
|
|
4345
|
+
}];
|
|
4346
|
+
}
|
|
4347
|
+
|
|
4348
|
+
- (void)enumerateKeysAndObjectsInGroup:(NSString *)group
|
|
4349
|
+
withOptions:(NSEnumerationOptions)options
|
|
4350
|
+
usingBlock:
|
|
4351
|
+
(void (^)(NSString *collection, NSString *key, id object, NSUInteger index, BOOL *stop))block
|
|
4352
|
+
{
|
|
4353
|
+
if (block == NULL) return;
|
|
4354
|
+
|
|
4355
|
+
[self enumerateRowidsInGroup:group
|
|
4356
|
+
withOptions:options
|
|
4357
|
+
usingBlock:^(int64_t rowid, NSUInteger index, BOOL *stop) {
|
|
4358
|
+
|
|
4359
|
+
NSString *key = nil;
|
|
4360
|
+
NSString *collection = nil;
|
|
4361
|
+
id object = nil;
|
|
4362
|
+
[databaseTransaction getKey:&key collection:&collection object:&object forRowid:rowid];
|
|
4363
|
+
|
|
4364
|
+
block(collection, key, object, index, stop);
|
|
4365
|
+
}];
|
|
4366
|
+
}
|
|
4367
|
+
|
|
4368
|
+
- (void)enumerateKeysAndObjectsInGroup:(NSString *)group
|
|
4369
|
+
withOptions:(NSEnumerationOptions)options
|
|
4370
|
+
range:(NSRange)range
|
|
4371
|
+
usingBlock:
|
|
4372
|
+
(void (^)(NSString *collection, NSString *key, id object, NSUInteger index, BOOL *stop))block
|
|
4373
|
+
{
|
|
4374
|
+
if (block == NULL) return;
|
|
4375
|
+
|
|
4376
|
+
[self enumerateRowidsInGroup:group
|
|
4377
|
+
withOptions:options
|
|
4378
|
+
range:range
|
|
4379
|
+
usingBlock:^(int64_t rowid, NSUInteger index, BOOL *stop) {
|
|
4380
|
+
|
|
4381
|
+
NSString *key = nil;
|
|
4382
|
+
NSString *collection = nil;
|
|
4383
|
+
id object = nil;
|
|
4384
|
+
[databaseTransaction getKey:&key collection:&collection object:&object forRowid:rowid];
|
|
4385
|
+
|
|
4386
|
+
block(collection, key, object, index, stop);
|
|
4387
|
+
}];
|
|
4388
|
+
}
|
|
4389
|
+
|
|
4390
|
+
- (void)enumerateRowsInGroup:(NSString *)group
|
|
4391
|
+
usingBlock:
|
|
4392
|
+
(void (^)(NSString *collection, NSString *key, id object, id metadata, NSUInteger index, BOOL *stop))block
|
|
4393
|
+
{
|
|
4394
|
+
if (block == NULL) return;
|
|
4395
|
+
|
|
4396
|
+
[self enumerateRowidsInGroup:group usingBlock:^(int64_t rowid, NSUInteger index, BOOL *stop) {
|
|
4397
|
+
|
|
4398
|
+
NSString *key = nil;
|
|
4399
|
+
NSString *collection = nil;
|
|
4400
|
+
id object = nil;
|
|
4401
|
+
id metadata = nil;
|
|
4402
|
+
[databaseTransaction getKey:&key collection:&collection object:&object metadata:&metadata forRowid:rowid];
|
|
4403
|
+
|
|
4404
|
+
block(collection, key, object, metadata, index, stop);
|
|
4405
|
+
}];
|
|
4406
|
+
}
|
|
4407
|
+
|
|
4408
|
+
- (void)enumerateRowsInGroup:(NSString *)group
|
|
4409
|
+
withOptions:(NSEnumerationOptions)options
|
|
4410
|
+
usingBlock:
|
|
4411
|
+
(void (^)(NSString *collection, NSString *key, id object, id metadata, NSUInteger index, BOOL *stop))block
|
|
4412
|
+
{
|
|
4413
|
+
if (block == NULL) return;
|
|
4414
|
+
|
|
4415
|
+
[self enumerateRowidsInGroup:group
|
|
4416
|
+
withOptions:options
|
|
4417
|
+
usingBlock:^(int64_t rowid, NSUInteger index, BOOL *stop) {
|
|
4418
|
+
|
|
4419
|
+
NSString *key = nil;
|
|
4420
|
+
NSString *collection = nil;
|
|
4421
|
+
id object = nil;
|
|
4422
|
+
id metadata = nil;
|
|
4423
|
+
[databaseTransaction getKey:&key collection:&collection object:&object metadata:&metadata forRowid:rowid];
|
|
4424
|
+
|
|
4425
|
+
block(collection, key, object, metadata, index, stop);
|
|
4426
|
+
}];
|
|
4427
|
+
}
|
|
4428
|
+
|
|
4429
|
+
- (void)enumerateRowsInGroup:(NSString *)group
|
|
4430
|
+
withOptions:(NSEnumerationOptions)options
|
|
4431
|
+
range:(NSRange)range
|
|
4432
|
+
usingBlock:
|
|
4433
|
+
(void (^)(NSString *collection, NSString *key, id object, id metadata, NSUInteger index, BOOL *stop))block
|
|
4434
|
+
{
|
|
4435
|
+
if (block == NULL) return;
|
|
4436
|
+
|
|
4437
|
+
[self enumerateRowidsInGroup:group
|
|
4438
|
+
withOptions:options
|
|
4439
|
+
range:range
|
|
4440
|
+
usingBlock:^(int64_t rowid, NSUInteger index, BOOL *stop) {
|
|
4441
|
+
|
|
4442
|
+
NSString *key = nil;
|
|
4443
|
+
NSString *collection = nil;
|
|
4444
|
+
id object = nil;
|
|
4445
|
+
id metadata = nil;
|
|
4446
|
+
[databaseTransaction getKey:&key collection:&collection object:&object metadata:&metadata forRowid:rowid];
|
|
4447
|
+
|
|
4448
|
+
block(collection, key, object, metadata, index, stop);
|
|
4449
|
+
}];
|
|
4450
|
+
}
|
|
4451
|
+
|
|
4452
|
+
@end
|
|
4453
|
+
|
|
4454
|
+
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
4455
|
+
#pragma mark -
|
|
4456
|
+
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
4457
|
+
|
|
4458
|
+
@implementation YapDatabaseViewTransaction (Mappings)
|
|
4459
|
+
|
|
4460
|
+
/**
|
|
4461
|
+
* Gets the key & collection at the given indexPath, assuming the given mappings are being used.
|
|
4462
|
+
* Returns NO if the indexPath is invalid, or the mappings aren't initialized.
|
|
4463
|
+
* Otherwise returns YES, and sets the key & collection ptr (both optional).
|
|
4464
|
+
**/
|
|
4465
|
+
- (BOOL)getKey:(NSString **)keyPtr
|
|
4466
|
+
collection:(NSString **)collectionPtr
|
|
4467
|
+
atIndexPath:(NSIndexPath *)indexPath
|
|
4468
|
+
withMappings:(YapDatabaseViewMappings *)mappings
|
|
4469
|
+
{
|
|
4470
|
+
if (indexPath && mappings)
|
|
4471
|
+
{
|
|
4472
|
+
NSString *group = nil;
|
|
4473
|
+
NSUInteger index = 0;
|
|
4474
|
+
|
|
4475
|
+
if ([mappings getGroup:&group index:&index forIndexPath:indexPath])
|
|
4476
|
+
{
|
|
4477
|
+
return [self getKey:keyPtr collection:collectionPtr atIndex:index inGroup:group];
|
|
4478
|
+
}
|
|
4479
|
+
}
|
|
4480
|
+
|
|
4481
|
+
if (keyPtr) *keyPtr = nil;
|
|
4482
|
+
if (collectionPtr) *collectionPtr = nil;
|
|
4483
|
+
return NO;
|
|
4484
|
+
}
|
|
4485
|
+
|
|
4486
|
+
/**
|
|
4487
|
+
* Fetches the indexPath for the given {collection, key} tuple, assuming the given mappings are being used.
|
|
4488
|
+
* Returns nil if the {collection, key} tuple isn't included in the view + mappings.
|
|
4489
|
+
**/
|
|
4490
|
+
- (NSIndexPath *)indexPathForKey:(NSString *)key
|
|
4491
|
+
inCollection:(NSString *)collection
|
|
4492
|
+
withMappings:(YapDatabaseViewMappings *)mappings
|
|
4493
|
+
{
|
|
4494
|
+
NSString *group = nil;
|
|
4495
|
+
NSUInteger index = 0;
|
|
4496
|
+
|
|
4497
|
+
if ([self getGroup:&group index:&index forKey:key inCollection:collection])
|
|
4498
|
+
{
|
|
4499
|
+
return [mappings indexPathForIndex:index inGroup:group];
|
|
4500
|
+
}
|
|
4501
|
+
|
|
4502
|
+
return nil;
|
|
4503
|
+
}
|
|
4504
|
+
|
|
4505
|
+
@end
|