motion-yapper 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (348) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +20 -0
  3. data/.travis.yml +6 -0
  4. data/Gemfile +5 -0
  5. data/Gemfile.lock +63 -0
  6. data/README.md +16 -0
  7. data/Rakefile +20 -0
  8. data/app/app_delegate.rb +5 -0
  9. data/lib/yapper.rb +30 -0
  10. data/lib/yapper/attachment.rb +48 -0
  11. data/lib/yapper/bson.rb +20 -0
  12. data/lib/yapper/config.rb +18 -0
  13. data/lib/yapper/db.rb +151 -0
  14. data/lib/yapper/document.rb +54 -0
  15. data/lib/yapper/document/attachment.rb +26 -0
  16. data/lib/yapper/document/callbacks.rb +86 -0
  17. data/lib/yapper/document/persistance.rb +171 -0
  18. data/lib/yapper/document/relation.rb +84 -0
  19. data/lib/yapper/document/selection.rb +100 -0
  20. data/lib/yapper/extensions.rb +80 -0
  21. data/lib/yapper/log.rb +5 -0
  22. data/lib/yapper/sync.rb +134 -0
  23. data/lib/yapper/sync/data.rb +12 -0
  24. data/lib/yapper/sync/event.rb +194 -0
  25. data/lib/yapper/sync/queue.rb +164 -0
  26. data/lib/yapper/timestamps.rb +16 -0
  27. data/lib/yapper/version.rb +3 -0
  28. data/lib/yapper/yapper.rb +2 -0
  29. data/spec/helpers/time_helper.rb +3 -0
  30. data/spec/integration/all_spec.rb +40 -0
  31. data/spec/integration/callback_spec.rb +87 -0
  32. data/spec/integration/db_spec.rb +40 -0
  33. data/spec/integration/find_spec.rb +51 -0
  34. data/spec/integration/persistance_spec.rb +118 -0
  35. data/spec/integration/relation_spec.rb +174 -0
  36. data/spec/integration/sync_spec.rb +42 -0
  37. data/spec/integration/timestamps_spec.rb +34 -0
  38. data/spec/integration/types_spec.rb +23 -0
  39. data/spec/integration/when_spec.rb +29 -0
  40. data/spec/integration/where_spec.rb +132 -0
  41. data/vendor/Podfile.lock +24 -0
  42. data/vendor/Pods/AFNetworking/AFNetworking/AFHTTPClient.h +641 -0
  43. data/vendor/Pods/AFNetworking/AFNetworking/AFHTTPClient.m +1396 -0
  44. data/vendor/Pods/AFNetworking/AFNetworking/AFHTTPRequestOperation.h +133 -0
  45. data/vendor/Pods/AFNetworking/AFNetworking/AFHTTPRequestOperation.m +327 -0
  46. data/vendor/Pods/AFNetworking/AFNetworking/AFImageRequestOperation.h +113 -0
  47. data/vendor/Pods/AFNetworking/AFNetworking/AFImageRequestOperation.m +321 -0
  48. data/vendor/Pods/AFNetworking/AFNetworking/AFJSONRequestOperation.h +71 -0
  49. data/vendor/Pods/AFNetworking/AFNetworking/AFJSONRequestOperation.m +150 -0
  50. data/vendor/Pods/AFNetworking/AFNetworking/AFNetworkActivityIndicatorManager.h +75 -0
  51. data/vendor/Pods/AFNetworking/AFNetworking/AFNetworkActivityIndicatorManager.m +157 -0
  52. data/vendor/Pods/AFNetworking/AFNetworking/AFNetworking.h +43 -0
  53. data/vendor/Pods/AFNetworking/AFNetworking/AFPropertyListRequestOperation.h +68 -0
  54. data/vendor/Pods/AFNetworking/AFNetworking/AFPropertyListRequestOperation.m +143 -0
  55. data/vendor/Pods/AFNetworking/AFNetworking/AFURLConnectionOperation.h +370 -0
  56. data/vendor/Pods/AFNetworking/AFNetworking/AFURLConnectionOperation.m +848 -0
  57. data/vendor/Pods/AFNetworking/AFNetworking/AFXMLRequestOperation.h +89 -0
  58. data/vendor/Pods/AFNetworking/AFNetworking/AFXMLRequestOperation.m +167 -0
  59. data/vendor/Pods/AFNetworking/AFNetworking/UIImageView+AFNetworking.h +78 -0
  60. data/vendor/Pods/AFNetworking/AFNetworking/UIImageView+AFNetworking.m +191 -0
  61. data/vendor/Pods/AFNetworking/LICENSE +19 -0
  62. data/vendor/Pods/AFNetworking/README.md +208 -0
  63. data/vendor/Pods/BuildHeaders/AFNetworking/AFHTTPClient.h +641 -0
  64. data/vendor/Pods/BuildHeaders/AFNetworking/AFHTTPRequestOperation.h +133 -0
  65. data/vendor/Pods/BuildHeaders/AFNetworking/AFImageRequestOperation.h +113 -0
  66. data/vendor/Pods/BuildHeaders/AFNetworking/AFJSONRequestOperation.h +71 -0
  67. data/vendor/Pods/BuildHeaders/AFNetworking/AFNetworkActivityIndicatorManager.h +75 -0
  68. data/vendor/Pods/BuildHeaders/AFNetworking/AFNetworking.h +43 -0
  69. data/vendor/Pods/BuildHeaders/AFNetworking/AFPropertyListRequestOperation.h +68 -0
  70. data/vendor/Pods/BuildHeaders/AFNetworking/AFURLConnectionOperation.h +370 -0
  71. data/vendor/Pods/BuildHeaders/AFNetworking/AFXMLRequestOperation.h +89 -0
  72. data/vendor/Pods/BuildHeaders/AFNetworking/UIImageView+AFNetworking.h +78 -0
  73. data/vendor/Pods/BuildHeaders/CocoaLumberjack/ContextFilterLogFormatter.h +65 -0
  74. data/vendor/Pods/BuildHeaders/CocoaLumberjack/DDASLLogger.h +41 -0
  75. data/vendor/Pods/BuildHeaders/CocoaLumberjack/DDAbstractDatabaseLogger.h +102 -0
  76. data/vendor/Pods/BuildHeaders/CocoaLumberjack/DDFileLogger.h +334 -0
  77. data/vendor/Pods/BuildHeaders/CocoaLumberjack/DDLog.h +601 -0
  78. data/vendor/Pods/BuildHeaders/CocoaLumberjack/DDTTYLogger.h +167 -0
  79. data/vendor/Pods/BuildHeaders/CocoaLumberjack/DispatchQueueLogFormatter.h +116 -0
  80. data/vendor/Pods/BuildHeaders/NSData+MD5Digest/NSData+MD5Digest.h +18 -0
  81. data/vendor/Pods/BuildHeaders/Reachability/Reachability.h +109 -0
  82. data/vendor/Pods/BuildHeaders/YapDatabase/YapCache.h +90 -0
  83. data/vendor/Pods/BuildHeaders/YapDatabase/YapCollectionKey.h +20 -0
  84. data/vendor/Pods/BuildHeaders/YapDatabase/YapDatabase.h +547 -0
  85. data/vendor/Pods/BuildHeaders/YapDatabase/YapDatabaseConnection.h +447 -0
  86. data/vendor/Pods/BuildHeaders/YapDatabase/YapDatabaseConnectionState.h +29 -0
  87. data/vendor/Pods/BuildHeaders/YapDatabase/YapDatabaseDefaults.h +37 -0
  88. data/vendor/Pods/BuildHeaders/YapDatabase/YapDatabaseExtension.h +15 -0
  89. data/vendor/Pods/BuildHeaders/YapDatabase/YapDatabaseExtensionConnection.h +11 -0
  90. data/vendor/Pods/BuildHeaders/YapDatabase/YapDatabaseExtensionPrivate.h +440 -0
  91. data/vendor/Pods/BuildHeaders/YapDatabase/YapDatabaseExtensionTransaction.h +11 -0
  92. data/vendor/Pods/BuildHeaders/YapDatabase/YapDatabaseFilteredView.h +108 -0
  93. data/vendor/Pods/BuildHeaders/YapDatabase/YapDatabaseFilteredViewConnection.h +12 -0
  94. data/vendor/Pods/BuildHeaders/YapDatabase/YapDatabaseFilteredViewPrivate.h +19 -0
  95. data/vendor/Pods/BuildHeaders/YapDatabase/YapDatabaseFilteredViewTransaction.h +39 -0
  96. data/vendor/Pods/BuildHeaders/YapDatabase/YapDatabaseFullTextSearch.h +89 -0
  97. data/vendor/Pods/BuildHeaders/YapDatabase/YapDatabaseFullTextSearchConnection.h +32 -0
  98. data/vendor/Pods/BuildHeaders/YapDatabase/YapDatabaseFullTextSearchPrivate.h +69 -0
  99. data/vendor/Pods/BuildHeaders/YapDatabase/YapDatabaseFullTextSearchSnippetOptions.h +79 -0
  100. data/vendor/Pods/BuildHeaders/YapDatabase/YapDatabaseFullTextSearchTransaction.h +68 -0
  101. data/vendor/Pods/BuildHeaders/YapDatabase/YapDatabaseLogging.h +158 -0
  102. data/vendor/Pods/BuildHeaders/YapDatabase/YapDatabaseManager.h +17 -0
  103. data/vendor/Pods/BuildHeaders/YapDatabase/YapDatabasePrivate.h +424 -0
  104. data/vendor/Pods/BuildHeaders/YapDatabase/YapDatabaseQuery.h +42 -0
  105. data/vendor/Pods/BuildHeaders/YapDatabase/YapDatabaseSecondaryIndex.h +100 -0
  106. data/vendor/Pods/BuildHeaders/YapDatabase/YapDatabaseSecondaryIndexConnection.h +33 -0
  107. data/vendor/Pods/BuildHeaders/YapDatabase/YapDatabaseSecondaryIndexPrivate.h +73 -0
  108. data/vendor/Pods/BuildHeaders/YapDatabase/YapDatabaseSecondaryIndexSetup.h +33 -0
  109. data/vendor/Pods/BuildHeaders/YapDatabase/YapDatabaseSecondaryIndexTransaction.h +58 -0
  110. data/vendor/Pods/BuildHeaders/YapDatabase/YapDatabaseStatement.h +13 -0
  111. data/vendor/Pods/BuildHeaders/YapDatabase/YapDatabaseString.h +121 -0
  112. data/vendor/Pods/BuildHeaders/YapDatabase/YapDatabaseTransaction.h +541 -0
  113. data/vendor/Pods/BuildHeaders/YapDatabase/YapDatabaseView.h +186 -0
  114. data/vendor/Pods/BuildHeaders/YapDatabase/YapDatabaseViewChange.h +272 -0
  115. data/vendor/Pods/BuildHeaders/YapDatabase/YapDatabaseViewChangePrivate.h +94 -0
  116. data/vendor/Pods/BuildHeaders/YapDatabase/YapDatabaseViewConnection.h +115 -0
  117. data/vendor/Pods/BuildHeaders/YapDatabase/YapDatabaseViewMappings.h +825 -0
  118. data/vendor/Pods/BuildHeaders/YapDatabase/YapDatabaseViewMappingsPrivate.h +72 -0
  119. data/vendor/Pods/BuildHeaders/YapDatabase/YapDatabaseViewOptions.h +56 -0
  120. data/vendor/Pods/BuildHeaders/YapDatabase/YapDatabaseViewPage.h +36 -0
  121. data/vendor/Pods/BuildHeaders/YapDatabase/YapDatabaseViewPageMetadata.h +27 -0
  122. data/vendor/Pods/BuildHeaders/YapDatabase/YapDatabaseViewPrivate.h +153 -0
  123. data/vendor/Pods/BuildHeaders/YapDatabase/YapDatabaseViewRangeOptions.h +330 -0
  124. data/vendor/Pods/BuildHeaders/YapDatabase/YapDatabaseViewRangeOptionsPrivate.h +17 -0
  125. data/vendor/Pods/BuildHeaders/YapDatabase/YapDatabaseViewTransaction.h +447 -0
  126. data/vendor/Pods/BuildHeaders/YapDatabase/YapMemoryTable.h +74 -0
  127. data/vendor/Pods/BuildHeaders/YapDatabase/YapNull.h +17 -0
  128. data/vendor/Pods/BuildHeaders/YapDatabase/YapSet.h +41 -0
  129. data/vendor/Pods/BuildHeaders/YapDatabase/YapTouch.h +15 -0
  130. data/vendor/Pods/CocoaLumberjack/LICENSE.txt +18 -0
  131. data/vendor/Pods/CocoaLumberjack/Lumberjack/DDASLLogger.h +41 -0
  132. data/vendor/Pods/CocoaLumberjack/Lumberjack/DDASLLogger.m +99 -0
  133. data/vendor/Pods/CocoaLumberjack/Lumberjack/DDAbstractDatabaseLogger.h +102 -0
  134. data/vendor/Pods/CocoaLumberjack/Lumberjack/DDAbstractDatabaseLogger.m +727 -0
  135. data/vendor/Pods/CocoaLumberjack/Lumberjack/DDFileLogger.h +334 -0
  136. data/vendor/Pods/CocoaLumberjack/Lumberjack/DDFileLogger.m +1353 -0
  137. data/vendor/Pods/CocoaLumberjack/Lumberjack/DDLog.h +601 -0
  138. data/vendor/Pods/CocoaLumberjack/Lumberjack/DDLog.m +1083 -0
  139. data/vendor/Pods/CocoaLumberjack/Lumberjack/DDTTYLogger.h +167 -0
  140. data/vendor/Pods/CocoaLumberjack/Lumberjack/DDTTYLogger.m +1479 -0
  141. data/vendor/Pods/CocoaLumberjack/Lumberjack/Extensions/ContextFilterLogFormatter.h +65 -0
  142. data/vendor/Pods/CocoaLumberjack/Lumberjack/Extensions/ContextFilterLogFormatter.m +191 -0
  143. data/vendor/Pods/CocoaLumberjack/Lumberjack/Extensions/DispatchQueueLogFormatter.h +116 -0
  144. data/vendor/Pods/CocoaLumberjack/Lumberjack/Extensions/DispatchQueueLogFormatter.m +251 -0
  145. data/vendor/Pods/CocoaLumberjack/Lumberjack/Extensions/README.txt +7 -0
  146. data/vendor/Pods/CocoaLumberjack/README.markdown +37 -0
  147. data/vendor/Pods/Headers/AFNetworking/AFHTTPClient.h +641 -0
  148. data/vendor/Pods/Headers/AFNetworking/AFHTTPRequestOperation.h +133 -0
  149. data/vendor/Pods/Headers/AFNetworking/AFImageRequestOperation.h +113 -0
  150. data/vendor/Pods/Headers/AFNetworking/AFJSONRequestOperation.h +71 -0
  151. data/vendor/Pods/Headers/AFNetworking/AFNetworkActivityIndicatorManager.h +75 -0
  152. data/vendor/Pods/Headers/AFNetworking/AFNetworking.h +43 -0
  153. data/vendor/Pods/Headers/AFNetworking/AFPropertyListRequestOperation.h +68 -0
  154. data/vendor/Pods/Headers/AFNetworking/AFURLConnectionOperation.h +370 -0
  155. data/vendor/Pods/Headers/AFNetworking/AFXMLRequestOperation.h +89 -0
  156. data/vendor/Pods/Headers/AFNetworking/UIImageView+AFNetworking.h +78 -0
  157. data/vendor/Pods/Headers/CocoaLumberjack/ContextFilterLogFormatter.h +65 -0
  158. data/vendor/Pods/Headers/CocoaLumberjack/DDASLLogger.h +41 -0
  159. data/vendor/Pods/Headers/CocoaLumberjack/DDAbstractDatabaseLogger.h +102 -0
  160. data/vendor/Pods/Headers/CocoaLumberjack/DDFileLogger.h +334 -0
  161. data/vendor/Pods/Headers/CocoaLumberjack/DDLog.h +601 -0
  162. data/vendor/Pods/Headers/CocoaLumberjack/DDTTYLogger.h +167 -0
  163. data/vendor/Pods/Headers/CocoaLumberjack/DispatchQueueLogFormatter.h +116 -0
  164. data/vendor/Pods/Headers/NSData+MD5Digest/NSData+MD5Digest.h +18 -0
  165. data/vendor/Pods/Headers/Reachability/Reachability.h +109 -0
  166. data/vendor/Pods/Headers/YapDatabase/YapCache.h +90 -0
  167. data/vendor/Pods/Headers/YapDatabase/YapCollectionKey.h +20 -0
  168. data/vendor/Pods/Headers/YapDatabase/YapDatabase.h +547 -0
  169. data/vendor/Pods/Headers/YapDatabase/YapDatabaseConnection.h +447 -0
  170. data/vendor/Pods/Headers/YapDatabase/YapDatabaseConnectionState.h +29 -0
  171. data/vendor/Pods/Headers/YapDatabase/YapDatabaseDefaults.h +37 -0
  172. data/vendor/Pods/Headers/YapDatabase/YapDatabaseExtension.h +15 -0
  173. data/vendor/Pods/Headers/YapDatabase/YapDatabaseExtensionConnection.h +11 -0
  174. data/vendor/Pods/Headers/YapDatabase/YapDatabaseExtensionPrivate.h +440 -0
  175. data/vendor/Pods/Headers/YapDatabase/YapDatabaseExtensionTransaction.h +11 -0
  176. data/vendor/Pods/Headers/YapDatabase/YapDatabaseFilteredView.h +108 -0
  177. data/vendor/Pods/Headers/YapDatabase/YapDatabaseFilteredViewConnection.h +12 -0
  178. data/vendor/Pods/Headers/YapDatabase/YapDatabaseFilteredViewPrivate.h +19 -0
  179. data/vendor/Pods/Headers/YapDatabase/YapDatabaseFilteredViewTransaction.h +39 -0
  180. data/vendor/Pods/Headers/YapDatabase/YapDatabaseFullTextSearch.h +89 -0
  181. data/vendor/Pods/Headers/YapDatabase/YapDatabaseFullTextSearchConnection.h +32 -0
  182. data/vendor/Pods/Headers/YapDatabase/YapDatabaseFullTextSearchPrivate.h +69 -0
  183. data/vendor/Pods/Headers/YapDatabase/YapDatabaseFullTextSearchSnippetOptions.h +79 -0
  184. data/vendor/Pods/Headers/YapDatabase/YapDatabaseFullTextSearchTransaction.h +68 -0
  185. data/vendor/Pods/Headers/YapDatabase/YapDatabaseLogging.h +158 -0
  186. data/vendor/Pods/Headers/YapDatabase/YapDatabaseManager.h +17 -0
  187. data/vendor/Pods/Headers/YapDatabase/YapDatabasePrivate.h +424 -0
  188. data/vendor/Pods/Headers/YapDatabase/YapDatabaseQuery.h +42 -0
  189. data/vendor/Pods/Headers/YapDatabase/YapDatabaseSecondaryIndex.h +100 -0
  190. data/vendor/Pods/Headers/YapDatabase/YapDatabaseSecondaryIndexConnection.h +33 -0
  191. data/vendor/Pods/Headers/YapDatabase/YapDatabaseSecondaryIndexPrivate.h +73 -0
  192. data/vendor/Pods/Headers/YapDatabase/YapDatabaseSecondaryIndexSetup.h +33 -0
  193. data/vendor/Pods/Headers/YapDatabase/YapDatabaseSecondaryIndexTransaction.h +58 -0
  194. data/vendor/Pods/Headers/YapDatabase/YapDatabaseStatement.h +13 -0
  195. data/vendor/Pods/Headers/YapDatabase/YapDatabaseString.h +121 -0
  196. data/vendor/Pods/Headers/YapDatabase/YapDatabaseTransaction.h +541 -0
  197. data/vendor/Pods/Headers/YapDatabase/YapDatabaseView.h +186 -0
  198. data/vendor/Pods/Headers/YapDatabase/YapDatabaseViewChange.h +272 -0
  199. data/vendor/Pods/Headers/YapDatabase/YapDatabaseViewChangePrivate.h +94 -0
  200. data/vendor/Pods/Headers/YapDatabase/YapDatabaseViewConnection.h +115 -0
  201. data/vendor/Pods/Headers/YapDatabase/YapDatabaseViewMappings.h +825 -0
  202. data/vendor/Pods/Headers/YapDatabase/YapDatabaseViewMappingsPrivate.h +72 -0
  203. data/vendor/Pods/Headers/YapDatabase/YapDatabaseViewOptions.h +56 -0
  204. data/vendor/Pods/Headers/YapDatabase/YapDatabaseViewPage.h +36 -0
  205. data/vendor/Pods/Headers/YapDatabase/YapDatabaseViewPageMetadata.h +27 -0
  206. data/vendor/Pods/Headers/YapDatabase/YapDatabaseViewPrivate.h +153 -0
  207. data/vendor/Pods/Headers/YapDatabase/YapDatabaseViewRangeOptions.h +330 -0
  208. data/vendor/Pods/Headers/YapDatabase/YapDatabaseViewRangeOptionsPrivate.h +17 -0
  209. data/vendor/Pods/Headers/YapDatabase/YapDatabaseViewTransaction.h +447 -0
  210. data/vendor/Pods/Headers/YapDatabase/YapMemoryTable.h +74 -0
  211. data/vendor/Pods/Headers/YapDatabase/YapNull.h +17 -0
  212. data/vendor/Pods/Headers/YapDatabase/YapSet.h +41 -0
  213. data/vendor/Pods/Headers/YapDatabase/YapTouch.h +15 -0
  214. data/vendor/Pods/Headers/____Pods-AFNetworking-prefix.h +17 -0
  215. data/vendor/Pods/Headers/____Pods-CocoaLumberjack-prefix.h +5 -0
  216. data/vendor/Pods/Headers/____Pods-NSData+MD5Digest-prefix.h +5 -0
  217. data/vendor/Pods/Headers/____Pods-Reachability-prefix.h +5 -0
  218. data/vendor/Pods/Headers/____Pods-YapDatabase-prefix.h +5 -0
  219. data/vendor/Pods/Headers/____Pods-environment.h +38 -0
  220. data/vendor/Pods/Manifest.lock +24 -0
  221. data/vendor/Pods/NSData+MD5Digest/NSData+MD5Digest/NSData+MD5Digest.h +18 -0
  222. data/vendor/Pods/NSData+MD5Digest/NSData+MD5Digest/NSData+MD5Digest.m +39 -0
  223. data/vendor/Pods/NSData+MD5Digest/README.md +11 -0
  224. data/vendor/Pods/Pods-AFNetworking-Private.xcconfig +5 -0
  225. data/vendor/Pods/Pods-AFNetworking-dummy.m +5 -0
  226. data/vendor/Pods/Pods-AFNetworking-prefix.pch +17 -0
  227. data/vendor/Pods/Pods-AFNetworking.xcconfig +1 -0
  228. data/vendor/Pods/Pods-Acknowledgements.markdown +96 -0
  229. data/vendor/Pods/Pods-Acknowledgements.plist +142 -0
  230. data/vendor/Pods/Pods-CocoaLumberjack-Private.xcconfig +5 -0
  231. data/vendor/Pods/Pods-CocoaLumberjack-dummy.m +5 -0
  232. data/vendor/Pods/Pods-CocoaLumberjack-prefix.pch +5 -0
  233. data/vendor/Pods/Pods-CocoaLumberjack.xcconfig +0 -0
  234. data/vendor/Pods/Pods-NSData+MD5Digest-Private.xcconfig +5 -0
  235. data/vendor/Pods/Pods-NSData+MD5Digest-dummy.m +5 -0
  236. data/vendor/Pods/Pods-NSData+MD5Digest-prefix.pch +5 -0
  237. data/vendor/Pods/Pods-NSData+MD5Digest.xcconfig +0 -0
  238. data/vendor/Pods/Pods-Reachability-Private.xcconfig +5 -0
  239. data/vendor/Pods/Pods-Reachability-dummy.m +5 -0
  240. data/vendor/Pods/Pods-Reachability-prefix.pch +5 -0
  241. data/vendor/Pods/Pods-Reachability.xcconfig +1 -0
  242. data/vendor/Pods/Pods-YapDatabase-Private.xcconfig +5 -0
  243. data/vendor/Pods/Pods-YapDatabase-dummy.m +5 -0
  244. data/vendor/Pods/Pods-YapDatabase-prefix.pch +5 -0
  245. data/vendor/Pods/Pods-YapDatabase.xcconfig +1 -0
  246. data/vendor/Pods/Pods-dummy.m +5 -0
  247. data/vendor/Pods/Pods-environment.h +38 -0
  248. data/vendor/Pods/Pods-resources.sh +68 -0
  249. data/vendor/Pods/Pods.bridgesupport +5096 -0
  250. data/vendor/Pods/Pods.xcconfig +5 -0
  251. data/vendor/Pods/Pods.xcodeproj/project.pbxproj +5536 -0
  252. data/vendor/Pods/Reachability/LICENCE.txt +24 -0
  253. data/vendor/Pods/Reachability/README.md +65 -0
  254. data/vendor/Pods/Reachability/Reachability.h +109 -0
  255. data/vendor/Pods/Reachability/Reachability.m +527 -0
  256. data/vendor/Pods/YapDatabase/LICENSE.txt +18 -0
  257. data/vendor/Pods/YapDatabase/README.md +30 -0
  258. data/vendor/Pods/YapDatabase/YapDatabase/Extensions/FilteredViews/Internal/YapDatabaseFilteredViewPrivate.h +19 -0
  259. data/vendor/Pods/YapDatabase/YapDatabase/Extensions/FilteredViews/YapDatabaseFilteredView.h +108 -0
  260. data/vendor/Pods/YapDatabase/YapDatabase/Extensions/FilteredViews/YapDatabaseFilteredView.m +175 -0
  261. data/vendor/Pods/YapDatabase/YapDatabase/Extensions/FilteredViews/YapDatabaseFilteredViewConnection.h +12 -0
  262. data/vendor/Pods/YapDatabase/YapDatabase/Extensions/FilteredViews/YapDatabaseFilteredViewConnection.m +41 -0
  263. data/vendor/Pods/YapDatabase/YapDatabase/Extensions/FilteredViews/YapDatabaseFilteredViewTransaction.h +39 -0
  264. data/vendor/Pods/YapDatabase/YapDatabase/Extensions/FilteredViews/YapDatabaseFilteredViewTransaction.m +966 -0
  265. data/vendor/Pods/YapDatabase/YapDatabase/Extensions/FullTextSearch/Internal/YapDatabaseFullTextSearchPrivate.h +69 -0
  266. data/vendor/Pods/YapDatabase/YapDatabase/Extensions/FullTextSearch/YapDatabaseFullTextSearch.h +89 -0
  267. data/vendor/Pods/YapDatabase/YapDatabase/Extensions/FullTextSearch/YapDatabaseFullTextSearch.m +146 -0
  268. data/vendor/Pods/YapDatabase/YapDatabase/Extensions/FullTextSearch/YapDatabaseFullTextSearchConnection.h +32 -0
  269. data/vendor/Pods/YapDatabase/YapDatabase/Extensions/FullTextSearch/YapDatabaseFullTextSearchConnection.m +298 -0
  270. data/vendor/Pods/YapDatabase/YapDatabase/Extensions/FullTextSearch/YapDatabaseFullTextSearchSnippetOptions.h +79 -0
  271. data/vendor/Pods/YapDatabase/YapDatabase/Extensions/FullTextSearch/YapDatabaseFullTextSearchSnippetOptions.m +95 -0
  272. data/vendor/Pods/YapDatabase/YapDatabase/Extensions/FullTextSearch/YapDatabaseFullTextSearchTransaction.h +68 -0
  273. data/vendor/Pods/YapDatabase/YapDatabase/Extensions/FullTextSearch/YapDatabaseFullTextSearchTransaction.m +1352 -0
  274. data/vendor/Pods/YapDatabase/YapDatabase/Extensions/Protocol/Internal/YapDatabaseExtensionPrivate.h +440 -0
  275. data/vendor/Pods/YapDatabase/YapDatabase/Extensions/Protocol/YapDatabaseExtension.h +15 -0
  276. data/vendor/Pods/YapDatabase/YapDatabase/Extensions/Protocol/YapDatabaseExtension.m +58 -0
  277. data/vendor/Pods/YapDatabase/YapDatabase/Extensions/Protocol/YapDatabaseExtensionConnection.h +11 -0
  278. data/vendor/Pods/YapDatabase/YapDatabase/Extensions/Protocol/YapDatabaseExtensionConnection.m +46 -0
  279. data/vendor/Pods/YapDatabase/YapDatabase/Extensions/Protocol/YapDatabaseExtensionTransaction.h +11 -0
  280. data/vendor/Pods/YapDatabase/YapDatabase/Extensions/Protocol/YapDatabaseExtensionTransaction.m +180 -0
  281. data/vendor/Pods/YapDatabase/YapDatabase/Extensions/SecondaryIndex/Internal/YapDatabaseSecondaryIndexPrivate.h +73 -0
  282. data/vendor/Pods/YapDatabase/YapDatabase/Extensions/SecondaryIndex/YapDatabaseSecondaryIndex.h +100 -0
  283. data/vendor/Pods/YapDatabase/YapDatabase/Extensions/SecondaryIndex/YapDatabaseSecondaryIndex.m +149 -0
  284. data/vendor/Pods/YapDatabase/YapDatabase/Extensions/SecondaryIndex/YapDatabaseSecondaryIndexConnection.h +33 -0
  285. data/vendor/Pods/YapDatabase/YapDatabase/Extensions/SecondaryIndex/YapDatabaseSecondaryIndexConnection.m +330 -0
  286. data/vendor/Pods/YapDatabase/YapDatabase/Extensions/SecondaryIndex/YapDatabaseSecondaryIndexSetup.h +33 -0
  287. data/vendor/Pods/YapDatabase/YapDatabase/Extensions/SecondaryIndex/YapDatabaseSecondaryIndexSetup.m +184 -0
  288. data/vendor/Pods/YapDatabase/YapDatabase/Extensions/SecondaryIndex/YapDatabaseSecondaryIndexTransaction.h +58 -0
  289. data/vendor/Pods/YapDatabase/YapDatabase/Extensions/SecondaryIndex/YapDatabaseSecondaryIndexTransaction.m +1166 -0
  290. data/vendor/Pods/YapDatabase/YapDatabase/Extensions/Views/Internal/YapDatabaseViewChangePrivate.h +94 -0
  291. data/vendor/Pods/YapDatabase/YapDatabase/Extensions/Views/Internal/YapDatabaseViewMappingsPrivate.h +72 -0
  292. data/vendor/Pods/YapDatabase/YapDatabase/Extensions/Views/Internal/YapDatabaseViewPage.h +36 -0
  293. data/vendor/Pods/YapDatabase/YapDatabase/Extensions/Views/Internal/YapDatabaseViewPage.mm +296 -0
  294. data/vendor/Pods/YapDatabase/YapDatabase/Extensions/Views/Internal/YapDatabaseViewPageMetadata.h +27 -0
  295. data/vendor/Pods/YapDatabase/YapDatabase/Extensions/Views/Internal/YapDatabaseViewPageMetadata.m +28 -0
  296. data/vendor/Pods/YapDatabase/YapDatabase/Extensions/Views/Internal/YapDatabaseViewPrivate.h +153 -0
  297. data/vendor/Pods/YapDatabase/YapDatabase/Extensions/Views/Internal/YapDatabaseViewRangeOptionsPrivate.h +17 -0
  298. data/vendor/Pods/YapDatabase/YapDatabase/Extensions/Views/Utilities/YapDatabaseViewChange.h +272 -0
  299. data/vendor/Pods/YapDatabase/YapDatabase/Extensions/Views/Utilities/YapDatabaseViewChange.m +2494 -0
  300. data/vendor/Pods/YapDatabase/YapDatabase/Extensions/Views/Utilities/YapDatabaseViewMappings.h +825 -0
  301. data/vendor/Pods/YapDatabase/YapDatabase/Extensions/Views/Utilities/YapDatabaseViewMappings.m +1567 -0
  302. data/vendor/Pods/YapDatabase/YapDatabase/Extensions/Views/Utilities/YapDatabaseViewRangeOptions.h +330 -0
  303. data/vendor/Pods/YapDatabase/YapDatabase/Extensions/Views/Utilities/YapDatabaseViewRangeOptions.m +141 -0
  304. data/vendor/Pods/YapDatabase/YapDatabase/Extensions/Views/YapDatabaseView.h +186 -0
  305. data/vendor/Pods/YapDatabase/YapDatabase/Extensions/Views/YapDatabaseView.m +191 -0
  306. data/vendor/Pods/YapDatabase/YapDatabase/Extensions/Views/YapDatabaseViewConnection.h +115 -0
  307. data/vendor/Pods/YapDatabase/YapDatabase/Extensions/Views/YapDatabaseViewConnection.m +897 -0
  308. data/vendor/Pods/YapDatabase/YapDatabase/Extensions/Views/YapDatabaseViewOptions.h +56 -0
  309. data/vendor/Pods/YapDatabase/YapDatabase/Extensions/Views/YapDatabaseViewOptions.m +27 -0
  310. data/vendor/Pods/YapDatabase/YapDatabase/Extensions/Views/YapDatabaseViewTransaction.h +447 -0
  311. data/vendor/Pods/YapDatabase/YapDatabase/Extensions/Views/YapDatabaseViewTransaction.m +4505 -0
  312. data/vendor/Pods/YapDatabase/YapDatabase/Internal/YapCache.h +90 -0
  313. data/vendor/Pods/YapDatabase/YapDatabase/Internal/YapCache.m +453 -0
  314. data/vendor/Pods/YapDatabase/YapDatabase/Internal/YapDatabaseConnectionState.h +29 -0
  315. data/vendor/Pods/YapDatabase/YapDatabase/Internal/YapDatabaseConnectionState.m +48 -0
  316. data/vendor/Pods/YapDatabase/YapDatabase/Internal/YapDatabaseDefaults.h +37 -0
  317. data/vendor/Pods/YapDatabase/YapDatabase/Internal/YapDatabaseDefaults.m +83 -0
  318. data/vendor/Pods/YapDatabase/YapDatabase/Internal/YapDatabaseLogging.h +158 -0
  319. data/vendor/Pods/YapDatabase/YapDatabase/Internal/YapDatabaseLogging.m +73 -0
  320. data/vendor/Pods/YapDatabase/YapDatabase/Internal/YapDatabaseManager.h +17 -0
  321. data/vendor/Pods/YapDatabase/YapDatabase/Internal/YapDatabaseManager.m +56 -0
  322. data/vendor/Pods/YapDatabase/YapDatabase/Internal/YapDatabasePrivate.h +424 -0
  323. data/vendor/Pods/YapDatabase/YapDatabase/Internal/YapDatabaseStatement.h +13 -0
  324. data/vendor/Pods/YapDatabase/YapDatabase/Internal/YapDatabaseStatement.m +26 -0
  325. data/vendor/Pods/YapDatabase/YapDatabase/Internal/YapDatabaseString.h +121 -0
  326. data/vendor/Pods/YapDatabase/YapDatabase/Internal/YapMemoryTable.h +74 -0
  327. data/vendor/Pods/YapDatabase/YapDatabase/Internal/YapMemoryTable.m +603 -0
  328. data/vendor/Pods/YapDatabase/YapDatabase/Internal/YapNull.h +17 -0
  329. data/vendor/Pods/YapDatabase/YapDatabase/Internal/YapNull.m +31 -0
  330. data/vendor/Pods/YapDatabase/YapDatabase/Internal/YapTouch.h +15 -0
  331. data/vendor/Pods/YapDatabase/YapDatabase/Internal/YapTouch.m +31 -0
  332. data/vendor/Pods/YapDatabase/YapDatabase/Utilities/YapCollectionKey.h +20 -0
  333. data/vendor/Pods/YapDatabase/YapDatabase/Utilities/YapCollectionKey.m +194 -0
  334. data/vendor/Pods/YapDatabase/YapDatabase/Utilities/YapDatabaseQuery.h +42 -0
  335. data/vendor/Pods/YapDatabase/YapDatabase/Utilities/YapDatabaseQuery.m +96 -0
  336. data/vendor/Pods/YapDatabase/YapDatabase/Utilities/YapSet.h +41 -0
  337. data/vendor/Pods/YapDatabase/YapDatabase/Utilities/YapSet.m +82 -0
  338. data/vendor/Pods/YapDatabase/YapDatabase/YapDatabase.h +547 -0
  339. data/vendor/Pods/YapDatabase/YapDatabase/YapDatabase.m +2022 -0
  340. data/vendor/Pods/YapDatabase/YapDatabase/YapDatabaseConnection.h +447 -0
  341. data/vendor/Pods/YapDatabase/YapDatabase/YapDatabaseConnection.m +3874 -0
  342. data/vendor/Pods/YapDatabase/YapDatabase/YapDatabaseTransaction.h +541 -0
  343. data/vendor/Pods/YapDatabase/YapDatabase/YapDatabaseTransaction.m +5282 -0
  344. data/vendor/YapDatabaseRubyMotion/YapDatabaseRubyMotion.bridgesupport +16 -0
  345. data/vendor/YapDatabaseRubyMotion/YapDatabaseRubyMotion.h +13 -0
  346. data/vendor/YapDatabaseRubyMotion/YapDatabaseRubyMotion.m +20 -0
  347. data/yapper.gemspec +24 -0
  348. 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