motion-yapper 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (348) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +20 -0
  3. data/.travis.yml +6 -0
  4. data/Gemfile +5 -0
  5. data/Gemfile.lock +63 -0
  6. data/README.md +16 -0
  7. data/Rakefile +20 -0
  8. data/app/app_delegate.rb +5 -0
  9. data/lib/yapper.rb +30 -0
  10. data/lib/yapper/attachment.rb +48 -0
  11. data/lib/yapper/bson.rb +20 -0
  12. data/lib/yapper/config.rb +18 -0
  13. data/lib/yapper/db.rb +151 -0
  14. data/lib/yapper/document.rb +54 -0
  15. data/lib/yapper/document/attachment.rb +26 -0
  16. data/lib/yapper/document/callbacks.rb +86 -0
  17. data/lib/yapper/document/persistance.rb +171 -0
  18. data/lib/yapper/document/relation.rb +84 -0
  19. data/lib/yapper/document/selection.rb +100 -0
  20. data/lib/yapper/extensions.rb +80 -0
  21. data/lib/yapper/log.rb +5 -0
  22. data/lib/yapper/sync.rb +134 -0
  23. data/lib/yapper/sync/data.rb +12 -0
  24. data/lib/yapper/sync/event.rb +194 -0
  25. data/lib/yapper/sync/queue.rb +164 -0
  26. data/lib/yapper/timestamps.rb +16 -0
  27. data/lib/yapper/version.rb +3 -0
  28. data/lib/yapper/yapper.rb +2 -0
  29. data/spec/helpers/time_helper.rb +3 -0
  30. data/spec/integration/all_spec.rb +40 -0
  31. data/spec/integration/callback_spec.rb +87 -0
  32. data/spec/integration/db_spec.rb +40 -0
  33. data/spec/integration/find_spec.rb +51 -0
  34. data/spec/integration/persistance_spec.rb +118 -0
  35. data/spec/integration/relation_spec.rb +174 -0
  36. data/spec/integration/sync_spec.rb +42 -0
  37. data/spec/integration/timestamps_spec.rb +34 -0
  38. data/spec/integration/types_spec.rb +23 -0
  39. data/spec/integration/when_spec.rb +29 -0
  40. data/spec/integration/where_spec.rb +132 -0
  41. data/vendor/Podfile.lock +24 -0
  42. data/vendor/Pods/AFNetworking/AFNetworking/AFHTTPClient.h +641 -0
  43. data/vendor/Pods/AFNetworking/AFNetworking/AFHTTPClient.m +1396 -0
  44. data/vendor/Pods/AFNetworking/AFNetworking/AFHTTPRequestOperation.h +133 -0
  45. data/vendor/Pods/AFNetworking/AFNetworking/AFHTTPRequestOperation.m +327 -0
  46. data/vendor/Pods/AFNetworking/AFNetworking/AFImageRequestOperation.h +113 -0
  47. data/vendor/Pods/AFNetworking/AFNetworking/AFImageRequestOperation.m +321 -0
  48. data/vendor/Pods/AFNetworking/AFNetworking/AFJSONRequestOperation.h +71 -0
  49. data/vendor/Pods/AFNetworking/AFNetworking/AFJSONRequestOperation.m +150 -0
  50. data/vendor/Pods/AFNetworking/AFNetworking/AFNetworkActivityIndicatorManager.h +75 -0
  51. data/vendor/Pods/AFNetworking/AFNetworking/AFNetworkActivityIndicatorManager.m +157 -0
  52. data/vendor/Pods/AFNetworking/AFNetworking/AFNetworking.h +43 -0
  53. data/vendor/Pods/AFNetworking/AFNetworking/AFPropertyListRequestOperation.h +68 -0
  54. data/vendor/Pods/AFNetworking/AFNetworking/AFPropertyListRequestOperation.m +143 -0
  55. data/vendor/Pods/AFNetworking/AFNetworking/AFURLConnectionOperation.h +370 -0
  56. data/vendor/Pods/AFNetworking/AFNetworking/AFURLConnectionOperation.m +848 -0
  57. data/vendor/Pods/AFNetworking/AFNetworking/AFXMLRequestOperation.h +89 -0
  58. data/vendor/Pods/AFNetworking/AFNetworking/AFXMLRequestOperation.m +167 -0
  59. data/vendor/Pods/AFNetworking/AFNetworking/UIImageView+AFNetworking.h +78 -0
  60. data/vendor/Pods/AFNetworking/AFNetworking/UIImageView+AFNetworking.m +191 -0
  61. data/vendor/Pods/AFNetworking/LICENSE +19 -0
  62. data/vendor/Pods/AFNetworking/README.md +208 -0
  63. data/vendor/Pods/BuildHeaders/AFNetworking/AFHTTPClient.h +641 -0
  64. data/vendor/Pods/BuildHeaders/AFNetworking/AFHTTPRequestOperation.h +133 -0
  65. data/vendor/Pods/BuildHeaders/AFNetworking/AFImageRequestOperation.h +113 -0
  66. data/vendor/Pods/BuildHeaders/AFNetworking/AFJSONRequestOperation.h +71 -0
  67. data/vendor/Pods/BuildHeaders/AFNetworking/AFNetworkActivityIndicatorManager.h +75 -0
  68. data/vendor/Pods/BuildHeaders/AFNetworking/AFNetworking.h +43 -0
  69. data/vendor/Pods/BuildHeaders/AFNetworking/AFPropertyListRequestOperation.h +68 -0
  70. data/vendor/Pods/BuildHeaders/AFNetworking/AFURLConnectionOperation.h +370 -0
  71. data/vendor/Pods/BuildHeaders/AFNetworking/AFXMLRequestOperation.h +89 -0
  72. data/vendor/Pods/BuildHeaders/AFNetworking/UIImageView+AFNetworking.h +78 -0
  73. data/vendor/Pods/BuildHeaders/CocoaLumberjack/ContextFilterLogFormatter.h +65 -0
  74. data/vendor/Pods/BuildHeaders/CocoaLumberjack/DDASLLogger.h +41 -0
  75. data/vendor/Pods/BuildHeaders/CocoaLumberjack/DDAbstractDatabaseLogger.h +102 -0
  76. data/vendor/Pods/BuildHeaders/CocoaLumberjack/DDFileLogger.h +334 -0
  77. data/vendor/Pods/BuildHeaders/CocoaLumberjack/DDLog.h +601 -0
  78. data/vendor/Pods/BuildHeaders/CocoaLumberjack/DDTTYLogger.h +167 -0
  79. data/vendor/Pods/BuildHeaders/CocoaLumberjack/DispatchQueueLogFormatter.h +116 -0
  80. data/vendor/Pods/BuildHeaders/NSData+MD5Digest/NSData+MD5Digest.h +18 -0
  81. data/vendor/Pods/BuildHeaders/Reachability/Reachability.h +109 -0
  82. data/vendor/Pods/BuildHeaders/YapDatabase/YapCache.h +90 -0
  83. data/vendor/Pods/BuildHeaders/YapDatabase/YapCollectionKey.h +20 -0
  84. data/vendor/Pods/BuildHeaders/YapDatabase/YapDatabase.h +547 -0
  85. data/vendor/Pods/BuildHeaders/YapDatabase/YapDatabaseConnection.h +447 -0
  86. data/vendor/Pods/BuildHeaders/YapDatabase/YapDatabaseConnectionState.h +29 -0
  87. data/vendor/Pods/BuildHeaders/YapDatabase/YapDatabaseDefaults.h +37 -0
  88. data/vendor/Pods/BuildHeaders/YapDatabase/YapDatabaseExtension.h +15 -0
  89. data/vendor/Pods/BuildHeaders/YapDatabase/YapDatabaseExtensionConnection.h +11 -0
  90. data/vendor/Pods/BuildHeaders/YapDatabase/YapDatabaseExtensionPrivate.h +440 -0
  91. data/vendor/Pods/BuildHeaders/YapDatabase/YapDatabaseExtensionTransaction.h +11 -0
  92. data/vendor/Pods/BuildHeaders/YapDatabase/YapDatabaseFilteredView.h +108 -0
  93. data/vendor/Pods/BuildHeaders/YapDatabase/YapDatabaseFilteredViewConnection.h +12 -0
  94. data/vendor/Pods/BuildHeaders/YapDatabase/YapDatabaseFilteredViewPrivate.h +19 -0
  95. data/vendor/Pods/BuildHeaders/YapDatabase/YapDatabaseFilteredViewTransaction.h +39 -0
  96. data/vendor/Pods/BuildHeaders/YapDatabase/YapDatabaseFullTextSearch.h +89 -0
  97. data/vendor/Pods/BuildHeaders/YapDatabase/YapDatabaseFullTextSearchConnection.h +32 -0
  98. data/vendor/Pods/BuildHeaders/YapDatabase/YapDatabaseFullTextSearchPrivate.h +69 -0
  99. data/vendor/Pods/BuildHeaders/YapDatabase/YapDatabaseFullTextSearchSnippetOptions.h +79 -0
  100. data/vendor/Pods/BuildHeaders/YapDatabase/YapDatabaseFullTextSearchTransaction.h +68 -0
  101. data/vendor/Pods/BuildHeaders/YapDatabase/YapDatabaseLogging.h +158 -0
  102. data/vendor/Pods/BuildHeaders/YapDatabase/YapDatabaseManager.h +17 -0
  103. data/vendor/Pods/BuildHeaders/YapDatabase/YapDatabasePrivate.h +424 -0
  104. data/vendor/Pods/BuildHeaders/YapDatabase/YapDatabaseQuery.h +42 -0
  105. data/vendor/Pods/BuildHeaders/YapDatabase/YapDatabaseSecondaryIndex.h +100 -0
  106. data/vendor/Pods/BuildHeaders/YapDatabase/YapDatabaseSecondaryIndexConnection.h +33 -0
  107. data/vendor/Pods/BuildHeaders/YapDatabase/YapDatabaseSecondaryIndexPrivate.h +73 -0
  108. data/vendor/Pods/BuildHeaders/YapDatabase/YapDatabaseSecondaryIndexSetup.h +33 -0
  109. data/vendor/Pods/BuildHeaders/YapDatabase/YapDatabaseSecondaryIndexTransaction.h +58 -0
  110. data/vendor/Pods/BuildHeaders/YapDatabase/YapDatabaseStatement.h +13 -0
  111. data/vendor/Pods/BuildHeaders/YapDatabase/YapDatabaseString.h +121 -0
  112. data/vendor/Pods/BuildHeaders/YapDatabase/YapDatabaseTransaction.h +541 -0
  113. data/vendor/Pods/BuildHeaders/YapDatabase/YapDatabaseView.h +186 -0
  114. data/vendor/Pods/BuildHeaders/YapDatabase/YapDatabaseViewChange.h +272 -0
  115. data/vendor/Pods/BuildHeaders/YapDatabase/YapDatabaseViewChangePrivate.h +94 -0
  116. data/vendor/Pods/BuildHeaders/YapDatabase/YapDatabaseViewConnection.h +115 -0
  117. data/vendor/Pods/BuildHeaders/YapDatabase/YapDatabaseViewMappings.h +825 -0
  118. data/vendor/Pods/BuildHeaders/YapDatabase/YapDatabaseViewMappingsPrivate.h +72 -0
  119. data/vendor/Pods/BuildHeaders/YapDatabase/YapDatabaseViewOptions.h +56 -0
  120. data/vendor/Pods/BuildHeaders/YapDatabase/YapDatabaseViewPage.h +36 -0
  121. data/vendor/Pods/BuildHeaders/YapDatabase/YapDatabaseViewPageMetadata.h +27 -0
  122. data/vendor/Pods/BuildHeaders/YapDatabase/YapDatabaseViewPrivate.h +153 -0
  123. data/vendor/Pods/BuildHeaders/YapDatabase/YapDatabaseViewRangeOptions.h +330 -0
  124. data/vendor/Pods/BuildHeaders/YapDatabase/YapDatabaseViewRangeOptionsPrivate.h +17 -0
  125. data/vendor/Pods/BuildHeaders/YapDatabase/YapDatabaseViewTransaction.h +447 -0
  126. data/vendor/Pods/BuildHeaders/YapDatabase/YapMemoryTable.h +74 -0
  127. data/vendor/Pods/BuildHeaders/YapDatabase/YapNull.h +17 -0
  128. data/vendor/Pods/BuildHeaders/YapDatabase/YapSet.h +41 -0
  129. data/vendor/Pods/BuildHeaders/YapDatabase/YapTouch.h +15 -0
  130. data/vendor/Pods/CocoaLumberjack/LICENSE.txt +18 -0
  131. data/vendor/Pods/CocoaLumberjack/Lumberjack/DDASLLogger.h +41 -0
  132. data/vendor/Pods/CocoaLumberjack/Lumberjack/DDASLLogger.m +99 -0
  133. data/vendor/Pods/CocoaLumberjack/Lumberjack/DDAbstractDatabaseLogger.h +102 -0
  134. data/vendor/Pods/CocoaLumberjack/Lumberjack/DDAbstractDatabaseLogger.m +727 -0
  135. data/vendor/Pods/CocoaLumberjack/Lumberjack/DDFileLogger.h +334 -0
  136. data/vendor/Pods/CocoaLumberjack/Lumberjack/DDFileLogger.m +1353 -0
  137. data/vendor/Pods/CocoaLumberjack/Lumberjack/DDLog.h +601 -0
  138. data/vendor/Pods/CocoaLumberjack/Lumberjack/DDLog.m +1083 -0
  139. data/vendor/Pods/CocoaLumberjack/Lumberjack/DDTTYLogger.h +167 -0
  140. data/vendor/Pods/CocoaLumberjack/Lumberjack/DDTTYLogger.m +1479 -0
  141. data/vendor/Pods/CocoaLumberjack/Lumberjack/Extensions/ContextFilterLogFormatter.h +65 -0
  142. data/vendor/Pods/CocoaLumberjack/Lumberjack/Extensions/ContextFilterLogFormatter.m +191 -0
  143. data/vendor/Pods/CocoaLumberjack/Lumberjack/Extensions/DispatchQueueLogFormatter.h +116 -0
  144. data/vendor/Pods/CocoaLumberjack/Lumberjack/Extensions/DispatchQueueLogFormatter.m +251 -0
  145. data/vendor/Pods/CocoaLumberjack/Lumberjack/Extensions/README.txt +7 -0
  146. data/vendor/Pods/CocoaLumberjack/README.markdown +37 -0
  147. data/vendor/Pods/Headers/AFNetworking/AFHTTPClient.h +641 -0
  148. data/vendor/Pods/Headers/AFNetworking/AFHTTPRequestOperation.h +133 -0
  149. data/vendor/Pods/Headers/AFNetworking/AFImageRequestOperation.h +113 -0
  150. data/vendor/Pods/Headers/AFNetworking/AFJSONRequestOperation.h +71 -0
  151. data/vendor/Pods/Headers/AFNetworking/AFNetworkActivityIndicatorManager.h +75 -0
  152. data/vendor/Pods/Headers/AFNetworking/AFNetworking.h +43 -0
  153. data/vendor/Pods/Headers/AFNetworking/AFPropertyListRequestOperation.h +68 -0
  154. data/vendor/Pods/Headers/AFNetworking/AFURLConnectionOperation.h +370 -0
  155. data/vendor/Pods/Headers/AFNetworking/AFXMLRequestOperation.h +89 -0
  156. data/vendor/Pods/Headers/AFNetworking/UIImageView+AFNetworking.h +78 -0
  157. data/vendor/Pods/Headers/CocoaLumberjack/ContextFilterLogFormatter.h +65 -0
  158. data/vendor/Pods/Headers/CocoaLumberjack/DDASLLogger.h +41 -0
  159. data/vendor/Pods/Headers/CocoaLumberjack/DDAbstractDatabaseLogger.h +102 -0
  160. data/vendor/Pods/Headers/CocoaLumberjack/DDFileLogger.h +334 -0
  161. data/vendor/Pods/Headers/CocoaLumberjack/DDLog.h +601 -0
  162. data/vendor/Pods/Headers/CocoaLumberjack/DDTTYLogger.h +167 -0
  163. data/vendor/Pods/Headers/CocoaLumberjack/DispatchQueueLogFormatter.h +116 -0
  164. data/vendor/Pods/Headers/NSData+MD5Digest/NSData+MD5Digest.h +18 -0
  165. data/vendor/Pods/Headers/Reachability/Reachability.h +109 -0
  166. data/vendor/Pods/Headers/YapDatabase/YapCache.h +90 -0
  167. data/vendor/Pods/Headers/YapDatabase/YapCollectionKey.h +20 -0
  168. data/vendor/Pods/Headers/YapDatabase/YapDatabase.h +547 -0
  169. data/vendor/Pods/Headers/YapDatabase/YapDatabaseConnection.h +447 -0
  170. data/vendor/Pods/Headers/YapDatabase/YapDatabaseConnectionState.h +29 -0
  171. data/vendor/Pods/Headers/YapDatabase/YapDatabaseDefaults.h +37 -0
  172. data/vendor/Pods/Headers/YapDatabase/YapDatabaseExtension.h +15 -0
  173. data/vendor/Pods/Headers/YapDatabase/YapDatabaseExtensionConnection.h +11 -0
  174. data/vendor/Pods/Headers/YapDatabase/YapDatabaseExtensionPrivate.h +440 -0
  175. data/vendor/Pods/Headers/YapDatabase/YapDatabaseExtensionTransaction.h +11 -0
  176. data/vendor/Pods/Headers/YapDatabase/YapDatabaseFilteredView.h +108 -0
  177. data/vendor/Pods/Headers/YapDatabase/YapDatabaseFilteredViewConnection.h +12 -0
  178. data/vendor/Pods/Headers/YapDatabase/YapDatabaseFilteredViewPrivate.h +19 -0
  179. data/vendor/Pods/Headers/YapDatabase/YapDatabaseFilteredViewTransaction.h +39 -0
  180. data/vendor/Pods/Headers/YapDatabase/YapDatabaseFullTextSearch.h +89 -0
  181. data/vendor/Pods/Headers/YapDatabase/YapDatabaseFullTextSearchConnection.h +32 -0
  182. data/vendor/Pods/Headers/YapDatabase/YapDatabaseFullTextSearchPrivate.h +69 -0
  183. data/vendor/Pods/Headers/YapDatabase/YapDatabaseFullTextSearchSnippetOptions.h +79 -0
  184. data/vendor/Pods/Headers/YapDatabase/YapDatabaseFullTextSearchTransaction.h +68 -0
  185. data/vendor/Pods/Headers/YapDatabase/YapDatabaseLogging.h +158 -0
  186. data/vendor/Pods/Headers/YapDatabase/YapDatabaseManager.h +17 -0
  187. data/vendor/Pods/Headers/YapDatabase/YapDatabasePrivate.h +424 -0
  188. data/vendor/Pods/Headers/YapDatabase/YapDatabaseQuery.h +42 -0
  189. data/vendor/Pods/Headers/YapDatabase/YapDatabaseSecondaryIndex.h +100 -0
  190. data/vendor/Pods/Headers/YapDatabase/YapDatabaseSecondaryIndexConnection.h +33 -0
  191. data/vendor/Pods/Headers/YapDatabase/YapDatabaseSecondaryIndexPrivate.h +73 -0
  192. data/vendor/Pods/Headers/YapDatabase/YapDatabaseSecondaryIndexSetup.h +33 -0
  193. data/vendor/Pods/Headers/YapDatabase/YapDatabaseSecondaryIndexTransaction.h +58 -0
  194. data/vendor/Pods/Headers/YapDatabase/YapDatabaseStatement.h +13 -0
  195. data/vendor/Pods/Headers/YapDatabase/YapDatabaseString.h +121 -0
  196. data/vendor/Pods/Headers/YapDatabase/YapDatabaseTransaction.h +541 -0
  197. data/vendor/Pods/Headers/YapDatabase/YapDatabaseView.h +186 -0
  198. data/vendor/Pods/Headers/YapDatabase/YapDatabaseViewChange.h +272 -0
  199. data/vendor/Pods/Headers/YapDatabase/YapDatabaseViewChangePrivate.h +94 -0
  200. data/vendor/Pods/Headers/YapDatabase/YapDatabaseViewConnection.h +115 -0
  201. data/vendor/Pods/Headers/YapDatabase/YapDatabaseViewMappings.h +825 -0
  202. data/vendor/Pods/Headers/YapDatabase/YapDatabaseViewMappingsPrivate.h +72 -0
  203. data/vendor/Pods/Headers/YapDatabase/YapDatabaseViewOptions.h +56 -0
  204. data/vendor/Pods/Headers/YapDatabase/YapDatabaseViewPage.h +36 -0
  205. data/vendor/Pods/Headers/YapDatabase/YapDatabaseViewPageMetadata.h +27 -0
  206. data/vendor/Pods/Headers/YapDatabase/YapDatabaseViewPrivate.h +153 -0
  207. data/vendor/Pods/Headers/YapDatabase/YapDatabaseViewRangeOptions.h +330 -0
  208. data/vendor/Pods/Headers/YapDatabase/YapDatabaseViewRangeOptionsPrivate.h +17 -0
  209. data/vendor/Pods/Headers/YapDatabase/YapDatabaseViewTransaction.h +447 -0
  210. data/vendor/Pods/Headers/YapDatabase/YapMemoryTable.h +74 -0
  211. data/vendor/Pods/Headers/YapDatabase/YapNull.h +17 -0
  212. data/vendor/Pods/Headers/YapDatabase/YapSet.h +41 -0
  213. data/vendor/Pods/Headers/YapDatabase/YapTouch.h +15 -0
  214. data/vendor/Pods/Headers/____Pods-AFNetworking-prefix.h +17 -0
  215. data/vendor/Pods/Headers/____Pods-CocoaLumberjack-prefix.h +5 -0
  216. data/vendor/Pods/Headers/____Pods-NSData+MD5Digest-prefix.h +5 -0
  217. data/vendor/Pods/Headers/____Pods-Reachability-prefix.h +5 -0
  218. data/vendor/Pods/Headers/____Pods-YapDatabase-prefix.h +5 -0
  219. data/vendor/Pods/Headers/____Pods-environment.h +38 -0
  220. data/vendor/Pods/Manifest.lock +24 -0
  221. data/vendor/Pods/NSData+MD5Digest/NSData+MD5Digest/NSData+MD5Digest.h +18 -0
  222. data/vendor/Pods/NSData+MD5Digest/NSData+MD5Digest/NSData+MD5Digest.m +39 -0
  223. data/vendor/Pods/NSData+MD5Digest/README.md +11 -0
  224. data/vendor/Pods/Pods-AFNetworking-Private.xcconfig +5 -0
  225. data/vendor/Pods/Pods-AFNetworking-dummy.m +5 -0
  226. data/vendor/Pods/Pods-AFNetworking-prefix.pch +17 -0
  227. data/vendor/Pods/Pods-AFNetworking.xcconfig +1 -0
  228. data/vendor/Pods/Pods-Acknowledgements.markdown +96 -0
  229. data/vendor/Pods/Pods-Acknowledgements.plist +142 -0
  230. data/vendor/Pods/Pods-CocoaLumberjack-Private.xcconfig +5 -0
  231. data/vendor/Pods/Pods-CocoaLumberjack-dummy.m +5 -0
  232. data/vendor/Pods/Pods-CocoaLumberjack-prefix.pch +5 -0
  233. data/vendor/Pods/Pods-CocoaLumberjack.xcconfig +0 -0
  234. data/vendor/Pods/Pods-NSData+MD5Digest-Private.xcconfig +5 -0
  235. data/vendor/Pods/Pods-NSData+MD5Digest-dummy.m +5 -0
  236. data/vendor/Pods/Pods-NSData+MD5Digest-prefix.pch +5 -0
  237. data/vendor/Pods/Pods-NSData+MD5Digest.xcconfig +0 -0
  238. data/vendor/Pods/Pods-Reachability-Private.xcconfig +5 -0
  239. data/vendor/Pods/Pods-Reachability-dummy.m +5 -0
  240. data/vendor/Pods/Pods-Reachability-prefix.pch +5 -0
  241. data/vendor/Pods/Pods-Reachability.xcconfig +1 -0
  242. data/vendor/Pods/Pods-YapDatabase-Private.xcconfig +5 -0
  243. data/vendor/Pods/Pods-YapDatabase-dummy.m +5 -0
  244. data/vendor/Pods/Pods-YapDatabase-prefix.pch +5 -0
  245. data/vendor/Pods/Pods-YapDatabase.xcconfig +1 -0
  246. data/vendor/Pods/Pods-dummy.m +5 -0
  247. data/vendor/Pods/Pods-environment.h +38 -0
  248. data/vendor/Pods/Pods-resources.sh +68 -0
  249. data/vendor/Pods/Pods.bridgesupport +5096 -0
  250. data/vendor/Pods/Pods.xcconfig +5 -0
  251. data/vendor/Pods/Pods.xcodeproj/project.pbxproj +5536 -0
  252. data/vendor/Pods/Reachability/LICENCE.txt +24 -0
  253. data/vendor/Pods/Reachability/README.md +65 -0
  254. data/vendor/Pods/Reachability/Reachability.h +109 -0
  255. data/vendor/Pods/Reachability/Reachability.m +527 -0
  256. data/vendor/Pods/YapDatabase/LICENSE.txt +18 -0
  257. data/vendor/Pods/YapDatabase/README.md +30 -0
  258. data/vendor/Pods/YapDatabase/YapDatabase/Extensions/FilteredViews/Internal/YapDatabaseFilteredViewPrivate.h +19 -0
  259. data/vendor/Pods/YapDatabase/YapDatabase/Extensions/FilteredViews/YapDatabaseFilteredView.h +108 -0
  260. data/vendor/Pods/YapDatabase/YapDatabase/Extensions/FilteredViews/YapDatabaseFilteredView.m +175 -0
  261. data/vendor/Pods/YapDatabase/YapDatabase/Extensions/FilteredViews/YapDatabaseFilteredViewConnection.h +12 -0
  262. data/vendor/Pods/YapDatabase/YapDatabase/Extensions/FilteredViews/YapDatabaseFilteredViewConnection.m +41 -0
  263. data/vendor/Pods/YapDatabase/YapDatabase/Extensions/FilteredViews/YapDatabaseFilteredViewTransaction.h +39 -0
  264. data/vendor/Pods/YapDatabase/YapDatabase/Extensions/FilteredViews/YapDatabaseFilteredViewTransaction.m +966 -0
  265. data/vendor/Pods/YapDatabase/YapDatabase/Extensions/FullTextSearch/Internal/YapDatabaseFullTextSearchPrivate.h +69 -0
  266. data/vendor/Pods/YapDatabase/YapDatabase/Extensions/FullTextSearch/YapDatabaseFullTextSearch.h +89 -0
  267. data/vendor/Pods/YapDatabase/YapDatabase/Extensions/FullTextSearch/YapDatabaseFullTextSearch.m +146 -0
  268. data/vendor/Pods/YapDatabase/YapDatabase/Extensions/FullTextSearch/YapDatabaseFullTextSearchConnection.h +32 -0
  269. data/vendor/Pods/YapDatabase/YapDatabase/Extensions/FullTextSearch/YapDatabaseFullTextSearchConnection.m +298 -0
  270. data/vendor/Pods/YapDatabase/YapDatabase/Extensions/FullTextSearch/YapDatabaseFullTextSearchSnippetOptions.h +79 -0
  271. data/vendor/Pods/YapDatabase/YapDatabase/Extensions/FullTextSearch/YapDatabaseFullTextSearchSnippetOptions.m +95 -0
  272. data/vendor/Pods/YapDatabase/YapDatabase/Extensions/FullTextSearch/YapDatabaseFullTextSearchTransaction.h +68 -0
  273. data/vendor/Pods/YapDatabase/YapDatabase/Extensions/FullTextSearch/YapDatabaseFullTextSearchTransaction.m +1352 -0
  274. data/vendor/Pods/YapDatabase/YapDatabase/Extensions/Protocol/Internal/YapDatabaseExtensionPrivate.h +440 -0
  275. data/vendor/Pods/YapDatabase/YapDatabase/Extensions/Protocol/YapDatabaseExtension.h +15 -0
  276. data/vendor/Pods/YapDatabase/YapDatabase/Extensions/Protocol/YapDatabaseExtension.m +58 -0
  277. data/vendor/Pods/YapDatabase/YapDatabase/Extensions/Protocol/YapDatabaseExtensionConnection.h +11 -0
  278. data/vendor/Pods/YapDatabase/YapDatabase/Extensions/Protocol/YapDatabaseExtensionConnection.m +46 -0
  279. data/vendor/Pods/YapDatabase/YapDatabase/Extensions/Protocol/YapDatabaseExtensionTransaction.h +11 -0
  280. data/vendor/Pods/YapDatabase/YapDatabase/Extensions/Protocol/YapDatabaseExtensionTransaction.m +180 -0
  281. data/vendor/Pods/YapDatabase/YapDatabase/Extensions/SecondaryIndex/Internal/YapDatabaseSecondaryIndexPrivate.h +73 -0
  282. data/vendor/Pods/YapDatabase/YapDatabase/Extensions/SecondaryIndex/YapDatabaseSecondaryIndex.h +100 -0
  283. data/vendor/Pods/YapDatabase/YapDatabase/Extensions/SecondaryIndex/YapDatabaseSecondaryIndex.m +149 -0
  284. data/vendor/Pods/YapDatabase/YapDatabase/Extensions/SecondaryIndex/YapDatabaseSecondaryIndexConnection.h +33 -0
  285. data/vendor/Pods/YapDatabase/YapDatabase/Extensions/SecondaryIndex/YapDatabaseSecondaryIndexConnection.m +330 -0
  286. data/vendor/Pods/YapDatabase/YapDatabase/Extensions/SecondaryIndex/YapDatabaseSecondaryIndexSetup.h +33 -0
  287. data/vendor/Pods/YapDatabase/YapDatabase/Extensions/SecondaryIndex/YapDatabaseSecondaryIndexSetup.m +184 -0
  288. data/vendor/Pods/YapDatabase/YapDatabase/Extensions/SecondaryIndex/YapDatabaseSecondaryIndexTransaction.h +58 -0
  289. data/vendor/Pods/YapDatabase/YapDatabase/Extensions/SecondaryIndex/YapDatabaseSecondaryIndexTransaction.m +1166 -0
  290. data/vendor/Pods/YapDatabase/YapDatabase/Extensions/Views/Internal/YapDatabaseViewChangePrivate.h +94 -0
  291. data/vendor/Pods/YapDatabase/YapDatabase/Extensions/Views/Internal/YapDatabaseViewMappingsPrivate.h +72 -0
  292. data/vendor/Pods/YapDatabase/YapDatabase/Extensions/Views/Internal/YapDatabaseViewPage.h +36 -0
  293. data/vendor/Pods/YapDatabase/YapDatabase/Extensions/Views/Internal/YapDatabaseViewPage.mm +296 -0
  294. data/vendor/Pods/YapDatabase/YapDatabase/Extensions/Views/Internal/YapDatabaseViewPageMetadata.h +27 -0
  295. data/vendor/Pods/YapDatabase/YapDatabase/Extensions/Views/Internal/YapDatabaseViewPageMetadata.m +28 -0
  296. data/vendor/Pods/YapDatabase/YapDatabase/Extensions/Views/Internal/YapDatabaseViewPrivate.h +153 -0
  297. data/vendor/Pods/YapDatabase/YapDatabase/Extensions/Views/Internal/YapDatabaseViewRangeOptionsPrivate.h +17 -0
  298. data/vendor/Pods/YapDatabase/YapDatabase/Extensions/Views/Utilities/YapDatabaseViewChange.h +272 -0
  299. data/vendor/Pods/YapDatabase/YapDatabase/Extensions/Views/Utilities/YapDatabaseViewChange.m +2494 -0
  300. data/vendor/Pods/YapDatabase/YapDatabase/Extensions/Views/Utilities/YapDatabaseViewMappings.h +825 -0
  301. data/vendor/Pods/YapDatabase/YapDatabase/Extensions/Views/Utilities/YapDatabaseViewMappings.m +1567 -0
  302. data/vendor/Pods/YapDatabase/YapDatabase/Extensions/Views/Utilities/YapDatabaseViewRangeOptions.h +330 -0
  303. data/vendor/Pods/YapDatabase/YapDatabase/Extensions/Views/Utilities/YapDatabaseViewRangeOptions.m +141 -0
  304. data/vendor/Pods/YapDatabase/YapDatabase/Extensions/Views/YapDatabaseView.h +186 -0
  305. data/vendor/Pods/YapDatabase/YapDatabase/Extensions/Views/YapDatabaseView.m +191 -0
  306. data/vendor/Pods/YapDatabase/YapDatabase/Extensions/Views/YapDatabaseViewConnection.h +115 -0
  307. data/vendor/Pods/YapDatabase/YapDatabase/Extensions/Views/YapDatabaseViewConnection.m +897 -0
  308. data/vendor/Pods/YapDatabase/YapDatabase/Extensions/Views/YapDatabaseViewOptions.h +56 -0
  309. data/vendor/Pods/YapDatabase/YapDatabase/Extensions/Views/YapDatabaseViewOptions.m +27 -0
  310. data/vendor/Pods/YapDatabase/YapDatabase/Extensions/Views/YapDatabaseViewTransaction.h +447 -0
  311. data/vendor/Pods/YapDatabase/YapDatabase/Extensions/Views/YapDatabaseViewTransaction.m +4505 -0
  312. data/vendor/Pods/YapDatabase/YapDatabase/Internal/YapCache.h +90 -0
  313. data/vendor/Pods/YapDatabase/YapDatabase/Internal/YapCache.m +453 -0
  314. data/vendor/Pods/YapDatabase/YapDatabase/Internal/YapDatabaseConnectionState.h +29 -0
  315. data/vendor/Pods/YapDatabase/YapDatabase/Internal/YapDatabaseConnectionState.m +48 -0
  316. data/vendor/Pods/YapDatabase/YapDatabase/Internal/YapDatabaseDefaults.h +37 -0
  317. data/vendor/Pods/YapDatabase/YapDatabase/Internal/YapDatabaseDefaults.m +83 -0
  318. data/vendor/Pods/YapDatabase/YapDatabase/Internal/YapDatabaseLogging.h +158 -0
  319. data/vendor/Pods/YapDatabase/YapDatabase/Internal/YapDatabaseLogging.m +73 -0
  320. data/vendor/Pods/YapDatabase/YapDatabase/Internal/YapDatabaseManager.h +17 -0
  321. data/vendor/Pods/YapDatabase/YapDatabase/Internal/YapDatabaseManager.m +56 -0
  322. data/vendor/Pods/YapDatabase/YapDatabase/Internal/YapDatabasePrivate.h +424 -0
  323. data/vendor/Pods/YapDatabase/YapDatabase/Internal/YapDatabaseStatement.h +13 -0
  324. data/vendor/Pods/YapDatabase/YapDatabase/Internal/YapDatabaseStatement.m +26 -0
  325. data/vendor/Pods/YapDatabase/YapDatabase/Internal/YapDatabaseString.h +121 -0
  326. data/vendor/Pods/YapDatabase/YapDatabase/Internal/YapMemoryTable.h +74 -0
  327. data/vendor/Pods/YapDatabase/YapDatabase/Internal/YapMemoryTable.m +603 -0
  328. data/vendor/Pods/YapDatabase/YapDatabase/Internal/YapNull.h +17 -0
  329. data/vendor/Pods/YapDatabase/YapDatabase/Internal/YapNull.m +31 -0
  330. data/vendor/Pods/YapDatabase/YapDatabase/Internal/YapTouch.h +15 -0
  331. data/vendor/Pods/YapDatabase/YapDatabase/Internal/YapTouch.m +31 -0
  332. data/vendor/Pods/YapDatabase/YapDatabase/Utilities/YapCollectionKey.h +20 -0
  333. data/vendor/Pods/YapDatabase/YapDatabase/Utilities/YapCollectionKey.m +194 -0
  334. data/vendor/Pods/YapDatabase/YapDatabase/Utilities/YapDatabaseQuery.h +42 -0
  335. data/vendor/Pods/YapDatabase/YapDatabase/Utilities/YapDatabaseQuery.m +96 -0
  336. data/vendor/Pods/YapDatabase/YapDatabase/Utilities/YapSet.h +41 -0
  337. data/vendor/Pods/YapDatabase/YapDatabase/Utilities/YapSet.m +82 -0
  338. data/vendor/Pods/YapDatabase/YapDatabase/YapDatabase.h +547 -0
  339. data/vendor/Pods/YapDatabase/YapDatabase/YapDatabase.m +2022 -0
  340. data/vendor/Pods/YapDatabase/YapDatabase/YapDatabaseConnection.h +447 -0
  341. data/vendor/Pods/YapDatabase/YapDatabase/YapDatabaseConnection.m +3874 -0
  342. data/vendor/Pods/YapDatabase/YapDatabase/YapDatabaseTransaction.h +541 -0
  343. data/vendor/Pods/YapDatabase/YapDatabase/YapDatabaseTransaction.m +5282 -0
  344. data/vendor/YapDatabaseRubyMotion/YapDatabaseRubyMotion.bridgesupport +16 -0
  345. data/vendor/YapDatabaseRubyMotion/YapDatabaseRubyMotion.h +13 -0
  346. data/vendor/YapDatabaseRubyMotion/YapDatabaseRubyMotion.m +20 -0
  347. data/yapper.gemspec +24 -0
  348. metadata +458 -0
@@ -0,0 +1,2022 @@
1
+ #import "YapDatabase.h"
2
+ #import "YapDatabasePrivate.h"
3
+ #import "YapDatabaseExtensionPrivate.h"
4
+ #import "YapCollectionKey.h"
5
+ #import "YapDatabaseManager.h"
6
+ #import "YapDatabaseConnectionState.h"
7
+ #import "YapDatabaseLogging.h"
8
+
9
+ #import "sqlite3.h"
10
+
11
+ #import <libkern/OSAtomic.h>
12
+
13
+ #if ! __has_feature(objc_arc)
14
+ #warning This file must be compiled with ARC. Use -fobjc-arc flag (or convert project to ARC).
15
+ #endif
16
+
17
+ /**
18
+ * Define log level for this file: OFF, ERROR, WARN, INFO, VERBOSE
19
+ * See YapDatabaseLogging.h for more information.
20
+ **/
21
+ #if DEBUG
22
+ static const int ydbLogLevel = YDB_LOG_LEVEL_INFO;
23
+ #else
24
+ static const int ydbLogLevel = YDB_LOG_LEVEL_WARN;
25
+ #endif
26
+
27
+ NSString *const YapDatabaseModifiedNotification = @"YapDatabaseModifiedNotification";
28
+
29
+ NSString *const YapDatabaseSnapshotKey = @"snapshot";
30
+ NSString *const YapDatabaseConnectionKey = @"connection";
31
+ NSString *const YapDatabaseExtensionsKey = @"extensions";
32
+ NSString *const YapDatabaseCustomKey = @"custom";
33
+
34
+ NSString *const YapDatabaseObjectChangesKey = @"objectChanges";
35
+ NSString *const YapDatabaseMetadataChangesKey = @"metadataChanges";
36
+ NSString *const YapDatabaseRemovedKeysKey = @"removedKeys";
37
+ NSString *const YapDatabaseRemovedCollectionsKey = @"removedCollections";
38
+ NSString *const YapDatabaseAllKeysRemovedKey = @"allKeysRemoved";
39
+
40
+ NSString *const YapDatabaseRegisteredExtensionsKey = @"registeredExtensions";
41
+ NSString *const YapDatabaseRegisteredTablesKey = @"registeredTables";
42
+ NSString *const YapDatabaseExtensionsOrderKey = @"extensionsOrder";
43
+ NSString *const YapDatabaseNotificationKey = @"notification";
44
+
45
+ /**
46
+ * The database version is stored (via pragma user_version) to sqlite.
47
+ * It is used to represent the version of the userlying architecture of YapDatabase.
48
+ * In the event of future changes to the sqlite underpinnings of YapDatabase,
49
+ * the version can be consulted to allow for proper on-the-fly upgrades.
50
+ * For more information, see the upgradeTable method.
51
+ **/
52
+ #define YAP_DATABASE_CURRENT_VERION 3
53
+
54
+ /**
55
+ * Default values
56
+ **/
57
+ #define DEFAULT_MAX_CONNECTION_POOL_COUNT 5 // connections
58
+ #define DEFAULT_CONNECTION_POOL_LIFETIME 90.0 // seconds
59
+
60
+
61
+ @implementation YapDatabase
62
+
63
+ /**
64
+ * The default serializer & deserializer use NSCoding (NSKeyedArchiver & NSKeyedUnarchiver).
65
+ * Thus the objects need only support the NSCoding protocol.
66
+ **/
67
+ + (YapDatabaseSerializer)defaultSerializer
68
+ {
69
+ return ^ NSData* (NSString *collection, NSString *key, id object){
70
+ return [NSKeyedArchiver archivedDataWithRootObject:object];
71
+ };
72
+ }
73
+
74
+ /**
75
+ * The default serializer & deserializer use NSCoding (NSKeyedArchiver & NSKeyedUnarchiver).
76
+ * Thus the objects need only support the NSCoding protocol.
77
+ **/
78
+ + (YapDatabaseDeserializer)defaultDeserializer
79
+ {
80
+ return ^ id (NSString *collection, NSString *key, NSData *data){
81
+ return [NSKeyedUnarchiver unarchiveObjectWithData:data];
82
+ };
83
+ }
84
+
85
+ /**
86
+ * Property lists ONLY support the following: NSData, NSString, NSArray, NSDictionary, NSDate, and NSNumber.
87
+ * Property lists are highly optimized and are used extensively by Apple.
88
+ *
89
+ * Property lists make a good fit when your existing code already uses them,
90
+ * such as replacing NSUserDefaults with a database.
91
+ **/
92
+ + (YapDatabaseSerializer)propertyListSerializer
93
+ {
94
+ return ^ NSData* (NSString *collection, NSString *key, id object){
95
+ return [NSPropertyListSerialization dataWithPropertyList:object
96
+ format:NSPropertyListBinaryFormat_v1_0
97
+ options:NSPropertyListImmutable
98
+ error:NULL];
99
+ };
100
+ }
101
+
102
+ /**
103
+ * Property lists ONLY support the following: NSData, NSString, NSArray, NSDictionary, NSDate, and NSNumber.
104
+ * Property lists are highly optimized and are used extensively by Apple.
105
+ *
106
+ * Property lists make a good fit when your existing code already uses them,
107
+ * such as replacing NSUserDefaults with a database.
108
+ **/
109
+ + (YapDatabaseDeserializer)propertyListDeserializer
110
+ {
111
+ return ^ id (NSString *collection, NSString *key, NSData *data){
112
+ return [NSPropertyListSerialization propertyListWithData:data options:0 format:NULL error:NULL];
113
+ };
114
+ }
115
+
116
+ /**
117
+ * A FASTER serializer than the default, if serializing ONLY a NSDate object.
118
+ * You may want to use timestampSerializer & timestampDeserializer if your metadata is simply an NSDate.
119
+ **/
120
+ + (YapDatabaseSerializer)timestampSerializer
121
+ {
122
+ return ^ NSData* (NSString *collection, NSString *key, id object) {
123
+
124
+ if ([object isKindOfClass:[NSDate class]])
125
+ {
126
+ NSTimeInterval timestamp = [(NSDate *)object timeIntervalSinceReferenceDate];
127
+
128
+ return [[NSData alloc] initWithBytes:(void *)&timestamp length:sizeof(NSTimeInterval)];
129
+ }
130
+ else
131
+ {
132
+ return [NSKeyedArchiver archivedDataWithRootObject:object];
133
+ }
134
+ };
135
+ }
136
+
137
+ /**
138
+ * A FASTER deserializer than the default, if deserializing data from timestampSerializer.
139
+ * You may want to use timestampSerializer & timestampDeserializer if your metadata is simply an NSDate.
140
+ **/
141
+ + (YapDatabaseDeserializer)timestampDeserializer
142
+ {
143
+ return ^ id (NSString *collection, NSString *key, NSData *data) {
144
+
145
+ if ([data length] == sizeof(NSTimeInterval))
146
+ {
147
+ NSTimeInterval timestamp;
148
+ memcpy((void *)&timestamp, [data bytes], sizeof(NSTimeInterval));
149
+
150
+ return [[NSDate alloc] initWithTimeIntervalSinceReferenceDate:timestamp];
151
+ }
152
+ else
153
+ {
154
+ return [NSKeyedUnarchiver unarchiveObjectWithData:data];
155
+ }
156
+ };
157
+ }
158
+
159
+ ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
160
+ #pragma mark Properties
161
+ ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
162
+
163
+ @synthesize databasePath;
164
+
165
+ @synthesize objectSerializer = objectSerializer;
166
+ @synthesize objectDeserializer = objectDeserializer;
167
+ @synthesize metadataSerializer = metadataSerializer;
168
+ @synthesize metadataDeserializer = metadataDeserializer;
169
+ @synthesize objectSanitizer = objectSanitizer;
170
+ @synthesize metadataSanitizer = metadataSanitizer;
171
+
172
+ ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
173
+ #pragma mark Init
174
+ ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
175
+
176
+ - (id)initWithPath:(NSString *)inPath
177
+ {
178
+ return [self initWithPath:inPath
179
+ objectSerializer:NULL
180
+ objectDeserializer:NULL
181
+ metadataSerializer:NULL
182
+ metadataDeserializer:NULL
183
+ objectSanitizer:NULL
184
+ metadataSanitizer:NULL];
185
+ }
186
+
187
+ - (id)initWithPath:(NSString *)inPath
188
+ serializer:(YapDatabaseSerializer)inSerializer
189
+ deserializer:(YapDatabaseDeserializer)inDeserializer
190
+ {
191
+ return [self initWithPath:inPath
192
+ objectSerializer:inSerializer
193
+ objectDeserializer:inDeserializer
194
+ metadataSerializer:inSerializer
195
+ metadataDeserializer:inDeserializer
196
+ objectSanitizer:NULL
197
+ metadataSanitizer:NULL];
198
+ }
199
+
200
+ - (id)initWithPath:(NSString *)inPath
201
+ serializer:(YapDatabaseSerializer)inSerializer
202
+ deserializer:(YapDatabaseDeserializer)inDeserializer
203
+ sanitizer:(YapDatabaseSanitizer)inSanitizer
204
+ {
205
+ return [self initWithPath:inPath
206
+ objectSerializer:inSerializer
207
+ objectDeserializer:inDeserializer
208
+ metadataSerializer:inSerializer
209
+ metadataDeserializer:inDeserializer
210
+ objectSanitizer:inSanitizer
211
+ metadataSanitizer:inSanitizer];
212
+ }
213
+
214
+ - (id)initWithPath:(NSString *)inPath objectSerializer:(YapDatabaseSerializer)inObjectSerializer
215
+ objectDeserializer:(YapDatabaseDeserializer)inObjectDeserializer
216
+ metadataSerializer:(YapDatabaseSerializer)inMetadataSerializer
217
+ metadataDeserializer:(YapDatabaseDeserializer)inMetadataDeserializer
218
+ {
219
+ return [self initWithPath:inPath
220
+ objectSerializer:inObjectSerializer
221
+ objectDeserializer:inObjectDeserializer
222
+ metadataSerializer:inMetadataSerializer
223
+ metadataDeserializer:inMetadataDeserializer
224
+ objectSanitizer:NULL
225
+ metadataSanitizer:NULL];
226
+ }
227
+
228
+ - (id)initWithPath:(NSString *)inPath objectSerializer:(YapDatabaseSerializer)inObjectSerializer
229
+ objectDeserializer:(YapDatabaseDeserializer)inObjectDeserializer
230
+ metadataSerializer:(YapDatabaseSerializer)inMetadataSerializer
231
+ metadataDeserializer:(YapDatabaseDeserializer)inMetadataDeserializer
232
+ objectSanitizer:(YapDatabaseSanitizer)inObjectSanitizer
233
+ metadataSanitizer:(YapDatabaseSanitizer)inMetadataSanitizer;
234
+ {
235
+ // First, standardize path.
236
+ // This allows clients to be lazy when passing paths.
237
+ NSString *path = [inPath stringByStandardizingPath];
238
+
239
+ // Ensure there is only a single database instance per file.
240
+ // However, clients may create as many connections as desired.
241
+ if (![YapDatabaseManager registerDatabaseForPath:path])
242
+ {
243
+ YDBLogError(@"Only a single database instance is allowed per file. "
244
+ @"For concurrency you create multiple connections from a single database instance.");
245
+ return nil;
246
+ }
247
+
248
+ if ((self = [super init]))
249
+ {
250
+ databasePath = path;
251
+
252
+ BOOL(^openConfigCreate)(void) = ^BOOL (void) { @autoreleasepool {
253
+
254
+ BOOL result = YES;
255
+
256
+ if (result) result = [self openDatabase];
257
+ if (result) result = [self configureDatabase];
258
+ if (result) result = [self createTables];
259
+
260
+ if (!result && db)
261
+ {
262
+ sqlite3_close(db);
263
+ db = NULL;
264
+ }
265
+
266
+ return result;
267
+ }};
268
+
269
+ BOOL result = openConfigCreate();
270
+ if (!result)
271
+ {
272
+ // There are a few reasons why the database might not open.
273
+ // One possibility is if the database file gets corrupt.
274
+ // In the event of a problem, we simply delete the database file.
275
+ // This isn't a big deal since we can just redownload the data.
276
+
277
+ // Delete the (possibly corrupt) database file.
278
+ [[NSFileManager defaultManager] removeItemAtPath:path error:NULL];
279
+
280
+ // Then try opening a database again.
281
+
282
+ result = openConfigCreate();
283
+
284
+ if (result) {
285
+ YDBLogInfo(@"Database corruption resolved (name=%@)", [path lastPathComponent]);
286
+ }
287
+ else {
288
+ YDBLogError(@"Database corruption unresolved (name=%@)", [path lastPathComponent]);
289
+ }
290
+ }
291
+ if (!result)
292
+ {
293
+ return nil;
294
+ }
295
+
296
+ internalQueue = dispatch_queue_create("YapDatabase-Internal", NULL);
297
+ checkpointQueue = dispatch_queue_create("YapDatabase-Checkpoint", NULL);
298
+ snapshotQueue = dispatch_queue_create("YapDatabase-Snapshot", NULL);
299
+ writeQueue = dispatch_queue_create("YapDatabase-Write", NULL);
300
+
301
+ changesets = [[NSMutableArray alloc] init];
302
+ connectionStates = [[NSMutableArray alloc] init];
303
+
304
+ defaults = [[YapDatabaseDefaults alloc] init];
305
+
306
+ registeredExtensions = [[NSDictionary alloc] init];
307
+ registeredTables = [[NSDictionary alloc] init];
308
+
309
+ extensionDependencies = [[NSDictionary alloc] init];
310
+ extensionsOrder = [[NSArray alloc] init];
311
+
312
+ maxConnectionPoolCount = DEFAULT_MAX_CONNECTION_POOL_COUNT;
313
+ connectionPoolLifetime = DEFAULT_CONNECTION_POOL_LIFETIME;
314
+
315
+ YapDatabaseSerializer defaultSerializer = nil;
316
+ YapDatabaseDeserializer defaultDeserializer = nil;
317
+
318
+ if (!inObjectSerializer || !inMetadataSerializer)
319
+ defaultSerializer = [[self class] defaultSerializer];
320
+
321
+ if (!inObjectDeserializer || !inMetadataDeserializer)
322
+ defaultDeserializer = [[self class] defaultDeserializer];
323
+
324
+ objectSerializer = inObjectSerializer ? inObjectSerializer : defaultSerializer;
325
+ objectDeserializer = inObjectDeserializer ? inObjectDeserializer : defaultDeserializer;
326
+
327
+ metadataSerializer = inMetadataSerializer ? inMetadataSerializer : defaultSerializer;
328
+ metadataDeserializer = inMetadataDeserializer ? inMetadataDeserializer : defaultDeserializer;
329
+
330
+ objectSanitizer = inObjectSanitizer;
331
+ metadataSanitizer = inMetadataSanitizer;
332
+
333
+ // Mark the queues so we can identify them.
334
+ // There are several methods whose use is restricted to within a certain queue.
335
+
336
+ IsOnSnapshotQueueKey = &IsOnSnapshotQueueKey;
337
+ dispatch_queue_set_specific(snapshotQueue, IsOnSnapshotQueueKey, IsOnSnapshotQueueKey, NULL);
338
+
339
+ IsOnWriteQueueKey = &IsOnWriteQueueKey;
340
+ dispatch_queue_set_specific(writeQueue, IsOnWriteQueueKey, IsOnWriteQueueKey, NULL);
341
+
342
+ // Complete database setup in the background
343
+ dispatch_async(snapshotQueue, ^{ @autoreleasepool {
344
+
345
+ [self upgradeTable];
346
+ [self prepare];
347
+ }});
348
+ }
349
+ return self;
350
+ }
351
+
352
+ - (void)dealloc
353
+ {
354
+ YDBLogVerbose(@"Dealloc <%@ %p: databaseName=%@>", [self class], self, [databasePath lastPathComponent]);
355
+
356
+ while ([connectionPoolValues count] > 0)
357
+ {
358
+ sqlite3 *aDb = (sqlite3 *)[[connectionPoolValues objectAtIndex:0] pointerValue];
359
+
360
+ int status = sqlite3_close(aDb);
361
+ if (status != SQLITE_OK)
362
+ {
363
+ YDBLogError(@"Error in sqlite_close: %d %s", status, sqlite3_errmsg(aDb));
364
+ }
365
+
366
+ [connectionPoolValues removeObjectAtIndex:0];
367
+ [connectionPoolDates removeObjectAtIndex:0];
368
+ }
369
+
370
+ if (connectionPoolTimer)
371
+ dispatch_source_cancel(connectionPoolTimer);
372
+
373
+ if (db) {
374
+ sqlite3_close(db);
375
+ db = NULL;
376
+ }
377
+
378
+ [YapDatabaseManager deregisterDatabaseForPath:databasePath];
379
+
380
+ #if !OS_OBJECT_USE_OBJC
381
+ if (internalQueue)
382
+ dispatch_release(internalQueue);
383
+ if (snapshotQueue)
384
+ dispatch_release(snapshotQueue);
385
+ if (writeQueue)
386
+ dispatch_release(writeQueue);
387
+ if (checkpointQueue)
388
+ dispatch_release(checkpointQueue);
389
+ #endif
390
+ }
391
+
392
+ ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
393
+ #pragma mark Setup
394
+ ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
395
+
396
+ /**
397
+ * Attempts to open (or create & open) the database connection.
398
+ **/
399
+ - (BOOL)openDatabase
400
+ {
401
+ // Open the database connection.
402
+ //
403
+ // We use SQLITE_OPEN_NOMUTEX to use the multi-thread threading mode,
404
+ // as we will be serializing access to the connection externally.
405
+
406
+ int flags = SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_NOMUTEX | SQLITE_OPEN_PRIVATECACHE;
407
+
408
+ int status = sqlite3_open_v2([databasePath UTF8String], &db, flags, NULL);
409
+ if (status != SQLITE_OK)
410
+ {
411
+ // There are a few reasons why the database might not open.
412
+ // One possibility is if the database file gets corrupt.
413
+ // In the event of a problem, we simply delete the database file.
414
+ // This isn't a big deal since we can just redownload the data.
415
+
416
+ // Sometimes the open function returns a db to allow us to query it for the error message
417
+ if (db) {
418
+ YDBLogWarn(@"Error opening database: %d %s", status, sqlite3_errmsg(db));
419
+ }
420
+ else {
421
+ YDBLogError(@"Error opening database: %d", status);
422
+ }
423
+
424
+ return NO;
425
+ }
426
+
427
+ return YES;
428
+ }
429
+
430
+ /**
431
+ * Configures the database connection.
432
+ * This mainly means enabling WAL mode, and configuring the auto-checkpoint.
433
+ **/
434
+ - (BOOL)configureDatabase
435
+ {
436
+ int status;
437
+
438
+ status = sqlite3_exec(db, "PRAGMA legacy_file_format = 0;", NULL, NULL, NULL);
439
+ if (status != SQLITE_OK)
440
+ {
441
+ YDBLogError(@"Error setting legacy_file_format: %d %s", status, sqlite3_errmsg(db));
442
+ return NO;
443
+ }
444
+
445
+ status = sqlite3_exec(db, "PRAGMA journal_mode = WAL;", NULL, NULL, NULL);
446
+ if (status != SQLITE_OK)
447
+ {
448
+ YDBLogError(@"Error setting journal_mode: %d %s", status, sqlite3_errmsg(db));
449
+ return NO;
450
+ }
451
+
452
+ // Disable autocheckpointing.
453
+ //
454
+ // YapDatabase has its own optimized checkpointing algorithm built-in.
455
+ // It knows the state of every active connection for the database,
456
+ // so it can invoke the checkpoint methods at the precise time in which a checkpoint can be most effective.
457
+
458
+ sqlite3_wal_autocheckpoint(db, 0);
459
+
460
+ return YES;
461
+ }
462
+
463
+ /**
464
+ * Creates the database tables we need:
465
+ *
466
+ * - yap2 : stores snapshot and metadata for extensions
467
+ * - database2 : stores collection/key/value/metadata rows
468
+ **/
469
+ - (BOOL)createTables
470
+ {
471
+ int status;
472
+
473
+ char *createYapTableStatement =
474
+ "CREATE TABLE IF NOT EXISTS \"yap2\""
475
+ " (\"extension\" CHAR NOT NULL, "
476
+ " \"key\" CHAR NOT NULL, "
477
+ " \"data\" BLOB, "
478
+ " PRIMARY KEY (\"extension\", \"key\")"
479
+ " );";
480
+
481
+ status = sqlite3_exec(db, createYapTableStatement, NULL, NULL, NULL);
482
+ if (status != SQLITE_OK)
483
+ {
484
+ YDBLogError(@"Failed creating 'yap2' table: %d %s", status, sqlite3_errmsg(db));
485
+ return NO;
486
+ }
487
+
488
+ char *createDatabaseTableStatement =
489
+ "CREATE TABLE IF NOT EXISTS \"database2\""
490
+ " (\"rowid\" INTEGER PRIMARY KEY,"
491
+ " \"collection\" CHAR NOT NULL,"
492
+ " \"key\" CHAR NOT NULL,"
493
+ " \"data\" BLOB,"
494
+ " \"metadata\" BLOB"
495
+ " );";
496
+
497
+ status = sqlite3_exec(db, createDatabaseTableStatement, NULL, NULL, NULL);
498
+ if (status != SQLITE_OK)
499
+ {
500
+ YDBLogError(@"Failed creating 'database2' table: %d %s", status, sqlite3_errmsg(db));
501
+ return NO;
502
+ }
503
+
504
+ char *createIndexStatement =
505
+ "CREATE UNIQUE INDEX IF NOT EXISTS \"true_primary_key\" ON \"database2\" ( \"collection\", \"key\" );";
506
+
507
+ status = sqlite3_exec(db, createIndexStatement, NULL, NULL, NULL);
508
+ if (status != SQLITE_OK)
509
+ {
510
+ YDBLogError(@"Failed creating index on 'database' table: %d %s", status, sqlite3_errmsg(db));
511
+ return NO;
512
+ }
513
+
514
+ return YES;
515
+ }
516
+
517
+ ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
518
+ #pragma mark Utilities
519
+ ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
520
+
521
+ /**
522
+ * Returns whether or not the given table exists.
523
+ **/
524
+ - (BOOL)tableExists:(NSString *)tableName using:(sqlite3 *)aDb
525
+ {
526
+ if (tableName == nil) return NO;
527
+
528
+ sqlite3_stmt *statement;
529
+ char *stmt = "SELECT count(*) FROM sqlite_master WHERE type = 'table' AND name = ?";
530
+
531
+ int status = sqlite3_prepare_v2(aDb, stmt, (int)strlen(stmt)+1, &statement, NULL);
532
+ if (status != SQLITE_OK)
533
+ {
534
+ YDBLogError(@"%@: Error creating statement! %d %s", THIS_METHOD, status, sqlite3_errmsg(aDb));
535
+ return NO;
536
+ }
537
+
538
+ BOOL result = NO;
539
+
540
+ sqlite3_bind_text(statement, 1, [tableName UTF8String], -1, SQLITE_TRANSIENT);
541
+
542
+ status = sqlite3_step(statement);
543
+ if (status == SQLITE_ROW)
544
+ {
545
+ int count = sqlite3_column_int(statement, 1);
546
+
547
+ result = (count > 0);
548
+ }
549
+ else if (status == SQLITE_ERROR)
550
+ {
551
+ YDBLogError(@"%@: Error executing statement! %d %s", THIS_METHOD, status, sqlite3_errmsg(aDb));
552
+ }
553
+
554
+ sqlite3_finalize(statement);
555
+ statement = NULL;
556
+
557
+ return result;
558
+ }
559
+
560
+ /**
561
+ * Extracts and returns column names of our database.
562
+ **/
563
+ - (NSArray *)columnNamesForTable:(NSString *)tableName using:(sqlite3 *)aDb
564
+ {
565
+ if (tableName == nil) return nil;
566
+
567
+ sqlite3_stmt *statement;
568
+ char *stmt = "PRAGMA table_info(?);";
569
+
570
+ int status = sqlite3_prepare_v2(aDb, stmt, (int)strlen(stmt)+1, &statement, NULL);
571
+ if (status != SQLITE_OK)
572
+ {
573
+ YDBLogError(@"%@: Error creating statement! %d %s", THIS_METHOD, status, sqlite3_errmsg(aDb));
574
+ return nil;
575
+ }
576
+
577
+ NSMutableArray *tableColumnNames = [NSMutableArray array];
578
+
579
+ sqlite3_bind_text(statement, 1, [tableName UTF8String], -1, SQLITE_TRANSIENT);
580
+
581
+ while ((status = sqlite3_step(statement)) == SQLITE_ROW)
582
+ {
583
+ const unsigned char *text = sqlite3_column_text(statement, 1);
584
+ int textSize = sqlite3_column_bytes(statement, 1);
585
+
586
+ NSString *columnName = [[NSString alloc] initWithBytes:text length:textSize encoding:NSUTF8StringEncoding];
587
+ if (columnName)
588
+ {
589
+ [tableColumnNames addObject:columnName];
590
+ }
591
+ }
592
+
593
+ if (status != SQLITE_DONE)
594
+ {
595
+ YDBLogError(@"%@: Error executing statement! %d %s", THIS_METHOD, status, sqlite3_errmsg(aDb));
596
+ }
597
+
598
+ sqlite3_finalize(statement);
599
+ statement = NULL;
600
+
601
+ return tableColumnNames;
602
+ }
603
+
604
+ ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
605
+ #pragma mark Upgrade
606
+ ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
607
+
608
+ /**
609
+ * Gets the version of the table.
610
+ * This is used to perform the various upgrade paths.
611
+ **/
612
+ - (BOOL)get_user_version:(int *)user_version_ptr
613
+ {
614
+ sqlite3_stmt *pragmaStatement;
615
+ int status;
616
+ int user_version;
617
+
618
+ char *stmt = "PRAGMA user_version;";
619
+
620
+ status = sqlite3_prepare_v2(db, stmt, (int)strlen(stmt)+1, &pragmaStatement, NULL);
621
+ if (status != SQLITE_OK)
622
+ {
623
+ YDBLogError(@"Error creating pragma user_version statement! %d %s", status, sqlite3_errmsg(db));
624
+ return NO;
625
+ }
626
+
627
+ status = sqlite3_step(pragmaStatement);
628
+ if (status == SQLITE_ROW)
629
+ {
630
+ user_version = sqlite3_column_int(pragmaStatement, 0);
631
+ }
632
+ else
633
+ {
634
+ YDBLogError(@"Error fetching user_version: %d %s", status, sqlite3_errmsg(db));
635
+ return NO;
636
+ }
637
+
638
+ sqlite3_finalize(pragmaStatement);
639
+ pragmaStatement = NULL;
640
+
641
+ // If user_version is zero, then this is a new database
642
+
643
+ if (user_version == 0)
644
+ {
645
+ user_version = YAP_DATABASE_CURRENT_VERION;
646
+ [self set_user_version:user_version];
647
+ }
648
+
649
+ if (user_version_ptr)
650
+ *user_version_ptr = user_version;
651
+ return YES;
652
+ }
653
+
654
+ /**
655
+ * Sets the version of the table.
656
+ * The version is used to check and perform upgrade logic if needed.
657
+ **/
658
+ - (BOOL)set_user_version:(int)user_version
659
+ {
660
+ NSString *query = [NSString stringWithFormat:@"PRAGMA user_version = %d;", user_version];
661
+
662
+ int status = sqlite3_exec(db, [query UTF8String], NULL, NULL, NULL);
663
+ if (status != SQLITE_OK)
664
+ {
665
+ YDBLogError(@"Error setting user_version: %d %s", status, sqlite3_errmsg(db));
666
+ return NO;
667
+ }
668
+
669
+ return YES;
670
+ }
671
+
672
+ - (BOOL)upgradeTable_1_2
673
+ {
674
+ // In version 1, we used a table named "yap" which had {key, data}.
675
+ // In version 2, we use a table named "yap2" which has {extension, key, data}
676
+
677
+ int status = sqlite3_exec(db, "DROP TABLE IF EXISTS \"yap\"", NULL, NULL, NULL);
678
+ if (status != SQLITE_OK)
679
+ {
680
+ YDBLogError(@"Failed dropping 'yap' table: %d %s", status, sqlite3_errmsg(db));
681
+ }
682
+
683
+ return YES;
684
+ }
685
+
686
+ /**
687
+ * In version 3 (more commonly known as version 2.1),
688
+ * we altered the tables to use INTEGER PRIMARY KEY's so we could pass rowid's to extensions.
689
+ *
690
+ * This method migrates 'database' to 'database2'.
691
+ **/
692
+ - (BOOL)upgradeTable_2_3
693
+ {
694
+ int status;
695
+
696
+ char *stmt = "INSERT INTO \"database2\" (\"collection\", \"key\", \"data\", \"metadata\")"
697
+ " SELECT \"collection\", \"key\", \"data\", \"metadata\" FROM \"database\";";
698
+
699
+ status = sqlite3_exec(db, stmt, NULL, NULL, NULL);
700
+ if (status != SQLITE_OK)
701
+ {
702
+ YDBLogError(@"Error migrating 'database' to 'database2': %d %s", status, sqlite3_errmsg(db));
703
+ return NO;
704
+ }
705
+
706
+ status = sqlite3_exec(db, "DROP TABLE IF EXISTS \"database\"", NULL, NULL, NULL);
707
+ if (status != SQLITE_OK)
708
+ {
709
+ YDBLogError(@"Failed dropping 'database' table: %d %s", status, sqlite3_errmsg(db));
710
+ return NO;
711
+ }
712
+
713
+ return YES;
714
+ }
715
+
716
+ /**
717
+ * Performs upgrade checks, and implements the upgrade "plumbing" by invoking the appropriate upgrade methods.
718
+ *
719
+ * To add custom upgrade logic, implement a method named "upgradeTable_X_Y",
720
+ * where X is the previous version, and Y is the new version.
721
+ * For example:
722
+ *
723
+ * - (BOOL)upgradeTable_1_2 {
724
+ * // Upgrades from version 1 to version 2 of YapDatabase.
725
+ * // Return YES if successful.
726
+ * }
727
+ *
728
+ * IMPORTANT:
729
+ * This is for upgrades of the database schema, and low-level operations of YapDatabase.
730
+ * This is NOT for upgrading data within the database (i.e. objects, metadata, or keys).
731
+ * Such data upgrades should be performed client side.
732
+ *
733
+ * This method is run asynchronously on the queue.
734
+ **/
735
+ - (void)upgradeTable
736
+ {
737
+ int user_version = 0;
738
+ if (![self get_user_version:&user_version]) return;
739
+
740
+ while (user_version < YAP_DATABASE_CURRENT_VERION)
741
+ {
742
+ // Invoke method upgradeTable_X_Y
743
+ // where X == current_version, and Y == current_version+1.
744
+ //
745
+ // Do this until we're up-to-date.
746
+
747
+ int new_user_version = user_version + 1;
748
+
749
+ NSString *selName = [NSString stringWithFormat:@"upgradeTable_%d_%d", user_version, new_user_version];
750
+ SEL sel = NSSelectorFromString(selName);
751
+
752
+ if ([self respondsToSelector:sel])
753
+ {
754
+ YDBLogInfo(@"Upgrading database (%@) from version %d to %d...",
755
+ [databasePath lastPathComponent], user_version, new_user_version);
756
+
757
+ #pragma clang diagnostic push
758
+ #pragma clang diagnostic ignored "-Warc-performSelector-leaks"
759
+ if ([self performSelector:sel])
760
+ #pragma clang diagnostic pop
761
+ {
762
+ [self set_user_version:new_user_version];
763
+ }
764
+ else
765
+ {
766
+ YDBLogError(@"Error upgrading database (%@)", [databasePath lastPathComponent]);
767
+ break;
768
+ }
769
+ }
770
+ else
771
+ {
772
+ YDBLogWarn(@"Missing upgrade method: %@", selName);
773
+ }
774
+
775
+ user_version = new_user_version;
776
+ }
777
+ }
778
+
779
+ ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
780
+ #pragma mark Prepare
781
+ ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
782
+
783
+ /**
784
+ * Optional override hook.
785
+ * Don't forget to invoke [super prepare] so super can prepare too.
786
+ *
787
+ * This method is run asynchronously on the snapshotQueue.
788
+ **/
789
+ - (void)prepare
790
+ {
791
+ // Initialize snapshot
792
+
793
+ snapshot = 0;
794
+
795
+ // Write it to disk (replacing any previous value from last app run)
796
+
797
+ [self beginTransaction];
798
+ [self writeSnapshot];
799
+ [self fetchPreviouslyRegisteredExtensionNames];
800
+ [self commitTransaction];
801
+ }
802
+
803
+ - (void)beginTransaction
804
+ {
805
+ int status = status = sqlite3_exec(db, "BEGIN TRANSACTION;", NULL, NULL, NULL);
806
+ if (status != SQLITE_OK)
807
+ {
808
+ YDBLogError(@"Error in '%@': %d %s", NSStringFromSelector(_cmd), status, sqlite3_errmsg(db));
809
+ }
810
+ }
811
+
812
+ - (void)commitTransaction
813
+ {
814
+ int status = status = sqlite3_exec(db, "COMMIT TRANSACTION;", NULL, NULL, NULL);
815
+ if (status != SQLITE_OK)
816
+ {
817
+ YDBLogError(@"Error in '%@': %d %s", NSStringFromSelector(_cmd), status, sqlite3_errmsg(db));
818
+ }
819
+ }
820
+
821
+ - (void)writeSnapshot
822
+ {
823
+ int status;
824
+ sqlite3_stmt *statement;
825
+
826
+ char *stmt = "INSERT OR REPLACE INTO \"yap2\" (\"extension\", \"key\", \"data\") VALUES (?, ?, ?);";
827
+
828
+ status = sqlite3_prepare_v2(db, stmt, (int)strlen(stmt)+1, &statement, NULL);
829
+ if (status != SQLITE_OK)
830
+ {
831
+ YDBLogError(@"%@: Error creating statement: %d %s",
832
+ NSStringFromSelector(_cmd), status, sqlite3_errmsg(db));
833
+ }
834
+ else
835
+ {
836
+ char *extension = "";
837
+ sqlite3_bind_text(statement, 1, extension, (int)strlen(extension), SQLITE_STATIC);
838
+
839
+ char *key = "snapshot";
840
+ sqlite3_bind_text(statement, 2, key, (int)strlen(key), SQLITE_STATIC);
841
+
842
+ sqlite3_bind_int64(statement, 3, (sqlite3_int64)snapshot);
843
+
844
+ status = sqlite3_step(statement);
845
+ if (status != SQLITE_DONE)
846
+ {
847
+ YDBLogError(@"%@: Error in statement: %d %s", NSStringFromSelector(_cmd), status, sqlite3_errmsg(db));
848
+ }
849
+
850
+ sqlite3_finalize(statement);
851
+ }
852
+ }
853
+
854
+ - (void)fetchPreviouslyRegisteredExtensionNames
855
+ {
856
+ int status;
857
+ sqlite3_stmt *statement;
858
+
859
+ char *stmt = "SELECT DISTINCT \"extension\" FROM \"yap2\";";
860
+
861
+ NSMutableArray *extensionNames = [NSMutableArray array];
862
+
863
+ status = sqlite3_prepare_v2(db, stmt, (int)strlen(stmt)+1, &statement, NULL);
864
+ if (status != SQLITE_OK)
865
+ {
866
+ YDBLogError(@"%@: Error creating statement: %d %s",
867
+ NSStringFromSelector(_cmd), status, sqlite3_errmsg(db));
868
+ }
869
+ else
870
+ {
871
+ while ((status = sqlite3_step(statement)) == SQLITE_ROW)
872
+ {
873
+ const unsigned char *text = sqlite3_column_text(statement, 0);
874
+ int textSize = sqlite3_column_bytes(statement, 0);
875
+
876
+ NSString *extensionName =
877
+ [[NSString alloc] initWithBytes:text length:textSize encoding:NSUTF8StringEncoding];
878
+
879
+ if ([extensionName length] > 0)
880
+ {
881
+ [extensionNames addObject:extensionName];
882
+ }
883
+ }
884
+
885
+ if (status != SQLITE_DONE)
886
+ {
887
+ YDBLogError(@"%@: Error in statement: %d %s", NSStringFromSelector(_cmd), status, sqlite3_errmsg(db));
888
+ }
889
+
890
+ sqlite3_finalize(statement);
891
+ }
892
+
893
+ previouslyRegisteredExtensionNames = extensionNames;
894
+ }
895
+
896
+ ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
897
+ #pragma mark Defaults
898
+ ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
899
+
900
+ - (YapDatabaseDefaults *)defaults
901
+ {
902
+ __block YapDatabaseDefaults *result = nil;
903
+
904
+ dispatch_sync(internalQueue, ^{
905
+
906
+ result = [defaults copy];
907
+ });
908
+
909
+ return result;
910
+ }
911
+
912
+ - (BOOL)defaultObjectCacheEnabled
913
+ {
914
+ __block BOOL result = NO;
915
+
916
+ dispatch_sync(internalQueue, ^{
917
+
918
+ result = defaults.objectCacheEnabled;
919
+ });
920
+
921
+ return result;
922
+ }
923
+
924
+ - (void)setDefaultObjectCacheEnabled:(BOOL)defaultObjectCacheEnabled
925
+ {
926
+ dispatch_sync(internalQueue, ^{
927
+
928
+ defaults.objectCacheEnabled = defaultObjectCacheEnabled;
929
+ });
930
+ }
931
+
932
+ - (NSUInteger)defaultObjectCacheLimit
933
+ {
934
+ __block NSUInteger result = NO;
935
+
936
+ dispatch_sync(internalQueue, ^{
937
+
938
+ result = defaults.objectCacheLimit;
939
+ });
940
+
941
+ return result;
942
+ }
943
+
944
+ - (void)setDefaultObjectCacheLimit:(NSUInteger)defaultObjectCacheLimit
945
+ {
946
+ dispatch_sync(internalQueue, ^{
947
+
948
+ defaults.objectCacheLimit = defaultObjectCacheLimit;
949
+ });
950
+ }
951
+
952
+ - (BOOL)defaultMetadataCacheEnabled
953
+ {
954
+ __block BOOL result = NO;
955
+
956
+ dispatch_sync(internalQueue, ^{
957
+
958
+ result = defaults.metadataCacheEnabled;
959
+ });
960
+
961
+ return result;
962
+ }
963
+
964
+ - (void)setDefaultMetadataCacheEnabled:(BOOL)defaultMetadataCacheEnabled
965
+ {
966
+ dispatch_sync(internalQueue, ^{
967
+
968
+ defaults.metadataCacheEnabled = defaultMetadataCacheEnabled;
969
+ });
970
+ }
971
+
972
+ - (NSUInteger)defaultMetadataCacheLimit
973
+ {
974
+ __block NSUInteger result = 0;
975
+
976
+ dispatch_sync(internalQueue, ^{
977
+
978
+ result = defaults.metadataCacheLimit;
979
+ });
980
+
981
+ return result;
982
+ }
983
+
984
+ - (void)setDefaultMetadataCacheLimit:(NSUInteger)defaultMetadataCacheLimit
985
+ {
986
+ dispatch_sync(internalQueue, ^{
987
+
988
+ defaults.metadataCacheLimit = defaultMetadataCacheLimit;
989
+ });
990
+ }
991
+
992
+ - (YapDatabasePolicy)defaultObjectPolicy
993
+ {
994
+ __block YapDatabasePolicy result = YapDatabasePolicyShare;
995
+
996
+ dispatch_sync(internalQueue, ^{
997
+
998
+ result = defaults.objectPolicy;
999
+ });
1000
+
1001
+ return result;
1002
+ }
1003
+
1004
+ - (void)setDefaultObjectPolicy:(YapDatabasePolicy)defaultObjectPolicy
1005
+ {
1006
+ dispatch_sync(internalQueue, ^{
1007
+
1008
+ defaults.objectPolicy = defaultObjectPolicy;
1009
+ });
1010
+ }
1011
+
1012
+ - (YapDatabasePolicy)defaultMetadataPolicy
1013
+ {
1014
+ __block YapDatabasePolicy result = YapDatabasePolicyShare;
1015
+
1016
+ dispatch_sync(internalQueue, ^{
1017
+
1018
+ result = defaults.metadataPolicy;
1019
+ });
1020
+
1021
+ return result;
1022
+ }
1023
+
1024
+ - (void)setDefaultMetadataPolicy:(YapDatabasePolicy)defaultMetadataPolicy
1025
+ {
1026
+ dispatch_sync(internalQueue, ^{
1027
+
1028
+ defaults.metadataPolicy = defaultMetadataPolicy;
1029
+ });
1030
+ }
1031
+
1032
+ #if TARGET_OS_IPHONE
1033
+
1034
+ - (int)defaultAutoFlushMemoryLevel
1035
+ {
1036
+ __block int result = YapDatabaseConnectionFlushMemoryLevelNone;
1037
+
1038
+ dispatch_sync(internalQueue, ^{
1039
+
1040
+ result = defaults.autoFlushMemoryLevel;
1041
+ });
1042
+
1043
+ return result;
1044
+ }
1045
+
1046
+ - (void)setDefaultAutoFlushMemoryLevel:(int)defaultAutoFlushMemoryLevel
1047
+ {
1048
+ dispatch_sync(internalQueue, ^{
1049
+
1050
+ defaults.autoFlushMemoryLevel = defaultAutoFlushMemoryLevel;
1051
+ });
1052
+ }
1053
+
1054
+ #endif
1055
+
1056
+ ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
1057
+ #pragma mark Connections
1058
+ ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
1059
+
1060
+ /**
1061
+ * This method is called from newConnection, either above or from a subclass.
1062
+ **/
1063
+ - (void)addConnection:(YapDatabaseConnection *)connection
1064
+ {
1065
+ // We can asynchronously add the connection to the state table.
1066
+ // This is safe as the connection itself must go through the same queue in order to do anything.
1067
+ //
1068
+ // The primary motivation in adding the asynchronous functionality is due to the following common use case:
1069
+ //
1070
+ // YapDatabase *database = [[YapDatabase alloc] initWithPath:path];
1071
+ // YapDatabaseConnection *databaseConnection = [database newConnection];
1072
+ //
1073
+ // The YapDatabase init method is asynchronously preparing itself through the snapshot queue.
1074
+ // We'd like to avoid blocking the very next line of code and allow the asynchronous prepare to continue.
1075
+
1076
+ dispatch_async(connection->connectionQueue, ^{
1077
+
1078
+ dispatch_sync(snapshotQueue, ^{ @autoreleasepool {
1079
+
1080
+ // Add the connection to the state table
1081
+
1082
+ YapDatabaseConnectionState *state = [[YapDatabaseConnectionState alloc] initWithConnection:connection];
1083
+ [connectionStates addObject:state];
1084
+
1085
+ YDBLogVerbose(@"Created new connection(%p) for <%@ %p: databaseName=%@, connectionCount=%lu>",
1086
+ connection, [self class], self, [databasePath lastPathComponent],
1087
+ (unsigned long)[connectionStates count]);
1088
+
1089
+ // Invoke the one-time prepare method, so the connection can perform any needed initialization.
1090
+ // Be sure to do this within the snapshotQueue, as the prepare method depends on this.
1091
+
1092
+ [connection prepare];
1093
+ }});
1094
+ });
1095
+ }
1096
+
1097
+ /**
1098
+ * This method is called from YapDatabaseConnection's dealloc method.
1099
+ **/
1100
+ - (void)removeConnection:(YapDatabaseConnection *)connection
1101
+ {
1102
+ dispatch_block_t block = ^{ @autoreleasepool {
1103
+
1104
+ NSUInteger index = 0;
1105
+ for (YapDatabaseConnectionState *state in connectionStates)
1106
+ {
1107
+ if (state->connection == connection)
1108
+ {
1109
+ [connectionStates removeObjectAtIndex:index];
1110
+ break;
1111
+ }
1112
+
1113
+ index++;
1114
+ }
1115
+
1116
+ YDBLogVerbose(@"Removed connection(%p) from <%@ %p: databaseName=%@, connectionCount=%lu>",
1117
+ connection, [self class], self, [databasePath lastPathComponent],
1118
+ (unsigned long)[connectionStates count]);
1119
+ }};
1120
+
1121
+ // We prefer to invoke this method synchronously.
1122
+ //
1123
+ // The connection may be the last object retaining the database.
1124
+ // It's easier to trace object deallocations when they happen in a predictable order.
1125
+
1126
+ if (dispatch_get_specific(IsOnSnapshotQueueKey))
1127
+ block();
1128
+ else
1129
+ dispatch_sync(snapshotQueue, block);
1130
+ }
1131
+
1132
+ /**
1133
+ * This is a public method called to create a new connection.
1134
+ **/
1135
+ - (YapDatabaseConnection *)newConnection
1136
+ {
1137
+ YapDatabaseConnection *connection = [[YapDatabaseConnection alloc] initWithDatabase:self];
1138
+
1139
+ [self addConnection:connection];
1140
+ return connection;
1141
+ }
1142
+
1143
+ ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
1144
+ #pragma mark Extensions
1145
+ ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
1146
+
1147
+ /**
1148
+ * Registers the extension with the database using the given name.
1149
+ * After registration everything works automatically using just the extension name.
1150
+ *
1151
+ * The registration process is equivalent to a readwrite transaction.
1152
+ * It involves persisting various information about the extension to the database,
1153
+ * as well as possibly populating the extension by enumerating existing rows in the database.
1154
+ *
1155
+ * @return
1156
+ * YES if the extension was properly registered.
1157
+ * NO if an error occurred, such as the extensionName is already registered.
1158
+ *
1159
+ * @see asyncRegisterExtension:withName:completionBlock:
1160
+ * @see asyncRegisterExtension:withName:completionBlock:completionQueue:
1161
+ **/
1162
+ - (BOOL)registerExtension:(YapDatabaseExtension *)extension withName:(NSString *)extensionName
1163
+ {
1164
+ __block BOOL ready = NO;
1165
+
1166
+ dispatch_sync(writeQueue, ^{ @autoreleasepool {
1167
+
1168
+ ready = [self _registerExtension:extension withName:extensionName];
1169
+ }});
1170
+
1171
+ return ready;
1172
+ }
1173
+
1174
+ /**
1175
+ * Asynchronoulsy starts the extension registration process.
1176
+ * After registration everything works automatically using just the extension name.
1177
+ *
1178
+ * The registration process is equivalent to a readwrite transaction.
1179
+ * It involves persisting various information about the extension to the database,
1180
+ * as well as possibly populating the extension by enumerating existing rows in the database.
1181
+ *
1182
+ * An optional completion block may be used.
1183
+ * If the extension registration was successful then the ready parameter will be YES.
1184
+ *
1185
+ * The completionBlock will be invoked on the main thread (dispatch_get_main_queue()).
1186
+ **/
1187
+ - (void)asyncRegisterExtension:(YapDatabaseExtension *)extension
1188
+ withName:(NSString *)extensionName
1189
+ completionBlock:(void(^)(BOOL ready))completionBlock
1190
+ {
1191
+ [self asyncRegisterExtension:extension
1192
+ withName:extensionName
1193
+ completionBlock:completionBlock
1194
+ completionQueue:NULL];
1195
+ }
1196
+
1197
+ /**
1198
+ * Asynchronoulsy starts the extension registration process.
1199
+ * After registration everything works automatically using just the extension name.
1200
+ *
1201
+ * The registration process is equivalent to a readwrite transaction.
1202
+ * It involves persisting various information about the extension to the database,
1203
+ * as well as possibly populating the extension by enumerating existing rows in the database.
1204
+ *
1205
+ * An optional completion block may be used.
1206
+ * If the extension registration was successful then the ready parameter will be YES.
1207
+ *
1208
+ * Additionally the dispatch_queue to invoke the completion block may also be specified.
1209
+ * If NULL, dispatch_get_main_queue() is automatically used.
1210
+ **/
1211
+ - (void)asyncRegisterExtension:(YapDatabaseExtension *)extension
1212
+ withName:(NSString *)extensionName
1213
+ completionBlock:(void(^)(BOOL ready))completionBlock
1214
+ completionQueue:(dispatch_queue_t)completionQueue
1215
+ {
1216
+ if (completionQueue == NULL && completionBlock != NULL)
1217
+ completionQueue = dispatch_get_main_queue();
1218
+
1219
+ dispatch_async(writeQueue, ^{ @autoreleasepool {
1220
+
1221
+ BOOL ready = [self _registerExtension:extension withName:extensionName];
1222
+
1223
+ if (completionBlock)
1224
+ {
1225
+ dispatch_async(completionQueue, ^{ @autoreleasepool {
1226
+
1227
+ completionBlock(ready);
1228
+ }});
1229
+ }
1230
+ }});
1231
+ }
1232
+
1233
+ /**
1234
+ *
1235
+ **/
1236
+ - (void)unregisterExtension:(NSString *)extensionName
1237
+ {
1238
+ dispatch_sync(writeQueue, ^{ @autoreleasepool {
1239
+
1240
+ [self _unregisterExtension:extensionName];
1241
+ }});
1242
+ }
1243
+
1244
+ - (void)asyncUnregisterExtension:(NSString *)extensionName
1245
+ completionBlock:(dispatch_block_t)completionBlock
1246
+ {
1247
+ [self asyncUnregisterExtension:extensionName
1248
+ completionBlock:completionBlock
1249
+ completionQueue:NULL];
1250
+ }
1251
+
1252
+ - (void)asyncUnregisterExtension:(NSString *)extensionName
1253
+ completionBlock:(dispatch_block_t)completionBlock
1254
+ completionQueue:(dispatch_queue_t)completionQueue
1255
+ {
1256
+ if (completionQueue == NULL && completionBlock != NULL)
1257
+ completionQueue = dispatch_get_main_queue();
1258
+
1259
+ dispatch_async(writeQueue, ^{ @autoreleasepool {
1260
+
1261
+ [self _unregisterExtension:extensionName];
1262
+
1263
+ if (completionBlock)
1264
+ {
1265
+ dispatch_async(completionQueue, ^{ @autoreleasepool {
1266
+
1267
+ completionBlock();
1268
+ }});
1269
+ }
1270
+ }});
1271
+ }
1272
+
1273
+ /**
1274
+ * Internal utility method.
1275
+ * Handles lazy creation and destruction of short-lived registrationConnection instance.
1276
+ *
1277
+ * @see _registerExtension:withName:
1278
+ * @see _unregisterExtension:
1279
+ **/
1280
+ - (YapDatabaseConnection *)registrationConnection
1281
+ {
1282
+ if (registrationConnection == nil)
1283
+ {
1284
+ registrationConnection = [self newConnection];
1285
+ registrationConnection.name = @"YapDatabase_extensionRegistrationConnection";
1286
+
1287
+ NSTimeInterval delayInSeconds = 10.0;
1288
+ dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC));
1289
+ dispatch_after(popTime, writeQueue, ^(void){
1290
+
1291
+ registrationConnection = nil;
1292
+ });
1293
+ }
1294
+
1295
+ return registrationConnection;
1296
+ }
1297
+
1298
+ /**
1299
+ * Internal method that handles extension registration.
1300
+ * This method must be invoked on the writeQueue.
1301
+ **/
1302
+ - (BOOL)_registerExtension:(YapDatabaseExtension *)extension withName:(NSString *)extensionName
1303
+ {
1304
+ NSAssert(dispatch_get_specific(IsOnWriteQueueKey), @"Must go through writeQueue.");
1305
+
1306
+ // Validate parameters
1307
+
1308
+ if (extension == nil)
1309
+ {
1310
+ YDBLogError(@"Error registering extension: extension parameter is nil");
1311
+ return NO;
1312
+ }
1313
+ if ([extensionName length] == 0)
1314
+ {
1315
+ YDBLogError(@"Error registering extension: extensionName parameter is nil or empty string");
1316
+ return NO;
1317
+ }
1318
+
1319
+ // Check to ensure extension isn't already registered,
1320
+ // or that the extensionName isn't already taken.
1321
+
1322
+ NSDictionary *_registeredExtensions = [self registeredExtensions];
1323
+
1324
+ if (extension.registeredName != nil)
1325
+ {
1326
+ YDBLogError(@"Error registering extension: extension is already registered");
1327
+ return NO;
1328
+ }
1329
+ if ([_registeredExtensions objectForKey:extensionName] != nil)
1330
+ {
1331
+ YDBLogError(@"Error registering extension: extensionName(%@) already registered", extensionName);
1332
+ return NO;
1333
+ }
1334
+
1335
+ // Make sure the extension can be supported
1336
+
1337
+ if (![extension supportsDatabase:self withRegisteredExtensions:_registeredExtensions])
1338
+ {
1339
+ YDBLogError(@"Error registering extension: extension doesn't database type or configuration");
1340
+ return NO;
1341
+ }
1342
+
1343
+ // Set the registeredName now.
1344
+ // The extension will need this in order to perform the registration tasks such as creating tables, etc.
1345
+
1346
+ extension.registeredName = extensionName;
1347
+
1348
+ // Attempt registration
1349
+
1350
+ BOOL result = [[self registrationConnection] registerExtension:extension withName:extensionName];
1351
+ if (result)
1352
+ {
1353
+ // Extension registered
1354
+ // Record dependencies (if there are any)
1355
+
1356
+ NSSet *dependencies = [extension dependencies];
1357
+
1358
+ if (dependencies == nil)
1359
+ dependencies = [NSSet set];
1360
+
1361
+ NSMutableDictionary *newExtensionDependencies = [extensionDependencies mutableCopy];
1362
+ [newExtensionDependencies setObject:dependencies forKey:extensionName];
1363
+
1364
+ extensionDependencies = [newExtensionDependencies copy];
1365
+ }
1366
+ else
1367
+ {
1368
+ // Registration failed
1369
+
1370
+ extension.registeredName = nil;
1371
+ }
1372
+
1373
+ return result;
1374
+ }
1375
+
1376
+ /**
1377
+ * Internal method that handles extension unregistration.
1378
+ * This method must be invoked on the writeQueue.
1379
+ **/
1380
+ - (void)_unregisterExtension:(NSString *)extensionName
1381
+ {
1382
+ NSAssert(dispatch_get_specific(IsOnWriteQueueKey), @"Must go through writeQueue.");
1383
+
1384
+ if ([extensionName length] == 0)
1385
+ {
1386
+ YDBLogError(@"Error unregistering extension: extensionName parameter is nil or empty string");
1387
+ return;
1388
+ }
1389
+
1390
+ YapDatabaseExtension *extension = [self registeredExtension:extensionName];
1391
+
1392
+ [[self registrationConnection] unregisterExtension:extensionName];
1393
+ extension.registeredName = nil;
1394
+
1395
+ NSMutableDictionary *newExtensionDependencies = [extensionDependencies mutableCopy];
1396
+ [newExtensionDependencies removeObjectForKey:extensionName];
1397
+
1398
+ // Automatically unregister any extensions that were dependent upon this one.
1399
+
1400
+ BOOL done;
1401
+ do
1402
+ {
1403
+ __block NSString *dependentExtName = nil;
1404
+ [newExtensionDependencies enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop){
1405
+
1406
+ // __unsafe_unretained NSString *extName = (NSString *)key;
1407
+ __unsafe_unretained NSSet *extDependencies = (NSSet *)obj;
1408
+
1409
+ if ([extDependencies containsObject:extensionName])
1410
+ {
1411
+ dependentExtName = (NSString *)key;
1412
+ *stop = YES;
1413
+ }
1414
+ }];
1415
+
1416
+ if (dependentExtName)
1417
+ {
1418
+ YapDatabaseExtension *dependentExt = [self registeredExtension:dependentExtName];
1419
+
1420
+ [[self registrationConnection] unregisterExtension:dependentExtName];
1421
+ dependentExt.registeredName = nil;
1422
+
1423
+ [newExtensionDependencies removeObjectForKey:dependentExtName];
1424
+
1425
+ done = NO;
1426
+ }
1427
+ else
1428
+ {
1429
+ done = YES;
1430
+ }
1431
+
1432
+ } while (!done);
1433
+
1434
+ extensionDependencies = [newExtensionDependencies copy];
1435
+ }
1436
+
1437
+ /**
1438
+ * Returns the registered extension with the given name.
1439
+ **/
1440
+ - (id)registeredExtension:(NSString *)extensionName
1441
+ {
1442
+ // This method is public
1443
+
1444
+ __block YapDatabaseExtension *result = nil;
1445
+
1446
+ dispatch_block_t block = ^{
1447
+
1448
+ result = [registeredExtensions objectForKey:extensionName];
1449
+ };
1450
+
1451
+ if (dispatch_get_specific(IsOnSnapshotQueueKey))
1452
+ block();
1453
+ else
1454
+ dispatch_sync(snapshotQueue, block);
1455
+
1456
+ return result;
1457
+ }
1458
+
1459
+ /**
1460
+ * Returns all currently registered extensions as a dictionary.
1461
+ * The key is the registed name (NSString), and the value is the extension (YapDatabaseExtension subclass).
1462
+ **/
1463
+ - (NSDictionary *)registeredExtensions
1464
+ {
1465
+ // This method is public
1466
+
1467
+ __block NSDictionary *extensionsCopy = nil;
1468
+
1469
+ dispatch_block_t block = ^{
1470
+
1471
+ extensionsCopy = registeredExtensions;
1472
+ };
1473
+
1474
+ if (dispatch_get_specific(IsOnSnapshotQueueKey))
1475
+ block();
1476
+ else
1477
+ dispatch_sync(snapshotQueue, block);
1478
+
1479
+ return extensionsCopy;
1480
+ }
1481
+
1482
+ /**
1483
+ * This method is only accessible from within the snapshotQueue.
1484
+ * Used by [YapDatabaseConnection prepare].
1485
+ **/
1486
+ - (NSArray *)extensionsOrder
1487
+ {
1488
+ NSAssert(dispatch_get_specific(IsOnSnapshotQueueKey), @"Must go through snapshotQueue for atomic access.");
1489
+
1490
+ return extensionsOrder;
1491
+ }
1492
+
1493
+ ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
1494
+ #pragma mark Pooling
1495
+ ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
1496
+
1497
+ - (NSUInteger)maxConnectionPoolCount
1498
+ {
1499
+ __block NSUInteger count = 0;
1500
+
1501
+ dispatch_sync(internalQueue, ^{
1502
+
1503
+ count = maxConnectionPoolCount;
1504
+ });
1505
+
1506
+ return count;
1507
+ }
1508
+
1509
+ - (void)setMaxConnectionPoolCount:(NSUInteger)count
1510
+ {
1511
+ dispatch_sync(internalQueue, ^{
1512
+
1513
+ // Update ivar
1514
+ maxConnectionPoolCount = count;
1515
+
1516
+ // Immediately drop any excess connections
1517
+ if ([connectionPoolValues count] > maxConnectionPoolCount)
1518
+ {
1519
+ do
1520
+ {
1521
+ sqlite3 *aDb = (sqlite3 *)[[connectionPoolValues objectAtIndex:0] pointerValue];
1522
+
1523
+ int status = sqlite3_close(aDb);
1524
+ if (status != SQLITE_OK)
1525
+ {
1526
+ YDBLogError(@"Error in sqlite_close: %d %s", status, sqlite3_errmsg(aDb));
1527
+ }
1528
+
1529
+ [connectionPoolValues removeObjectAtIndex:0];
1530
+ [connectionPoolDates removeObjectAtIndex:0];
1531
+
1532
+ } while ([connectionPoolValues count] > maxConnectionPoolCount);
1533
+
1534
+ [self resetConnectionPoolTimer];
1535
+ }
1536
+ });
1537
+ }
1538
+
1539
+ - (NSTimeInterval)connectionPoolLifetime
1540
+ {
1541
+ __block NSTimeInterval lifetime = 0;
1542
+
1543
+ dispatch_sync(internalQueue, ^{
1544
+
1545
+ lifetime = connectionPoolLifetime;
1546
+ });
1547
+
1548
+ return lifetime;
1549
+ }
1550
+
1551
+ - (void)setConnectionPoolLifetime:(NSTimeInterval)lifetime
1552
+ {
1553
+ dispatch_sync(internalQueue, ^{
1554
+
1555
+ // Update ivar
1556
+ connectionPoolLifetime = lifetime;
1557
+
1558
+ // Update timer (if needed)
1559
+ [self resetConnectionPoolTimer];
1560
+ });
1561
+ }
1562
+
1563
+ /**
1564
+ * Adds the given connection to the connection pool if possible.
1565
+ *
1566
+ * Returns YES if the instance was added to the pool.
1567
+ * If so, the YapDatabaseConnection must not close the instance.
1568
+ *
1569
+ * Returns NO if the instance was not added to the pool.
1570
+ * If so, the YapDatabaseConnection must close the instance.
1571
+ **/
1572
+ - (BOOL)connectionPoolEnqueue:(sqlite3 *)aDb
1573
+ {
1574
+ __block BOOL result = NO;
1575
+
1576
+ dispatch_sync(internalQueue, ^{
1577
+
1578
+ if ([connectionPoolValues count] < maxConnectionPoolCount)
1579
+ {
1580
+ if (connectionPoolValues == nil)
1581
+ {
1582
+ connectionPoolValues = [[NSMutableArray alloc] init];
1583
+ connectionPoolDates = [[NSMutableArray alloc] init];
1584
+ }
1585
+
1586
+ YDBLogVerbose(@"Enqueuing connection to pool: %p", aDb);
1587
+
1588
+ [connectionPoolValues addObject:[NSValue valueWithPointer:(const void *)aDb]];
1589
+ [connectionPoolDates addObject:[NSDate date]];
1590
+
1591
+ result = YES;
1592
+
1593
+ if ([connectionPoolValues count] == 1)
1594
+ {
1595
+ [self resetConnectionPoolTimer];
1596
+ }
1597
+ }
1598
+ });
1599
+
1600
+ return result;
1601
+ }
1602
+
1603
+ /**
1604
+ * Retrieves a connection from the connection pool if available.
1605
+ * Returns NULL if no connections are available.
1606
+ **/
1607
+ - (sqlite3 *)connectionPoolDequeue
1608
+ {
1609
+ __block sqlite3 *aDb = NULL;
1610
+
1611
+ dispatch_sync(internalQueue, ^{
1612
+
1613
+ if ([connectionPoolValues count] > 0)
1614
+ {
1615
+ aDb = (sqlite3 *)[[connectionPoolValues objectAtIndex:0] pointerValue];
1616
+
1617
+ YDBLogVerbose(@"Dequeuing connection from pool: %p", aDb);
1618
+
1619
+ [connectionPoolValues removeObjectAtIndex:0];
1620
+ [connectionPoolDates removeObjectAtIndex:0];
1621
+
1622
+ [self resetConnectionPoolTimer];
1623
+ }
1624
+ });
1625
+
1626
+ return aDb;
1627
+ }
1628
+
1629
+ /**
1630
+ * Internal utility method to handle setting/resetting the timer.
1631
+ **/
1632
+ - (void)resetConnectionPoolTimer
1633
+ {
1634
+ YDBLogAutoTrace();
1635
+
1636
+ if (connectionPoolLifetime <= 0.0 || [connectionPoolValues count] == 0)
1637
+ {
1638
+ if (connectionPoolTimer)
1639
+ {
1640
+ dispatch_source_cancel(connectionPoolTimer);
1641
+ connectionPoolTimer = NULL;
1642
+ }
1643
+
1644
+ return;
1645
+ }
1646
+
1647
+ BOOL isNewTimer = NO;
1648
+
1649
+ if (connectionPoolTimer == NULL)
1650
+ {
1651
+ connectionPoolTimer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, internalQueue);
1652
+
1653
+ __weak YapDatabase *weakSelf = self;
1654
+ dispatch_source_set_event_handler(connectionPoolTimer, ^{ @autoreleasepool {
1655
+
1656
+ __strong YapDatabase *strongSelf = weakSelf;
1657
+ if (strongSelf)
1658
+ {
1659
+ [strongSelf handleConnectionPoolTimerFire];
1660
+ }
1661
+ }});
1662
+
1663
+ #if !OS_OBJECT_USE_OBJC
1664
+ dispatch_source_t timer = connectionPoolTimer;
1665
+ dispatch_source_set_cancel_handler(connectionPoolTimer, ^{
1666
+ dispatch_release(timer);
1667
+ });
1668
+ #endif
1669
+
1670
+ isNewTimer = YES;
1671
+ }
1672
+
1673
+ NSDate *date = [connectionPoolDates objectAtIndex:0];
1674
+ NSTimeInterval interval = [date timeIntervalSinceNow] + connectionPoolLifetime;
1675
+
1676
+ dispatch_time_t tt = dispatch_time(DISPATCH_TIME_NOW, (interval * NSEC_PER_SEC));
1677
+ dispatch_source_set_timer(connectionPoolTimer, tt, DISPATCH_TIME_FOREVER, 0);
1678
+
1679
+ if (isNewTimer) {
1680
+ dispatch_resume(connectionPoolTimer);
1681
+ }
1682
+ }
1683
+
1684
+ /**
1685
+ * Internal method to handle removing stale connections from the connection pool.
1686
+ **/
1687
+ - (void)handleConnectionPoolTimerFire
1688
+ {
1689
+ YDBLogAutoTrace();
1690
+
1691
+ NSDate *now = [NSDate date];
1692
+
1693
+ BOOL done = NO;
1694
+ while ([connectionPoolValues count] > 0 && !done)
1695
+ {
1696
+ NSTimeInterval interval = [[connectionPoolDates objectAtIndex:0] timeIntervalSinceDate:now] * -1.0;
1697
+
1698
+ if ((interval >= connectionPoolLifetime) || (interval < 0))
1699
+ {
1700
+ sqlite3 *aDb = (sqlite3 *)[[connectionPoolValues objectAtIndex:0] pointerValue];
1701
+
1702
+ YDBLogVerbose(@"Closing connection from pool: %p", aDb);
1703
+
1704
+ int status = sqlite3_close(aDb);
1705
+ if (status != SQLITE_OK)
1706
+ {
1707
+ YDBLogError(@"Error in sqlite_close: %d %s", status, sqlite3_errmsg(aDb));
1708
+ }
1709
+
1710
+ [connectionPoolValues removeObjectAtIndex:0];
1711
+ [connectionPoolDates removeObjectAtIndex:0];
1712
+ }
1713
+ else
1714
+ {
1715
+ done = YES;
1716
+ }
1717
+ }
1718
+
1719
+ [self resetConnectionPoolTimer];
1720
+ }
1721
+
1722
+ ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
1723
+ #pragma mark Tables
1724
+ ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
1725
+
1726
+ /**
1727
+ * This method is only accessible from within the snapshotQueue.
1728
+ * Used by [YapDatabaseConnection prepare].
1729
+ **/
1730
+ - (NSDictionary *)registeredTables
1731
+ {
1732
+ NSAssert(dispatch_get_specific(IsOnSnapshotQueueKey), @"Must go through snapshotQueue for atomic access.");
1733
+
1734
+ return registeredTables;
1735
+ }
1736
+
1737
+ ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
1738
+ #pragma mark Snapshot Architecture
1739
+ ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
1740
+
1741
+ /**
1742
+ * The snapshot represents when the database was last modified by a read-write transaction.
1743
+ * This information isn persisted to the 'yap' database, and is separately held in memory.
1744
+ * It serves multiple purposes.
1745
+ *
1746
+ * First is assists in validation of a connection's cache.
1747
+ * When a connection begins a new transaction, it may have items sitting in the cache.
1748
+ * However the connection doesn't know if the items are still valid because another connection may have made changes.
1749
+ *
1750
+ * The snapshot also assists in correcting for a race condition.
1751
+ * It order to minimize blocking we allow read-write transactions to commit outside the context
1752
+ * of the snapshotQueue. This is because the commit may be a time consuming operation, and we
1753
+ * don't want to block read-only transactions during this period. The race condition occurs if a read-only
1754
+ * transactions starts in the midst of a read-write commit, and the read-only transaction gets
1755
+ * a "yap-level" snapshot that's out of sync with the "sql-level" snapshot. This is easily correctable if caught.
1756
+ * Thus we maintain the snapshot in memory, and fetchable via a select query.
1757
+ * One represents the "yap-level" snapshot, and the other represents the "sql-level" snapshot.
1758
+ *
1759
+ * The snapshot is simply a 64-bit integer.
1760
+ * It is reset when the YapDatabase instance is initialized,
1761
+ * and incremented by each read-write transaction (if changes are actually made).
1762
+ **/
1763
+ - (uint64_t)snapshot
1764
+ {
1765
+ if (dispatch_get_specific(IsOnSnapshotQueueKey))
1766
+ {
1767
+ // Very common case.
1768
+ // This method is called on just about every transaction.
1769
+ return snapshot;
1770
+ }
1771
+ else
1772
+ {
1773
+ // Non-common case.
1774
+ // Public access implementation.
1775
+ __block uint64_t result = 0;
1776
+
1777
+ dispatch_sync(snapshotQueue, ^{
1778
+ result = snapshot;
1779
+ });
1780
+
1781
+ return result;
1782
+ }
1783
+ }
1784
+
1785
+ /**
1786
+ * This method is only accessible from within the snapshotQueue.
1787
+ *
1788
+ * Prior to starting the sqlite commit, the connection must report its changeset to the database.
1789
+ * The database will store the changeset, and provide it to other connections if needed (due to a race condition).
1790
+ *
1791
+ * The following MUST be in the dictionary:
1792
+ *
1793
+ * - snapshot : NSNumber with the changeset's snapshot
1794
+ **/
1795
+ - (void)notePendingChanges:(NSDictionary *)pendingChangeset fromConnection:(YapDatabaseConnection *)sender
1796
+ {
1797
+ NSAssert(dispatch_get_specific(IsOnSnapshotQueueKey), @"Must go through snapshotQueue for atomic access.");
1798
+ NSAssert([pendingChangeset objectForKey:YapDatabaseSnapshotKey], @"Missing required change key: snapshot");
1799
+
1800
+ // The sender is preparing to start the sqlite commit.
1801
+ // We save the changeset in advance to handle possible edge cases.
1802
+
1803
+ [changesets addObject:pendingChangeset];
1804
+
1805
+ YDBLogVerbose(@"Adding pending changeset %@ for database: %@",
1806
+ [[changesets lastObject] objectForKey:YapDatabaseSnapshotKey], self);
1807
+ }
1808
+
1809
+ /**
1810
+ * This method is only accessible from within the snapshotQueue.
1811
+ *
1812
+ * This method is used if a transaction finds itself in a race condition.
1813
+ * It should retrieve the database's pending and/or committed changes,
1814
+ * and then process them via [connection noteCommittedChanges:].
1815
+ **/
1816
+ - (NSArray *)pendingAndCommittedChangesSince:(uint64_t)connectionSnapshot until:(uint64_t)maxSnapshot
1817
+ {
1818
+ NSAssert(dispatch_get_specific(IsOnSnapshotQueueKey), @"Must go through snapshotQueue for atomic access.");
1819
+
1820
+ NSMutableArray *relevantChangesets = [NSMutableArray arrayWithCapacity:[changesets count]];
1821
+
1822
+ for (NSDictionary *changeset in changesets)
1823
+ {
1824
+ uint64_t changesetSnapshot = [[changeset objectForKey:YapDatabaseSnapshotKey] unsignedLongLongValue];
1825
+
1826
+ if ((changesetSnapshot > connectionSnapshot) && (changesetSnapshot <= maxSnapshot))
1827
+ {
1828
+ [relevantChangesets addObject:changeset];
1829
+ }
1830
+ }
1831
+
1832
+ return relevantChangesets;
1833
+ }
1834
+
1835
+ /**
1836
+ * This method is only accessible from within the snapshotQueue.
1837
+ *
1838
+ * Upon completion of a readwrite transaction, the connection should report it's changeset to the database.
1839
+ * The database will then forward the changes to all other connection's.
1840
+ *
1841
+ * The following MUST be in the dictionary:
1842
+ *
1843
+ * - snapshot : NSNumber with the changeset's snapshot
1844
+ **/
1845
+ - (void)noteCommittedChanges:(NSDictionary *)changeset fromConnection:(YapDatabaseConnection *)sender
1846
+ {
1847
+ NSAssert(dispatch_get_specific(IsOnSnapshotQueueKey), @"Must go through snapshotQueue for atomic access.");
1848
+ NSAssert([changeset objectForKey:YapDatabaseSnapshotKey], @"Missing required change key: snapshot");
1849
+
1850
+ // The sender has finished the sqlite commit, and all data is now written to disk.
1851
+
1852
+ // Update the in-memory snapshot,
1853
+ // which represents the most recent snapshot of the last committed readwrite transaction.
1854
+
1855
+ snapshot = [[changeset objectForKey:YapDatabaseSnapshotKey] unsignedLongLongValue];
1856
+
1857
+ // Update registeredExtensions, if changed.
1858
+
1859
+ NSDictionary *newRegisteredExtensions = [changeset objectForKey:YapDatabaseRegisteredExtensionsKey];
1860
+ if (newRegisteredExtensions)
1861
+ {
1862
+ registeredExtensions = newRegisteredExtensions;
1863
+ extensionsOrder = [changeset objectForKey:YapDatabaseExtensionsOrderKey];
1864
+ }
1865
+
1866
+ // Update registeredTables, if changed.
1867
+
1868
+ NSDictionary *newRegisteredTables = [changeset objectForKey:YapDatabaseRegisteredTablesKey];
1869
+ if (newRegisteredTables)
1870
+ {
1871
+ registeredTables = newRegisteredTables;
1872
+ }
1873
+
1874
+ // Forward the changeset to all other connections so they can perform any needed updates.
1875
+ // Generally this means updating the in-memory components such as the cache.
1876
+
1877
+ dispatch_group_t group = NULL;
1878
+
1879
+ for (YapDatabaseConnectionState *state in connectionStates)
1880
+ {
1881
+ if (state->connection != sender)
1882
+ {
1883
+ // Create strong reference (state->connection is weak)
1884
+ __strong YapDatabaseConnection *connection = state->connection;
1885
+
1886
+ if (connection)
1887
+ {
1888
+ if (group == NULL)
1889
+ group = dispatch_group_create();
1890
+
1891
+ dispatch_group_async(group, connection->connectionQueue, ^{ @autoreleasepool {
1892
+
1893
+ [connection noteCommittedChanges:changeset];
1894
+ }});
1895
+ }
1896
+ }
1897
+ }
1898
+
1899
+ // Schedule block to be executed once all connections have processed the changes.
1900
+
1901
+ BOOL isInternalChangeset = (sender == nil);
1902
+
1903
+ dispatch_block_t block = ^{
1904
+
1905
+ // All connections have now processed the changes.
1906
+ // So we no longer need to retain the changeset in memory.
1907
+
1908
+ if (isInternalChangeset)
1909
+ {
1910
+ YDBLogVerbose(@"Completed internal changeset %@ for database: %@",
1911
+ [changeset objectForKey:YapDatabaseSnapshotKey], self);
1912
+ }
1913
+ else
1914
+ {
1915
+ YDBLogVerbose(@"Dropping processed changeset %@ for database: %@",
1916
+ [changeset objectForKey:YapDatabaseSnapshotKey], self);
1917
+
1918
+ [changesets removeObjectAtIndex:0];
1919
+ }
1920
+
1921
+ #if !OS_OBJECT_USE_OBJC
1922
+ if (group)
1923
+ dispatch_release(group);
1924
+ #endif
1925
+ };
1926
+
1927
+ if (group)
1928
+ dispatch_group_notify(group, snapshotQueue, block);
1929
+ else
1930
+ block();
1931
+ }
1932
+
1933
+ ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
1934
+ #pragma mark Manual Checkpointing
1935
+ ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
1936
+
1937
+ /**
1938
+ * This method should be called whenever the maximum checkpointable snapshot is incremented.
1939
+ * That is, the state of every connection is known to the system.
1940
+ * And a snaphot cannot be checkpointed until every connection is at or past that snapshot.
1941
+ * Thus, we can know the point at which a snapshot becomes checkpointable,
1942
+ * and we can thus optimize the checkpoint invocations such that
1943
+ * each invocation is able to checkpoint one or more commits.
1944
+ **/
1945
+ - (void)asyncCheckpoint:(uint64_t)maxCheckpointableSnapshot
1946
+ {
1947
+ __weak YapDatabase *weakSelf = self;
1948
+
1949
+ dispatch_async(checkpointQueue, ^{ @autoreleasepool {
1950
+
1951
+ __strong YapDatabase *strongSelf = weakSelf;
1952
+ if (strongSelf == nil) return;
1953
+
1954
+ YDBLogVerbose(@"Checkpointing up to snapshot %llu", maxCheckpointableSnapshot);
1955
+
1956
+ // We're ready to checkpoint more frames.
1957
+ //
1958
+ // So we're going to execute a passive checkpoint.
1959
+ // That is, without disrupting any connections, we're going to write pages from the WAL into the database.
1960
+ // The checkpoint can only write pages from snapshots if all connections are at or beyond the snapshot.
1961
+ // Thus, this method is only called by a connection that moves the min snapshot forward.
1962
+
1963
+ int frameCount = 0;
1964
+ int checkpointCount = 0;
1965
+
1966
+ int result = sqlite3_wal_checkpoint_v2(strongSelf->db, "main",
1967
+ SQLITE_CHECKPOINT_PASSIVE, &frameCount, &checkpointCount);
1968
+
1969
+ // frameCount = total number of frames in the log file
1970
+ // checkpointCount = total number of checkpointed frames
1971
+ // (including any that were already checkpointed before the function was called)
1972
+
1973
+ if (result != SQLITE_OK)
1974
+ {
1975
+ if (result == SQLITE_BUSY) {
1976
+ YDBLogVerbose(@"sqlite3_wal_checkpoint_v2 returned SQLITE_BUSY");
1977
+ }
1978
+ else {
1979
+ YDBLogWarn(@"sqlite3_wal_checkpoint_v2 returned error code: %d", result);
1980
+ }
1981
+
1982
+ return;// from_block
1983
+ }
1984
+
1985
+ YDBLogVerbose(@"Post-checkpoint (%llu): frames(%d) checkpointed(%d)",
1986
+ maxCheckpointableSnapshot, frameCount, checkpointCount);
1987
+
1988
+ // Have we checkpointed the entire WAL yet?
1989
+
1990
+ if (frameCount == checkpointCount)
1991
+ {
1992
+ // We've checkpointed every single frame.
1993
+ // This means the next read-write transaction will reset the WAL (instead of appending to it).
1994
+ //
1995
+ // However, this will get spoiled if there are active read-only transactions that
1996
+ // were started before our checkpoint finished, and continue to exist during the next read-write.
1997
+ // It's not a big deal if the occasional read-only transaction happens to spoil the WAL reset.
1998
+ // In those cases, the WAL generally gets reset shortly thereafter.
1999
+ // Long-lived read transactions are a different case entirely.
2000
+ // These transactions spoil it every single time, and could potentially cause the WAL to grow indefinitely.
2001
+ //
2002
+ // The solution is to notify active long-lived connections, and tell them to re-begin their transaction
2003
+ // on the same snapshot. But this time the sqlite machinery will read directly from the database,
2004
+ // and thus unlock the WAL so it can be reset.
2005
+
2006
+ dispatch_async(snapshotQueue, ^{
2007
+
2008
+ for (YapDatabaseConnectionState *state in connectionStates)
2009
+ {
2010
+ if (state->yapLevelSharedReadLock &&
2011
+ state->longLivedReadTransaction &&
2012
+ state->lastKnownSnapshot == snapshot)
2013
+ {
2014
+ [state->connection maybeResetLongLivedReadTransaction];
2015
+ }
2016
+ }
2017
+ });
2018
+ }
2019
+ }});
2020
+ }
2021
+
2022
+ @end