motion-yapper 0.0.3 → 0.1.0

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 (294) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/.travis.yml +3 -1
  4. data/lib/yapper.rb +3 -3
  5. data/lib/yapper/db.rb +61 -4
  6. data/lib/yapper/document/persistence.rb +4 -0
  7. data/lib/yapper/document/selection.rb +13 -0
  8. data/lib/yapper/version.rb +1 -1
  9. data/motion-yapper.gemspec +1 -1
  10. data/spec/integration/search_spec.rb +111 -0
  11. data/vendor/Podfile.lock +11 -7
  12. data/vendor/YapDatabaseRubyMotion/YapDatabaseRubyMotion.bridgesupport +16 -2
  13. data/vendor/YapDatabaseRubyMotion/YapDatabaseRubyMotion.h +10 -7
  14. data/vendor/YapDatabaseRubyMotion/YapDatabaseRubyMotion.m +20 -2
  15. metadata +19 -297
  16. data/vendor/Pods/.build/libPods-CocoaLumberjack.a +0 -0
  17. data/vendor/Pods/.build/libPods-NSData+MD5Digest.a +0 -0
  18. data/vendor/Pods/.build/libPods-YapDatabase.a +0 -0
  19. data/vendor/Pods/.build/libPods.a +0 -0
  20. data/vendor/Pods/BuildHeaders/CocoaLumberjack/DDASLLogger.h +0 -41
  21. data/vendor/Pods/BuildHeaders/CocoaLumberjack/DDAbstractDatabaseLogger.h +0 -102
  22. data/vendor/Pods/BuildHeaders/CocoaLumberjack/DDContextFilterLogFormatter.h +0 -63
  23. data/vendor/Pods/BuildHeaders/CocoaLumberjack/DDDispatchQueueLogFormatter.h +0 -128
  24. data/vendor/Pods/BuildHeaders/CocoaLumberjack/DDFileLogger.h +0 -369
  25. data/vendor/Pods/BuildHeaders/CocoaLumberjack/DDLog+LOGV.h +0 -99
  26. data/vendor/Pods/BuildHeaders/CocoaLumberjack/DDLog.h +0 -634
  27. data/vendor/Pods/BuildHeaders/CocoaLumberjack/DDMultiFormatter.h +0 -30
  28. data/vendor/Pods/BuildHeaders/CocoaLumberjack/DDTTYLogger.h +0 -181
  29. data/vendor/Pods/BuildHeaders/NSData+MD5Digest/NSData+MD5Digest.h +0 -18
  30. data/vendor/Pods/BuildHeaders/YapDatabase/NSDictionary+YapDatabase.h +0 -8
  31. data/vendor/Pods/BuildHeaders/YapDatabase/YapCache.h +0 -91
  32. data/vendor/Pods/BuildHeaders/YapDatabase/YapCollectionKey.h +0 -23
  33. data/vendor/Pods/BuildHeaders/YapDatabase/YapDatabase.h +0 -547
  34. data/vendor/Pods/BuildHeaders/YapDatabase/YapDatabaseConnection.h +0 -449
  35. data/vendor/Pods/BuildHeaders/YapDatabase/YapDatabaseConnectionState.h +0 -29
  36. data/vendor/Pods/BuildHeaders/YapDatabase/YapDatabaseDefaults.h +0 -37
  37. data/vendor/Pods/BuildHeaders/YapDatabase/YapDatabaseExtension.h +0 -15
  38. data/vendor/Pods/BuildHeaders/YapDatabase/YapDatabaseExtensionConnection.h +0 -11
  39. data/vendor/Pods/BuildHeaders/YapDatabase/YapDatabaseExtensionPrivate.h +0 -444
  40. data/vendor/Pods/BuildHeaders/YapDatabase/YapDatabaseExtensionTransaction.h +0 -11
  41. data/vendor/Pods/BuildHeaders/YapDatabase/YapDatabaseFilteredView.h +0 -81
  42. data/vendor/Pods/BuildHeaders/YapDatabase/YapDatabaseFilteredViewConnection.h +0 -12
  43. data/vendor/Pods/BuildHeaders/YapDatabase/YapDatabaseFilteredViewPrivate.h +0 -17
  44. data/vendor/Pods/BuildHeaders/YapDatabase/YapDatabaseFilteredViewTransaction.h +0 -39
  45. data/vendor/Pods/BuildHeaders/YapDatabase/YapDatabaseFilteredViewTypes.h +0 -32
  46. data/vendor/Pods/BuildHeaders/YapDatabase/YapDatabaseFullTextSearch.h +0 -89
  47. data/vendor/Pods/BuildHeaders/YapDatabase/YapDatabaseFullTextSearchConnection.h +0 -32
  48. data/vendor/Pods/BuildHeaders/YapDatabase/YapDatabaseFullTextSearchPrivate.h +0 -77
  49. data/vendor/Pods/BuildHeaders/YapDatabase/YapDatabaseFullTextSearchSnippetOptions.h +0 -79
  50. data/vendor/Pods/BuildHeaders/YapDatabase/YapDatabaseFullTextSearchTransaction.h +0 -68
  51. data/vendor/Pods/BuildHeaders/YapDatabase/YapDatabaseLogging.h +0 -158
  52. data/vendor/Pods/BuildHeaders/YapDatabase/YapDatabaseManager.h +0 -17
  53. data/vendor/Pods/BuildHeaders/YapDatabase/YapDatabasePrivate.h +0 -446
  54. data/vendor/Pods/BuildHeaders/YapDatabase/YapDatabaseQuery.h +0 -42
  55. data/vendor/Pods/BuildHeaders/YapDatabase/YapDatabaseRelationship.h +0 -35
  56. data/vendor/Pods/BuildHeaders/YapDatabase/YapDatabaseRelationshipConnection.h +0 -29
  57. data/vendor/Pods/BuildHeaders/YapDatabase/YapDatabaseRelationshipEdge.h +0 -163
  58. data/vendor/Pods/BuildHeaders/YapDatabase/YapDatabaseRelationshipEdgePrivate.h +0 -79
  59. data/vendor/Pods/BuildHeaders/YapDatabase/YapDatabaseRelationshipNode.h +0 -99
  60. data/vendor/Pods/BuildHeaders/YapDatabase/YapDatabaseRelationshipOptions.h +0 -59
  61. data/vendor/Pods/BuildHeaders/YapDatabase/YapDatabaseRelationshipPrivate.h +0 -125
  62. data/vendor/Pods/BuildHeaders/YapDatabase/YapDatabaseRelationshipTransaction.h +0 -384
  63. data/vendor/Pods/BuildHeaders/YapDatabase/YapDatabaseSecondaryIndex.h +0 -149
  64. data/vendor/Pods/BuildHeaders/YapDatabase/YapDatabaseSecondaryIndexConnection.h +0 -33
  65. data/vendor/Pods/BuildHeaders/YapDatabase/YapDatabaseSecondaryIndexPrivate.h +0 -83
  66. data/vendor/Pods/BuildHeaders/YapDatabase/YapDatabaseSecondaryIndexSetup.h +0 -33
  67. data/vendor/Pods/BuildHeaders/YapDatabase/YapDatabaseSecondaryIndexSetupPrivate.h +0 -18
  68. data/vendor/Pods/BuildHeaders/YapDatabase/YapDatabaseSecondaryIndexTransaction.h +0 -58
  69. data/vendor/Pods/BuildHeaders/YapDatabase/YapDatabaseStatement.h +0 -13
  70. data/vendor/Pods/BuildHeaders/YapDatabase/YapDatabaseString.h +0 -121
  71. data/vendor/Pods/BuildHeaders/YapDatabase/YapDatabaseTransaction.h +0 -651
  72. data/vendor/Pods/BuildHeaders/YapDatabase/YapDatabaseView.h +0 -127
  73. data/vendor/Pods/BuildHeaders/YapDatabase/YapDatabaseViewChange.h +0 -272
  74. data/vendor/Pods/BuildHeaders/YapDatabase/YapDatabaseViewChangePrivate.h +0 -94
  75. data/vendor/Pods/BuildHeaders/YapDatabase/YapDatabaseViewConnection.h +0 -116
  76. data/vendor/Pods/BuildHeaders/YapDatabase/YapDatabaseViewMappings.h +0 -842
  77. data/vendor/Pods/BuildHeaders/YapDatabase/YapDatabaseViewMappingsPrivate.h +0 -72
  78. data/vendor/Pods/BuildHeaders/YapDatabase/YapDatabaseViewOptions.h +0 -63
  79. data/vendor/Pods/BuildHeaders/YapDatabase/YapDatabaseViewPage.h +0 -36
  80. data/vendor/Pods/BuildHeaders/YapDatabase/YapDatabaseViewPageMetadata.h +0 -27
  81. data/vendor/Pods/BuildHeaders/YapDatabase/YapDatabaseViewPrivate.h +0 -170
  82. data/vendor/Pods/BuildHeaders/YapDatabase/YapDatabaseViewRangeOptions.h +0 -330
  83. data/vendor/Pods/BuildHeaders/YapDatabase/YapDatabaseViewRangeOptionsPrivate.h +0 -17
  84. data/vendor/Pods/BuildHeaders/YapDatabase/YapDatabaseViewTransaction.h +0 -498
  85. data/vendor/Pods/BuildHeaders/YapDatabase/YapDatabaseViewTypes.h +0 -99
  86. data/vendor/Pods/BuildHeaders/YapDatabase/YapMemoryTable.h +0 -74
  87. data/vendor/Pods/BuildHeaders/YapDatabase/YapNull.h +0 -17
  88. data/vendor/Pods/BuildHeaders/YapDatabase/YapSet.h +0 -41
  89. data/vendor/Pods/BuildHeaders/YapDatabase/YapTouch.h +0 -15
  90. data/vendor/Pods/CocoaLumberjack/LICENSE.txt +0 -18
  91. data/vendor/Pods/CocoaLumberjack/Lumberjack/DDASLLogger.h +0 -41
  92. data/vendor/Pods/CocoaLumberjack/Lumberjack/DDASLLogger.m +0 -100
  93. data/vendor/Pods/CocoaLumberjack/Lumberjack/DDAbstractDatabaseLogger.h +0 -102
  94. data/vendor/Pods/CocoaLumberjack/Lumberjack/DDAbstractDatabaseLogger.m +0 -727
  95. data/vendor/Pods/CocoaLumberjack/Lumberjack/DDFileLogger.h +0 -369
  96. data/vendor/Pods/CocoaLumberjack/Lumberjack/DDFileLogger.m +0 -1539
  97. data/vendor/Pods/CocoaLumberjack/Lumberjack/DDLog+LOGV.h +0 -99
  98. data/vendor/Pods/CocoaLumberjack/Lumberjack/DDLog.h +0 -634
  99. data/vendor/Pods/CocoaLumberjack/Lumberjack/DDLog.m +0 -1208
  100. data/vendor/Pods/CocoaLumberjack/Lumberjack/DDTTYLogger.h +0 -181
  101. data/vendor/Pods/CocoaLumberjack/Lumberjack/DDTTYLogger.m +0 -1520
  102. data/vendor/Pods/CocoaLumberjack/Lumberjack/Extensions/DDContextFilterLogFormatter.h +0 -63
  103. data/vendor/Pods/CocoaLumberjack/Lumberjack/Extensions/DDContextFilterLogFormatter.m +0 -191
  104. data/vendor/Pods/CocoaLumberjack/Lumberjack/Extensions/DDDispatchQueueLogFormatter.h +0 -128
  105. data/vendor/Pods/CocoaLumberjack/Lumberjack/Extensions/DDDispatchQueueLogFormatter.m +0 -253
  106. data/vendor/Pods/CocoaLumberjack/Lumberjack/Extensions/DDMultiFormatter.h +0 -30
  107. data/vendor/Pods/CocoaLumberjack/Lumberjack/Extensions/DDMultiFormatter.m +0 -127
  108. data/vendor/Pods/CocoaLumberjack/Lumberjack/Extensions/README.txt +0 -7
  109. data/vendor/Pods/CocoaLumberjack/README.markdown +0 -74
  110. data/vendor/Pods/Headers/CocoaLumberjack/DDASLLogger.h +0 -41
  111. data/vendor/Pods/Headers/CocoaLumberjack/DDAbstractDatabaseLogger.h +0 -102
  112. data/vendor/Pods/Headers/CocoaLumberjack/DDContextFilterLogFormatter.h +0 -63
  113. data/vendor/Pods/Headers/CocoaLumberjack/DDDispatchQueueLogFormatter.h +0 -128
  114. data/vendor/Pods/Headers/CocoaLumberjack/DDFileLogger.h +0 -369
  115. data/vendor/Pods/Headers/CocoaLumberjack/DDLog+LOGV.h +0 -99
  116. data/vendor/Pods/Headers/CocoaLumberjack/DDLog.h +0 -634
  117. data/vendor/Pods/Headers/CocoaLumberjack/DDMultiFormatter.h +0 -30
  118. data/vendor/Pods/Headers/CocoaLumberjack/DDTTYLogger.h +0 -181
  119. data/vendor/Pods/Headers/NSData+MD5Digest/NSData+MD5Digest.h +0 -18
  120. data/vendor/Pods/Headers/YapDatabase/YapCollectionKey.h +0 -23
  121. data/vendor/Pods/Headers/YapDatabase/YapDatabase.h +0 -547
  122. data/vendor/Pods/Headers/YapDatabase/YapDatabaseConnection.h +0 -449
  123. data/vendor/Pods/Headers/YapDatabase/YapDatabaseExtension.h +0 -15
  124. data/vendor/Pods/Headers/YapDatabase/YapDatabaseExtensionConnection.h +0 -11
  125. data/vendor/Pods/Headers/YapDatabase/YapDatabaseExtensionTransaction.h +0 -11
  126. data/vendor/Pods/Headers/YapDatabase/YapDatabaseFilteredView.h +0 -81
  127. data/vendor/Pods/Headers/YapDatabase/YapDatabaseFilteredViewConnection.h +0 -12
  128. data/vendor/Pods/Headers/YapDatabase/YapDatabaseFilteredViewTransaction.h +0 -39
  129. data/vendor/Pods/Headers/YapDatabase/YapDatabaseFilteredViewTypes.h +0 -32
  130. data/vendor/Pods/Headers/YapDatabase/YapDatabaseFullTextSearch.h +0 -89
  131. data/vendor/Pods/Headers/YapDatabase/YapDatabaseFullTextSearchConnection.h +0 -32
  132. data/vendor/Pods/Headers/YapDatabase/YapDatabaseFullTextSearchSnippetOptions.h +0 -79
  133. data/vendor/Pods/Headers/YapDatabase/YapDatabaseFullTextSearchTransaction.h +0 -68
  134. data/vendor/Pods/Headers/YapDatabase/YapDatabaseQuery.h +0 -42
  135. data/vendor/Pods/Headers/YapDatabase/YapDatabaseRelationship.h +0 -35
  136. data/vendor/Pods/Headers/YapDatabase/YapDatabaseRelationshipConnection.h +0 -29
  137. data/vendor/Pods/Headers/YapDatabase/YapDatabaseRelationshipEdge.h +0 -163
  138. data/vendor/Pods/Headers/YapDatabase/YapDatabaseRelationshipNode.h +0 -99
  139. data/vendor/Pods/Headers/YapDatabase/YapDatabaseRelationshipOptions.h +0 -59
  140. data/vendor/Pods/Headers/YapDatabase/YapDatabaseRelationshipTransaction.h +0 -384
  141. data/vendor/Pods/Headers/YapDatabase/YapDatabaseSecondaryIndex.h +0 -149
  142. data/vendor/Pods/Headers/YapDatabase/YapDatabaseSecondaryIndexConnection.h +0 -33
  143. data/vendor/Pods/Headers/YapDatabase/YapDatabaseSecondaryIndexSetup.h +0 -33
  144. data/vendor/Pods/Headers/YapDatabase/YapDatabaseSecondaryIndexTransaction.h +0 -58
  145. data/vendor/Pods/Headers/YapDatabase/YapDatabaseTransaction.h +0 -651
  146. data/vendor/Pods/Headers/YapDatabase/YapDatabaseView.h +0 -127
  147. data/vendor/Pods/Headers/YapDatabase/YapDatabaseViewChange.h +0 -272
  148. data/vendor/Pods/Headers/YapDatabase/YapDatabaseViewConnection.h +0 -116
  149. data/vendor/Pods/Headers/YapDatabase/YapDatabaseViewMappings.h +0 -842
  150. data/vendor/Pods/Headers/YapDatabase/YapDatabaseViewOptions.h +0 -63
  151. data/vendor/Pods/Headers/YapDatabase/YapDatabaseViewRangeOptions.h +0 -330
  152. data/vendor/Pods/Headers/YapDatabase/YapDatabaseViewTransaction.h +0 -498
  153. data/vendor/Pods/Headers/YapDatabase/YapDatabaseViewTypes.h +0 -99
  154. data/vendor/Pods/Headers/YapDatabase/YapSet.h +0 -41
  155. data/vendor/Pods/Headers/____Pods-CocoaLumberjack-prefix.h +0 -7
  156. data/vendor/Pods/Headers/____Pods-NSData+MD5Digest-prefix.h +0 -5
  157. data/vendor/Pods/Headers/____Pods-YapDatabase-prefix.h +0 -5
  158. data/vendor/Pods/Headers/____Pods-environment.h +0 -38
  159. data/vendor/Pods/Manifest.lock +0 -22
  160. data/vendor/Pods/NSData+MD5Digest/NSData+MD5Digest/NSData+MD5Digest.h +0 -18
  161. data/vendor/Pods/NSData+MD5Digest/NSData+MD5Digest/NSData+MD5Digest.m +0 -39
  162. data/vendor/Pods/NSData+MD5Digest/README.md +0 -11
  163. data/vendor/Pods/Pods-CocoaLumberjack-Private.xcconfig +0 -5
  164. data/vendor/Pods/Pods-CocoaLumberjack-dummy.m +0 -5
  165. data/vendor/Pods/Pods-CocoaLumberjack-prefix.pch +0 -7
  166. data/vendor/Pods/Pods-CocoaLumberjack.xcconfig +0 -0
  167. data/vendor/Pods/Pods-NSData+MD5Digest-Private.xcconfig +0 -5
  168. data/vendor/Pods/Pods-NSData+MD5Digest-dummy.m +0 -5
  169. data/vendor/Pods/Pods-NSData+MD5Digest-prefix.pch +0 -5
  170. data/vendor/Pods/Pods-NSData+MD5Digest.xcconfig +0 -0
  171. data/vendor/Pods/Pods-YapDatabase-Private.xcconfig +0 -5
  172. data/vendor/Pods/Pods-YapDatabase-dummy.m +0 -5
  173. data/vendor/Pods/Pods-YapDatabase-prefix.pch +0 -5
  174. data/vendor/Pods/Pods-YapDatabase.xcconfig +0 -1
  175. data/vendor/Pods/Pods-acknowledgements.markdown +0 -59
  176. data/vendor/Pods/Pods-acknowledgements.plist +0 -97
  177. data/vendor/Pods/Pods-dummy.m +0 -5
  178. data/vendor/Pods/Pods-environment.h +0 -38
  179. data/vendor/Pods/Pods-resources.sh +0 -68
  180. data/vendor/Pods/Pods.xcconfig +0 -5
  181. data/vendor/Pods/Pods.xcodeproj/project.pbxproj +0 -4807
  182. data/vendor/Pods/Pods.xcodeproj/xcuserdata/kareemk.xcuserdatad/xcschemes/Pods-CocoaLumberjack.xcscheme +0 -59
  183. data/vendor/Pods/Pods.xcodeproj/xcuserdata/kareemk.xcuserdatad/xcschemes/Pods-NSData+MD5Digest.xcscheme +0 -59
  184. data/vendor/Pods/Pods.xcodeproj/xcuserdata/kareemk.xcuserdatad/xcschemes/Pods-YapDatabase.xcscheme +0 -59
  185. data/vendor/Pods/Pods.xcodeproj/xcuserdata/kareemk.xcuserdatad/xcschemes/Pods.xcscheme +0 -59
  186. data/vendor/Pods/Pods.xcodeproj/xcuserdata/kareemk.xcuserdatad/xcschemes/xcschememanagement.plist +0 -31
  187. data/vendor/Pods/YapDatabase/LICENSE.txt +0 -18
  188. data/vendor/Pods/YapDatabase/README.md +0 -32
  189. data/vendor/Pods/YapDatabase/YapDatabase/Extensions/FilteredViews/Internal/YapDatabaseFilteredViewPrivate.h +0 -17
  190. data/vendor/Pods/YapDatabase/YapDatabase/Extensions/FilteredViews/YapDatabaseFilteredView.h +0 -81
  191. data/vendor/Pods/YapDatabase/YapDatabase/Extensions/FilteredViews/YapDatabaseFilteredView.m +0 -159
  192. data/vendor/Pods/YapDatabase/YapDatabase/Extensions/FilteredViews/YapDatabaseFilteredViewConnection.h +0 -12
  193. data/vendor/Pods/YapDatabase/YapDatabase/Extensions/FilteredViews/YapDatabaseFilteredViewConnection.m +0 -41
  194. data/vendor/Pods/YapDatabase/YapDatabase/Extensions/FilteredViews/YapDatabaseFilteredViewTransaction.h +0 -39
  195. data/vendor/Pods/YapDatabase/YapDatabase/Extensions/FilteredViews/YapDatabaseFilteredViewTransaction.m +0 -1235
  196. data/vendor/Pods/YapDatabase/YapDatabase/Extensions/FilteredViews/YapDatabaseFilteredViewTypes.h +0 -32
  197. data/vendor/Pods/YapDatabase/YapDatabase/Extensions/FullTextSearch/Internal/YapDatabaseFullTextSearchPrivate.h +0 -77
  198. data/vendor/Pods/YapDatabase/YapDatabase/Extensions/FullTextSearch/YapDatabaseFullTextSearch.h +0 -89
  199. data/vendor/Pods/YapDatabase/YapDatabase/Extensions/FullTextSearch/YapDatabaseFullTextSearch.m +0 -146
  200. data/vendor/Pods/YapDatabase/YapDatabase/Extensions/FullTextSearch/YapDatabaseFullTextSearchConnection.h +0 -32
  201. data/vendor/Pods/YapDatabase/YapDatabase/Extensions/FullTextSearch/YapDatabaseFullTextSearchConnection.m +0 -298
  202. data/vendor/Pods/YapDatabase/YapDatabase/Extensions/FullTextSearch/YapDatabaseFullTextSearchSnippetOptions.h +0 -79
  203. data/vendor/Pods/YapDatabase/YapDatabase/Extensions/FullTextSearch/YapDatabaseFullTextSearchSnippetOptions.m +0 -95
  204. data/vendor/Pods/YapDatabase/YapDatabase/Extensions/FullTextSearch/YapDatabaseFullTextSearchTransaction.h +0 -68
  205. data/vendor/Pods/YapDatabase/YapDatabase/Extensions/FullTextSearch/YapDatabaseFullTextSearchTransaction.m +0 -1403
  206. data/vendor/Pods/YapDatabase/YapDatabase/Extensions/Protocol/Internal/YapDatabaseExtensionPrivate.h +0 -444
  207. data/vendor/Pods/YapDatabase/YapDatabase/Extensions/Protocol/YapDatabaseExtension.h +0 -15
  208. data/vendor/Pods/YapDatabase/YapDatabase/Extensions/Protocol/YapDatabaseExtension.m +0 -83
  209. data/vendor/Pods/YapDatabase/YapDatabase/Extensions/Protocol/YapDatabaseExtensionConnection.h +0 -11
  210. data/vendor/Pods/YapDatabase/YapDatabase/Extensions/Protocol/YapDatabaseExtensionConnection.m +0 -46
  211. data/vendor/Pods/YapDatabase/YapDatabase/Extensions/Protocol/YapDatabaseExtensionTransaction.h +0 -11
  212. data/vendor/Pods/YapDatabase/YapDatabase/Extensions/Protocol/YapDatabaseExtensionTransaction.m +0 -204
  213. data/vendor/Pods/YapDatabase/YapDatabase/Extensions/Relationships/Internal/YapDatabaseRelationshipEdgePrivate.h +0 -79
  214. data/vendor/Pods/YapDatabase/YapDatabase/Extensions/Relationships/Internal/YapDatabaseRelationshipPrivate.h +0 -125
  215. data/vendor/Pods/YapDatabase/YapDatabase/Extensions/Relationships/YapDatabaseRelationship.h +0 -35
  216. data/vendor/Pods/YapDatabase/YapDatabase/Extensions/Relationships/YapDatabaseRelationship.m +0 -129
  217. data/vendor/Pods/YapDatabase/YapDatabase/Extensions/Relationships/YapDatabaseRelationshipConnection.h +0 -29
  218. data/vendor/Pods/YapDatabase/YapDatabase/Extensions/Relationships/YapDatabaseRelationshipConnection.m +0 -802
  219. data/vendor/Pods/YapDatabase/YapDatabase/Extensions/Relationships/YapDatabaseRelationshipEdge.h +0 -163
  220. data/vendor/Pods/YapDatabase/YapDatabase/Extensions/Relationships/YapDatabaseRelationshipEdge.m +0 -408
  221. data/vendor/Pods/YapDatabase/YapDatabase/Extensions/Relationships/YapDatabaseRelationshipNode.h +0 -99
  222. data/vendor/Pods/YapDatabase/YapDatabase/Extensions/Relationships/YapDatabaseRelationshipOptions.h +0 -59
  223. data/vendor/Pods/YapDatabase/YapDatabase/Extensions/Relationships/YapDatabaseRelationshipOptions.m +0 -29
  224. data/vendor/Pods/YapDatabase/YapDatabase/Extensions/Relationships/YapDatabaseRelationshipTransaction.h +0 -384
  225. data/vendor/Pods/YapDatabase/YapDatabase/Extensions/Relationships/YapDatabaseRelationshipTransaction.m +0 -5254
  226. data/vendor/Pods/YapDatabase/YapDatabase/Extensions/SecondaryIndex/Internal/YapDatabaseSecondaryIndexPrivate.h +0 -83
  227. data/vendor/Pods/YapDatabase/YapDatabase/Extensions/SecondaryIndex/Internal/YapDatabaseSecondaryIndexSetupPrivate.h +0 -18
  228. data/vendor/Pods/YapDatabase/YapDatabase/Extensions/SecondaryIndex/YapDatabaseSecondaryIndex.h +0 -149
  229. data/vendor/Pods/YapDatabase/YapDatabase/Extensions/SecondaryIndex/YapDatabaseSecondaryIndex.m +0 -161
  230. data/vendor/Pods/YapDatabase/YapDatabase/Extensions/SecondaryIndex/YapDatabaseSecondaryIndexConnection.h +0 -33
  231. data/vendor/Pods/YapDatabase/YapDatabase/Extensions/SecondaryIndex/YapDatabaseSecondaryIndexConnection.m +0 -331
  232. data/vendor/Pods/YapDatabase/YapDatabase/Extensions/SecondaryIndex/YapDatabaseSecondaryIndexSetup.h +0 -33
  233. data/vendor/Pods/YapDatabase/YapDatabase/Extensions/SecondaryIndex/YapDatabaseSecondaryIndexSetup.m +0 -245
  234. data/vendor/Pods/YapDatabase/YapDatabase/Extensions/SecondaryIndex/YapDatabaseSecondaryIndexTransaction.h +0 -58
  235. data/vendor/Pods/YapDatabase/YapDatabase/Extensions/SecondaryIndex/YapDatabaseSecondaryIndexTransaction.m +0 -1269
  236. data/vendor/Pods/YapDatabase/YapDatabase/Extensions/Views/Internal/YapDatabaseViewChangePrivate.h +0 -94
  237. data/vendor/Pods/YapDatabase/YapDatabase/Extensions/Views/Internal/YapDatabaseViewMappingsPrivate.h +0 -72
  238. data/vendor/Pods/YapDatabase/YapDatabase/Extensions/Views/Internal/YapDatabaseViewPage.h +0 -36
  239. data/vendor/Pods/YapDatabase/YapDatabase/Extensions/Views/Internal/YapDatabaseViewPage.mm +0 -296
  240. data/vendor/Pods/YapDatabase/YapDatabase/Extensions/Views/Internal/YapDatabaseViewPageMetadata.h +0 -27
  241. data/vendor/Pods/YapDatabase/YapDatabase/Extensions/Views/Internal/YapDatabaseViewPageMetadata.m +0 -28
  242. data/vendor/Pods/YapDatabase/YapDatabase/Extensions/Views/Internal/YapDatabaseViewPrivate.h +0 -170
  243. data/vendor/Pods/YapDatabase/YapDatabase/Extensions/Views/Internal/YapDatabaseViewRangeOptionsPrivate.h +0 -17
  244. data/vendor/Pods/YapDatabase/YapDatabase/Extensions/Views/Utilities/YapDatabaseViewChange.h +0 -272
  245. data/vendor/Pods/YapDatabase/YapDatabase/Extensions/Views/Utilities/YapDatabaseViewChange.m +0 -2500
  246. data/vendor/Pods/YapDatabase/YapDatabase/Extensions/Views/Utilities/YapDatabaseViewMappings.h +0 -842
  247. data/vendor/Pods/YapDatabase/YapDatabase/Extensions/Views/Utilities/YapDatabaseViewMappings.m +0 -1714
  248. data/vendor/Pods/YapDatabase/YapDatabase/Extensions/Views/Utilities/YapDatabaseViewRangeOptions.h +0 -330
  249. data/vendor/Pods/YapDatabase/YapDatabase/Extensions/Views/Utilities/YapDatabaseViewRangeOptions.m +0 -141
  250. data/vendor/Pods/YapDatabase/YapDatabase/Extensions/Views/YapDatabaseView.h +0 -127
  251. data/vendor/Pods/YapDatabase/YapDatabase/Extensions/Views/YapDatabaseView.m +0 -194
  252. data/vendor/Pods/YapDatabase/YapDatabase/Extensions/Views/YapDatabaseViewConnection.h +0 -116
  253. data/vendor/Pods/YapDatabase/YapDatabase/Extensions/Views/YapDatabaseViewConnection.m +0 -888
  254. data/vendor/Pods/YapDatabase/YapDatabase/Extensions/Views/YapDatabaseViewOptions.h +0 -63
  255. data/vendor/Pods/YapDatabase/YapDatabase/Extensions/Views/YapDatabaseViewOptions.m +0 -27
  256. data/vendor/Pods/YapDatabase/YapDatabase/Extensions/Views/YapDatabaseViewTransaction.h +0 -498
  257. data/vendor/Pods/YapDatabase/YapDatabase/Extensions/Views/YapDatabaseViewTransaction.m +0 -4986
  258. data/vendor/Pods/YapDatabase/YapDatabase/Extensions/Views/YapDatabaseViewTypes.h +0 -99
  259. data/vendor/Pods/YapDatabase/YapDatabase/Internal/NSDictionary+YapDatabase.h +0 -8
  260. data/vendor/Pods/YapDatabase/YapDatabase/Internal/NSDictionary+YapDatabase.m +0 -19
  261. data/vendor/Pods/YapDatabase/YapDatabase/Internal/YapCache.h +0 -91
  262. data/vendor/Pods/YapDatabase/YapDatabase/Internal/YapCache.m +0 -465
  263. data/vendor/Pods/YapDatabase/YapDatabase/Internal/YapDatabaseConnectionState.h +0 -29
  264. data/vendor/Pods/YapDatabase/YapDatabase/Internal/YapDatabaseConnectionState.m +0 -48
  265. data/vendor/Pods/YapDatabase/YapDatabase/Internal/YapDatabaseDefaults.h +0 -37
  266. data/vendor/Pods/YapDatabase/YapDatabase/Internal/YapDatabaseDefaults.m +0 -83
  267. data/vendor/Pods/YapDatabase/YapDatabase/Internal/YapDatabaseLogging.h +0 -158
  268. data/vendor/Pods/YapDatabase/YapDatabase/Internal/YapDatabaseLogging.m +0 -73
  269. data/vendor/Pods/YapDatabase/YapDatabase/Internal/YapDatabaseManager.h +0 -17
  270. data/vendor/Pods/YapDatabase/YapDatabase/Internal/YapDatabaseManager.m +0 -56
  271. data/vendor/Pods/YapDatabase/YapDatabase/Internal/YapDatabasePrivate.h +0 -446
  272. data/vendor/Pods/YapDatabase/YapDatabase/Internal/YapDatabaseStatement.h +0 -13
  273. data/vendor/Pods/YapDatabase/YapDatabase/Internal/YapDatabaseStatement.m +0 -26
  274. data/vendor/Pods/YapDatabase/YapDatabase/Internal/YapDatabaseString.h +0 -121
  275. data/vendor/Pods/YapDatabase/YapDatabase/Internal/YapMemoryTable.h +0 -74
  276. data/vendor/Pods/YapDatabase/YapDatabase/Internal/YapMemoryTable.m +0 -603
  277. data/vendor/Pods/YapDatabase/YapDatabase/Internal/YapNull.h +0 -17
  278. data/vendor/Pods/YapDatabase/YapDatabase/Internal/YapNull.m +0 -35
  279. data/vendor/Pods/YapDatabase/YapDatabase/Internal/YapTouch.h +0 -15
  280. data/vendor/Pods/YapDatabase/YapDatabase/Internal/YapTouch.m +0 -31
  281. data/vendor/Pods/YapDatabase/YapDatabase/Utilities/YapCollectionKey.h +0 -23
  282. data/vendor/Pods/YapDatabase/YapDatabase/Utilities/YapCollectionKey.m +0 -199
  283. data/vendor/Pods/YapDatabase/YapDatabase/Utilities/YapDatabaseQuery.h +0 -42
  284. data/vendor/Pods/YapDatabase/YapDatabase/Utilities/YapDatabaseQuery.m +0 -96
  285. data/vendor/Pods/YapDatabase/YapDatabase/Utilities/YapSet.h +0 -41
  286. data/vendor/Pods/YapDatabase/YapDatabase/Utilities/YapSet.m +0 -82
  287. data/vendor/Pods/YapDatabase/YapDatabase/YapDatabase.h +0 -547
  288. data/vendor/Pods/YapDatabase/YapDatabase/YapDatabase.m +0 -2013
  289. data/vendor/Pods/YapDatabase/YapDatabase/YapDatabaseConnection.h +0 -449
  290. data/vendor/Pods/YapDatabase/YapDatabase/YapDatabaseConnection.m +0 -4046
  291. data/vendor/Pods/YapDatabase/YapDatabase/YapDatabaseTransaction.h +0 -651
  292. data/vendor/Pods/YapDatabase/YapDatabase/YapDatabaseTransaction.m +0 -5602
  293. data/vendor/Pods/build-iPhoneSimulator/Pods.bridgesupport +0 -3172
  294. data/vendor/Pods/build-iPhoneSimulator/libPods.a +0 -0
@@ -1,449 +0,0 @@
1
- #import <Foundation/Foundation.h>
2
-
3
- @class YapDatabase;
4
- @class YapDatabaseReadTransaction;
5
- @class YapDatabaseReadWriteTransaction;
6
-
7
- /**
8
- * Welcome to YapDatabase!
9
- *
10
- * The project page has a wealth of documentation if you have any questions.
11
- * https://github.com/yaptv/YapDatabase
12
- *
13
- * If you're new to the project you may want to visit the wiki.
14
- * https://github.com/yaptv/YapDatabase/wiki
15
- *
16
- * From a single YapDatabase instance you can create multiple connections.
17
- * Each connection is thread-safe and may be used concurrently with other connections.
18
- *
19
- * Multiple connections can simultaneously read from the database.
20
- * Multiple connections can simultaneously read from the database while another connection is modifying the database.
21
- * For example, the main thread could be reading from the database via connection A,
22
- * while a background thread is writing to the database via connection B.
23
- *
24
- * However, only a single connection may be writing to the database at any one time.
25
- * This is an inherent limitation of the underlying sqlite database.
26
- *
27
- * A connection instance is thread-safe, and operates by serializing access to itself.
28
- * Thus you can share a single connection between multiple threads.
29
- * But for conncurrent access between multiple threads you must use multiple connections.
30
- **/
31
-
32
- enum {
33
- YapDatabaseConnectionFlushMemoryFlags_None = 0,
34
- YapDatabaseConnectionFlushMemoryFlags_Caches = 1 << 0,
35
- YapDatabaseConnectionFlushMemoryFlags_Statements = 1 << 1,
36
- YapDatabaseConnectionFlushMemoryFlags_All = (YapDatabaseConnectionFlushMemoryFlags_Caches |
37
- YapDatabaseConnectionFlushMemoryFlags_Statements),
38
- };
39
- typedef int YapDatabaseConnectionFlushMemoryFlags;
40
-
41
- typedef enum {
42
- YapDatabasePolicyContainment = 0,
43
- YapDatabasePolicyShare = 1,
44
- YapDatabasePolicyCopy = 2,
45
- } YapDatabasePolicy;
46
-
47
-
48
- @interface YapDatabaseConnection : NSObject
49
-
50
- /**
51
- * A database connection maintains a strong reference to its parent.
52
- *
53
- * This is to enforce the following core architecture rule:
54
- * A database instance cannot be deallocated if a corresponding connection is stil alive.
55
- **/
56
- @property (nonatomic, strong, readonly) YapDatabase *database;
57
-
58
- /**
59
- * The optional name property assists in debugging.
60
- * It is only used internally for log statements.
61
- **/
62
- @property (atomic, copy, readwrite) NSString *name;
63
-
64
- ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
65
- #pragma mark Cache
66
- ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
67
-
68
- /**
69
- * Each database connection maintains an independent cache of deserialized objects.
70
- * This reduces disk IO and the overhead of the deserialization process.
71
- * You can optionally configure the cache size, or disable it completely.
72
- *
73
- * The cache is properly kept in sync with the atomic snapshot architecture of the database system.
74
- *
75
- * You can configure the objectCache at any time, including within readBlocks or readWriteBlocks.
76
- * To disable the object cache entirely, set objectCacheEnabled to NO.
77
- * To use an inifinite cache size, set the objectCacheLimit to zero.
78
- *
79
- * By default the objectCache is enabled and has a limit of 250.
80
- *
81
- * New connections will inherit the default values set by the parent database object.
82
- * Thus the default values for new connection instances are configurable.
83
- *
84
- * @see YapDatabase defaultObjectCacheEnabled
85
- * @see YapDatabase defaultObjectCacheLimit
86
- *
87
- * Also see the wiki for a bit more info:
88
- * https://github.com/yaptv/YapDatabase/wiki/Cache
89
- **/
90
- @property (atomic, assign, readwrite) BOOL objectCacheEnabled;
91
- @property (atomic, assign, readwrite) NSUInteger objectCacheLimit;
92
-
93
- /**
94
- * Each database connection maintains an independent cache of deserialized metadata.
95
- * This reduces disk IO and the overhead of the deserialization process.
96
- * You can optionally configure the cache size, or disable it completely.
97
- *
98
- * The cache is properly kept in sync with the atomic snapshot architecture of the database system.
99
- *
100
- * You can configure the metadataCache at any time, including within readBlocks or readWriteBlocks.
101
- * To disable the metadata cache entirely, set metadataCacheEnabled to NO.
102
- * To use an inifinite cache size, set the metadataCacheLimit to zero.
103
- *
104
- * By default the metadataCache is enabled and has a limit of 500.
105
- *
106
- * New connections will inherit the default values set by the parent database object.
107
- * Thus the default values for new connection instances are configurable.
108
- *
109
- * @see YapDatabase defaultMetadataCacheEnabled
110
- * @see YapDatabase defaultMetadataCacheLimit
111
- *
112
- * Also see the wiki for a bit more info:
113
- * https://github.com/yaptv/YapDatabase/wiki/Cache
114
- **/
115
- @property (atomic, assign, readwrite) BOOL metadataCacheEnabled;
116
- @property (atomic, assign, readwrite) NSUInteger metadataCacheLimit;
117
-
118
- ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
119
- #pragma mark Policy
120
- ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
121
-
122
- /**
123
- * YapDatabase can use various optimizations to reduce overhead and memory footprint.
124
- * The policy properties allow you to opt in to these optimizations when ready.
125
- *
126
- * The default value is YapDatabasePolicyContainment.
127
- *
128
- * It is the slowest, but also the safest policy.
129
- * The other policies require a little more work, and little deeper understanding.
130
- *
131
- * These optimizations are discussed extensively in the wiki article "Performance Pro":
132
- * https://github.com/yaptv/YapDatabase/wiki/Performance-Pro
133
- **/
134
- @property (atomic, assign, readwrite) YapDatabasePolicy objectPolicy;
135
- @property (atomic, assign, readwrite) YapDatabasePolicy metadataPolicy;
136
-
137
- ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
138
- #pragma mark State
139
- ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
140
-
141
- /**
142
- * The snapshot number is the internal synchronization state primitive for the connection.
143
- * It's generally only useful for database internals,
144
- * but it can sometimes come in handy for general debugging of your app.
145
- *
146
- * The snapshot is a simple 64-bit number that gets incremented upon every readwrite transaction
147
- * that makes modifications to the database. Due to the concurrent architecture of YapDatabase,
148
- * there may be multiple concurrent connections that are inspecting the database at similar times,
149
- * yet they are looking at slightly different "snapshots" of the database.
150
- *
151
- * The snapshot number may thus be inspected to determine (in a general fashion) what state the connection
152
- * is in compared with other connections.
153
- *
154
- * You may also query YapDatabase.snapshot to determine the most up-to-date snapshot among all connections.
155
- *
156
- * Example:
157
- *
158
- * YapDatabase *database = [[YapDatabase alloc] init...];
159
- * database.snapshot; // returns zero
160
- *
161
- * YapDatabaseConnection *connection1 = [database newConnection];
162
- * YapDatabaseConnection *connection2 = [database newConnection];
163
- *
164
- * connection1.snapshot; // returns zero
165
- * connection2.snapshot; // returns zero
166
- *
167
- * [connection1 readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction){
168
- * [transaction setObject:objectA forKey:keyA];
169
- * }];
170
- *
171
- * database.snapshot; // returns 1
172
- * connection1.snapshot; // returns 1
173
- * connection2.snapshot; // returns 1
174
- *
175
- * [connection1 asyncReadWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction){
176
- * [transaction setObject:objectB forKey:keyB];
177
- * [NSThread sleepForTimeInterval:1.0]; // sleep for 1 second
178
- *
179
- * connection1.snapshot; // returns 1 (we know it will turn into 2 once the transaction completes)
180
- * } completion:^{
181
- *
182
- * connection1.snapshot; // returns 2
183
- * }];
184
- *
185
- * [connection2 asyncReadWithBlock:^(YapDatabaseReadTransaction *transaction){
186
- * [NSThread sleepForTimeInterval:5.0]; // sleep for 5 seconds
187
- *
188
- * connection2.snapshot; // returns 1. See why?
189
- * }];
190
- *
191
- * It's because connection2 started its transaction when the database was in snapshot 1.
192
- * Thus, for the duration of its transaction, the database remains in that state.
193
- *
194
- * However, once connection2 completes its transaction, it will automatically update itself to snapshot 2.
195
- *
196
- * In general, the snapshot is primarily for internal use.
197
- * However, it may come in handy for some tricky edge-case bugs (why doesn't my connection see that other commit?)
198
- **/
199
- @property (atomic, assign, readonly) uint64_t snapshot;
200
-
201
- ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
202
- #pragma mark Transactions
203
- ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
204
-
205
- /**
206
- * Read-only access to the database.
207
- *
208
- * The given block can run concurrently with sibling connections,
209
- * regardless of whether the sibling connections are executing read-only or read-write transactions.
210
- *
211
- * The only time this method ever blocks is if another thread is currently using this connection instance
212
- * to execute a readBlock or readWriteBlock. Recall that you may create multiple connections for concurrent access.
213
- *
214
- * This method is synchronous.
215
- **/
216
- - (void)readWithBlock:(void (^)(YapDatabaseReadTransaction *transaction))block;
217
-
218
- /**
219
- * Read-write access to the database.
220
- *
221
- * Only a single read-write block can execute among all sibling connections.
222
- * Thus this method may block if another sibling connection is currently executing a read-write block.
223
- **/
224
- - (void)readWriteWithBlock:(void (^)(YapDatabaseReadWriteTransaction *transaction))block;
225
-
226
- /**
227
- * Read-only access to the database.
228
- *
229
- * The given block can run concurrently with sibling connections,
230
- * regardless of whether the sibling connections are executing read-only or read-write transactions.
231
- *
232
- * This method is asynchronous.
233
- **/
234
- - (void)asyncReadWithBlock:(void (^)(YapDatabaseReadTransaction *transaction))block;
235
-
236
- /**
237
- * Read-only access to the database.
238
- *
239
- * The given block can run concurrently with sibling connections,
240
- * regardless of whether the sibling connections are executing read-only or read-write transactions.
241
- *
242
- * This method is asynchronous.
243
- *
244
- * An optional completion block may be used.
245
- * The completionBlock will be invoked on the main thread (dispatch_get_main_queue()).
246
- **/
247
- - (void)asyncReadWithBlock:(void (^)(YapDatabaseReadTransaction *transaction))block
248
- completionBlock:(dispatch_block_t)completionBlock;
249
-
250
- /**
251
- * Read-only access to the database.
252
- *
253
- * The given block can run concurrently with sibling connections,
254
- * regardless of whether the sibling connections are executing read-only or read-write transactions.
255
- *
256
- * This method is asynchronous.
257
- *
258
- * An optional completion block may be used.
259
- * Additionally the dispatch_queue to invoke the completion block may also be specified.
260
- * If NULL, dispatch_get_main_queue() is automatically used.
261
- **/
262
- - (void)asyncReadWithBlock:(void (^)(YapDatabaseReadTransaction *transaction))block
263
- completionBlock:(dispatch_block_t)completionBlock
264
- completionQueue:(dispatch_queue_t)completionQueue;
265
-
266
- /**
267
- * Read-write access to the database.
268
- *
269
- * Only a single read-write block can execute among all sibling connections.
270
- * Thus this method may block if another sibling connection is currently executing a read-write block.
271
- *
272
- * This method is asynchronous.
273
- **/
274
- - (void)asyncReadWriteWithBlock:(void (^)(YapDatabaseReadWriteTransaction *transaction))block;
275
-
276
- /**
277
- * Read-write access to the database.
278
- *
279
- * Only a single read-write block can execute among all sibling connections.
280
- * Thus the execution of the block may be delayed if another sibling connection
281
- * is currently executing a read-write block.
282
- *
283
- * This method is asynchronous.
284
- *
285
- * An optional completion block may be used.
286
- * The completionBlock will be invoked on the main thread (dispatch_get_main_queue()).
287
- **/
288
- - (void)asyncReadWriteWithBlock:(void (^)(YapDatabaseReadWriteTransaction *transaction))block
289
- completionBlock:(dispatch_block_t)completionBlock;
290
-
291
- /**
292
- * Read-write access to the database.
293
- *
294
- * Only a single read-write block can execute among all sibling connections.
295
- * Thus the execution of the block may be delayed if another sibling connection
296
- * is currently executing a read-write block.
297
- *
298
- * This method is asynchronous.
299
- *
300
- * An optional completion block may be used.
301
- * Additionally the dispatch_queue to invoke the completion block may also be specified.
302
- * If NULL, dispatch_get_main_queue() is automatically used.
303
- **/
304
- - (void)asyncReadWriteWithBlock:(void (^)(YapDatabaseReadWriteTransaction *transaction))block
305
- completionBlock:(dispatch_block_t)completionBlock
306
- completionQueue:(dispatch_queue_t)completionQueue;
307
-
308
- ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
309
- #pragma mark Long-Lived Transactions
310
- ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
311
-
312
- /**
313
- * Invoke this method to start a long-lived read-only transaction.
314
- * This allows you to effectively create a stable state for the connection.
315
- * This is most often used for connections that service the main thread for UI data.
316
- *
317
- * For a complete discussion, please see the wiki page:
318
- * https://github.com/yaptv/YapDatabase/wiki/LongLivedReadTransactions
319
- **/
320
- - (NSArray *)beginLongLivedReadTransaction;
321
- - (NSArray *)endLongLivedReadTransaction;
322
-
323
- - (BOOL)isInLongLivedReadTransaction;
324
-
325
- /**
326
- * A long-lived read-only transaction is most often setup on a connection that is designed to be read-only.
327
- * But sometimes we forget, and a read-write transaction gets added that uses the read-only connection.
328
- * This will implicitly end the long-lived read-only transaction. Oops.
329
- *
330
- * This is a bug waiting to happen.
331
- * And when it does happen, it will be one of those bugs that's nearly impossible to reproduce.
332
- * So its better to have an early warning system to help you fix the bug before it occurs.
333
- *
334
- * For a complete discussion, please see the wiki page:
335
- * https://github.com/yaptv/YapDatabase/wiki/LongLivedReadTransactions
336
- *
337
- * In debug mode (#if DEBUG), these exceptions are turned ON by default.
338
- * In non-debug mode (#if !DEBUG), these exceptions are turned OFF by default.
339
- **/
340
- - (void)enableExceptionsForImplicitlyEndingLongLivedReadTransaction;
341
- - (void)disableExceptionsForImplicitlyEndingLongLivedReadTransaction;
342
-
343
- ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
344
- #pragma mark Changesets
345
- ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
346
-
347
- /**
348
- * A YapDatabaseModifiedNotification is posted for every readwrite transaction that makes changes to the database.
349
- *
350
- * Given one or more notifications, these methods allow you to easily
351
- * query to see if a change affects a given collection, key, or combinary.
352
- *
353
- * This is most often used in conjunction with longLivedReadTransactions.
354
- *
355
- * For more information on longLivedReadTransaction, see the following wiki article:
356
- * https://github.com/yaptv/YapDatabase/wiki/LongLivedReadTransactions
357
- **/
358
-
359
- // Query for any change to a collection
360
-
361
- - (BOOL)hasChangeForCollection:(NSString *)collection inNotifications:(NSArray *)notifications;
362
- - (BOOL)hasObjectChangeForCollection:(NSString *)collection inNotifications:(NSArray *)notifications;
363
- - (BOOL)hasMetadataChangeForCollection:(NSString *)collection inNotifications:(NSArray *)notifications;
364
-
365
- // Query for a change to a particular key/collection tuple
366
-
367
- - (BOOL)hasChangeForKey:(NSString *)key
368
- inCollection:(NSString *)collection
369
- inNotifications:(NSArray *)notifications;
370
-
371
- - (BOOL)hasObjectChangeForKey:(NSString *)key
372
- inCollection:(NSString *)collection
373
- inNotifications:(NSArray *)notifications;
374
-
375
- - (BOOL)hasMetadataChangeForKey:(NSString *)key
376
- inCollection:(NSString *)collection
377
- inNotifications:(NSArray *)notifications;
378
-
379
- // Query for a change to a particular set of keys in a collection
380
-
381
- - (BOOL)hasChangeForAnyKeys:(NSSet *)keys
382
- inCollection:(NSString *)collection
383
- inNotifications:(NSArray *)notifications;
384
-
385
- - (BOOL)hasObjectChangeForAnyKeys:(NSSet *)keys
386
- inCollection:(NSString *)collection
387
- inNotifications:(NSArray *)notifications;
388
-
389
- - (BOOL)hasMetadataChangeForAnyKeys:(NSSet *)keys
390
- inCollection:(NSString *)collection
391
- inNotifications:(NSArray *)notifications;
392
-
393
- ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
394
- #pragma mark Extensions
395
- ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
396
-
397
- /**
398
- * Creates or fetches the extension with the given name.
399
- * If this connection has not yet initialized the proper extension connection, it is done automatically.
400
- *
401
- * @return
402
- * A subclass of YapDatabaseExtensionConnection,
403
- * according to the type of extension registered under the given name.
404
- *
405
- * One must register an extension with the database before it can be accessed from within connections or transactions.
406
- * After registration everything works automatically using just the registered extension name.
407
- *
408
- * @see YapDatabase registerExtension:withName:
409
- **/
410
- - (id)extension:(NSString *)extensionName;
411
- - (id)ext:(NSString *)extensionName; // <-- Shorthand (same as extension: method)
412
-
413
- ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
414
- #pragma mark Memory
415
- ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
416
-
417
- /**
418
- * This method may be used to flush the internal caches used by the connection,
419
- * as well as flushing pre-compiled sqlite statements.
420
- * Depending upon how often you use the database connection,
421
- * you may want to be more or less aggressive on how much stuff you flush.
422
- *
423
- * YapDatabaseConnectionFlushMemoryFlags_None:
424
- * No-op. Doesn't flush anything.
425
- *
426
- * YapDatabaseConnectionFlushMemoryFlags_Caches:
427
- * Flushes all caches, including the object cache and metadata cache.
428
- *
429
- * YapDatabaseConnectionFlushMemoryFlags_Statements:
430
- * Flushes all pre-compiled sqlite statements.
431
- *
432
- * YapDatabaseConnectionFlushMemoryFlags_All:
433
- * Full flush of all caches and pre-compiled sqlite statements.
434
- **/
435
- - (void)flushMemoryWithFlags:(YapDatabaseConnectionFlushMemoryFlags)flags;
436
-
437
- #if TARGET_OS_IPHONE
438
- /**
439
- * When a UIApplicationDidReceiveMemoryWarningNotification is received,
440
- * the code automatically invokes flushMemoryWithFlags and passes the set flags.
441
- *
442
- * The default value is YapDatabaseConnectionFlushMemoryFlags_All.
443
- *
444
- * @see flushMemoryWithFlags:
445
- **/
446
- @property (atomic, assign, readwrite) YapDatabaseConnectionFlushMemoryFlags autoFlushMemoryFlags;
447
- #endif
448
-
449
- @end
@@ -1,4046 +0,0 @@
1
- #import "YapDatabaseConnection.h"
2
- #import "YapDatabaseConnectionState.h"
3
- #import "YapDatabasePrivate.h"
4
- #import "YapDatabaseExtensionPrivate.h"
5
-
6
- #import "YapCollectionKey.h"
7
- #import "YapCache.h"
8
- #import "YapTouch.h"
9
- #import "YapNull.h"
10
- #import "YapSet.h"
11
-
12
- #import "YapDatabaseString.h"
13
- #import "YapDatabaseLogging.h"
14
-
15
- #import <objc/runtime.h>
16
- #import <libkern/OSAtomic.h>
17
-
18
- #if ! __has_feature(objc_arc)
19
- #warning This file must be compiled with ARC. Use -fobjc-arc flag (or convert project to ARC).
20
- #endif
21
-
22
- /**
23
- * Define log level for this file: OFF, ERROR, WARN, INFO, VERBOSE
24
- * See YapDatabaseLogging.h for more information.
25
- **/
26
- #if DEBUG
27
- static const int ydbLogLevel = YDB_LOG_LEVEL_INFO;
28
- #else
29
- static const int ydbLogLevel = YDB_LOG_LEVEL_WARN;
30
- #endif
31
-
32
- #define UNLIMITED_CACHE_LIMIT 0
33
- #define MIN_KEY_CACHE_LIMIT 500
34
- #define DEFAULT_OBJECT_CACHE_LIMIT 250
35
- #define DEFAULT_METADATA_CACHE_LIMIT 500
36
-
37
-
38
- @implementation YapDatabaseConnection {
39
- @private
40
-
41
- sqlite3_stmt *beginTransactionStatement;
42
- sqlite3_stmt *commitTransactionStatement;
43
- sqlite3_stmt *rollbackTransactionStatement;
44
-
45
- sqlite3_stmt *yapGetDataForKeyStatement; // Against "yap" database, for internal use
46
- sqlite3_stmt *yapSetDataForKeyStatement; // Against "yap" database, for internal use
47
- sqlite3_stmt *yapRemoveForKeyStatement; // Against "yap" database, for internal use
48
- sqlite3_stmt *yapRemoveExtensionStatement; // Against "yap" database, for internal use
49
-
50
- sqlite3_stmt *getCollectionCountStatement;
51
- sqlite3_stmt *getKeyCountForCollectionStatement;
52
- sqlite3_stmt *getKeyCountForAllStatement;
53
- sqlite3_stmt *getCountForRowidStatement;
54
- sqlite3_stmt *getRowidForKeyStatement;
55
- sqlite3_stmt *getKeyForRowidStatement;
56
- sqlite3_stmt *getDataForRowidStatement;
57
- sqlite3_stmt *getMetadataForRowidStatement;
58
- sqlite3_stmt *getAllForRowidStatement;
59
- sqlite3_stmt *getDataForKeyStatement;
60
- sqlite3_stmt *getMetadataForKeyStatement;
61
- sqlite3_stmt *getAllForKeyStatement;
62
- sqlite3_stmt *insertForRowidStatement;
63
- sqlite3_stmt *updateAllForRowidStatement;
64
- sqlite3_stmt *updateObjectForRowidStatement;
65
- sqlite3_stmt *updateMetadataForRowidStatement;
66
- sqlite3_stmt *removeForRowidStatement;
67
- sqlite3_stmt *removeCollectionStatement;
68
- sqlite3_stmt *removeAllStatement;
69
- sqlite3_stmt *enumerateCollectionsStatement;
70
- sqlite3_stmt *enumerateCollectionsForKeyStatement;
71
- sqlite3_stmt *enumerateKeysInCollectionStatement;
72
- sqlite3_stmt *enumerateKeysInAllCollectionsStatement;
73
- sqlite3_stmt *enumerateKeysAndMetadataInCollectionStatement;
74
- sqlite3_stmt *enumerateKeysAndMetadataInAllCollectionsStatement;
75
- sqlite3_stmt *enumerateKeysAndObjectsInCollectionStatement;
76
- sqlite3_stmt *enumerateKeysAndObjectsInAllCollectionsStatement;
77
- sqlite3_stmt *enumerateRowsInCollectionStatement;
78
- sqlite3_stmt *enumerateRowsInAllCollectionsStatement;
79
-
80
- OSSpinLock lock;
81
- BOOL writeQueueSuspended;
82
- BOOL activeReadWriteTransaction;
83
- }
84
-
85
- + (void)load
86
- {
87
- static BOOL loaded = NO;
88
- if (!loaded)
89
- {
90
- // Method swizzle:
91
- // Both 'extension:' and 'ext:' are designed to be the same method (with ext: shorthand for extension:).
92
- // So swap out the ext: method to point to extension:.
93
-
94
- Method extMethod = class_getInstanceMethod([self class], @selector(ext:));
95
- IMP extensionIMP = class_getMethodImplementation([self class], @selector(extension:));
96
-
97
- method_setImplementation(extMethod, extensionIMP);
98
- loaded = YES;
99
- }
100
- }
101
-
102
- - (id)initWithDatabase:(YapDatabase *)inDatabase
103
- {
104
- if ((self = [super init]))
105
- {
106
- database = inDatabase;
107
- connectionQueue = dispatch_queue_create("YapDatabaseConnection", NULL);
108
-
109
- IsOnConnectionQueueKey = &IsOnConnectionQueueKey;
110
- dispatch_queue_set_specific(connectionQueue, IsOnConnectionQueueKey, IsOnConnectionQueueKey, NULL);
111
-
112
- #if DEBUG
113
- throwExceptionsForImplicitlyEndingLongLivedReadTransaction = YES;
114
- #else
115
- throwExceptionsForImplicitlyEndingLongLivedReadTransaction = NO;
116
- #endif
117
-
118
- pendingChangesets = [[NSMutableArray alloc] init];
119
- processedChangesets = [[NSMutableArray alloc] init];
120
-
121
- sharedKeySetForInternalChangeset = [NSDictionary sharedKeySetForKeys:[self internalChangesetKeys]];
122
- sharedKeySetForExternalChangeset = [NSDictionary sharedKeySetForKeys:[self externalChangesetKeys]];
123
- sharedKeySetForExtensions = [NSDictionary sharedKeySetForKeys:@[]];
124
-
125
- extensions = [[NSMutableDictionary alloc] init];
126
-
127
- YapDatabaseDefaults *defaults = [database defaults];
128
-
129
- NSUInteger keyCacheLimit = MIN_KEY_CACHE_LIMIT;
130
-
131
- if (defaults.objectCacheEnabled)
132
- {
133
- objectCacheLimit = defaults.objectCacheLimit;
134
- objectCache = [[YapCache alloc] initWithKeyClass:[YapCollectionKey class]];
135
- objectCache.countLimit = objectCacheLimit;
136
-
137
- if (keyCacheLimit != UNLIMITED_CACHE_LIMIT)
138
- {
139
- if (objectCacheLimit == UNLIMITED_CACHE_LIMIT)
140
- keyCacheLimit = UNLIMITED_CACHE_LIMIT;
141
- else
142
- keyCacheLimit = MAX(keyCacheLimit, objectCacheLimit);
143
- }
144
- }
145
- if (defaults.metadataCacheEnabled)
146
- {
147
- metadataCacheLimit = defaults.metadataCacheLimit;
148
- metadataCache = [[YapCache alloc] initWithKeyClass:[YapCollectionKey class]];
149
- metadataCache.countLimit = metadataCacheLimit;
150
-
151
- if (keyCacheLimit != UNLIMITED_CACHE_LIMIT)
152
- {
153
- if (metadataCacheLimit == UNLIMITED_CACHE_LIMIT)
154
- keyCacheLimit = UNLIMITED_CACHE_LIMIT;
155
- else
156
- keyCacheLimit = MAX(keyCacheLimit, objectCacheLimit);
157
- }
158
- }
159
-
160
- keyCache = [[YapCache alloc] initWithKeyClass:[NSNumber class] countLimit:keyCacheLimit];
161
-
162
- #if TARGET_OS_IPHONE
163
- self.autoFlushMemoryFlags = defaults.autoFlushMemoryFlags;
164
- #endif
165
-
166
- lock = OS_SPINLOCK_INIT;
167
-
168
- db = [database connectionPoolDequeue];
169
- if (db == NULL)
170
- {
171
- // Open the database connection.
172
- //
173
- // We use SQLITE_OPEN_NOMUTEX to use the multi-thread threading mode,
174
- // as we will be serializing access to the connection externally.
175
-
176
- int flags = SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_NOMUTEX | SQLITE_OPEN_PRIVATECACHE;
177
-
178
- int status = sqlite3_open_v2([database.databasePath UTF8String], &db, flags, NULL);
179
- if (status != SQLITE_OK)
180
- {
181
- // Sometimes the open function returns a db to allow us to query it for the error message
182
- if (db) {
183
- YDBLogWarn(@"Error opening database: %d %s", status, sqlite3_errmsg(db));
184
- }
185
- else {
186
- YDBLogError(@"Error opening database: %d", status);
187
- }
188
- }
189
- else
190
- {
191
- // Disable autocheckpointing.
192
- //
193
- // YapDatabase has its own optimized checkpointing algorithm built-in.
194
- // It knows the state of every active connection for the database,
195
- // so it can invoke the checkpoint methods at the precise time
196
- // in which a checkpoint can be most effective.
197
-
198
- sqlite3_wal_autocheckpoint(db, 0);
199
-
200
- // Edge case workaround.
201
- //
202
- // If there's an active checkpoint operation,
203
- // then the very first time we call sqlite3_prepare_v2 on this db,
204
- // we sometimes get a SQLITE_BUSY error.
205
- //
206
- // This only seems to happen once, and only during the very first use of the db instance.
207
- // I'm still tyring to figure out exactly why this is.
208
- // For now I'm setting a busy timeout as a temporary workaround.
209
- //
210
- // Note: I've also tested setting a busy_handler which logs the number of times its called.
211
- // And in all my testing, I've only seen the busy_handler called once per db.
212
-
213
- sqlite3_busy_timeout(db, 50); // milliseconds
214
- }
215
- }
216
-
217
- #if TARGET_OS_IPHONE
218
- [[NSNotificationCenter defaultCenter] addObserver:self
219
- selector:@selector(didReceiveMemoryWarning:)
220
- name:UIApplicationDidReceiveMemoryWarningNotification
221
- object:nil];
222
- #endif
223
- }
224
- return self;
225
- }
226
-
227
- /**
228
- * This method will be invoked before any other method.
229
- * It can be used to do any setup that may be needed.
230
- **/
231
- - (void)prepare
232
- {
233
- // This method is invoked from our connectionQueue, within the snapshotQueue.
234
- // Don't do anything expensive here that might tie up the snapshotQueue.
235
-
236
- snapshot = [database snapshot];
237
- registeredExtensions = [database registeredExtensions];
238
- registeredTables = [database registeredTables];
239
- extensionsOrder = [database extensionsOrder];
240
- extensionDependencies = [database extensionDependencies];
241
-
242
- extensionsReady = ([registeredExtensions count] == 0);
243
- }
244
-
245
- - (void)dealloc
246
- {
247
- YDBLogVerbose(@"Dealloc <YapDatabaseConnection %p: databaseName=%@>",
248
- self, [database.databasePath lastPathComponent]);
249
-
250
- dispatch_block_t block = ^{ @autoreleasepool {
251
-
252
- if (longLivedReadTransaction) {
253
- [self postReadTransaction:longLivedReadTransaction];
254
- longLivedReadTransaction = nil;
255
- }
256
- }};
257
-
258
- if (dispatch_get_specific(IsOnConnectionQueueKey))
259
- block();
260
- else
261
- dispatch_sync(connectionQueue, block);
262
-
263
- [[NSNotificationCenter defaultCenter] removeObserver:self];
264
-
265
- [extensions removeAllObjects];
266
-
267
- [self _flushStatements];
268
-
269
- if (db)
270
- {
271
- if (![database connectionPoolEnqueue:db])
272
- {
273
- int status = sqlite3_close(db);
274
- if (status != SQLITE_OK)
275
- {
276
- YDBLogError(@"Error in sqlite_close: %d %s", status, sqlite3_errmsg(db));
277
- }
278
- }
279
-
280
- db = NULL;
281
- }
282
-
283
- [database removeConnection:self];
284
-
285
- #if !OS_OBJECT_USE_OBJC
286
- if (connectionQueue)
287
- dispatch_release(connectionQueue);
288
- #endif
289
- }
290
-
291
- ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
292
- #pragma mark Memory
293
- ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
294
-
295
- - (void)_flushStatements
296
- {
297
- sqlite_finalize_null(&beginTransactionStatement);
298
- sqlite_finalize_null(&commitTransactionStatement);
299
- sqlite_finalize_null(&rollbackTransactionStatement);
300
-
301
- sqlite_finalize_null(&yapGetDataForKeyStatement);
302
- sqlite_finalize_null(&yapSetDataForKeyStatement);
303
- sqlite_finalize_null(&yapRemoveForKeyStatement);
304
- sqlite_finalize_null(&yapRemoveExtensionStatement);
305
-
306
- sqlite_finalize_null(&getCollectionCountStatement);
307
- sqlite_finalize_null(&getKeyCountForCollectionStatement);
308
- sqlite_finalize_null(&getKeyCountForAllStatement);
309
- sqlite_finalize_null(&getCountForRowidStatement);
310
- sqlite_finalize_null(&getRowidForKeyStatement);
311
- sqlite_finalize_null(&getKeyForRowidStatement);
312
- sqlite_finalize_null(&getDataForRowidStatement);
313
- sqlite_finalize_null(&getMetadataForRowidStatement);
314
- sqlite_finalize_null(&getAllForRowidStatement);
315
- sqlite_finalize_null(&getDataForKeyStatement);
316
- sqlite_finalize_null(&getMetadataForKeyStatement);
317
- sqlite_finalize_null(&getAllForKeyStatement);
318
- sqlite_finalize_null(&insertForRowidStatement);
319
- sqlite_finalize_null(&updateAllForRowidStatement);
320
- sqlite_finalize_null(&updateObjectForRowidStatement);
321
- sqlite_finalize_null(&updateMetadataForRowidStatement);
322
- sqlite_finalize_null(&removeForRowidStatement);
323
- sqlite_finalize_null(&removeCollectionStatement);
324
- sqlite_finalize_null(&removeAllStatement);
325
- sqlite_finalize_null(&enumerateCollectionsStatement);
326
- sqlite_finalize_null(&enumerateCollectionsForKeyStatement);
327
- sqlite_finalize_null(&enumerateKeysInCollectionStatement);
328
- sqlite_finalize_null(&enumerateKeysInAllCollectionsStatement);
329
- sqlite_finalize_null(&enumerateKeysAndMetadataInCollectionStatement);
330
- sqlite_finalize_null(&enumerateKeysAndMetadataInAllCollectionsStatement);
331
- sqlite_finalize_null(&enumerateKeysAndObjectsInCollectionStatement);
332
- sqlite_finalize_null(&enumerateKeysAndObjectsInAllCollectionsStatement);
333
- sqlite_finalize_null(&enumerateRowsInCollectionStatement);
334
- sqlite_finalize_null(&enumerateRowsInAllCollectionsStatement);
335
- }
336
-
337
- - (void)_flushMemoryWithFlags:(YapDatabaseConnectionFlushMemoryFlags)flags
338
- {
339
- if (flags & YapDatabaseConnectionFlushMemoryFlags_Caches)
340
- {
341
- [keyCache removeAllObjects];
342
- [objectCache removeAllObjects];
343
- [metadataCache removeAllObjects];
344
- }
345
-
346
- if (flags & YapDatabaseConnectionFlushMemoryFlags_Statements)
347
- {
348
- [self _flushStatements];
349
- }
350
-
351
- [extensions enumerateKeysAndObjectsUsingBlock:^(id extNameObj, id extConnectionObj, BOOL *stop) {
352
-
353
- [(YapDatabaseExtensionConnection *)extConnectionObj _flushMemoryWithFlags:flags];
354
- }];
355
- }
356
-
357
- /**
358
- * This method may be used to flush the internal caches used by the connection,
359
- * as well as flushing pre-compiled sqlite statements.
360
- * Depending upon how often you use the database connection,
361
- * you may want to be more or less aggressive on how much stuff you flush.
362
- *
363
- * YapDatabaseConnectionFlushMemoryLevelNone (0):
364
- * No-op. Doesn't flush any caches or anything from internal memory.
365
- *
366
- * YapDatabaseConnectionFlushMemoryLevelMild (1):
367
- * Flushes the object cache and metadata cache.
368
- *
369
- * YapDatabaseConnectionFlushMemoryLevelModerate (2):
370
- * Mild plus drops less common pre-compiled sqlite statements.
371
- *
372
- * YapDatabaseConnectionFlushMemoryLevelFull (3):
373
- * Full flush of all caches and removes all pre-compiled sqlite statements.
374
- **/
375
- - (void)flushMemoryWithFlags:(YapDatabaseConnectionFlushMemoryFlags)flags
376
- {
377
- dispatch_block_t block = ^{
378
-
379
- [self _flushMemoryWithFlags:flags];
380
- };
381
-
382
- if (dispatch_get_specific(IsOnConnectionQueueKey))
383
- block();
384
- else
385
- dispatch_sync(connectionQueue, block);
386
- }
387
-
388
- #if TARGET_OS_IPHONE
389
- - (void)didReceiveMemoryWarning:(NSNotification *)notification
390
- {
391
- [self flushMemoryWithFlags:[self autoFlushMemoryFlags]];
392
- }
393
- #endif
394
-
395
- ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
396
- #pragma mark Properties
397
- ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
398
-
399
- @synthesize database = database;
400
- @synthesize name = _name;
401
-
402
- #if TARGET_OS_IPHONE
403
- @synthesize autoFlushMemoryFlags;
404
- #endif
405
-
406
- - (BOOL)objectCacheEnabled
407
- {
408
- __block BOOL result = NO;
409
-
410
- dispatch_block_t block = ^{
411
- result = (objectCache != nil);
412
- };
413
-
414
- if (dispatch_get_specific(IsOnConnectionQueueKey))
415
- block();
416
- else
417
- dispatch_sync(connectionQueue, block);
418
-
419
- return result;
420
- }
421
-
422
- - (void)setObjectCacheEnabled:(BOOL)flag
423
- {
424
- dispatch_block_t block = ^{
425
-
426
- if (flag) // Enabled
427
- {
428
- if (objectCache == nil)
429
- {
430
- objectCache = [[YapCache alloc] initWithKeyClass:[YapCollectionKey class]];
431
- objectCache.countLimit = objectCacheLimit;
432
- }
433
- }
434
- else // Disabled
435
- {
436
- objectCache = nil;
437
- }
438
-
439
- [self updateKeyCacheLimit];
440
- };
441
-
442
- if (dispatch_get_specific(IsOnConnectionQueueKey))
443
- block();
444
- else
445
- dispatch_async(connectionQueue, block);
446
- }
447
-
448
- - (NSUInteger)objectCacheLimit
449
- {
450
- __block NSUInteger result = 0;
451
-
452
- dispatch_block_t block = ^{
453
- result = objectCacheLimit;
454
- };
455
-
456
- if (dispatch_get_specific(IsOnConnectionQueueKey))
457
- block();
458
- else
459
- dispatch_sync(connectionQueue, block);
460
-
461
- return result;
462
- }
463
-
464
- - (void)setObjectCacheLimit:(NSUInteger)newObjectCacheLimit
465
- {
466
- dispatch_block_t block = ^{
467
-
468
- if (objectCacheLimit != newObjectCacheLimit)
469
- {
470
- objectCacheLimit = newObjectCacheLimit;
471
-
472
- if (objectCache == nil)
473
- {
474
- // Limit changed, but objectCache is still disabled
475
- }
476
- else
477
- {
478
- objectCache.countLimit = objectCacheLimit;
479
- [self updateKeyCacheLimit];
480
- }
481
- }
482
- };
483
-
484
- if (dispatch_get_specific(IsOnConnectionQueueKey))
485
- block();
486
- else
487
- dispatch_async(connectionQueue, block);
488
- }
489
-
490
- - (BOOL)metadataCacheEnabled
491
- {
492
- __block BOOL result = NO;
493
-
494
- dispatch_block_t block = ^{
495
- result = (metadataCache != nil);
496
- };
497
-
498
- if (dispatch_get_specific(IsOnConnectionQueueKey))
499
- block();
500
- else
501
- dispatch_sync(connectionQueue, block);
502
-
503
- return result;
504
- }
505
-
506
- - (void)setMetadataCacheEnabled:(BOOL)flag
507
- {
508
- dispatch_block_t block = ^{
509
-
510
- if (flag) // Enabled
511
- {
512
- if (metadataCache == nil)
513
- {
514
- metadataCache = [[YapCache alloc] initWithKeyClass:[YapCollectionKey class]];
515
- metadataCache.countLimit = metadataCacheLimit;
516
- }
517
- }
518
- else // Disabled
519
- {
520
- metadataCache = nil;
521
- }
522
-
523
- [self updateKeyCacheLimit];
524
- };
525
-
526
- if (dispatch_get_specific(IsOnConnectionQueueKey))
527
- block();
528
- else
529
- dispatch_async(connectionQueue, block);
530
- }
531
-
532
- - (NSUInteger)metadataCacheLimit
533
- {
534
- __block NSUInteger result = 0;
535
-
536
- dispatch_block_t block = ^{
537
- result = metadataCacheLimit;
538
- };
539
-
540
- if (dispatch_get_specific(IsOnConnectionQueueKey))
541
- block();
542
- else
543
- dispatch_sync(connectionQueue, block);
544
-
545
- return result;
546
- }
547
-
548
- - (void)setMetadataCacheLimit:(NSUInteger)newMetadataCacheLimit
549
- {
550
- dispatch_block_t block = ^{
551
-
552
- if (metadataCacheLimit != newMetadataCacheLimit)
553
- {
554
- metadataCacheLimit = newMetadataCacheLimit;
555
-
556
- if (metadataCache == nil)
557
- {
558
- // Limit changed but metadataCache still disabled
559
- }
560
- else
561
- {
562
- metadataCache.countLimit = metadataCacheLimit;
563
- [self updateKeyCacheLimit];
564
- }
565
- }
566
- };
567
-
568
- if (dispatch_get_specific(IsOnConnectionQueueKey))
569
- block();
570
- else
571
- dispatch_async(connectionQueue, block);
572
- }
573
-
574
- - (YapDatabasePolicy)objectPolicy
575
- {
576
- __block YapDatabasePolicy policy = YapDatabasePolicyShare;
577
-
578
- dispatch_block_t block = ^{
579
- policy = objectPolicy;
580
- };
581
-
582
- if (dispatch_get_specific(IsOnConnectionQueueKey))
583
- block();
584
- else
585
- dispatch_sync(connectionQueue, block);
586
-
587
- return policy;
588
- }
589
-
590
- - (void)setObjectPolicy:(YapDatabasePolicy)newObjectPolicy
591
- {
592
- dispatch_block_t block = ^{
593
-
594
- // sanity check
595
- switch (newObjectPolicy)
596
- {
597
- case YapDatabasePolicyContainment :
598
- case YapDatabasePolicyShare :
599
- case YapDatabasePolicyCopy : objectPolicy = newObjectPolicy; break;
600
- default : objectPolicy = YapDatabasePolicyContainment;
601
- }
602
- };
603
-
604
- if (dispatch_get_specific(IsOnConnectionQueueKey))
605
- block();
606
- else
607
- dispatch_async(connectionQueue, block);
608
- }
609
-
610
- - (YapDatabasePolicy)metadataPolicy
611
- {
612
- __block YapDatabasePolicy policy = YapDatabasePolicyShare;
613
-
614
- dispatch_block_t block = ^{
615
- policy = metadataPolicy;
616
- };
617
-
618
- if (dispatch_get_specific(IsOnConnectionQueueKey))
619
- block();
620
- else
621
- dispatch_sync(connectionQueue, block);
622
-
623
- return policy;
624
- }
625
-
626
- - (void)setMetadataPolicy:(YapDatabasePolicy)newMetadataPolicy
627
- {
628
- dispatch_block_t block = ^{
629
-
630
- // sanity check
631
- switch (newMetadataPolicy)
632
- {
633
- case YapDatabasePolicyContainment :
634
- case YapDatabasePolicyShare :
635
- case YapDatabasePolicyCopy : metadataPolicy = newMetadataPolicy; break;
636
- default : metadataPolicy = YapDatabasePolicyContainment;
637
- }
638
- };
639
-
640
- if (dispatch_get_specific(IsOnConnectionQueueKey))
641
- block();
642
- else
643
- dispatch_async(connectionQueue, block);
644
- }
645
-
646
- - (uint64_t)snapshot
647
- {
648
- __block uint64_t result = 0;
649
-
650
- dispatch_block_t block = ^{
651
- result = snapshot;
652
- };
653
-
654
- if (dispatch_get_specific(IsOnConnectionQueueKey))
655
- block();
656
- else
657
- dispatch_sync(connectionQueue, block);
658
-
659
- return result;
660
- }
661
-
662
- ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
663
- #pragma mark Utilities
664
- ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
665
-
666
- - (void)updateKeyCacheLimit
667
- {
668
- NSUInteger keyCacheLimit = MIN_KEY_CACHE_LIMIT;
669
-
670
- if (keyCacheLimit != UNLIMITED_CACHE_LIMIT)
671
- {
672
- if (objectCache)
673
- {
674
- if (objectCacheLimit == UNLIMITED_CACHE_LIMIT)
675
- keyCacheLimit = UNLIMITED_CACHE_LIMIT;
676
- else
677
- keyCacheLimit = MAX(keyCacheLimit, objectCacheLimit);
678
- }
679
- }
680
-
681
- if (keyCacheLimit != UNLIMITED_CACHE_LIMIT)
682
- {
683
- if (metadataCache)
684
- {
685
- if (objectCacheLimit == UNLIMITED_CACHE_LIMIT)
686
- keyCacheLimit = UNLIMITED_CACHE_LIMIT;
687
- else
688
- keyCacheLimit = MAX(keyCacheLimit, objectCacheLimit);
689
- }
690
- }
691
-
692
- keyCache.countLimit = keyCacheLimit;
693
- }
694
-
695
- ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
696
- #pragma mark Statements
697
- ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
698
-
699
- - (sqlite3_stmt *)beginTransactionStatement
700
- {
701
- if (beginTransactionStatement == NULL)
702
- {
703
- char *stmt = "BEGIN TRANSACTION;";
704
- int stmtLen = (int)strlen(stmt);
705
-
706
- int status = sqlite3_prepare_v2(db, stmt, stmtLen+1, &beginTransactionStatement, NULL);
707
- if (status != SQLITE_OK)
708
- {
709
- YDBLogError(@"Error creating '%@': %d %s", THIS_METHOD, status, sqlite3_errmsg(db));
710
- }
711
- }
712
-
713
- return beginTransactionStatement;
714
- }
715
-
716
- - (sqlite3_stmt *)commitTransactionStatement
717
- {
718
- if (commitTransactionStatement == NULL)
719
- {
720
- char *stmt = "COMMIT TRANSACTION;";
721
- int stmtLen = (int)strlen(stmt);
722
-
723
- int status = sqlite3_prepare_v2(db, stmt, stmtLen+1, &commitTransactionStatement, NULL);
724
- if (status != SQLITE_OK)
725
- {
726
- YDBLogError(@"Error creating '%@': %d %s", THIS_METHOD, status, sqlite3_errmsg(db));
727
- }
728
- }
729
-
730
- return commitTransactionStatement;
731
- }
732
-
733
- - (sqlite3_stmt *)rollbackTransactionStatement
734
- {
735
- if (rollbackTransactionStatement == NULL)
736
- {
737
- char *stmt = "ROLLBACK TRANSACTION;";
738
- int stmtLen = (int)strlen(stmt);
739
-
740
- int status = sqlite3_prepare_v2(db, stmt, stmtLen+1, &rollbackTransactionStatement, NULL);
741
- if (status != SQLITE_OK)
742
- {
743
- YDBLogError(@"Error creating '%@': %d %s", THIS_METHOD, status, sqlite3_errmsg(db));
744
- }
745
- }
746
-
747
- return rollbackTransactionStatement;
748
- }
749
-
750
- - (sqlite3_stmt *)yapGetDataForKeyStatement
751
- {
752
- if (yapGetDataForKeyStatement == NULL)
753
- {
754
- char *stmt = "SELECT \"data\" FROM \"yap2\" WHERE \"extension\" = ? AND \"key\" = ?;";
755
- int stmtLen = (int)strlen(stmt);
756
-
757
- int status = sqlite3_prepare_v2(db, stmt, stmtLen+1, &yapGetDataForKeyStatement, NULL);
758
- if (status != SQLITE_OK)
759
- {
760
- YDBLogError(@"Error creating '%@': %d %s", THIS_METHOD, status, sqlite3_errmsg(db));
761
- }
762
- }
763
-
764
- return yapGetDataForKeyStatement;
765
- }
766
-
767
- - (sqlite3_stmt *)yapSetDataForKeyStatement
768
- {
769
- if (yapSetDataForKeyStatement == NULL)
770
- {
771
- char *stmt = "INSERT OR REPLACE INTO \"yap2\" (\"extension\", \"key\", \"data\") VALUES (?, ?, ?);";
772
- int stmtLen = (int)strlen(stmt);
773
-
774
- int status = sqlite3_prepare_v2(db, stmt, stmtLen+1, &yapSetDataForKeyStatement, NULL);
775
- if (status != SQLITE_OK)
776
- {
777
- YDBLogError(@"Error creating '%@': %d %s", THIS_METHOD, status, sqlite3_errmsg(db));
778
- }
779
- }
780
-
781
- return yapSetDataForKeyStatement;
782
- }
783
-
784
- - (sqlite3_stmt *)yapRemoveForKeyStatement
785
- {
786
- if (yapRemoveForKeyStatement == NULL)
787
- {
788
- char *stmt = "DELETE FROM \"yap2\" WHERE \"extension\" = ? AND \"key\" = ?;";
789
- int stmtLen = (int)strlen(stmt);
790
-
791
- int status = sqlite3_prepare_v2(db, stmt, stmtLen+1, &yapRemoveForKeyStatement, NULL);
792
- if (status != SQLITE_OK)
793
- {
794
- YDBLogError(@"Error creating '%@': %d %s", THIS_METHOD, status, sqlite3_errmsg(db));
795
- }
796
- }
797
-
798
- return yapRemoveForKeyStatement;
799
- }
800
-
801
- - (sqlite3_stmt *)yapRemoveExtensionStatement
802
- {
803
- if (yapRemoveExtensionStatement == NULL)
804
- {
805
- char *stmt = "DELETE FROM \"yap2\" WHERE \"extension\" = ?;";
806
- int stmtLen = (int)strlen(stmt);
807
-
808
- int status = sqlite3_prepare_v2(db, stmt, stmtLen+1, &yapRemoveExtensionStatement, NULL);
809
- if (status != SQLITE_OK)
810
- {
811
- YDBLogError(@"Error creating '%@': %d %s", THIS_METHOD, status, sqlite3_errmsg(db));
812
- }
813
- }
814
-
815
- return yapRemoveExtensionStatement;
816
- }
817
-
818
- - (sqlite3_stmt *)getCollectionCountStatement
819
- {
820
- if (getCollectionCountStatement == NULL)
821
- {
822
- char *stmt = "SELECT COUNT(DISTINCT collection) AS NumberOfRows FROM \"database2\";";
823
- int stmtLen = (int)strlen(stmt);
824
-
825
- int status = sqlite3_prepare_v2(db, stmt, stmtLen+1, &getCollectionCountStatement, NULL);
826
- if (status != SQLITE_OK)
827
- {
828
- YDBLogError(@"Error creating '%@': %d %s", THIS_METHOD, status, sqlite3_errmsg(db));
829
- }
830
- }
831
-
832
- return getCollectionCountStatement;
833
- }
834
-
835
- - (sqlite3_stmt *)getKeyCountForCollectionStatement
836
- {
837
- if (getKeyCountForCollectionStatement == NULL)
838
- {
839
- char *stmt = "SELECT COUNT(*) AS NumberOfRows FROM \"database2\" WHERE \"collection\" = ?;";
840
- int stmtLen = (int)strlen(stmt);
841
-
842
- int status = sqlite3_prepare_v2(db, stmt, stmtLen+1, &getKeyCountForCollectionStatement, NULL);
843
- if (status != SQLITE_OK)
844
- {
845
- YDBLogError(@"Error creating '%@': %d %s", THIS_METHOD, status, sqlite3_errmsg(db));
846
- }
847
- }
848
-
849
- return getKeyCountForCollectionStatement;
850
- }
851
-
852
- - (sqlite3_stmt *)getKeyCountForAllStatement
853
- {
854
- if (getKeyCountForAllStatement == NULL)
855
- {
856
- char *stmt = "SELECT COUNT(*) AS NumberOfRows FROM \"database2\";";
857
- int stmtLen = (int)strlen(stmt);
858
-
859
- int status = sqlite3_prepare_v2(db, stmt, stmtLen+1, &getKeyCountForAllStatement, NULL);
860
- if (status != SQLITE_OK)
861
- {
862
- YDBLogError(@"Error creating '%@': %d %s", THIS_METHOD, status, sqlite3_errmsg(db));
863
- }
864
- }
865
-
866
- return getKeyCountForAllStatement;
867
- }
868
-
869
- - (sqlite3_stmt *)getCountForRowidStatement
870
- {
871
- if (getCountForRowidStatement == NULL)
872
- {
873
- char *stmt = "SELECT COUNT(*) AS NumberOfRows FROM \"database2\" WHERE \"rowid\" = ?;";
874
- int stmtLen = (int)strlen(stmt);
875
-
876
- int status = sqlite3_prepare_v2(db, stmt, stmtLen+1, &getCountForRowidStatement, NULL);
877
- if (status != SQLITE_OK)
878
- {
879
- YDBLogError(@"Error creating '%@': %d %s", THIS_METHOD, status, sqlite3_errmsg(db));
880
- }
881
- }
882
-
883
- return getCountForRowidStatement;
884
- }
885
-
886
- - (sqlite3_stmt *)getRowidForKeyStatement
887
- {
888
- if (getRowidForKeyStatement == NULL)
889
- {
890
- char *stmt = "SELECT \"rowid\" FROM \"database2\" WHERE \"collection\" = ? AND \"key\" = ?;";
891
- int stmtLen = (int)strlen(stmt);
892
-
893
- int status = sqlite3_prepare_v2(db, stmt, stmtLen+1, &getRowidForKeyStatement, NULL);
894
- if (status != SQLITE_OK)
895
- {
896
- YDBLogError(@"Error creating '%@': %d %s", THIS_METHOD, status, sqlite3_errmsg(db));
897
- }
898
- }
899
-
900
- return getRowidForKeyStatement;
901
- }
902
-
903
- - (sqlite3_stmt *)getKeyForRowidStatement
904
- {
905
- if (getKeyForRowidStatement == NULL)
906
- {
907
- char *stmt = "SELECT \"collection\", \"key\" FROM \"database2\" WHERE \"rowid\" = ?;";
908
- int stmtLen = (int)strlen(stmt);
909
-
910
- int status = sqlite3_prepare_v2(db, stmt, stmtLen+1, &getKeyForRowidStatement, NULL);
911
- if (status != SQLITE_OK)
912
- {
913
- YDBLogError(@"Error creating '%@': %d %s", THIS_METHOD, status, sqlite3_errmsg(db));
914
- }
915
- }
916
-
917
- return getKeyForRowidStatement;
918
- }
919
-
920
- - (sqlite3_stmt *)getDataForRowidStatement
921
- {
922
- if (getDataForRowidStatement == NULL)
923
- {
924
- char *stmt = "SELECT \"data\" FROM \"database2\" WHERE \"rowid\" = ?;";
925
- int stmtLen = (int)strlen(stmt);
926
-
927
- int status = sqlite3_prepare_v2(db, stmt, stmtLen+1, &getDataForRowidStatement, NULL);
928
- if (status != SQLITE_OK)
929
- {
930
- YDBLogError(@"Error creating '%@': %d %s", THIS_METHOD, status, sqlite3_errmsg(db));
931
- }
932
- }
933
-
934
- return getDataForRowidStatement;
935
- }
936
-
937
- - (sqlite3_stmt *)getMetadataForRowidStatement
938
- {
939
- if (getMetadataForRowidStatement == NULL)
940
- {
941
- char *stmt = "SELECT \"metadata\" FROM \"database2\" WHERE \"rowid\" = ?;";
942
- int stmtLen = (int)strlen(stmt);
943
-
944
- int status = sqlite3_prepare_v2(db, stmt, stmtLen+1, &getMetadataForRowidStatement, NULL);
945
- if (status != SQLITE_OK)
946
- {
947
- YDBLogError(@"Error creating '%@': %d %s", THIS_METHOD, status, sqlite3_errmsg(db));
948
- }
949
- }
950
-
951
- return getMetadataForRowidStatement;
952
- }
953
-
954
- - (sqlite3_stmt *)getAllForRowidStatement
955
- {
956
- if (getAllForRowidStatement == NULL)
957
- {
958
- char *stmt = "SELECT \"data\", \"metadata\" FROM \"database2\" WHERE \"rowid\" = ?;";
959
- int stmtLen = (int)strlen(stmt);
960
-
961
- int status = sqlite3_prepare_v2(db, stmt, stmtLen+1, &getAllForRowidStatement, NULL);
962
- if (status != SQLITE_OK)
963
- {
964
- YDBLogError(@"Error creating '%@': %d %s", THIS_METHOD, status, sqlite3_errmsg(db));
965
- }
966
- }
967
-
968
- return getAllForRowidStatement;
969
- }
970
-
971
- - (sqlite3_stmt *)getDataForKeyStatement
972
- {
973
- if (getDataForKeyStatement == NULL)
974
- {
975
- char *stmt = "SELECT \"data\" FROM \"database2\" WHERE \"collection\" = ? AND \"key\" = ?;";
976
- int stmtLen = (int)strlen(stmt);
977
-
978
- int status = sqlite3_prepare_v2(db, stmt, stmtLen+1, &getDataForKeyStatement, NULL);
979
- if (status != SQLITE_OK)
980
- {
981
- YDBLogError(@"Error creating '%@': %d %s", THIS_METHOD, status, sqlite3_errmsg(db));
982
- }
983
- }
984
-
985
- return getDataForKeyStatement;
986
- }
987
-
988
- - (sqlite3_stmt *)getMetadataForKeyStatement
989
- {
990
- if (getMetadataForKeyStatement == NULL)
991
- {
992
- char *stmt = "SELECT \"metadata\" FROM \"database2\" WHERE \"collection\" = ? AND \"key\" = ?;";
993
- int stmtLen = (int)strlen(stmt);
994
-
995
- int status = sqlite3_prepare_v2(db, stmt, stmtLen+1, &getMetadataForKeyStatement, NULL);
996
- if (status != SQLITE_OK)
997
- {
998
- YDBLogError(@"Error creating '%@': %d %s", THIS_METHOD, status, sqlite3_errmsg(db));
999
- }
1000
- }
1001
-
1002
- return getMetadataForKeyStatement;
1003
- }
1004
-
1005
- - (sqlite3_stmt *)getAllForKeyStatement
1006
- {
1007
- if (getAllForKeyStatement == NULL)
1008
- {
1009
- char *stmt = "SELECT \"data\", \"metadata\" FROM \"database2\" WHERE \"collection\" = ? AND \"key\" = ?;";
1010
- int stmtLen = (int)strlen(stmt);
1011
-
1012
- int status = sqlite3_prepare_v2(db, stmt, stmtLen+1, &getAllForKeyStatement, NULL);
1013
- if (status != SQLITE_OK)
1014
- {
1015
- YDBLogError(@"Error creating '%@': %d %s", THIS_METHOD, status, sqlite3_errmsg(db));
1016
- }
1017
- }
1018
-
1019
- return getAllForKeyStatement;
1020
- }
1021
-
1022
- - (sqlite3_stmt *)insertForRowidStatement
1023
- {
1024
- if (insertForRowidStatement == NULL)
1025
- {
1026
- char *stmt = "INSERT INTO \"database2\""
1027
- " (\"collection\", \"key\", \"data\", \"metadata\") VALUES (?, ?, ?, ?);";
1028
- int stmtLen = (int)strlen(stmt);
1029
-
1030
- int status = sqlite3_prepare_v2(db, stmt, stmtLen+1, &insertForRowidStatement, NULL);
1031
- if (status != SQLITE_OK)
1032
- {
1033
- YDBLogError(@"Error creating '%@': %d %s", THIS_METHOD, status, sqlite3_errmsg(db));
1034
- }
1035
- }
1036
-
1037
- return insertForRowidStatement;
1038
- }
1039
-
1040
- - (sqlite3_stmt *)updateAllForRowidStatement
1041
- {
1042
- if (updateAllForRowidStatement == NULL)
1043
- {
1044
- char *stmt = "UPDATE \"database2\" SET \"data\" = ?, \"metadata\" = ? WHERE \"rowid\" = ?;";
1045
- int stmtLen = (int)strlen(stmt);
1046
-
1047
- int status = sqlite3_prepare_v2(db, stmt, stmtLen+1, &updateAllForRowidStatement, NULL);
1048
- if (status != SQLITE_OK)
1049
- {
1050
- YDBLogError(@"Error creating '%@': %d %s", THIS_METHOD, status, sqlite3_errmsg(db));
1051
- }
1052
- }
1053
-
1054
- return updateAllForRowidStatement;
1055
- }
1056
-
1057
- - (sqlite3_stmt *)updateObjectForRowidStatement
1058
- {
1059
- if (updateObjectForRowidStatement == NULL)
1060
- {
1061
- char *stmt = "UPDATE \"database2\" SET \"data\" = ? WHERE \"rowid\" = ?;";
1062
- int stmtLen = (int)strlen(stmt);
1063
-
1064
- int status = sqlite3_prepare_v2(db, stmt, stmtLen+1, &updateObjectForRowidStatement, NULL);
1065
- if (status != SQLITE_OK)
1066
- {
1067
- YDBLogError(@"Error creating '%@': %d %s", THIS_METHOD, status, sqlite3_errmsg(db));
1068
- }
1069
- }
1070
-
1071
- return updateObjectForRowidStatement;
1072
- }
1073
-
1074
- - (sqlite3_stmt *)updateMetadataForRowidStatement
1075
- {
1076
- if (updateMetadataForRowidStatement == NULL)
1077
- {
1078
- char *stmt = "UPDATE \"database2\" SET \"metadata\" = ? WHERE \"rowid\" = ?;";
1079
- int stmtLen = (int)strlen(stmt);
1080
-
1081
- int status = sqlite3_prepare_v2(db, stmt, stmtLen+1, &updateMetadataForRowidStatement, NULL);
1082
- if (status != SQLITE_OK)
1083
- {
1084
- YDBLogError(@"Error creating '%@': %d %s", THIS_METHOD, status, sqlite3_errmsg(db));
1085
- }
1086
- }
1087
-
1088
- return updateMetadataForRowidStatement;
1089
- }
1090
-
1091
- - (sqlite3_stmt *)removeForRowidStatement
1092
- {
1093
- if (removeForRowidStatement == NULL)
1094
- {
1095
- char *stmt = "DELETE FROM \"database2\" WHERE \"rowid\" = ?;";
1096
- int stmtLen = (int)strlen(stmt);
1097
-
1098
- int status = sqlite3_prepare_v2(db, stmt, stmtLen+1, &removeForRowidStatement, NULL);
1099
- if (status != SQLITE_OK)
1100
- {
1101
- YDBLogError(@"Error creating '%@': %d %s", THIS_METHOD, status, sqlite3_errmsg(db));
1102
- }
1103
- }
1104
-
1105
- return removeForRowidStatement;
1106
- }
1107
-
1108
- - (sqlite3_stmt *)removeCollectionStatement
1109
- {
1110
- if (removeCollectionStatement == NULL)
1111
- {
1112
- char *stmt = "DELETE FROM \"database2\" WHERE \"collection\" = ?;";
1113
- int stmtLen = (int)strlen(stmt);
1114
-
1115
- int status = sqlite3_prepare_v2(db, stmt, stmtLen+1, &removeCollectionStatement, NULL);
1116
- if (status != SQLITE_OK)
1117
- {
1118
- YDBLogError(@"Error creating '%@': %d %s", THIS_METHOD, status, sqlite3_errmsg(db));
1119
- }
1120
- }
1121
-
1122
- return removeCollectionStatement;
1123
- }
1124
-
1125
- - (sqlite3_stmt *)removeAllStatement
1126
- {
1127
- if (removeAllStatement == NULL)
1128
- {
1129
- char *stmt = "DELETE FROM \"database2\";";
1130
- int stmtLen = (int)strlen(stmt);
1131
-
1132
- int status = sqlite3_prepare_v2(db, stmt, stmtLen+1, &removeAllStatement, NULL);
1133
- if (status != SQLITE_OK)
1134
- {
1135
- YDBLogError(@"Error creating '%@': %d %s", THIS_METHOD, status, sqlite3_errmsg(db));
1136
- }
1137
- }
1138
-
1139
- return removeAllStatement;
1140
- }
1141
-
1142
- - (sqlite3_stmt *)enumerateCollectionsStatement
1143
- {
1144
- if (enumerateCollectionsStatement == NULL)
1145
- {
1146
- char *stmt = "SELECT DISTINCT \"collection\" FROM \"database2\";";
1147
- int stmtLen = (int)strlen(stmt);
1148
-
1149
- int status = sqlite3_prepare_v2(db, stmt, stmtLen+1, &enumerateCollectionsStatement, NULL);
1150
- if (status != SQLITE_OK)
1151
- {
1152
- YDBLogError(@"Error creating '%@': %d %s", THIS_METHOD, status, sqlite3_errmsg(db));
1153
- }
1154
- }
1155
-
1156
- return enumerateCollectionsStatement;
1157
- }
1158
-
1159
- - (sqlite3_stmt *)enumerateCollectionsForKeyStatement
1160
- {
1161
- if (enumerateCollectionsForKeyStatement == NULL)
1162
- {
1163
- char *stmt = "SELECT \"collection\" FROM \"database2\" WHERE \"key\" = ?;";
1164
- int stmtLen = (int)strlen(stmt);
1165
-
1166
- int status = sqlite3_prepare_v2(db, stmt, stmtLen+1, &enumerateCollectionsForKeyStatement, NULL);
1167
- if (status != SQLITE_OK)
1168
- {
1169
- YDBLogError(@"Error creating '%@': %d %s", THIS_METHOD, status, sqlite3_errmsg(db));
1170
- }
1171
- }
1172
-
1173
- return enumerateCollectionsForKeyStatement;
1174
- }
1175
-
1176
- - (sqlite3_stmt *)enumerateKeysInCollectionStatement
1177
- {
1178
- if (enumerateKeysInCollectionStatement == NULL)
1179
- {
1180
- char *stmt = "SELECT \"rowid\", \"key\" FROM \"database2\" WHERE collection = ?;";
1181
- int stmtLen = (int)strlen(stmt);
1182
-
1183
- int status = sqlite3_prepare_v2(db, stmt, stmtLen+1, &enumerateKeysInCollectionStatement, NULL);
1184
- if (status != SQLITE_OK)
1185
- {
1186
- YDBLogError(@"Error creating '%@': %d %s", THIS_METHOD, status, sqlite3_errmsg(db));
1187
- }
1188
- }
1189
-
1190
- return enumerateKeysInCollectionStatement;
1191
- }
1192
-
1193
- - (sqlite3_stmt *)enumerateKeysInAllCollectionsStatement
1194
- {
1195
- if (enumerateKeysInAllCollectionsStatement == NULL)
1196
- {
1197
- char *stmt = "SELECT \"rowid\", \"collection\", \"key\" FROM \"database2\";";
1198
- int stmtLen = (int)strlen(stmt);
1199
-
1200
- int status = sqlite3_prepare_v2(db, stmt, stmtLen+1, &enumerateKeysInAllCollectionsStatement, NULL);
1201
- if (status != SQLITE_OK)
1202
- {
1203
- YDBLogError(@"Error creating '%@': %d %s", THIS_METHOD, status, sqlite3_errmsg(db));
1204
- }
1205
- }
1206
-
1207
- return enumerateKeysInAllCollectionsStatement;
1208
- }
1209
-
1210
- - (sqlite3_stmt *)enumerateKeysAndMetadataInCollectionStatement
1211
- {
1212
- if (enumerateKeysAndMetadataInCollectionStatement == NULL)
1213
- {
1214
- char *stmt = "SELECT \"rowid\", \"key\", \"metadata\" FROM \"database2\" WHERE collection = ?;";
1215
- int stmtLen = (int)strlen(stmt);
1216
-
1217
- int status = sqlite3_prepare_v2(db, stmt, stmtLen+1, &enumerateKeysAndMetadataInCollectionStatement, NULL);
1218
- if (status != SQLITE_OK)
1219
- {
1220
- YDBLogError(@"Error creating '%@': %d %s", THIS_METHOD, status, sqlite3_errmsg(db));
1221
- }
1222
- }
1223
-
1224
- return enumerateKeysAndMetadataInCollectionStatement;
1225
- }
1226
-
1227
- - (sqlite3_stmt *)enumerateKeysAndMetadataInAllCollectionsStatement
1228
- {
1229
- if (enumerateKeysAndMetadataInAllCollectionsStatement == NULL)
1230
- {
1231
- char *stmt = "SELECT \"rowid\", \"collection\", \"key\", \"metadata\""
1232
- " FROM \"database2\" ORDER BY \"collection\" ASC;";
1233
- int stmtLen = (int)strlen(stmt);
1234
-
1235
- int status = sqlite3_prepare_v2(db, stmt, stmtLen+1, &enumerateKeysAndMetadataInAllCollectionsStatement, NULL);
1236
- if (status != SQLITE_OK)
1237
- {
1238
- YDBLogError(@"Error creating '%@': %d %s", THIS_METHOD, status, sqlite3_errmsg(db));
1239
- }
1240
- }
1241
-
1242
- return enumerateKeysAndMetadataInAllCollectionsStatement;
1243
- }
1244
-
1245
- - (sqlite3_stmt *)enumerateKeysAndObjectsInCollectionStatement
1246
- {
1247
- if (enumerateKeysAndObjectsInCollectionStatement == NULL)
1248
- {
1249
- char *stmt = "SELECT \"rowid\", \"key\", \"data\" FROM \"database2\" WHERE \"collection\" = ?;";
1250
- int stmtLen = (int)strlen(stmt);
1251
-
1252
- int status = sqlite3_prepare_v2(db, stmt, stmtLen+1, &enumerateKeysAndObjectsInCollectionStatement, NULL);
1253
- if (status != SQLITE_OK)
1254
- {
1255
- YDBLogError(@"Error creating '%@': %d %s", THIS_METHOD, status, sqlite3_errmsg(db));
1256
- }
1257
- }
1258
-
1259
- return enumerateKeysAndObjectsInCollectionStatement;
1260
- }
1261
-
1262
- - (sqlite3_stmt *)enumerateKeysAndObjectsInAllCollectionsStatement
1263
- {
1264
- if (enumerateKeysAndObjectsInAllCollectionsStatement == NULL)
1265
- {
1266
- char *stmt = "SELECT \"rowid\", \"collection\", \"key\", \"data\""
1267
- " FROM \"database2\" ORDER BY \"collection\" ASC;";
1268
- int stmtLen = (int)strlen(stmt);
1269
-
1270
- int status = sqlite3_prepare_v2(db, stmt, stmtLen+1, &enumerateKeysAndObjectsInAllCollectionsStatement, NULL);
1271
- if (status != SQLITE_OK)
1272
- {
1273
- YDBLogError(@"Error creating '%@': %d %s", THIS_METHOD, status, sqlite3_errmsg(db));
1274
- }
1275
- }
1276
-
1277
- return enumerateKeysAndObjectsInAllCollectionsStatement;
1278
- }
1279
-
1280
- - (sqlite3_stmt *)enumerateRowsInCollectionStatement
1281
- {
1282
- if (enumerateRowsInCollectionStatement == NULL)
1283
- {
1284
- char *stmt = "SELECT \"rowid\", \"key\", \"data\", \"metadata\" FROM \"database2\" WHERE \"collection\" = ?;";
1285
- int stmtLen = (int)strlen(stmt);
1286
-
1287
- int status = sqlite3_prepare_v2(db, stmt, stmtLen+1, &enumerateRowsInCollectionStatement, NULL);
1288
- if (status != SQLITE_OK)
1289
- {
1290
- YDBLogError(@"Error creating '%@': %d %s", THIS_METHOD, status, sqlite3_errmsg(db));
1291
- }
1292
- }
1293
-
1294
- return enumerateRowsInCollectionStatement;
1295
- }
1296
-
1297
- - (sqlite3_stmt *)enumerateRowsInAllCollectionsStatement
1298
- {
1299
- if (enumerateRowsInAllCollectionsStatement == NULL)
1300
- {
1301
- char *stmt =
1302
- "SELECT \"rowid\", \"collection\", \"key\", \"data\", \"metadata\""
1303
- " FROM \"database2\" ORDER BY \"collection\" ASC;";
1304
- int stmtLen = (int)strlen(stmt);
1305
-
1306
- int status = sqlite3_prepare_v2(db, stmt, stmtLen+1, &enumerateRowsInAllCollectionsStatement, NULL);
1307
- if (status != SQLITE_OK)
1308
- {
1309
- YDBLogError(@"Error creating '%@': %d %s", THIS_METHOD, status, sqlite3_errmsg(db));
1310
- }
1311
- }
1312
-
1313
- return enumerateRowsInAllCollectionsStatement;
1314
- }
1315
-
1316
- ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
1317
- #pragma mark Transactions
1318
- ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
1319
-
1320
- /**
1321
- * Read-only access to the database.
1322
- *
1323
- * The given block can run concurrently with sibling connections,
1324
- * regardless of whether the sibling connections are executing read-only or read-write transactions.
1325
- *
1326
- * The only time this method ever blocks is if another thread is currently using this connection instance
1327
- * to execute a readBlock or readWriteBlock. Recall that you may create multiple connections for concurrent access.
1328
- *
1329
- * This method is synchronous.
1330
- **/
1331
- - (void)readWithBlock:(void (^)(YapDatabaseReadTransaction *))block
1332
- {
1333
- dispatch_sync(connectionQueue, ^{ @autoreleasepool {
1334
-
1335
- if (longLivedReadTransaction)
1336
- {
1337
- block(longLivedReadTransaction);
1338
- }
1339
- else
1340
- {
1341
- YapDatabaseReadTransaction *transaction = [self newReadTransaction];
1342
-
1343
- [self preReadTransaction:transaction];
1344
- block(transaction);
1345
- [self postReadTransaction:transaction];
1346
- }
1347
- }});
1348
- }
1349
-
1350
- /**
1351
- * Read-write access to the database.
1352
- *
1353
- * Only a single read-write block can execute among all sibling connections.
1354
- * Thus this method may block if another sibling connection is currently executing a read-write block.
1355
- *
1356
- * This method is synchronous.
1357
- **/
1358
- - (void)readWriteWithBlock:(void (^)(YapDatabaseReadWriteTransaction *transaction))block
1359
- {
1360
- // Order matters.
1361
- // First go through the serial connection queue.
1362
- // Then go through serial write queue for the database.
1363
- //
1364
- // Once we're inside the database writeQueue, we know that we are the only write transaction.
1365
- // No other transaction can possibly modify the database except us, even in other connections.
1366
-
1367
- dispatch_sync(connectionQueue, ^{
1368
-
1369
- if (longLivedReadTransaction)
1370
- {
1371
- if (throwExceptionsForImplicitlyEndingLongLivedReadTransaction)
1372
- {
1373
- @throw [self implicitlyEndingLongLivedReadTransactionException];
1374
- }
1375
- else
1376
- {
1377
- YDBLogWarn(@"Implicitly ending long-lived read transaction on connection %@, database %@",
1378
- self, database);
1379
-
1380
- [self endLongLivedReadTransaction];
1381
- }
1382
- }
1383
-
1384
- __preWriteQueue(self);
1385
- dispatch_sync(database->writeQueue, ^{ @autoreleasepool {
1386
-
1387
- YapDatabaseReadWriteTransaction *transaction = [self newReadWriteTransaction];
1388
-
1389
- [self preReadWriteTransaction:transaction];
1390
- block(transaction);
1391
- [self postReadWriteTransaction:transaction];
1392
-
1393
- }}); // End dispatch_sync(database->writeQueue)
1394
- __postWriteQueue(self);
1395
- }); // End dispatch_sync(connectionQueue)
1396
- }
1397
-
1398
- /**
1399
- * Read-only access to the database.
1400
- *
1401
- * The given block can run concurrently with sibling connections,
1402
- * regardless of whether the sibling connections are executing read-only or read-write transactions.
1403
- *
1404
- * This method is asynchronous.
1405
- **/
1406
- - (void)asyncReadWithBlock:(void (^)(YapDatabaseReadTransaction *transaction))block
1407
- {
1408
- [self asyncReadWithBlock:block completionBlock:NULL completionQueue:NULL];
1409
- }
1410
-
1411
- /**
1412
- * Read-write access to the database.
1413
- *
1414
- * The given block can run concurrently with sibling connections,
1415
- * regardless of whether the sibling connections are executing read-only or read-write transactions.
1416
- *
1417
- * This method is asynchronous.
1418
- *
1419
- * An optional completion block may be used.
1420
- * The completionBlock will be invoked on the main thread (dispatch_get_main_queue()).
1421
- **/
1422
- - (void)asyncReadWithBlock:(void (^)(YapDatabaseReadTransaction *transaction))block
1423
- completionBlock:(dispatch_block_t)completionBlock
1424
- {
1425
- [self asyncReadWithBlock:block completionBlock:completionBlock completionQueue:NULL];
1426
- }
1427
-
1428
- /**
1429
- * Read-write access to the database.
1430
- *
1431
- * The given block can run concurrently with sibling connections,
1432
- * regardless of whether the sibling connections are executing read-only or read-write transactions.
1433
- *
1434
- * This method is asynchronous.
1435
- *
1436
- * An optional completion block may be used.
1437
- * Additionally the dispatch_queue to invoke the completion block may also be specified.
1438
- * If NULL, dispatch_get_main_queue() is automatically used.
1439
- **/
1440
- - (void)asyncReadWithBlock:(void (^)(YapDatabaseReadTransaction *transaction))block
1441
- completionBlock:(dispatch_block_t)completionBlock
1442
- completionQueue:(dispatch_queue_t)completionQueue
1443
- {
1444
- if (completionQueue == NULL && completionBlock != NULL)
1445
- completionQueue = dispatch_get_main_queue();
1446
-
1447
- dispatch_async(connectionQueue, ^{ @autoreleasepool {
1448
-
1449
- if (longLivedReadTransaction)
1450
- {
1451
- block(longLivedReadTransaction);
1452
- }
1453
- else
1454
- {
1455
- YapDatabaseReadTransaction *transaction = [self newReadTransaction];
1456
-
1457
- [self preReadTransaction:transaction];
1458
- block(transaction);
1459
- [self postReadTransaction:transaction];
1460
- }
1461
-
1462
- if (completionBlock)
1463
- dispatch_async(completionQueue, completionBlock);
1464
- }});
1465
- }
1466
-
1467
- /**
1468
- * Read-write access to the database.
1469
- *
1470
- * Only a single read-write block can execute among all sibling connections.
1471
- * Thus this method may block if another sibling connection is currently executing a read-write block.
1472
- *
1473
- * This method is asynchronous.
1474
- **/
1475
- - (void)asyncReadWriteWithBlock:(void (^)(YapDatabaseReadWriteTransaction *transaction))block
1476
- {
1477
- [self asyncReadWriteWithBlock:block completionBlock:NULL completionQueue:NULL];
1478
- }
1479
-
1480
- /**
1481
- * Read-write access to the database.
1482
- *
1483
- * Only a single read-write block can execute among all sibling connections.
1484
- * Thus the execution of the block may be delayted if another sibling connection
1485
- * is currently executing a read-write block.
1486
- *
1487
- * This method is asynchronous.
1488
- *
1489
- * An optional completion block may be used.
1490
- * The completionBlock will be invoked on the main thread (dispatch_get_main_queue()).
1491
- **/
1492
- - (void)asyncReadWriteWithBlock:(void (^)(YapDatabaseReadWriteTransaction *transaction))block
1493
- completionBlock:(dispatch_block_t)completionBlock
1494
- {
1495
- [self asyncReadWriteWithBlock:block completionBlock:completionBlock completionQueue:NULL];
1496
- }
1497
-
1498
- /**
1499
- * Read-write access to the database.
1500
- *
1501
- * Only a single read-write block can execute among all sibling connections.
1502
- * Thus the execution of the block may be delayted if another sibling connection
1503
- * is currently executing a read-write block.
1504
- *
1505
- * This method is asynchronous.
1506
- *
1507
- * An optional completion block may be used.
1508
- * Additionally the dispatch_queue to invoke the completion block may also be specified.
1509
- * If NULL, dispatch_get_main_queue() is automatically used.
1510
- **/
1511
- - (void)asyncReadWriteWithBlock:(void (^)(YapDatabaseReadWriteTransaction *transaction))block
1512
- completionBlock:(dispatch_block_t)completionBlock
1513
- completionQueue:(dispatch_queue_t)completionQueue
1514
- {
1515
- if (completionQueue == NULL && completionBlock != NULL)
1516
- completionQueue = dispatch_get_main_queue();
1517
-
1518
- // Order matters.
1519
- // First go through the serial connection queue.
1520
- // Then go through serial write queue for the database.
1521
- //
1522
- // Once we're inside the database writeQueue, we know that we are the only write transaction.
1523
- // No other transaction can possibly modify the database except us, even in other connections.
1524
-
1525
- dispatch_async(connectionQueue, ^{
1526
-
1527
- if (longLivedReadTransaction)
1528
- {
1529
- if (throwExceptionsForImplicitlyEndingLongLivedReadTransaction)
1530
- {
1531
- @throw [self implicitlyEndingLongLivedReadTransactionException];
1532
- }
1533
- else
1534
- {
1535
- YDBLogWarn(@"Implicitly ending long-lived read transaction on connection %@, database %@",
1536
- self, database);
1537
-
1538
- [self endLongLivedReadTransaction];
1539
- }
1540
- }
1541
-
1542
- __preWriteQueue(self);
1543
- dispatch_sync(database->writeQueue, ^{ @autoreleasepool {
1544
-
1545
- YapDatabaseReadWriteTransaction *transaction = [self newReadWriteTransaction];
1546
-
1547
- [self preReadWriteTransaction:transaction];
1548
- block(transaction);
1549
- [self postReadWriteTransaction:transaction];
1550
-
1551
- if (completionBlock)
1552
- dispatch_async(completionQueue, completionBlock);
1553
-
1554
- }}); // End dispatch_sync(database->writeQueue)
1555
- __postWriteQueue(self);
1556
- }); // End dispatch_async(connectionQueue)
1557
- }
1558
-
1559
- ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
1560
- #pragma mark Transaction States
1561
- ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
1562
-
1563
- /**
1564
- * Required method.
1565
- * Returns the proper type of transaction for this connection class.
1566
- **/
1567
- - (YapDatabaseReadTransaction *)newReadTransaction
1568
- {
1569
- return [[YapDatabaseReadTransaction alloc] initWithConnection:self isReadWriteTransaction:NO];
1570
- }
1571
-
1572
- /**
1573
- * Required method.
1574
- * Returns the proper type of transaction for this connection class.
1575
- **/
1576
- - (YapDatabaseReadWriteTransaction *)newReadWriteTransaction
1577
- {
1578
- return [[YapDatabaseReadWriteTransaction alloc] initWithConnection:self isReadWriteTransaction:YES];
1579
- }
1580
-
1581
- /**
1582
- * This method executes the state transition steps required before executing a read-only transaction block.
1583
- *
1584
- * This method must be invoked from within the connectionQueue.
1585
- **/
1586
- - (void)preReadTransaction:(YapDatabaseReadTransaction *)transaction
1587
- {
1588
- // Pre-Read-Transaction: Step 1 of 3
1589
- //
1590
- // Execute "BEGIN TRANSACTION" on database connection.
1591
- // This is actually a deferred transaction, meaning the sqlite connection won't actually
1592
- // acquire a shared read lock until it executes a select statement.
1593
- // There are alternatives to this, including a "begin immediate transaction".
1594
- // However, this doesn't do what we want. Instead it blocks other read-only transactions.
1595
- // The deferred transaction is actually what we want, as many read-only transactions only
1596
- // hit our in-memory caches. Thus we avoid sqlite machinery when unneeded.
1597
-
1598
- [transaction beginTransaction];
1599
-
1600
- dispatch_sync(database->snapshotQueue, ^{ @autoreleasepool {
1601
-
1602
- // Pre-Read-Transaction: Step 2 of 3
1603
- //
1604
- // Update our connection state within the state table.
1605
- //
1606
- // First we need to mark this connection as being within a read-only transaction.
1607
- // We do this by marking a "yap-level" shared read lock flag.
1608
- //
1609
- // Now recall from step 1 that our "sql-level" transaction is deferred.
1610
- // The sql internals won't actually acquire the shared read lock until a we perform a select.
1611
- // If there are write transactions in progress, this is a big problem for us.
1612
- // Here's why:
1613
- //
1614
- // We have an in-memory snapshot via the caches.
1615
- // This is kept in-sync with what's on disk (in the sqlite database file).
1616
- // But what happens if the write transaction commits its changes before we perform our select statement?
1617
- // Our select statement would acquire a different snapshot than our in-memory snapshot.
1618
- // Thus, we look to see if there are any write transactions.
1619
- // If there are, then we immediately acquire the "sql-level" shared read lock.
1620
-
1621
- BOOL hasActiveWriteTransaction = NO;
1622
- YapDatabaseConnectionState *myState = nil;
1623
-
1624
- for (YapDatabaseConnectionState *state in database->connectionStates)
1625
- {
1626
- if (state->connection == self)
1627
- {
1628
- myState = state;
1629
- myState->yapLevelSharedReadLock = YES;
1630
- }
1631
- else if (state->yapLevelExclusiveWriteLock)
1632
- {
1633
- hasActiveWriteTransaction = YES;
1634
- }
1635
- }
1636
-
1637
- NSAssert(myState != nil, @"Missing state in database->connectionStates");
1638
-
1639
- // Pre-Read-Transaction: Step 3 of 3
1640
- //
1641
- // Update our in-memory data (caches, etc) if needed.
1642
-
1643
- if (hasActiveWriteTransaction || longLivedReadTransaction)
1644
- {
1645
- // If this is for a longLivedReadTransaction,
1646
- // then we need to immediately acquire a "sql-level" snapshot.
1647
- //
1648
- // Otherwise if there is a write transaction in progress,
1649
- // then it's not safe to proceed until we acquire a "sql-level" snapshot.
1650
- //
1651
- // During this process we need to ensure that our "yap-level" snapshot of the in-memory data (caches, etc)
1652
- // is in sync with our "sql-level" snapshot of the database.
1653
- //
1654
- // We can check this by comparing the connection's snapshot ivar with
1655
- // the snapshot read from disk (via sqlite select).
1656
- //
1657
- // If the two match then our snapshots are in sync.
1658
- // If they don't then we need to get caught up by processing changesets.
1659
-
1660
- uint64_t yapSnapshot = snapshot;
1661
- uint64_t sqlSnapshot = [self readSnapshotFromDatabase];
1662
-
1663
- if (yapSnapshot < sqlSnapshot)
1664
- {
1665
- // The transaction can see the sqlite commit from another transaction,
1666
- // and it hasn't processed the changeset(s) yet. We need to process them now.
1667
-
1668
- NSArray *changesets = [database pendingAndCommittedChangesSince:yapSnapshot until:sqlSnapshot];
1669
-
1670
- for (NSDictionary *changeset in changesets)
1671
- {
1672
- [self noteCommittedChanges:changeset];
1673
- }
1674
-
1675
- NSAssert(snapshot == sqlSnapshot,
1676
- @"Invalid connection state in preReadTransaction: snapshot(%llu) != sqlSnapshot(%llu): %@",
1677
- snapshot, sqlSnapshot, changesets);
1678
- }
1679
-
1680
- myState->lastKnownSnapshot = snapshot;
1681
- myState->longLivedReadTransaction = (longLivedReadTransaction != nil);
1682
- myState->sqlLevelSharedReadLock = YES;
1683
- needsMarkSqlLevelSharedReadLock = NO;
1684
- }
1685
- else
1686
- {
1687
- // There is NOT a write transaction in progress.
1688
- // Thus we are safe to proceed with only a "yap-level" snapshot.
1689
- //
1690
- // However, we MUST ensure that our "yap-level" snapshot of the in-memory data (caches, etc)
1691
- // are in sync with the rest of the system.
1692
- //
1693
- // That is, our connection may have started its transaction before it was
1694
- // able to process a changeset from a sibling connection.
1695
- // If this is the case then we need to get caught up by processing the changeset(s).
1696
-
1697
- uint64_t localSnapshot = snapshot;
1698
- uint64_t globalSnapshot = [database snapshot];
1699
-
1700
- if (localSnapshot < globalSnapshot)
1701
- {
1702
- // The transaction hasn't processed recent changeset(s) yet. We need to process them now.
1703
-
1704
- NSArray *changesets = [database pendingAndCommittedChangesSince:localSnapshot until:globalSnapshot];
1705
-
1706
- for (NSDictionary *changeset in changesets)
1707
- {
1708
- [self noteCommittedChanges:changeset];
1709
- }
1710
-
1711
- NSAssert(snapshot == globalSnapshot,
1712
- @"Invalid connection state in preReadTransaction: snapshot(%llu) != globalSnapshot(%llu): %@",
1713
- snapshot, globalSnapshot, changesets);
1714
- }
1715
-
1716
- myState->lastKnownSnapshot = snapshot;
1717
- myState->sqlLevelSharedReadLock = NO;
1718
- needsMarkSqlLevelSharedReadLock = YES;
1719
- }
1720
- }});
1721
- }
1722
-
1723
- /**
1724
- * This method executes the state transition steps required after executing a read-only transaction block.
1725
- *
1726
- * This method must be invoked from within the connectionQueue.
1727
- **/
1728
- - (void)postReadTransaction:(YapDatabaseReadTransaction *)transaction
1729
- {
1730
- // Post-Read-Transaction: Step 1 of 4
1731
- //
1732
- // 1. Execute "COMMIT TRANSACTION" on database connection.
1733
- // If we had acquired "sql-level" shared read lock, this will release associated resources.
1734
- // It may also free the auto-checkpointing architecture within sqlite to sync the WAL to the database.
1735
-
1736
- [transaction commitTransaction];
1737
-
1738
- __block uint64_t minSnapshot = 0;
1739
- __block YapDatabaseConnectionState *writeStateToSignal = nil;
1740
-
1741
- dispatch_sync(database->snapshotQueue, ^{ @autoreleasepool {
1742
-
1743
- // Post-Read-Transaction: Step 2 of 4
1744
- //
1745
- // Update our connection state within the state table.
1746
- //
1747
- // First we need to mark this connection as no longer being within a read-only transaction.
1748
- // We do this by unmarking the "yap-level" and "sql-level" shared read lock flags.
1749
- //
1750
- // While we're doing this we also check to see if we were possibly blocking a write transaction.
1751
- // When does a write transaction get blocked?
1752
- //
1753
- // Recall from the discussion above that we don't always acquire a "sql-level" shared read lock.
1754
- // Our sql transaction is deferred until our first select statement.
1755
- // Now if a write transaction comes along and discovers there are existing read transactions that
1756
- // have an in-memory metadata snapshot, but haven't acquired an "sql-level" snapshot of the actual
1757
- // database, it will block until these read transctions either complete,
1758
- // or acquire the needed "sql-level" snapshot.
1759
- //
1760
- // So if we never acquired an "sql-level" snapshot of the database, and we were the last transaction
1761
- // in such a state, and there's a blocked write transaction, then we need to signal it.
1762
-
1763
- minSnapshot = [database snapshot];
1764
-
1765
- BOOL wasMaybeBlockingWriteTransaction = NO;
1766
- NSUInteger countOtherMaybeBlockingWriteTransaction = 0;
1767
- YapDatabaseConnectionState *blockedWriteState = nil;
1768
-
1769
- for (YapDatabaseConnectionState *state in database->connectionStates)
1770
- {
1771
- if (state->connection == self)
1772
- {
1773
- wasMaybeBlockingWriteTransaction = state->yapLevelSharedReadLock && !state->sqlLevelSharedReadLock;
1774
- state->yapLevelSharedReadLock = NO;
1775
- state->sqlLevelSharedReadLock = NO;
1776
- state->longLivedReadTransaction = NO;
1777
- }
1778
- else if (state->yapLevelSharedReadLock)
1779
- {
1780
- // Active sibling connection: read-only
1781
-
1782
- minSnapshot = MIN(state->lastKnownSnapshot, minSnapshot);
1783
-
1784
- if (!state->sqlLevelSharedReadLock)
1785
- countOtherMaybeBlockingWriteTransaction++;
1786
- }
1787
- else if (state->yapLevelExclusiveWriteLock)
1788
- {
1789
- // Active sibling connection: read-write
1790
-
1791
- minSnapshot = MIN(state->lastKnownSnapshot, minSnapshot);
1792
-
1793
- if (state->waitingForWriteLock)
1794
- blockedWriteState = state;
1795
- }
1796
- }
1797
-
1798
- if (wasMaybeBlockingWriteTransaction && countOtherMaybeBlockingWriteTransaction == 0 && blockedWriteState)
1799
- {
1800
- writeStateToSignal = blockedWriteState;
1801
- }
1802
-
1803
- YDBLogVerbose(@"YapDatabaseConnection(%p) completing read-only transaction.", self);
1804
- }});
1805
-
1806
- // Post-Read-Transaction: Step 3 of 4
1807
- //
1808
- // Check to see if this connection has been holding back the checkpoint process.
1809
- // That is, was this connection the last active connection on an old snapshot?
1810
-
1811
- if (snapshot < minSnapshot)
1812
- {
1813
- // There are commits ahead of us that need to be checkpointed.
1814
- // And we were the oldest active connection,
1815
- // so we were previously preventing the checkpoint from progressing.
1816
- // Thus we can now continue the checkpoint operation.
1817
-
1818
- [database asyncCheckpoint:minSnapshot];
1819
-
1820
- [registeredTables enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) {
1821
-
1822
- [(YapMemoryTable *)obj asyncCheckpoint:minSnapshot];
1823
- }];
1824
- }
1825
-
1826
- // Post-Read-Transaction: Step 4 of 4
1827
- //
1828
- // If we discovered a blocked write transaction,
1829
- // and it was blocked waiting on us (because we had a "yap-level" snapshot without an "sql-level" snapshot),
1830
- // and it's no longer blocked on any other read transaction (that have "yap-level" snapshots
1831
- // without "sql-level snapshots"), then signal the write semaphore so the blocked thread wakes up.
1832
-
1833
- if (writeStateToSignal)
1834
- {
1835
- YDBLogVerbose(@"YapDatabaseConnection(%p) signaling blocked write on connection(%p)",
1836
- self, writeStateToSignal->connection);
1837
-
1838
- [writeStateToSignal signalWriteLock];
1839
- }
1840
- }
1841
-
1842
- /**
1843
- * This method executes the state transition steps required before executing a read-write transaction block.
1844
- *
1845
- * This method must be invoked from within the connectionQueue.
1846
- * This method must be invoked from within the database.writeQueue.
1847
- **/
1848
- - (void)preReadWriteTransaction:(YapDatabaseReadWriteTransaction *)transaction
1849
- {
1850
- // Pre-Write-Transaction: Step 1 of 5
1851
- //
1852
- // Execute "BEGIN TRANSACTION" on database connection.
1853
- // This is actually a deferred transaction, meaning the sqlite connection won't actually
1854
- // acquire any locks until it executes something.
1855
- // There are various alternatives to this, including a "immediate" and "exclusive" transactions.
1856
- // However, these don't do what we want. Instead they block other read-only transactions.
1857
- // The deferred transaction allows other read-only transactions and even avoids
1858
- // sqlite operations if no modifications are made.
1859
- //
1860
- // Remember, we are the only active write transaction for this database.
1861
- // No other write transactions can occur until this transaction completes.
1862
- // Thus no other transactions can possibly modify the database during our transaction.
1863
- // Therefore it doesn't matter when we acquire our "sql-level" locks for writing.
1864
-
1865
- [transaction beginTransaction];
1866
-
1867
- dispatch_sync(database->snapshotQueue, ^{ @autoreleasepool {
1868
-
1869
- // Pre-Write-Transaction: Step 2 of 5
1870
- //
1871
- // Update our connection state within the state table.
1872
- //
1873
- // We are the only write transaction for this database.
1874
- // It is important for read-only transactions on other connections to know there's a writer.
1875
-
1876
- YapDatabaseConnectionState *myState = nil;
1877
-
1878
- for (YapDatabaseConnectionState *state in database->connectionStates)
1879
- {
1880
- if (state->connection == self)
1881
- {
1882
- myState = state;
1883
- myState->yapLevelExclusiveWriteLock = YES;
1884
- }
1885
- }
1886
-
1887
- NSAssert(myState != nil, @"Missing state in database->connectionStates");
1888
-
1889
- // Pre-Write-Transaction: Step 3 of 5
1890
- //
1891
- // Validate our caches based on snapshot numbers
1892
-
1893
- uint64_t localSnapshot = snapshot;
1894
- uint64_t globalSnapshot = [database snapshot];
1895
-
1896
- if (localSnapshot < globalSnapshot)
1897
- {
1898
- NSArray *changesets = [database pendingAndCommittedChangesSince:localSnapshot until:globalSnapshot];
1899
-
1900
- for (NSDictionary *changeset in changesets)
1901
- {
1902
- [self noteCommittedChanges:changeset];
1903
- }
1904
-
1905
- NSAssert(snapshot == globalSnapshot,
1906
- @"Invalid connection state in preReadWriteTransaction: snapshot(%llu) != globalSnapshot(%llu)",
1907
- snapshot, globalSnapshot);
1908
- }
1909
-
1910
- myState->lastKnownSnapshot = snapshot;
1911
- needsMarkSqlLevelSharedReadLock = NO;
1912
-
1913
- YDBLogVerbose(@"YapDatabaseConnection(%p) starting read-write transaction.", self);
1914
- }});
1915
-
1916
- // Pre-Write-Transaction: Step 4 of 5
1917
- //
1918
- // Setup write state and changeset variables
1919
-
1920
- hasDiskChanges = NO;
1921
-
1922
- if (objectChanges == nil)
1923
- objectChanges = [[NSMutableDictionary alloc] init];
1924
-
1925
- if (metadataChanges == nil)
1926
- metadataChanges = [[NSMutableDictionary alloc] init];
1927
-
1928
- if (removedKeys == nil)
1929
- removedKeys = [[NSMutableSet alloc] init];
1930
-
1931
- if (removedCollections == nil)
1932
- removedCollections = [[NSMutableSet alloc] init];
1933
-
1934
- if (removedRowids == nil)
1935
- removedRowids = [[NSMutableSet alloc] init];
1936
-
1937
- allKeysRemoved = NO;
1938
-
1939
- // Pre-Write-Transaction: Step 5 of 5
1940
- //
1941
- // Add IsOnConnectionQueueKey flag to writeQueue.
1942
- // This allows various methods that depend on the flag to operate correctly.
1943
-
1944
- dispatch_queue_set_specific(database->writeQueue, IsOnConnectionQueueKey, IsOnConnectionQueueKey, NULL);
1945
- }
1946
-
1947
- /**
1948
- * This method executes the state transition steps required after executing a read-only transaction block.
1949
- *
1950
- * This method must be invoked from within the connectionQueue.
1951
- * This method must be invoked from within the database.writeQueue.
1952
- **/
1953
- - (void)postReadWriteTransaction:(YapDatabaseReadWriteTransaction *)transaction
1954
- {
1955
- if (transaction->rollback)
1956
- {
1957
- // Rollback-Write-Transaction: Step 1 of 2
1958
- //
1959
- // Update our connection state within the state table.
1960
- //
1961
- // We are the only write transaction for this database.
1962
- // It is important for read-only transactions on other connections to know we're no longer a writer.
1963
-
1964
- dispatch_sync(database->snapshotQueue, ^{
1965
-
1966
- for (YapDatabaseConnectionState *state in database->connectionStates)
1967
- {
1968
- if (state->connection == self)
1969
- {
1970
- state->yapLevelExclusiveWriteLock = NO;
1971
- break;
1972
- }
1973
- }
1974
- });
1975
-
1976
- // Rollback-Write-Transaction: Step 2 of 3
1977
- //
1978
- // Rollback sqlite database transaction.
1979
-
1980
- [transaction rollbackTransaction];
1981
-
1982
- // Rollback-Write-Transaction: Step 3 of 3
1983
- //
1984
- // Reset any in-memory variables which may be out-of-sync with the database.
1985
-
1986
- [self postRollbackCleanup];
1987
-
1988
- YDBLogVerbose(@"YapDatabaseConnection(%p) completing read-write transaction (rollback).", self);
1989
-
1990
- return;
1991
- }
1992
-
1993
- // Post-Write-Transaction: Step 1 of 11
1994
- //
1995
- // Run any pre-commit operations.
1996
- // This allows extensions to to perform any cleanup before the changeset is requested.
1997
-
1998
- [transaction preCommitReadWriteTransaction];
1999
-
2000
- // Post-Write-Transaction: Step 2 of 11
2001
- //
2002
- // Fetch changesets.
2003
- // Then update the snapshot in the 'yap' database (if any changes were made).
2004
- // We use 'yap' database and snapshot value to check for a race condition.
2005
- //
2006
- // The "internal" changeset gets sent directly to sibling database connections.
2007
- // The "external" changeset gets plugged into the YapDatabaseModifiedNotification as the userInfo dict.
2008
-
2009
- NSNotification *notification = nil;
2010
-
2011
- NSMutableDictionary *changeset = nil;
2012
- NSMutableDictionary *userInfo = nil;
2013
-
2014
- [self getInternalChangeset:&changeset externalChangeset:&userInfo];
2015
- if (changeset || userInfo || hasDiskChanges)
2016
- {
2017
- // If hasDiskChanges is YES, then the database file was modified.
2018
- // In this case, we're sure to write the incremented snapshot number to the database.
2019
- //
2020
- // If hasDiskChanges is NO, then the database file was not modified.
2021
- // However, something was "touched" or an in-memory extension was changed.
2022
-
2023
- if (hasDiskChanges)
2024
- snapshot = [self incrementSnapshotInDatabase];
2025
- else
2026
- snapshot++;
2027
-
2028
- if (changeset == nil)
2029
- changeset = [NSMutableDictionary dictionaryWithSharedKeySet:sharedKeySetForInternalChangeset];
2030
-
2031
- if (userInfo == nil)
2032
- userInfo = [NSMutableDictionary dictionaryWithSharedKeySet:sharedKeySetForExternalChangeset];
2033
-
2034
- [changeset setObject:@(snapshot) forKey:YapDatabaseSnapshotKey];
2035
- [userInfo setObject:@(snapshot) forKey:YapDatabaseSnapshotKey];
2036
-
2037
- [userInfo setObject:self forKey:YapDatabaseConnectionKey];
2038
-
2039
- if (transaction->customObjectForNotification)
2040
- [userInfo setObject:transaction->customObjectForNotification forKey:YapDatabaseCustomKey];
2041
-
2042
- notification = [NSNotification notificationWithName:YapDatabaseModifiedNotification
2043
- object:database
2044
- userInfo:userInfo];
2045
-
2046
- [changeset setObject:notification forKey:YapDatabaseNotificationKey];
2047
- }
2048
-
2049
- // Post-Write-Transaction: Step 3 of 11
2050
- //
2051
- // Auto-drop tables from previous extensions that aren't being used anymore.
2052
- //
2053
- // Note the timing of when this happens:
2054
- // - Only once
2055
- // - At the end of a readwrite transaction that has made modifications to the database
2056
- // - Only if the modifications weren't dedicated to registering/unregistring an extension
2057
-
2058
- if (database->previouslyRegisteredExtensionNames && changeset && !registeredExtensionsChanged)
2059
- {
2060
- for (NSString *prevExtensionName in database->previouslyRegisteredExtensionNames)
2061
- {
2062
- if ([registeredExtensions objectForKey:prevExtensionName] == nil)
2063
- {
2064
- [self _unregisterExtension:prevExtensionName withTransaction:transaction];
2065
- }
2066
- }
2067
-
2068
- database->previouslyRegisteredExtensionNames = nil;
2069
- }
2070
-
2071
- // Post-Write-Transaction: Step 4 of 11
2072
- //
2073
- // Check to see if it's safe to commit our changes.
2074
- //
2075
- // There may be read-only transactions that have acquired "yap-level" snapshots
2076
- // without "sql-level" snapshots. That is, these read-only transaction may have a snapshot
2077
- // of the in-memory metadata dictionary at the time they started, but as for the sqlite connection
2078
- // the only have a "BEGIN DEFERRED TRANSACTION", and haven't actually executed
2079
- // any "select" statements. Thus they haven't actually invoked the sqlite machinery to
2080
- // acquire the "sql-level" snapshot (last valid commit record in the WAL).
2081
- //
2082
- // It is our responsibility to block until all read-only transactions have either completed,
2083
- // or have acquired the necessary "sql-level" shared read lock.
2084
- //
2085
- // We avoid writer starvation by enforcing new read-only transactions that start after our writer
2086
- // started to immediately acquire "sql-level" shared read locks when they start.
2087
- // Thus we would only ever wait for read-only transactions that started before our
2088
- // read-write transaction started. And since most of the time the read-write transactions
2089
- // take longer than read-only transactions, we avoid any blocking in most cases.
2090
-
2091
- __block YapDatabaseConnectionState *myState = nil;
2092
- __block BOOL safeToCommit = NO;
2093
-
2094
- do
2095
- {
2096
- __block BOOL waitForReadOnlyTransactions = NO;
2097
-
2098
- dispatch_sync(database->snapshotQueue, ^{ @autoreleasepool {
2099
-
2100
- for (YapDatabaseConnectionState *state in database->connectionStates)
2101
- {
2102
- if (state->connection == self)
2103
- {
2104
- myState = state;
2105
- }
2106
- else if (state->yapLevelSharedReadLock && !state->sqlLevelSharedReadLock)
2107
- {
2108
- waitForReadOnlyTransactions = YES;
2109
- }
2110
- }
2111
-
2112
- NSAssert(myState != nil, @"Missing state in database->connectionStates");
2113
-
2114
- if (waitForReadOnlyTransactions)
2115
- {
2116
- myState->waitingForWriteLock = YES;
2117
- [myState prepareWriteLock];
2118
- }
2119
- else
2120
- {
2121
- myState->waitingForWriteLock = NO;
2122
- safeToCommit = YES;
2123
-
2124
- // Post-Write-Transaction: Step 5 of 11
2125
- //
2126
- // Register pending changeset with database.
2127
- // Our commit is actually a two step process.
2128
- // First we execute the sqlite level commit.
2129
- // Second we execute the final stages of the yap level commit.
2130
- //
2131
- // This two step process means we have an edge case,
2132
- // where another connection could come around and begin its yap level transaction
2133
- // before this connections yap level commit, but after this connections sqlite level commit.
2134
- //
2135
- // By registering the pending changeset in advance,
2136
- // we provide a near seamless workaround for the edge case.
2137
-
2138
- if (changeset)
2139
- {
2140
- [database notePendingChanges:changeset fromConnection:self];
2141
- }
2142
- }
2143
-
2144
- }});
2145
-
2146
- if (waitForReadOnlyTransactions)
2147
- {
2148
- // Block until a read-only transaction signals us.
2149
- // This will occur when the last read-only transaction (that started before our read-write
2150
- // transaction started) either completes or acquires an "sql-level" shared read lock.
2151
- //
2152
- // Note: Since we're using a dispatch semaphore, order doesn't matter.
2153
- // That is, it's fine if the read-only transaction signals our write lock before we start waiting on it.
2154
- // In this case we simply return immediately from the wait call.
2155
-
2156
- YDBLogVerbose(@"YapDatabaseConnection(%p) blocked waiting for write lock...", self);
2157
-
2158
- [myState waitForWriteLock];
2159
- }
2160
-
2161
- } while (!safeToCommit);
2162
-
2163
- // Post-Write-Transaction: Step 6 of 11
2164
- //
2165
- // Execute "COMMIT TRANSACTION" on database connection.
2166
- // This will write the changes to the WAL, and may invoke a checkpoint.
2167
- //
2168
- // Notice that we do this outside the context of the transactionStateQueue.
2169
- // We do this so we don't block read-only transactions from starting or finishing.
2170
- // However, this does leave us open for the possibility that a read-only transaction will
2171
- // get a "yap-level" snapshot of the metadata dictionary before this commit,
2172
- // but a "sql-level" snapshot of the sql database after this commit.
2173
- // This is rare but must be guarded against.
2174
- // The solution is pretty simple and straight-forward.
2175
- // When a read-only transaction starts, if there's an active write transaction,
2176
- // it immediately acquires an "sql-level" snapshot. It does this by invoking a select statement,
2177
- // which invokes the internal sqlite snapshot machinery for the transaction.
2178
- // So rather than using a dummy select statement that we ignore, we instead select a lastCommit number
2179
- // from the database. If it doesn't match what we expect, then we know we've run into the race condition,
2180
- // and we make the read-only transaction back out and try again.
2181
-
2182
- [transaction commitTransaction];
2183
-
2184
- __block uint64_t minSnapshot = UINT64_MAX;
2185
-
2186
- dispatch_sync(database->snapshotQueue, ^{ @autoreleasepool {
2187
-
2188
- // Post-Write-Transaction: Step 7 of 11
2189
- //
2190
- // Notify database of changes, and drop reference to set of changed keys.
2191
-
2192
- if (changeset)
2193
- {
2194
- [database noteCommittedChanges:changeset fromConnection:self];
2195
- }
2196
-
2197
- // Post-Write-Transaction: Step 8 of 11
2198
- //
2199
- // Update our connection state within the state table.
2200
- //
2201
- // We are the only write transaction for this database.
2202
- // It is important for read-only transactions on other connections to know we're no longer a writer.
2203
-
2204
- for (YapDatabaseConnectionState *state in database->connectionStates)
2205
- {
2206
- if (state->yapLevelSharedReadLock)
2207
- {
2208
- minSnapshot = MIN(state->lastKnownSnapshot, minSnapshot);
2209
- }
2210
- }
2211
-
2212
- myState->yapLevelExclusiveWriteLock = NO;
2213
- myState->waitingForWriteLock = NO;
2214
-
2215
- YDBLogVerbose(@"YapDatabaseConnection(%p) completing read-write transaction.", self);
2216
- }});
2217
-
2218
- // Post-Write-Transaction: Step 9 of 11
2219
-
2220
- if (changeset)
2221
- {
2222
- // We added frames to the WAL.
2223
- // We can invoke a checkpoint if there are no other active connections.
2224
-
2225
- if (minSnapshot == UINT64_MAX)
2226
- {
2227
- [database asyncCheckpoint:snapshot];
2228
-
2229
- [registeredTables enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) {
2230
-
2231
- [(YapMemoryTable *)obj asyncCheckpoint:snapshot];
2232
- }];
2233
- }
2234
- }
2235
-
2236
- // Post-Write-Transaction: Step 10 of 11
2237
- //
2238
- // Post YapDatabaseModifiedNotification (if needed),
2239
- // and clear changeset variables (which are now a part of the notification).
2240
-
2241
- if (notification)
2242
- {
2243
- dispatch_async(dispatch_get_main_queue(), ^{
2244
- [[NSNotificationCenter defaultCenter] postNotification:notification];
2245
- });
2246
- }
2247
-
2248
- if ([objectChanges count] > 0)
2249
- objectChanges = nil;
2250
-
2251
- if ([metadataChanges count] > 0)
2252
- metadataChanges = nil;
2253
-
2254
- if ([removedKeys count] > 0)
2255
- removedKeys = nil;
2256
-
2257
- if ([removedCollections count] > 0)
2258
- removedCollections = nil;
2259
-
2260
- if ([removedRowids count] > 0)
2261
- removedRowids = nil;
2262
-
2263
- // Post-Write-Transaction: Step 11 of 11
2264
- //
2265
- // Drop IsOnConnectionQueueKey flag from writeQueue since we're exiting writeQueue.
2266
-
2267
- dispatch_queue_set_specific(database->writeQueue, IsOnConnectionQueueKey, NULL, NULL);
2268
- }
2269
-
2270
- /**
2271
- * This method "kills two birds with one stone".
2272
- *
2273
- * First, it invokes a SELECT statement on the database.
2274
- * This executes the sqlite machinery to acquire a "sql-level" snapshot of the database.
2275
- * That is, the encompassing transaction will now reference a specific commit record in the WAL,
2276
- * and will ignore any commits made after this record.
2277
- *
2278
- * Second, it reads a specific value from the database, and tells us which commit record in the WAL its using.
2279
- * This allows us to validate the transaction, and check for a particular race condition.
2280
- **/
2281
- - (uint64_t)readSnapshotFromDatabase
2282
- {
2283
- sqlite3_stmt *statement = [self yapGetDataForKeyStatement];
2284
- if (statement == NULL) return 0;
2285
-
2286
- uint64_t result = 0;
2287
-
2288
- // SELECT data FROM 'yap2' WHERE extension = ? AND key = ? ;
2289
-
2290
- char *extension = "";
2291
- sqlite3_bind_text(statement, 1, extension, (int)strlen(extension), SQLITE_STATIC);
2292
-
2293
- char *key = "snapshot";
2294
- sqlite3_bind_text(statement, 2, key, (int)strlen(key), SQLITE_STATIC);
2295
-
2296
- int status = sqlite3_step(statement);
2297
- if (status == SQLITE_ROW)
2298
- {
2299
- result = (uint64_t)sqlite3_column_int64(statement, 0);
2300
- }
2301
- else if (status == SQLITE_ERROR)
2302
- {
2303
- YDBLogError(@"Error executing 'yapGetDataForKeyStatement': %d %s",
2304
- status, sqlite3_errmsg(db));
2305
- }
2306
-
2307
- sqlite3_clear_bindings(statement);
2308
- sqlite3_reset(statement);
2309
-
2310
- return result;
2311
- }
2312
-
2313
- /**
2314
- * This method updates the 'snapshot' row in the database.
2315
- **/
2316
- - (uint64_t)incrementSnapshotInDatabase
2317
- {
2318
- uint64_t newSnapshot = snapshot + 1;
2319
-
2320
- sqlite3_stmt *statement = [self yapSetDataForKeyStatement];
2321
- if (statement == NULL) return newSnapshot;
2322
-
2323
- // INSERT OR REPLACE INTO "yap2" ("extension", "key", "data") VALUES (?, ?, ?);
2324
-
2325
- char *extension = "";
2326
- sqlite3_bind_text(statement, 1, extension, (int)strlen(extension), SQLITE_STATIC);
2327
-
2328
- char *key = "snapshot";
2329
- sqlite3_bind_text(statement, 2, key, (int)strlen(key), SQLITE_STATIC);
2330
-
2331
- sqlite3_bind_int64(statement, 3, (sqlite3_int64)newSnapshot);
2332
-
2333
- int status = sqlite3_step(statement);
2334
- if (status != SQLITE_DONE)
2335
- {
2336
- YDBLogError(@"Error executing 'yapSetDataForKeyStatement': %d %s",
2337
- status, sqlite3_errmsg(db));
2338
- }
2339
-
2340
- sqlite3_clear_bindings(statement);
2341
- sqlite3_reset(statement);
2342
-
2343
- return newSnapshot;
2344
- }
2345
-
2346
- - (void)markSqlLevelSharedReadLockAcquired
2347
- {
2348
- NSAssert(needsMarkSqlLevelSharedReadLock, @"Method called but unneeded. Unnecessary overhead.");
2349
- if (!needsMarkSqlLevelSharedReadLock) return;
2350
-
2351
- __block YapDatabaseConnectionState *writeStateToSignal = nil;
2352
-
2353
- dispatch_sync(database->snapshotQueue, ^{ @autoreleasepool {
2354
-
2355
- // Update our connection state within the state table.
2356
- //
2357
- // We need to mark this connection as having acquired an "sql-level" shared read lock.
2358
- // That is, our sqlite connection has invoked a select statement, and has thus invoked the sqlite
2359
- // machinery that causes it to acquire the "sql-level" snapshot (last valid commit record in the WAL).
2360
- //
2361
- // While we're doing this we also check to see if we were possibly blocking a write transaction.
2362
- // When does a write transaction get blocked?
2363
- //
2364
- // If a write transaction goes to commit its changes and sees a read-only transaction with
2365
- // a "yap-level" snapshot of the in-memory metadata snapshot, but without an "sql-level" snapshot
2366
- // of the actual database, it will block until these read transctions either complete,
2367
- // or acquire the needed "sql-level" snapshot.
2368
- //
2369
- // So if we never acquired an "sql-level" snapshot of the database, and we were the last transaction
2370
- // in such a state, and there's a blocked write transaction, then we need to signal it.
2371
-
2372
- __block NSUInteger countOtherMaybeBlockingWriteTransaction = 0;
2373
- __block YapDatabaseConnectionState *blockedWriteState = nil;
2374
-
2375
- for (YapDatabaseConnectionState *state in database->connectionStates)
2376
- {
2377
- if (state->connection == self)
2378
- {
2379
- state->sqlLevelSharedReadLock = YES;
2380
- }
2381
- else if (state->yapLevelSharedReadLock && !state->sqlLevelSharedReadLock)
2382
- {
2383
- countOtherMaybeBlockingWriteTransaction++;
2384
- }
2385
- else if (state->waitingForWriteLock)
2386
- {
2387
- blockedWriteState = state;
2388
- }
2389
- }
2390
-
2391
- if (countOtherMaybeBlockingWriteTransaction == 0 && blockedWriteState)
2392
- {
2393
- writeStateToSignal = blockedWriteState;
2394
- }
2395
- }});
2396
-
2397
- needsMarkSqlLevelSharedReadLock = NO;
2398
-
2399
- if (writeStateToSignal)
2400
- {
2401
- YDBLogVerbose(@"YapDatabaseConnection(%p) signaling blocked write on connection(%p)",
2402
- self, writeStateToSignal->connection);
2403
- [writeStateToSignal signalWriteLock];
2404
- }
2405
- }
2406
-
2407
- /**
2408
- * This method is invoked after a read-write transaction completes, which was rolled-back.
2409
- * You should flush anything from memory that may be out-of-sync with the database.
2410
- **/
2411
- - (void)postRollbackCleanup
2412
- {
2413
- [objectCache removeAllObjects];
2414
- [metadataCache removeAllObjects];
2415
-
2416
- if ([objectChanges count] > 0)
2417
- objectChanges = nil;
2418
-
2419
- if ([metadataChanges count] > 0)
2420
- metadataChanges = nil;
2421
-
2422
- if ([removedKeys count] > 0)
2423
- removedKeys = nil;
2424
-
2425
- if ([removedCollections count] > 0)
2426
- removedCollections = nil;
2427
-
2428
- if ([removedRowids count] > 0)
2429
- removedRowids = nil;
2430
- }
2431
-
2432
- ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
2433
- #pragma mark Long-Lived Transactions
2434
- ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
2435
-
2436
- - (NSArray *)beginLongLivedReadTransaction
2437
- {
2438
- __block NSMutableArray *notifications = nil;
2439
-
2440
- dispatch_block_t block = ^{ @autoreleasepool {
2441
-
2442
- if (longLivedReadTransaction)
2443
- {
2444
- // Caller using implicit atomic reBeginLongLivedReadTransaction
2445
- notifications = (NSMutableArray *)[self endLongLivedReadTransaction];
2446
- }
2447
-
2448
- longLivedReadTransaction = [self newReadTransaction];
2449
- [self preReadTransaction:longLivedReadTransaction];
2450
-
2451
- // The preReadTransaction method acquires the "sqlite-level" snapshot.
2452
- // In doing so, if it needs to fetch and process any changesets,
2453
- // then it adds them to the processedChangesets ivar for us.
2454
-
2455
- if (notifications == nil)
2456
- notifications = [NSMutableArray arrayWithCapacity:[processedChangesets count]];
2457
-
2458
- for (NSDictionary *changeset in processedChangesets)
2459
- {
2460
- // The changeset has already been processed.
2461
-
2462
- NSNotification *notification = [changeset objectForKey:YapDatabaseNotificationKey];
2463
- if (notification) {
2464
- [notifications addObject:notification];
2465
- }
2466
- }
2467
-
2468
- [processedChangesets removeAllObjects];
2469
- }};
2470
-
2471
- if (dispatch_get_specific(IsOnConnectionQueueKey))
2472
- block();
2473
- else
2474
- dispatch_sync(connectionQueue, block);
2475
-
2476
- return notifications;
2477
- }
2478
-
2479
- - (NSArray *)endLongLivedReadTransaction
2480
- {
2481
- __block NSMutableArray *notifications = nil;
2482
-
2483
- dispatch_block_t block = ^{ @autoreleasepool {
2484
-
2485
- if (longLivedReadTransaction)
2486
- {
2487
- // End the transaction (sqlite commit)
2488
-
2489
- [self postReadTransaction:longLivedReadTransaction];
2490
- longLivedReadTransaction = nil;
2491
-
2492
- // Now process any changesets that were pending.
2493
- // And extract the corresponding external notifications to return the the caller.
2494
-
2495
- notifications = [NSMutableArray arrayWithCapacity:[pendingChangesets count]];
2496
-
2497
- for (NSDictionary *changeset in pendingChangesets)
2498
- {
2499
- [self noteCommittedChanges:changeset];
2500
-
2501
- NSNotification *notification = [changeset objectForKey:YapDatabaseNotificationKey];
2502
- if (notification) {
2503
- [notifications addObject:notification];
2504
- }
2505
- }
2506
-
2507
- [pendingChangesets removeAllObjects];
2508
- }
2509
- }};
2510
-
2511
- if (dispatch_get_specific(IsOnConnectionQueueKey))
2512
- block();
2513
- else
2514
- dispatch_sync(connectionQueue, block);
2515
-
2516
- return notifications;
2517
- }
2518
-
2519
- - (BOOL)isInLongLivedReadTransaction
2520
- {
2521
- __block BOOL result = NO;
2522
-
2523
- dispatch_block_t block = ^{
2524
-
2525
- result = (longLivedReadTransaction != nil);
2526
- };
2527
-
2528
- if (dispatch_get_specific(IsOnConnectionQueueKey))
2529
- block();
2530
- else
2531
- dispatch_sync(connectionQueue, block);
2532
-
2533
- return result;
2534
- }
2535
-
2536
- - (void)enableExceptionsForImplicitlyEndingLongLivedReadTransaction
2537
- {
2538
- dispatch_block_t block = ^{
2539
-
2540
- throwExceptionsForImplicitlyEndingLongLivedReadTransaction = YES;
2541
- };
2542
-
2543
- if (dispatch_get_specific(IsOnConnectionQueueKey))
2544
- block();
2545
- else
2546
- dispatch_async(connectionQueue, block);
2547
- }
2548
-
2549
- - (void)disableExceptionsForImplicitlyEndingLongLivedReadTransaction
2550
- {
2551
- dispatch_block_t block = ^{
2552
-
2553
- throwExceptionsForImplicitlyEndingLongLivedReadTransaction = NO;
2554
- };
2555
-
2556
- if (dispatch_get_specific(IsOnConnectionQueueKey))
2557
- block();
2558
- else
2559
- dispatch_async(connectionQueue, block);
2560
- }
2561
-
2562
- ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
2563
- #pragma mark Changeset Architecture
2564
- ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
2565
-
2566
- /**
2567
- * The creation of changeset dictionaries happens constantly.
2568
- * So, to optimize a bit, we use sharedKeySet's (part of NSDictionary).
2569
- *
2570
- * See ivar 'sharedKeySetForInternalChangeset'
2571
- **/
2572
- - (NSArray *)internalChangesetKeys
2573
- {
2574
- return @[ YapDatabaseSnapshotKey,
2575
- YapDatabaseExtensionsKey,
2576
- YapDatabaseRegisteredExtensionsKey,
2577
- YapDatabaseRegisteredTablesKey,
2578
- YapDatabaseExtensionsOrderKey,
2579
- YapDatabaseExtensionDependenciesKey,
2580
- YapDatabaseNotificationKey,
2581
- YapDatabaseObjectChangesKey,
2582
- YapDatabaseMetadataChangesKey,
2583
- YapDatabaseRemovedKeysKey,
2584
- YapDatabaseRemovedCollectionsKey,
2585
- YapDatabaseRemovedRowidsKey,
2586
- YapDatabaseAllKeysRemovedKey ];
2587
- }
2588
-
2589
- /**
2590
- * The creation of changeset dictionaries happens constantly.
2591
- * So, to optimize a bit, we use sharedKeySet's (part of NSDictionary).
2592
- *
2593
- * See ivar 'sharedKeySetForExternalChangeset'
2594
- **/
2595
- - (NSArray *)externalChangesetKeys
2596
- {
2597
- return @[ YapDatabaseSnapshotKey,
2598
- YapDatabaseConnectionKey,
2599
- YapDatabaseExtensionsKey,
2600
- YapDatabaseCustomKey,
2601
- YapDatabaseObjectChangesKey,
2602
- YapDatabaseMetadataChangesKey,
2603
- YapDatabaseRemovedKeysKey,
2604
- YapDatabaseRemovedCollectionsKey,
2605
- YapDatabaseAllKeysRemovedKey ];
2606
- }
2607
-
2608
- /**
2609
- * This method is invoked from within the postReadWriteTransaction operation.
2610
- * This method is invoked before anything has been committed.
2611
- *
2612
- * If changes have been made, it should return a changeset dictionary.
2613
- * If no changes have been made, it should return nil.
2614
- *
2615
- * @see processChangeset:
2616
- **/
2617
- - (void)getInternalChangeset:(NSMutableDictionary **)internalChangesetPtr
2618
- externalChangeset:(NSMutableDictionary **)externalChangesetPtr
2619
- {
2620
- // Step 1 of 2 - Process extensions
2621
- //
2622
- // Note: Use existing extensions (extensions ivar, not [self extensions]).
2623
- // There's no need to create any new extConnections at this point.
2624
-
2625
- __block NSMutableDictionary *internalChangeset_extensions = nil;
2626
- __block NSMutableDictionary *externalChangeset_extensions = nil;
2627
-
2628
- [extensions enumerateKeysAndObjectsUsingBlock:^(id extName, id extConnectionObj, BOOL *stop) {
2629
-
2630
- __unsafe_unretained YapDatabaseExtensionConnection *extConnection = extConnectionObj;
2631
-
2632
- NSMutableDictionary *internal = nil;
2633
- NSMutableDictionary *external = nil;
2634
- BOOL extHasDiskChanges = NO;
2635
-
2636
- [extConnection getInternalChangeset:&internal
2637
- externalChangeset:&external
2638
- hasDiskChanges:&extHasDiskChanges];
2639
-
2640
- if (internal)
2641
- {
2642
- if (internalChangeset_extensions == nil)
2643
- internalChangeset_extensions =
2644
- [NSMutableDictionary dictionaryWithSharedKeySet:sharedKeySetForExtensions];
2645
-
2646
- [internalChangeset_extensions setObject:internal forKey:extName];
2647
- }
2648
- if (external)
2649
- {
2650
- if (externalChangeset_extensions == nil)
2651
- externalChangeset_extensions =
2652
- [NSMutableDictionary dictionaryWithSharedKeySet:sharedKeySetForExtensions];
2653
-
2654
- [externalChangeset_extensions setObject:external forKey:extName];
2655
- }
2656
- if (extHasDiskChanges && !hasDiskChanges)
2657
- {
2658
- hasDiskChanges = YES;
2659
- }
2660
- }];
2661
-
2662
- NSMutableDictionary *internalChangeset = nil;
2663
- NSMutableDictionary *externalChangeset = nil;
2664
-
2665
- if (internalChangeset_extensions)
2666
- {
2667
- internalChangeset = [NSMutableDictionary dictionaryWithSharedKeySet:sharedKeySetForInternalChangeset];
2668
- [internalChangeset setObject:internalChangeset_extensions forKey:YapDatabaseExtensionsKey];
2669
- }
2670
-
2671
- if (externalChangeset_extensions)
2672
- {
2673
- externalChangeset = [NSMutableDictionary dictionaryWithSharedKeySet:sharedKeySetForExternalChangeset];
2674
- [externalChangeset setObject:externalChangeset_extensions forKey:YapDatabaseExtensionsKey];
2675
- }
2676
-
2677
- if (registeredExtensionsChanged)
2678
- {
2679
- if (internalChangeset == nil)
2680
- internalChangeset = [NSMutableDictionary dictionaryWithSharedKeySet:sharedKeySetForInternalChangeset];
2681
-
2682
- [internalChangeset setObject:registeredExtensions forKey:YapDatabaseRegisteredExtensionsKey];
2683
- [internalChangeset setObject:extensionsOrder forKey:YapDatabaseExtensionsOrderKey];
2684
- [internalChangeset setObject:extensionDependencies forKey:YapDatabaseExtensionDependenciesKey];
2685
- }
2686
-
2687
- if (registeredTablesChanged)
2688
- {
2689
- if (internalChangeset == nil)
2690
- internalChangeset = [NSMutableDictionary dictionaryWithSharedKeySet:sharedKeySetForInternalChangeset];
2691
-
2692
- [internalChangeset setObject:registeredTables forKey:YapDatabaseRegisteredTablesKey];
2693
- }
2694
-
2695
- // Step 2 of 2 - Process database changes
2696
- //
2697
- // Throughout the readwrite transaction we've been keeping a list of what changed.
2698
- // Copy this change information into the changeset for processing by other connections.
2699
-
2700
- if ([objectChanges count] > 0 ||
2701
- [metadataChanges count] > 0 ||
2702
- [removedKeys count] > 0 ||
2703
- [removedCollections count] > 0 ||
2704
- [removedRowids count] > 0 || allKeysRemoved)
2705
- {
2706
- if (internalChangeset == nil)
2707
- internalChangeset = [NSMutableDictionary dictionaryWithSharedKeySet:sharedKeySetForInternalChangeset];
2708
-
2709
- if (externalChangeset == nil)
2710
- externalChangeset = [NSMutableDictionary dictionaryWithSharedKeySet:sharedKeySetForExternalChangeset];
2711
-
2712
- if ([objectChanges count] > 0)
2713
- {
2714
- [internalChangeset setObject:objectChanges forKey:YapDatabaseObjectChangesKey];
2715
-
2716
- YapSet *immutableObjectChanges = [[YapSet alloc] initWithDictionary:objectChanges];
2717
- [externalChangeset setObject:immutableObjectChanges forKey:YapDatabaseObjectChangesKey];
2718
- }
2719
-
2720
- if ([metadataChanges count] > 0)
2721
- {
2722
- [internalChangeset setObject:metadataChanges forKey:YapDatabaseMetadataChangesKey];
2723
-
2724
- YapSet *immutableMetadataChanges = [[YapSet alloc] initWithDictionary:metadataChanges];
2725
- [externalChangeset setObject:immutableMetadataChanges forKey:YapDatabaseMetadataChangesKey];
2726
- }
2727
-
2728
- if ([removedKeys count] > 0)
2729
- {
2730
- [internalChangeset setObject:removedKeys forKey:YapDatabaseRemovedKeysKey];
2731
-
2732
- YapSet *immutableRemovedKeys = [[YapSet alloc] initWithSet:removedKeys];
2733
- [externalChangeset setObject:immutableRemovedKeys forKey:YapDatabaseRemovedKeysKey];
2734
- }
2735
-
2736
- if ([removedCollections count] > 0)
2737
- {
2738
- [internalChangeset setObject:removedCollections forKey:YapDatabaseRemovedCollectionsKey];
2739
-
2740
- YapSet *immutableRemovedCollections = [[YapSet alloc] initWithSet:removedCollections];
2741
- [externalChangeset setObject:immutableRemovedCollections
2742
- forKey:YapDatabaseRemovedCollectionsKey];
2743
- }
2744
-
2745
- if ([removedRowids count] > 0)
2746
- {
2747
- [internalChangeset setObject:removedRowids forKey:YapDatabaseRemovedRowidsKey];
2748
- }
2749
-
2750
- if (allKeysRemoved)
2751
- {
2752
- [internalChangeset setObject:@(YES) forKey:YapDatabaseAllKeysRemovedKey];
2753
- [externalChangeset setObject:@(YES) forKey:YapDatabaseAllKeysRemovedKey];
2754
- }
2755
- }
2756
-
2757
- *internalChangesetPtr = internalChangeset;
2758
- *externalChangesetPtr = externalChangeset;
2759
- }
2760
-
2761
- /**
2762
- * This method is invoked with the changeset from a sibling connection.
2763
- * The connection should update any in-memory components (such as the cache) to properly reflect the changeset.
2764
- *
2765
- * @see getInternalChangeset:externalChangeset:
2766
- **/
2767
- - (void)processChangeset:(NSDictionary *)changeset
2768
- {
2769
- // Did registered extensions change ?
2770
-
2771
- NSDictionary *changeset_registeredExtensions = [changeset objectForKey:YapDatabaseRegisteredExtensionsKey];
2772
- if (changeset_registeredExtensions)
2773
- {
2774
- // Retain new lists
2775
-
2776
- registeredExtensions = changeset_registeredExtensions;
2777
- extensionsOrder = [changeset objectForKey:YapDatabaseExtensionsOrderKey];
2778
- extensionDependencies = [changeset objectForKey:YapDatabaseExtensionDependenciesKey];
2779
-
2780
- // Remove any extensions that have been dropped
2781
-
2782
- for (NSString *extName in [extensions allKeys])
2783
- {
2784
- if ([registeredExtensions objectForKey:extName] == nil)
2785
- {
2786
- YDBLogVerbose(@"Dropping extension: %@", extName);
2787
-
2788
- [extensions removeObjectForKey:extName];
2789
- }
2790
- }
2791
-
2792
- // Make a note if there are extensions for which we haven't instantiated an extConnection instance.
2793
- // We lazily load these later, if needed.
2794
-
2795
- extensionsReady = ([registeredExtensions count] == [extensions count]);
2796
- }
2797
-
2798
- // Did registered memory tables change ?
2799
-
2800
- NSDictionary *changeset_registeredTables = [changeset objectForKey:YapDatabaseRegisteredTablesKey];
2801
- if (changeset_registeredTables)
2802
- {
2803
- // Retain new list
2804
- registeredTables = changeset_registeredTables;
2805
- }
2806
-
2807
- // Allow extensions to process their individual changesets
2808
-
2809
- NSDictionary *changeset_extensions = [changeset objectForKey:YapDatabaseExtensionsKey];
2810
- if (changeset_extensions)
2811
- {
2812
- // Use existing extensions (extensions ivar, not [self extensions]).
2813
- // There's no need to create any new extConnections at this point.
2814
-
2815
- [extensions enumerateKeysAndObjectsUsingBlock:^(id extName, id extConnectionObj, BOOL *stop) {
2816
-
2817
- __unsafe_unretained YapDatabaseExtensionConnection *extConnection = extConnectionObj;
2818
-
2819
- NSDictionary *changeset_extensions_extName = [changeset_extensions objectForKey:extName];
2820
- if (changeset_extensions_extName)
2821
- {
2822
- [extConnection processChangeset:changeset_extensions_extName];
2823
- }
2824
- }];
2825
- }
2826
-
2827
- // Process normal database changset information
2828
-
2829
- NSDictionary *changeset_objectChanges = [changeset objectForKey:YapDatabaseObjectChangesKey];
2830
- NSDictionary *changeset_metadataChanges = [changeset objectForKey:YapDatabaseMetadataChangesKey];
2831
-
2832
- NSSet *changeset_removedRowids = [changeset objectForKey:YapDatabaseRemovedRowidsKey];
2833
- NSSet *changeset_removedKeys = [changeset objectForKey:YapDatabaseRemovedKeysKey];
2834
- NSSet *changeset_removedCollections = [changeset objectForKey:YapDatabaseRemovedCollectionsKey];
2835
-
2836
- BOOL changeset_allKeysRemoved = [[changeset objectForKey:YapDatabaseAllKeysRemovedKey] boolValue];
2837
-
2838
- BOOL hasObjectChanges = [changeset_objectChanges count] > 0;
2839
- BOOL hasMetadataChanges = [changeset_metadataChanges count] > 0;
2840
- BOOL hasRemovedKeys = [changeset_removedKeys count] > 0;
2841
- BOOL hasRemovedCollections = [changeset_removedCollections count] > 0;
2842
-
2843
- // Update keyCache
2844
-
2845
- if (changeset_allKeysRemoved)
2846
- {
2847
- // Shortcut: Everything was removed from the database
2848
-
2849
- [keyCache removeAllObjects];
2850
- }
2851
- else
2852
- {
2853
- if (hasRemovedCollections)
2854
- {
2855
- __block NSMutableArray *toRemove = nil;
2856
- [keyCache enumerateKeysAndObjectsWithBlock:^(id key, id obj, BOOL *stop) {
2857
-
2858
- __unsafe_unretained NSNumber *rowidNumber = (NSNumber *)key;
2859
- __unsafe_unretained YapCollectionKey *collectionKey = (YapCollectionKey *)obj;
2860
-
2861
- if ([changeset_removedCollections containsObject:collectionKey.collection])
2862
- {
2863
- if (toRemove == nil)
2864
- toRemove = [NSMutableArray array];
2865
-
2866
- [toRemove addObject:rowidNumber];
2867
- }
2868
- }];
2869
-
2870
- [keyCache removeObjectsForKeys:toRemove];
2871
- }
2872
-
2873
- if (changeset_removedRowids)
2874
- {
2875
- [changeset_removedRowids enumerateObjectsUsingBlock:^(id obj, BOOL *stop) {
2876
-
2877
- [keyCache removeObjectForKey:obj];
2878
- }];
2879
- }
2880
- }
2881
-
2882
- // Update objectCache
2883
-
2884
- if (changeset_allKeysRemoved && !hasObjectChanges)
2885
- {
2886
- // Shortcut: Everything was removed from the database
2887
-
2888
- [objectCache removeAllObjects];
2889
- }
2890
- else if (hasObjectChanges && !hasRemovedKeys && !hasRemovedCollections && !changeset_allKeysRemoved)
2891
- {
2892
- // Shortcut: Nothing was removed from the database.
2893
- // So we can simply enumerate over the changes and update the cache inline as needed.
2894
-
2895
- id yapNull = [YapNull null]; // value == yapNull : setPrimitive or containment policy
2896
- id yapTouch = [YapTouch touch]; // value == yapTouch : touchObjectForKey: was used
2897
-
2898
- BOOL isPolicyContainment = (objectPolicy == YapDatabasePolicyContainment);
2899
- BOOL isPolicyShare = (objectPolicy == YapDatabasePolicyShare);
2900
-
2901
- [changeset_objectChanges enumerateKeysAndObjectsUsingBlock:^(id key, id newObject, BOOL *stop) {
2902
-
2903
- __unsafe_unretained YapCollectionKey *cacheKey = (YapCollectionKey *)key;
2904
-
2905
- if ([objectCache containsKey:cacheKey])
2906
- {
2907
- if (newObject == yapNull)
2908
- {
2909
- [objectCache removeObjectForKey:cacheKey];
2910
- }
2911
- else if (newObject != yapTouch)
2912
- {
2913
- if (isPolicyContainment) {
2914
- [objectCache removeObjectForKey:cacheKey];
2915
- }
2916
- else if (isPolicyShare) {
2917
- [objectCache setObject:newObject forKey:cacheKey];
2918
- }
2919
- else // if (isPolicyCopy)
2920
- {
2921
- if ([newObject conformsToProtocol:@protocol(NSCopying)])
2922
- [objectCache setObject:[newObject copy] forKey:cacheKey];
2923
- else
2924
- [objectCache removeObjectForKey:cacheKey];
2925
- }
2926
- }
2927
- }
2928
- }];
2929
- }
2930
- else if (hasObjectChanges || hasRemovedKeys || hasRemovedCollections)
2931
- {
2932
- NSUInteger updateCapacity = MIN([objectCache count], [changeset_objectChanges count]);
2933
- NSUInteger removeCapacity = MIN([objectCache count], [changeset_removedKeys count]);
2934
-
2935
- NSMutableArray *keysToUpdate = [NSMutableArray arrayWithCapacity:updateCapacity];
2936
- NSMutableArray *keysToRemove = [NSMutableArray arrayWithCapacity:removeCapacity];
2937
-
2938
- [objectCache enumerateKeysWithBlock:^(id key, BOOL *stop) {
2939
-
2940
- // Order matters.
2941
- // Consider the following database change:
2942
- //
2943
- // [transaction removeAllObjectsInAllCollections];
2944
- // [transaction setObject:obj forKey:key inCollection:collection];
2945
-
2946
- __unsafe_unretained YapCollectionKey *cacheKey = (YapCollectionKey *)key;
2947
-
2948
- if ([changeset_objectChanges objectForKey:cacheKey])
2949
- {
2950
- [keysToUpdate addObject:key];
2951
- }
2952
- else if ([changeset_removedKeys containsObject:cacheKey] ||
2953
- [changeset_removedCollections containsObject:cacheKey.collection] || changeset_allKeysRemoved)
2954
- {
2955
- [keysToRemove addObject:key];
2956
- }
2957
- }];
2958
-
2959
- [objectCache removeObjectsForKeys:keysToRemove];
2960
-
2961
- id yapNull = [YapNull null]; // value == yapNull : setPrimitive or containment policy
2962
- id yapTouch = [YapTouch touch]; // value == yapTouch : touchObjectForKey: was used
2963
-
2964
- BOOL isPolicyContainment = (objectPolicy == YapDatabasePolicyContainment);
2965
- BOOL isPolicyShare = (objectPolicy == YapDatabasePolicyShare);
2966
-
2967
- for (YapCollectionKey *cacheKey in keysToUpdate)
2968
- {
2969
- id newObject = [changeset_objectChanges objectForKey:cacheKey];
2970
-
2971
- if (newObject == yapNull)
2972
- {
2973
- [objectCache removeObjectForKey:cacheKey];
2974
- }
2975
- else if (newObject != yapTouch)
2976
- {
2977
- if (isPolicyContainment) {
2978
- [objectCache removeObjectForKey:cacheKey];
2979
- }
2980
- else if (isPolicyShare) {
2981
- [objectCache setObject:newObject forKey:cacheKey];
2982
- }
2983
- else // if (isPolicyCopy)
2984
- {
2985
- if ([newObject conformsToProtocol:@protocol(NSCopying)])
2986
- [objectCache setObject:[newObject copy] forKey:cacheKey];
2987
- else
2988
- [objectCache removeObjectForKey:cacheKey];
2989
- }
2990
- }
2991
- }
2992
- }
2993
-
2994
- // Update metadataCache
2995
-
2996
- if (changeset_allKeysRemoved && !hasMetadataChanges)
2997
- {
2998
- // Shortcut: Everything was removed from the database
2999
-
3000
- [metadataCache removeAllObjects];
3001
- }
3002
- else if (hasMetadataChanges && !hasRemovedKeys && !hasRemovedCollections && !changeset_allKeysRemoved)
3003
- {
3004
- // Shortcut: Nothing was removed from the database.
3005
- // So we can simply enumerate over the changes and update the cache inline as needed.
3006
-
3007
- id yapNull = [YapNull null]; // value == yapNull : setPrimitive or containment policy
3008
- id yapTouch = [YapTouch touch]; // value == yapTouch : touchObjectForKey: was used
3009
-
3010
- BOOL isPolicyContainment = (metadataPolicy == YapDatabasePolicyContainment);
3011
- BOOL isPolicyShare = (metadataPolicy == YapDatabasePolicyShare);
3012
-
3013
- [changeset_metadataChanges enumerateKeysAndObjectsUsingBlock:^(id key, id newMetadata, BOOL *stop) {
3014
-
3015
- __unsafe_unretained YapCollectionKey *cacheKey = (YapCollectionKey *)key;
3016
-
3017
- if ([metadataCache containsKey:cacheKey])
3018
- {
3019
- if (newMetadata == yapNull)
3020
- {
3021
- [metadataCache removeObjectForKey:cacheKey];
3022
- }
3023
- else if (newMetadata != yapTouch)
3024
- {
3025
- if (isPolicyContainment) {
3026
- [metadataCache removeObjectForKey:cacheKey];
3027
- }
3028
- else if (isPolicyShare) {
3029
- [metadataCache setObject:newMetadata forKey:cacheKey];
3030
- }
3031
- else // if (isPolicyCopy)
3032
- {
3033
- if ([newMetadata conformsToProtocol:@protocol(NSCopying)])
3034
- [metadataCache setObject:[newMetadata copy] forKey:cacheKey];
3035
- else
3036
- [metadataCache removeObjectForKey:cacheKey];
3037
- }
3038
- }
3039
- }
3040
- }];
3041
- }
3042
- else if (hasMetadataChanges || hasRemovedKeys || hasRemovedCollections)
3043
- {
3044
- NSUInteger updateCapacity = MIN([metadataCache count], [changeset_metadataChanges count]);
3045
- NSUInteger removeCapacity = MIN([metadataCache count], [changeset_removedKeys count]);
3046
-
3047
- NSMutableArray *keysToUpdate = [NSMutableArray arrayWithCapacity:updateCapacity];
3048
- NSMutableArray *keysToRemove = [NSMutableArray arrayWithCapacity:removeCapacity];
3049
-
3050
- [metadataCache enumerateKeysWithBlock:^(id key, BOOL *stop) {
3051
-
3052
- // Order matters.
3053
- // Consider the following database change:
3054
- //
3055
- // [transaction removeAllObjectsInAllCollections];
3056
- // [transaction setObject:obj forKey:key inCollection:collection];
3057
-
3058
- __unsafe_unretained YapCollectionKey *cacheKey = (YapCollectionKey *)key;
3059
-
3060
- if ([changeset_metadataChanges objectForKey:cacheKey])
3061
- {
3062
- [keysToUpdate addObject:key];
3063
- }
3064
- else if ([changeset_removedKeys containsObject:cacheKey] ||
3065
- [changeset_removedCollections containsObject:cacheKey.collection] || changeset_allKeysRemoved)
3066
- {
3067
- [keysToRemove addObject:key];
3068
- }
3069
- }];
3070
-
3071
- [metadataCache removeObjectsForKeys:keysToRemove];
3072
-
3073
- id yapNull = [YapNull null]; // value == yapNull : setPrimitive or containment policy
3074
- id yapTouch = [YapTouch touch]; // value == yapTouch : touchObjectForKey: was used
3075
-
3076
- BOOL isPolicyContainment = (metadataPolicy == YapDatabasePolicyContainment);
3077
- BOOL isPolicyShare = (metadataPolicy == YapDatabasePolicyShare);
3078
-
3079
- for (YapCollectionKey *cacheKey in keysToUpdate)
3080
- {
3081
- id newMetadata = [changeset_metadataChanges objectForKey:cacheKey];
3082
-
3083
- if (newMetadata == yapNull)
3084
- {
3085
- [metadataCache removeObjectForKey:cacheKey];
3086
- }
3087
- else if (newMetadata != yapTouch)
3088
- {
3089
- if (isPolicyContainment) {
3090
- [metadataCache removeObjectForKey:cacheKey];
3091
- }
3092
- else if (isPolicyShare) {
3093
- [metadataCache setObject:newMetadata forKey:cacheKey];
3094
- }
3095
- else // if (isPolicyCopy)
3096
- {
3097
- if ([newMetadata conformsToProtocol:@protocol(NSCopying)])
3098
- [metadataCache setObject:[newMetadata copy] forKey:cacheKey];
3099
- else
3100
- [metadataCache removeObjectForKey:cacheKey];
3101
- }
3102
- }
3103
- }
3104
- }
3105
- }
3106
-
3107
- /**
3108
- * Internal method.
3109
- *
3110
- * This method is invoked with the changeset from a sibling connection.
3111
- **/
3112
- - (void)noteCommittedChanges:(NSDictionary *)changeset
3113
- {
3114
- // This method must be invoked from within connectionQueue.
3115
- // It may be invoked from:
3116
- //
3117
- // 1. [database noteCommittedChanges:fromConnection:]
3118
- // via dispatch_async(connectionQueue, ...)
3119
- //
3120
- // 2. [self preReadTransaction:]
3121
- // via dispatch_X(connectionQueue) -> dispatch_sync(database->snapshotQueue)
3122
- //
3123
- // 3. [self preReadWriteTransaction:]
3124
- // via dispatch_X(connectionQueue) -> dispatch_sync(database->snapshotQueue)
3125
- //
3126
- // In case 1 (the common case) we can see IsOnConnectionQueueKey.
3127
- // In case 2 & 3 (the edge cases) we can see IsOnSnapshotQueueKey.
3128
-
3129
- NSAssert(dispatch_get_specific(IsOnConnectionQueueKey) ||
3130
- dispatch_get_specific(database->IsOnSnapshotQueueKey), @"Must be invoked within connectionQueue");
3131
-
3132
- // Grab the new snapshot.
3133
- // This tells us the minimum snapshot we could get if we started a transaction right now.
3134
-
3135
- uint64_t changesetSnapshot = [[changeset objectForKey:YapDatabaseSnapshotKey] unsignedLongLongValue];
3136
-
3137
- if (changesetSnapshot <= snapshot)
3138
- {
3139
- // We already noted this changeset.
3140
- //
3141
- // There is a "race condition" that occasionally happens when a readonly transaction is started
3142
- // around the same instant a readwrite transaction finishes committing its changes to disk.
3143
- // The readonly transaction enters our transaction state queue (to start) before
3144
- // the readwrite transaction enters our transaction state queue (to finish).
3145
- // However the readonly transaction gets a database snapshot post readwrite commit.
3146
- // That is, the readonly transaction can read the changes from the readwrite transaction at the sqlite layer,
3147
- // even though the readwrite transaction hasn't completed within the yap database layer.
3148
- //
3149
- // This race condition is handled automatically within the preReadTransaction method.
3150
- // In fact, it invokes this method to handle the race condition.
3151
- // Thus this method could be invoked twice to handle the same changeset.
3152
- // So catching it here and ignoring it is simply a minor optimization to avoid duplicate work.
3153
-
3154
- YDBLogVerbose(@"Ignoring previously processed changeset %lu for connection %@, database %@",
3155
- (unsigned long)changesetSnapshot, self, database);
3156
-
3157
- return;
3158
- }
3159
-
3160
- if (longLivedReadTransaction)
3161
- {
3162
- if (dispatch_get_specific(database->IsOnSnapshotQueueKey))
3163
- {
3164
- // This method is being invoked from preReadTransaction:.
3165
- // We are to process the changeset for it.
3166
-
3167
- [processedChangesets addObject:changeset];
3168
- }
3169
- else
3170
- {
3171
- // This method is being invoked from [database noteCommittedChanges:].
3172
- // We cannot process the changeset yet.
3173
- // We must wait for the longLivedReadTransaction to be reset.
3174
-
3175
- YDBLogVerbose(@"Storing pending changeset %lu for connection %@, database %@",
3176
- (unsigned long)changesetSnapshot, self, database);
3177
-
3178
- [pendingChangesets addObject:changeset];
3179
- return;
3180
- }
3181
- }
3182
-
3183
- // Changeset processing
3184
-
3185
- YDBLogVerbose(@"Processing changeset %lu for connection %@, database %@",
3186
- (unsigned long)changesetSnapshot, self, database);
3187
-
3188
- snapshot = changesetSnapshot;
3189
- [self processChangeset:changeset];
3190
- }
3191
-
3192
- ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
3193
- #pragma mark Changeset Inspection
3194
- ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
3195
-
3196
- - (BOOL)hasChangeForCollection:(NSString *)collection
3197
- inNotifications:(NSArray *)notifications
3198
- includingObjectChanges:(BOOL)includeObjectChanges
3199
- metadataChanges:(BOOL)includeMetadataChanges
3200
- {
3201
- if (collection == nil)
3202
- collection = @"";
3203
-
3204
- for (NSNotification *notification in notifications)
3205
- {
3206
- if (![notification isKindOfClass:[NSNotification class]])
3207
- {
3208
- YDBLogWarn(@"%@ - notifications parameter contains non-NSNotification object", THIS_METHOD);
3209
- continue;
3210
- }
3211
-
3212
- NSDictionary *changeset = notification.userInfo;
3213
-
3214
- if (includeObjectChanges)
3215
- {
3216
- YapSet *changeset_objectChanges = [changeset objectForKey:YapDatabaseObjectChangesKey];
3217
- for (YapCollectionKey *collectionKey in changeset_objectChanges)
3218
- {
3219
- if ([collectionKey.collection isEqualToString:collection])
3220
- {
3221
- return YES;
3222
- }
3223
- }
3224
- }
3225
-
3226
- if (includeMetadataChanges)
3227
- {
3228
- YapSet *changeset_metadataChanges = [changeset objectForKey:YapDatabaseMetadataChangesKey];
3229
- for (YapCollectionKey *collectionKey in changeset_metadataChanges)
3230
- {
3231
- if ([collectionKey.collection isEqualToString:collection])
3232
- {
3233
- return YES;
3234
- }
3235
- }
3236
- }
3237
-
3238
- YapSet *changeset_removedKeys = [changeset objectForKey:YapDatabaseRemovedKeysKey];
3239
- for (YapCollectionKey *collectionKey in changeset_removedKeys)
3240
- {
3241
- if ([collectionKey.collection isEqualToString:collection])
3242
- {
3243
- return YES;
3244
- }
3245
- }
3246
-
3247
- YapSet *changeset_removedCollections = [changeset objectForKey:YapDatabaseRemovedCollectionsKey];
3248
- if ([changeset_removedCollections containsObject:collection])
3249
- return YES;
3250
-
3251
- BOOL changeset_allKeysRemoved = [[changeset objectForKey:YapDatabaseAllKeysRemovedKey] boolValue];
3252
- if (changeset_allKeysRemoved)
3253
- return YES;
3254
- }
3255
-
3256
- return NO;
3257
- }
3258
-
3259
- - (BOOL)hasChangeForCollection:(NSString *)collection inNotifications:(NSArray *)notifications
3260
- {
3261
- return [self hasChangeForCollection:collection
3262
- inNotifications:notifications
3263
- includingObjectChanges:YES
3264
- metadataChanges:YES];
3265
- }
3266
-
3267
- - (BOOL)hasObjectChangeForCollection:(NSString *)collection inNotifications:(NSArray *)notifications
3268
- {
3269
- return [self hasChangeForCollection:collection
3270
- inNotifications:notifications
3271
- includingObjectChanges:YES
3272
- metadataChanges:NO];
3273
- }
3274
-
3275
- - (BOOL)hasMetadataChangeForCollection:(NSString *)collection inNotifications:(NSArray *)notifications
3276
- {
3277
- return [self hasChangeForCollection:collection
3278
- inNotifications:notifications
3279
- includingObjectChanges:NO
3280
- metadataChanges:YES];
3281
- }
3282
-
3283
- // Query for a change to a particular key/collection tuple
3284
-
3285
- - (BOOL)hasChangeForKey:(NSString *)key
3286
- inCollection:(NSString *)collection
3287
- inNotifications:(NSArray *)notifications
3288
- includingObjectChanges:(BOOL)includeObjectChanges
3289
- metadataChanges:(BOOL)includeMetadataChanges
3290
- {
3291
- if (key == nil) return NO;
3292
- if (collection == nil)
3293
- collection = @"";
3294
-
3295
- YapCollectionKey *collectionKey = [[YapCollectionKey alloc] initWithCollection:collection key:key];
3296
-
3297
- for (NSNotification *notification in notifications)
3298
- {
3299
- if (![notification isKindOfClass:[NSNotification class]])
3300
- {
3301
- YDBLogWarn(@"%@ - notifications parameter contains non-NSNotification object", THIS_METHOD);
3302
- continue;
3303
- }
3304
-
3305
- NSDictionary *changeset = notification.userInfo;
3306
-
3307
- if (includeObjectChanges)
3308
- {
3309
- YapSet *changeset_objectChanges = [changeset objectForKey:YapDatabaseObjectChangesKey];
3310
- if ([changeset_objectChanges containsObject:collectionKey])
3311
- return YES;
3312
- }
3313
-
3314
- if (includeMetadataChanges)
3315
- {
3316
- YapSet *changeset_metadataChanges = [changeset objectForKey:YapDatabaseMetadataChangesKey];
3317
- if ([changeset_metadataChanges containsObject:collectionKey])
3318
- return YES;
3319
- }
3320
-
3321
- YapSet *changeset_removedKeys = [changeset objectForKey:YapDatabaseRemovedKeysKey];
3322
- if ([changeset_removedKeys containsObject:collectionKey])
3323
- return YES;
3324
-
3325
- YapSet *changeset_removedCollections = [changeset objectForKey:YapDatabaseRemovedCollectionsKey];
3326
- if ([changeset_removedCollections containsObject:collection])
3327
- return YES;
3328
-
3329
- BOOL changeset_allKeysRemoved = [[changeset objectForKey:YapDatabaseAllKeysRemovedKey] boolValue];
3330
- if (changeset_allKeysRemoved)
3331
- return YES;
3332
- }
3333
-
3334
- return NO;
3335
- }
3336
-
3337
- - (BOOL)hasChangeForKey:(NSString *)key
3338
- inCollection:(NSString *)collection
3339
- inNotifications:(NSArray *)notifications
3340
- {
3341
- return [self hasChangeForKey:key
3342
- inCollection:collection
3343
- inNotifications:notifications
3344
- includingObjectChanges:YES
3345
- metadataChanges:YES];
3346
- }
3347
-
3348
- - (BOOL)hasObjectChangeForKey:(NSString *)key
3349
- inCollection:(NSString *)collection
3350
- inNotifications:(NSArray *)notifications
3351
- {
3352
- return [self hasChangeForKey:key
3353
- inCollection:collection
3354
- inNotifications:notifications
3355
- includingObjectChanges:YES
3356
- metadataChanges:NO];
3357
- }
3358
-
3359
- - (BOOL)hasMetadataChangeForKey:(NSString *)key
3360
- inCollection:(NSString *)collection
3361
- inNotifications:(NSArray *)notifications
3362
- {
3363
- return [self hasChangeForKey:key
3364
- inCollection:collection
3365
- inNotifications:notifications
3366
- includingObjectChanges:NO
3367
- metadataChanges:YES];
3368
- }
3369
-
3370
- // Query for a change to a particular set of keys in a collection
3371
-
3372
- - (BOOL)hasChangeForAnyKeys:(NSSet *)keys
3373
- inCollection:(NSString *)collection
3374
- inNotifications:(NSArray *)notifications
3375
- includingObjectChanges:(BOOL)includeObjectChanges
3376
- metadataChanges:(BOOL)includeMetadataChanges
3377
- {
3378
- if ([keys count] == 0) return NO;
3379
- if (collection == nil)
3380
- collection = @"";
3381
-
3382
- for (NSNotification *notification in notifications)
3383
- {
3384
- if (![notification isKindOfClass:[NSNotification class]])
3385
- {
3386
- YDBLogWarn(@"%@ - notifications parameter contains non-NSNotification object", THIS_METHOD);
3387
- continue;
3388
- }
3389
-
3390
- NSDictionary *changeset = notification.userInfo;
3391
-
3392
- if (includeObjectChanges)
3393
- {
3394
- YapSet *changeset_objectChanges = [changeset objectForKey:YapDatabaseObjectChangesKey];
3395
- for (YapCollectionKey *collectionKey in changeset_objectChanges)
3396
- {
3397
- if ([collectionKey.collection isEqualToString:collection])
3398
- {
3399
- if ([keys containsObject:collectionKey.key])
3400
- {
3401
- return YES;
3402
- }
3403
- }
3404
- }
3405
- }
3406
-
3407
- if (includeMetadataChanges)
3408
- {
3409
- YapSet *changeset_metadataChanges = [changeset objectForKey:YapDatabaseMetadataChangesKey];
3410
- for (YapCollectionKey *collectionKey in changeset_metadataChanges)
3411
- {
3412
- if ([collectionKey.collection isEqualToString:collection])
3413
- {
3414
- if ([keys containsObject:collectionKey.key])
3415
- {
3416
- return YES;
3417
- }
3418
- }
3419
- }
3420
- }
3421
-
3422
- YapSet *changeset_removedKeys = [changeset objectForKey:YapDatabaseRemovedKeysKey];
3423
- for (YapCollectionKey *collectionKey in changeset_removedKeys)
3424
- {
3425
- if ([collectionKey.collection isEqualToString:collection])
3426
- {
3427
- if ([keys containsObject:collectionKey.key])
3428
- {
3429
- return YES;
3430
- }
3431
- }
3432
- }
3433
-
3434
- YapSet *changeset_removedCollections = [changeset objectForKey:YapDatabaseRemovedCollectionsKey];
3435
- if ([changeset_removedCollections containsObject:collection])
3436
- return YES;
3437
-
3438
- BOOL changeset_allKeysRemoved = [[changeset objectForKey:YapDatabaseAllKeysRemovedKey] boolValue];
3439
- if (changeset_allKeysRemoved)
3440
- return YES;
3441
- }
3442
-
3443
- return NO;
3444
- }
3445
-
3446
- - (BOOL)hasChangeForAnyKeys:(NSSet *)keys
3447
- inCollection:(NSString *)collection
3448
- inNotifications:(NSArray *)notifications
3449
- {
3450
- return [self hasChangeForAnyKeys:keys
3451
- inCollection:collection
3452
- inNotifications:notifications
3453
- includingObjectChanges:YES
3454
- metadataChanges:YES];
3455
- }
3456
-
3457
- - (BOOL)hasObjectChangeForAnyKeys:(NSSet *)keys
3458
- inCollection:(NSString *)collection
3459
- inNotifications:(NSArray *)notifications
3460
- {
3461
- return [self hasChangeForAnyKeys:keys
3462
- inCollection:collection
3463
- inNotifications:notifications
3464
- includingObjectChanges:YES
3465
- metadataChanges:NO];
3466
- }
3467
-
3468
- - (BOOL)hasMetadataChangeForAnyKeys:(NSSet *)keys
3469
- inCollection:(NSString *)collection
3470
- inNotifications:(NSArray *)notifications
3471
- {
3472
- return [self hasChangeForAnyKeys:keys
3473
- inCollection:collection
3474
- inNotifications:notifications
3475
- includingObjectChanges:NO
3476
- metadataChanges:YES];
3477
- }
3478
-
3479
- ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
3480
- #pragma mark Extensions
3481
- ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
3482
-
3483
- /**
3484
- * Creates or fetches the extension with the given name.
3485
- * If this connection has not yet initialized the proper extensions connection, it is done automatically.
3486
- *
3487
- * @return
3488
- * A subclass of YapDatabaseExtensionConnection,
3489
- * according to the type of extension registered under the given name.
3490
- *
3491
- * One must register an extension with the database before it can be accessed from within connections or transactions.
3492
- * After registration everything works automatically using just the registered extension name.
3493
- *
3494
- * @see [YapDatabase registerExtension:withName:]
3495
- **/
3496
- - (id)extension:(NSString *)extName
3497
- {
3498
- // This method is PUBLIC.
3499
- //
3500
- // This method returns a subclass of YapDatabaseExtensionConnection.
3501
- // To get:
3502
- // - YapDatabaseExtension => [database registeredExtension:@"registeredNameOfExtension"]
3503
- // - YapDatabaseExtensionConnection => [databaseConnection extension:@"registeredNameOfExtension"]
3504
- // - YapDatabaseExtensionTransaction => [databaseTransaction extension:@"registeredNameOfExtension"]
3505
-
3506
- __block id extConnection = nil;
3507
-
3508
- dispatch_block_t block = ^{
3509
-
3510
- extConnection = [extensions objectForKey:extName];
3511
-
3512
- if (!extConnection && !extensionsReady)
3513
- {
3514
- // We don't have an existing connection for the extension.
3515
- // Create one (if we can).
3516
-
3517
- YapDatabaseExtension *ext = [registeredExtensions objectForKey:extName];
3518
- if (ext)
3519
- {
3520
- extConnection = [ext newConnection:self];
3521
- [extensions setObject:extConnection forKey:extName];
3522
- }
3523
- }
3524
- };
3525
-
3526
- if (dispatch_get_specific(IsOnConnectionQueueKey))
3527
- block();
3528
- else
3529
- dispatch_sync(connectionQueue, block);
3530
-
3531
- return extConnection;
3532
- }
3533
-
3534
- - (id)ext:(NSString *)extensionName
3535
- {
3536
- // The "+ (void)load" method swizzles the implementation of this class
3537
- // to point to the implementation of the extension: method.
3538
- //
3539
- // So the two methods are literally the same thing.
3540
-
3541
- return [self extension:extensionName]; // This method is swizzled !
3542
- }
3543
-
3544
- - (NSDictionary *)extensions
3545
- {
3546
- // This method is INTERNAL
3547
-
3548
- if (!extensionsReady)
3549
- {
3550
- [registeredExtensions enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) {
3551
-
3552
- __unsafe_unretained NSString *extName = key;
3553
- __unsafe_unretained YapDatabaseExtension *ext = obj;
3554
-
3555
- if ([extensions objectForKey:extName] == nil)
3556
- {
3557
- id extConnection = [ext newConnection:self];
3558
- [extensions setObject:extConnection forKey:extName];
3559
- }
3560
- }];
3561
-
3562
- extensionsReady = YES;
3563
- }
3564
-
3565
- return extensions;
3566
- }
3567
-
3568
- - (BOOL)registerExtension:(YapDatabaseExtension *)extension withName:(NSString *)extensionName
3569
- {
3570
- NSAssert(dispatch_get_specific(database->IsOnWriteQueueKey), @"Must go through writeQueue.");
3571
-
3572
- __block BOOL result = NO;
3573
-
3574
- dispatch_sync(connectionQueue, ^{ @autoreleasepool {
3575
-
3576
- YapDatabaseReadWriteTransaction *transaction = [self newReadWriteTransaction];
3577
- [self preReadWriteTransaction:transaction];
3578
-
3579
- // Set the registeredName now.
3580
- // The extension will need this in order to perform the registration tasks such as creating tables, etc.
3581
- extension.registeredName = extensionName;
3582
-
3583
- YapDatabaseExtensionConnection *extensionConnection;
3584
- YapDatabaseExtensionTransaction *extensionTransaction;
3585
-
3586
- extensionConnection = [extension newConnection:self];
3587
- extensionTransaction = [extensionConnection newReadWriteTransaction:transaction];
3588
-
3589
- BOOL needsClassValue = NO;
3590
- [self willRegisterExtension:extension
3591
- withTransaction:transaction
3592
- needsClassValue:&needsClassValue];
3593
-
3594
- result = [extensionTransaction createIfNeeded];
3595
-
3596
- if (result)
3597
- {
3598
- [self didRegisterExtension:extension
3599
- withTransaction:transaction
3600
- needsClassValue:needsClassValue];
3601
-
3602
- [self addRegisteredExtensionConnection:extensionConnection];
3603
- [transaction addRegisteredExtensionTransaction:extensionTransaction];
3604
- }
3605
- else
3606
- {
3607
- // Registration failed.
3608
-
3609
- extension.registeredName = nil;
3610
- [transaction rollback];
3611
- }
3612
-
3613
- [self postReadWriteTransaction:transaction];
3614
- registeredExtensionsChanged = NO;
3615
- }});
3616
-
3617
- return result;
3618
- }
3619
-
3620
- - (void)unregisterExtension:(NSString *)extensionName
3621
- {
3622
- NSAssert(dispatch_get_specific(database->IsOnWriteQueueKey), @"Must go through writeQueue.");
3623
-
3624
- dispatch_sync(connectionQueue, ^{ @autoreleasepool {
3625
-
3626
- YapDatabaseReadWriteTransaction *transaction = [self newReadWriteTransaction];
3627
- [self preReadWriteTransaction:transaction];
3628
-
3629
- // Unregister the given extension
3630
-
3631
- YapDatabaseExtension *extension = [registeredExtensions objectForKey:extensionName];
3632
-
3633
- [self _unregisterExtension:extensionName withTransaction:transaction];
3634
- extension.registeredName = nil;
3635
-
3636
- // Automatically unregister any extensions that were dependent upon this one.
3637
-
3638
- NSMutableArray *extensionNameStack = [NSMutableArray arrayWithCapacity:1];
3639
- [extensionNameStack addObject:extensionName];
3640
-
3641
- do
3642
- {
3643
- NSString *currentExtensionName = [extensionNameStack lastObject];
3644
-
3645
- __block NSString *dependentExtName = nil;
3646
- [extensionDependencies enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop){
3647
-
3648
- // __unsafe_unretained NSString *extName = (NSString *)key;
3649
- __unsafe_unretained NSSet *extDependencies = (NSSet *)obj;
3650
-
3651
- if ([extDependencies containsObject:currentExtensionName])
3652
- {
3653
- dependentExtName = (NSString *)key;
3654
- *stop = YES;
3655
- }
3656
- }];
3657
-
3658
- if (dependentExtName)
3659
- {
3660
- // We found an extension that was dependent upon the one we just unregistered.
3661
- // So we need to unregister it too.
3662
-
3663
- YapDatabaseExtension *dependentExt = [registeredExtensions objectForKey:dependentExtName];
3664
-
3665
- [self _unregisterExtension:dependentExtName withTransaction:transaction];
3666
- dependentExt.registeredName = nil;
3667
-
3668
- // And now we need to check and see if there were any extensions dependent upon this new one.
3669
- // So we add it to the top of the stack, and continue our search.
3670
-
3671
- [extensionNameStack addObject:dependentExtName];
3672
- }
3673
- else
3674
- {
3675
- [extensionNameStack removeLastObject];
3676
- }
3677
-
3678
- } while ([extensionNameStack count] > 0);
3679
-
3680
-
3681
- // Complete the transaction
3682
- [self postReadWriteTransaction:transaction];
3683
-
3684
- // And reset the registeredExtensionsChanged ivar.
3685
- // The above method already processed it, and included the appropriate information in the changeset.
3686
- registeredExtensionsChanged = NO;
3687
- }});
3688
- }
3689
-
3690
- - (void)_unregisterExtension:(NSString *)extensionName withTransaction:(YapDatabaseReadWriteTransaction *)transaction
3691
- {
3692
- NSString *className = [transaction stringValueForKey:@"class" extension:extensionName];
3693
- Class class = NSClassFromString(className);
3694
-
3695
- if (className == nil)
3696
- {
3697
- YDBLogWarn(@"Unable to unregister extension(%@). Doesn't appear to be registered.", extensionName);
3698
- }
3699
- else if (class == NULL)
3700
- {
3701
- YDBLogError(@"Unable to unregister extension(%@) with unknown class(%@)", extensionName, className);
3702
- }
3703
- if (![class isSubclassOfClass:[YapDatabaseExtension class]])
3704
- {
3705
- YDBLogError(@"Unable to unregister extension(%@) with improper class(%@)", extensionName, className);
3706
- }
3707
- else
3708
- {
3709
- // Drop tables
3710
- [class dropTablesForRegisteredName:extensionName withTransaction:transaction];
3711
-
3712
- // Drop preferences (rows in yap2 table)
3713
- [transaction removeAllValuesForExtension:extensionName];
3714
-
3715
- // Remove from registeredExtensions, extensionsOrder & extensionDependencies (if needed)
3716
- [self didUnregisterExtension:extensionName];
3717
-
3718
- // Remove YapDatabaseExtensionConnection subclass instance (if needed)
3719
- [self removeRegisteredExtensionConnection:extensionName];
3720
-
3721
- // Remove YapDatabaseExtensionTransaction subclass instance (if needed)
3722
- [transaction removeRegisteredExtensionTransaction:extensionName];
3723
- }
3724
- }
3725
-
3726
- - (void)willRegisterExtension:(YapDatabaseExtension *)extension
3727
- withTransaction:(YapDatabaseReadWriteTransaction *)transaction
3728
- needsClassValue:(BOOL *)needsClassValuePtr
3729
- {
3730
- // This method is INTERNAL
3731
- //
3732
- // The class name of every registered extension is recorded in the yap2 table.
3733
- // We ensure that re-registrations under the same name use the same extension class.
3734
- // If we detect a change, we auto-unregister the previous extension.
3735
- //
3736
- // Note: @"class" is a reserved key for all extensions.
3737
-
3738
- NSString *extensionName = extension.registeredName;
3739
-
3740
- NSString *prevExtensionClassName = [transaction stringValueForKey:@"class" extension:extensionName];
3741
- if (prevExtensionClassName == nil)
3742
- {
3743
- // First time registration
3744
- *needsClassValuePtr = YES;
3745
- return;
3746
- }
3747
-
3748
- NSString *extensionClassName = NSStringFromClass([extension class]);
3749
-
3750
- if ([extensionClassName isEqualToString:prevExtensionClassName])
3751
- {
3752
- // Re-registration
3753
- *needsClassValuePtr = NO;
3754
- return;
3755
- }
3756
-
3757
- NSArray *otherValidClassNames = [[extension class] previousClassNames];
3758
-
3759
- if ([otherValidClassNames containsObject:prevExtensionClassName])
3760
- {
3761
- // The extension class was renamed.
3762
- // We should update the class value in the database.
3763
- *needsClassValuePtr = YES;
3764
- return;
3765
- }
3766
-
3767
- YDBLogWarn(@"Dropping tables for previously registered extension with name(%@), class(%@) for new class(%@)",
3768
- extensionName, prevExtensionClassName, extensionClassName);
3769
-
3770
- Class prevExtensionClass = NSClassFromString(prevExtensionClassName);
3771
-
3772
- if (prevExtensionClass == NULL)
3773
- {
3774
- YDBLogError(@"Unable to drop tables for previously registered extension with name(%@), unknown class(%@)",
3775
- extensionName, prevExtensionClassName);
3776
- }
3777
- else if (![prevExtensionClass isSubclassOfClass:[YapDatabaseExtension class]])
3778
- {
3779
- YDBLogError(@"Unable to drop tables for previously registered extension with name(%@), invalid class(%@)",
3780
- extensionName, prevExtensionClassName);
3781
- }
3782
- else
3783
- {
3784
- // Drop tables
3785
- [prevExtensionClass dropTablesForRegisteredName:extensionName withTransaction:transaction];
3786
-
3787
- // Drop preferences (rows in yap2 table)
3788
- [transaction removeAllValuesForExtension:extensionName];
3789
- }
3790
-
3791
- *needsClassValuePtr = YES;
3792
- }
3793
-
3794
- - (void)didRegisterExtension:(YapDatabaseExtension *)extension
3795
- withTransaction:(YapDatabaseReadWriteTransaction *)transaction
3796
- needsClassValue:(BOOL)needsClassValue
3797
- {
3798
- // This method is INTERNAL
3799
-
3800
- NSString *extensionName = extension.registeredName;
3801
-
3802
- // Record the class name of the extension in the yap2 table.
3803
-
3804
- if (needsClassValue)
3805
- {
3806
- NSString *extensionClassName = NSStringFromClass([extension class]);
3807
-
3808
- [transaction setStringValue:extensionClassName forKey:@"class" extension:extensionName];
3809
- }
3810
-
3811
- // Update the list of registered extensions.
3812
-
3813
- NSMutableDictionary *newRegisteredExtensions = [registeredExtensions mutableCopy];
3814
- [newRegisteredExtensions setObject:extension forKey:extensionName];
3815
-
3816
- registeredExtensions = [newRegisteredExtensions copy];
3817
- extensionsOrder = [extensionsOrder arrayByAddingObject:extensionName];
3818
-
3819
- NSSet *dependencies = [extension dependencies];
3820
- if (dependencies == nil)
3821
- dependencies = [NSSet set];
3822
-
3823
- NSMutableDictionary *newExtensionDependencies = [extensionDependencies mutableCopy];
3824
- [newExtensionDependencies setObject:dependencies forKey:extensionName];
3825
-
3826
- extensionDependencies = [newExtensionDependencies copy];
3827
-
3828
- extensionsReady = NO;
3829
- sharedKeySetForExtensions = [NSDictionary sharedKeySetForKeys:[registeredExtensions allKeys]];
3830
-
3831
- // Set the registeredExtensionsChanged flag.
3832
- // This will be consulted during the creation of the changeset,
3833
- // and will cause us to add the updated registeredExtensions to the list of changes.
3834
- // It will then get propogated to the database, and all other connections.
3835
-
3836
- registeredExtensionsChanged = YES;
3837
- }
3838
-
3839
- - (void)didUnregisterExtension:(NSString *)extensionName
3840
- {
3841
- // This method is INTERNAL
3842
-
3843
- if ([registeredExtensions objectForKey:extensionName])
3844
- {
3845
- NSMutableDictionary *newRegisteredExtensions = [registeredExtensions mutableCopy];
3846
- [newRegisteredExtensions removeObjectForKey:extensionName];
3847
-
3848
- registeredExtensions = [newRegisteredExtensions copy];
3849
-
3850
- NSMutableArray *newExtensionsOrder = [extensionsOrder mutableCopy];
3851
- [newExtensionsOrder removeObject:extensionName];
3852
-
3853
- extensionsOrder = [newExtensionsOrder copy];
3854
-
3855
- NSMutableDictionary *newExtensionDependencies = [extensionDependencies mutableCopy];
3856
- [newExtensionDependencies removeObjectForKey:extensionName];
3857
-
3858
- extensionDependencies = [newExtensionDependencies copy];
3859
-
3860
- extensionsReady = NO;
3861
- sharedKeySetForExtensions = [NSDictionary sharedKeySetForKeys:[registeredExtensions allKeys]];
3862
-
3863
- // Set the registeredExtensionsChanged flag.
3864
- // This will be consulted during the creation of the changeset,
3865
- // and will cause us to add the updated registeredExtensions to the list of changes.
3866
- // It will then get propogated to the database, and all other connections.
3867
-
3868
- registeredExtensionsChanged = YES;
3869
- }
3870
- }
3871
-
3872
- - (void)addRegisteredExtensionConnection:(YapDatabaseExtensionConnection *)extConnection
3873
- {
3874
- // This method is INTERNAL
3875
-
3876
- if (extensions == nil)
3877
- extensions = [[NSMutableDictionary alloc] init];
3878
-
3879
- NSString *extName = [[extConnection extension] registeredName];
3880
-
3881
- [extensions setObject:extConnection forKey:extName];
3882
- }
3883
-
3884
- - (void)removeRegisteredExtensionConnection:(NSString *)extName
3885
- {
3886
- // This method is INTERNAL
3887
-
3888
- [extensions removeObjectForKey:extName];
3889
- }
3890
-
3891
- ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
3892
- #pragma mark Memory Tables
3893
- ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
3894
-
3895
- - (NSDictionary *)registeredTables
3896
- {
3897
- // This method is INTERNAL
3898
-
3899
- return registeredTables;
3900
- }
3901
-
3902
- - (BOOL)registerTable:(YapMemoryTable *)table withName:(NSString *)name
3903
- {
3904
- // This method is INTERNAL
3905
-
3906
- if ([registeredTables objectForKey:name])
3907
- return NO;
3908
-
3909
- NSMutableDictionary *newRegisteredTables = [registeredTables mutableCopy];
3910
- [newRegisteredTables setObject:table forKey:name];
3911
-
3912
- registeredTables = [newRegisteredTables copy];
3913
- registeredTablesChanged = YES;
3914
-
3915
- return YES;
3916
- }
3917
-
3918
- - (void)unregisterTableWithName:(NSString *)name
3919
- {
3920
- // This method is INTERNAL
3921
-
3922
- if ([registeredTables objectForKey:name])
3923
- {
3924
- NSMutableDictionary *newRegisteredTables = [registeredTables mutableCopy];
3925
- [newRegisteredTables removeObjectForKey:name];
3926
-
3927
- registeredTables = [newRegisteredTables copy];
3928
- registeredTablesChanged = YES;
3929
- }
3930
- }
3931
-
3932
- ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
3933
- #pragma mark Utilities
3934
- ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
3935
-
3936
- /**
3937
- * Long-lived read transactions are a great way to achive stability, especially in places like the main-thread.
3938
- * However, they pose a unique problem. These long-lived transactions often start out by
3939
- * locking the WAL (write ahead log). This prevents the WAL from ever getting reset,
3940
- * and thus causes the WAL to potentially grow infinitely large. In order to allow the WAL to get properly reset,
3941
- * we need the long-lived read transactions to "reset". That is, without changing their stable state (their snapshot),
3942
- * we need them to restart the transaction, but this time without locking this WAL.
3943
- *
3944
- * We use the maybeResetLongLivedReadTransaction method to achieve this.
3945
- **/
3946
- - (void)maybeResetLongLivedReadTransaction
3947
- {
3948
- // Async dispatch onto the writeQueue so we know there aren't any other active readWrite transactions
3949
-
3950
- dispatch_async(database->writeQueue, ^{
3951
-
3952
- // Pause the writeQueue so readWrite operations can't interfere with us.
3953
- // We abort if our connection has a readWrite transaction pending.
3954
-
3955
- BOOL abort = NO;
3956
-
3957
- OSSpinLockLock(&lock);
3958
- {
3959
- if (activeReadWriteTransaction) {
3960
- abort = YES;
3961
- }
3962
- else if (!writeQueueSuspended) {
3963
- dispatch_suspend(database->writeQueue);
3964
- writeQueueSuspended = YES;
3965
- }
3966
- }
3967
- OSSpinLockUnlock(&lock);
3968
-
3969
- if (abort) return;
3970
-
3971
- // Async dispatch onto our connectionQueue.
3972
-
3973
- dispatch_async(connectionQueue, ^{
3974
-
3975
- // If possible, silently reset the longLivedReadTransaction (same snapshot, no longer locking the WAL)
3976
-
3977
- if (longLivedReadTransaction && (snapshot == [database snapshot]))
3978
- {
3979
- NSArray *empty = [self beginLongLivedReadTransaction];
3980
-
3981
- if ([empty count] != 0)
3982
- {
3983
- YDBLogError(@"Core logic failure! "
3984
- @"Silent longLivedReadTransaction reset resulted in non-empty notification array!");
3985
- }
3986
- }
3987
-
3988
- // Resume the writeQueue
3989
-
3990
- OSSpinLockLock(&lock);
3991
- {
3992
- if (writeQueueSuspended) {
3993
- dispatch_resume(database->writeQueue);
3994
- writeQueueSuspended = NO;
3995
- }
3996
- }
3997
- OSSpinLockUnlock(&lock);
3998
- });
3999
- });
4000
- }
4001
-
4002
- NS_INLINE void __preWriteQueue(YapDatabaseConnection *connection)
4003
- {
4004
- OSSpinLockLock(&connection->lock);
4005
- {
4006
- if (connection->writeQueueSuspended) {
4007
- dispatch_resume(connection->database->writeQueue);
4008
- connection->writeQueueSuspended = NO;
4009
- }
4010
- connection->activeReadWriteTransaction = YES;
4011
- }
4012
- OSSpinLockUnlock(&connection->lock);
4013
- }
4014
-
4015
- NS_INLINE void __postWriteQueue(YapDatabaseConnection *connection)
4016
- {
4017
- OSSpinLockLock(&connection->lock);
4018
- {
4019
- connection->activeReadWriteTransaction = NO;
4020
- }
4021
- OSSpinLockUnlock(&connection->lock);
4022
- }
4023
-
4024
- ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
4025
- #pragma mark Exceptions
4026
- ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
4027
-
4028
- - (NSException *)implicitlyEndingLongLivedReadTransactionException
4029
- {
4030
- NSString *reason = [NSString stringWithFormat:
4031
- @"Database <%@: %p> had long-lived read transaction implicitly ended by executing a read-write transaction.",
4032
- NSStringFromClass([self class]), self];
4033
-
4034
- NSDictionary *userInfo = @{ NSLocalizedRecoverySuggestionErrorKey:
4035
- @"Connections with long-lived read transactions are generally designed to be read-only connections."
4036
- @" As such, you'll want to use a separate connection for the read-write transaction."
4037
- @" If this is not the case (very, very, very rare) you can disable this exception using"
4038
- @" disableExceptionsForImplicitlyEndingLongLivedReadTransaction."
4039
- @" Keep in mind that if you disable these exceptions without understanding why they're enabled by default"
4040
- @" then you're inevitably creating a hard-to-reproduce bug and likely a few crashes too."
4041
- @" Don't be lazy. You've been warned."};
4042
-
4043
- return [NSException exceptionWithName:@"YapDatabaseException" reason:reason userInfo:userInfo];
4044
- }
4045
-
4046
- @end