motion-yapper 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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