FMDBMigrationManager.h 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293
  1. //
  2. // FMDBMigrationManager.h
  3. // FMDBMigrationManager
  4. //
  5. // Created by Blake Watters on 6/4/14.
  6. // Copyright (c) 2014 Layer Inc. All rights reserved.
  7. //
  8. // Licensed under the Apache License, Version 2.0 (the "License");
  9. // you may not use this file except in compliance with the License.
  10. // You may obtain a copy of the License at
  11. //
  12. // http://www.apache.org/licenses/LICENSE-2.0
  13. //
  14. // Unless required by applicable law or agreed to in writing, software
  15. // distributed under the License is distributed on an "AS IS" BASIS,
  16. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  17. // See the License for the specific language governing permissions and
  18. // limitations under the License.
  19. //
  20. #import <fmdb/FMDatabase.h>
  21. #import <fmdb/FMDatabaseQueue.h>
  22. @protocol FMDBMigrating;
  23. /**
  24. @abstract The `FMDBMigrationManager` class provides a simple, flexible interface for managing migrations for
  25. a SQLite database that is accessed via FMDB.
  26. */
  27. @interface FMDBMigrationManager : NSObject
  28. ///-----------------------------------
  29. /// @name Creating a Migration Manager
  30. ///-----------------------------------
  31. /**
  32. @abstract Creates a new migration manager with a given database and migrations bundle.
  33. @param database The database with which to initialize the migration manager.
  34. @param bundle The bundle containing the migrations.
  35. @return A new migration manager.
  36. */
  37. + (instancetype)managerWithDatabase:(FMDatabase *)database migrationsBundle:(NSBundle *)bundle;
  38. /**
  39. @abstract Creates a new migration manager with a database for the given database and migrations bundle.
  40. @param path The path to a database with which to initialize the migration manager.
  41. @param bundle The bundle containing the migrations.
  42. @return A new migration manager.
  43. */
  44. + (instancetype)managerWithDatabaseAtPath:(NSString *)path migrationsBundle:(NSBundle *)bundle;
  45. /**
  46. @abstract Determines whether the receiver will perform a search for dynamically defined migrations. Default: `YES`.
  47. @discussion When `YES` all classes will be enumerated to search for any that conform to the `FMDBMigrating` protocol.
  48. */
  49. @property (nonatomic, assign) BOOL dynamicMigrationsEnabled;
  50. ///--------------------------------------------------
  51. /// @name Accessing Database Path & Migrations Bundle
  52. ///--------------------------------------------------
  53. /**
  54. @abstract Returns the database of the receiver.
  55. */
  56. @property (nonatomic, readonly) FMDatabase *database;
  57. /**
  58. @abstract Returns the migrations bundle for the receiver.
  59. */
  60. @property (nonatomic, readonly) NSBundle *migrationsBundle;
  61. ///-----------------------------
  62. /// @name Accessing Version Info
  63. ///-----------------------------
  64. /**
  65. @abstract Returns the current version of the database managed by the receiver or `0` if the
  66. migrations table is not present.
  67. */
  68. @property (nonatomic, readonly) uint64_t currentVersion;
  69. /**
  70. @abstract Returns the origin version of the database managed by the receiver or `0` if the
  71. migrations table is not present.
  72. */
  73. @property (nonatomic, readonly) uint64_t originVersion;
  74. ///---------------------------
  75. /// @name Accessing Migrations
  76. ///---------------------------
  77. /**
  78. @abstract Returns all migrations discovered by the receiver. Each object returned conforms to the `FMDBMigrating` protocol. The
  79. array is returned in ascending order by version.
  80. @discussion The manager discovers migrations by analyzing all files that end in a .sql extension in the `migrationsBundle`
  81. and accumulating all classes that conform to the `FMDBMigrating` protocol. These migrations can then be sorted and applied
  82. to the target database.
  83. @note The list of migrations is memoized for efficiency.
  84. */
  85. @property (nonatomic, readonly) NSArray *migrations;
  86. /**
  87. @abstract Returns the version numbers of the subset of `migrations` that have already been applied to the database
  88. managed by the receiver in ascending order.
  89. */
  90. @property (nonatomic, readonly) NSArray *appliedVersions;
  91. /**
  92. @abstract Returns the version numbers of the subset of `migrations` that have not yet been applied to the database
  93. managed by the receiver in ascending order.
  94. */
  95. @property (nonatomic, readonly) NSArray *pendingVersions;
  96. /**
  97. @abstract Returns a migration object with a given version number or `nil` if none could be found.
  98. @param version The version of the desired migration.
  99. @return A migration with the specified version or `nil` if none could be found.
  100. */
  101. - (id<FMDBMigrating>)migrationForVersion:(uint64_t)version;
  102. /**
  103. @abstract Returns a migration object with a given name or `nil` if none could be found.
  104. @param name The name of the desired migration.
  105. @return A migration with the specified named or `nil` if none could be found.
  106. */
  107. - (id<FMDBMigrating>)migrationForName:(NSString *)name;
  108. ///-------------------------
  109. /// @name Adding a Migration
  110. ///-------------------------
  111. /**
  112. @abstract Adds a migration to the receiver's list.
  113. @discussion This method can be used to append code based migrations to the set if you do not wish to use dynamic migration discovery. If
  114. the migration last has been previously computed, adding a migration will recompute the list.
  115. @param migration The migration to add.
  116. */
  117. - (void)addMigration:(id<FMDBMigrating>)migration;
  118. /**
  119. @abstract Adds migrations from the array to the receiver's list.
  120. @discussion This method can be used to append code based migrations to the set if you do not wish to use dynamic migration discovery. If
  121. the migration last has been previously computed, adding migrations will recompute the list.
  122. @param migrations An array of objects conforming to `FMDBMigrating` protocol.
  123. */
  124. - (void)addMigrations:(NSArray *)migrations;
  125. ///------------------------------------
  126. /// @name Managing the Migrations Table
  127. ///------------------------------------
  128. /**
  129. @abstract Returns a Boolean value that indicates if the `schema_migrations` table
  130. is present in the database.
  131. */
  132. @property (nonatomic, readonly) BOOL hasMigrationsTable;
  133. /**
  134. @abstract Creates the `schema_migrations` table used by `FMDBMigrationManager` to maintain an index of applied migrations.
  135. @param error A pointer to an error object that is set upon failure to create the migrations table.
  136. @return A Boolean value that indicates if the creation of the migrations table was successful.
  137. */
  138. - (BOOL)createMigrationsTable:(NSError **)error;
  139. ///--------------------------
  140. /// @name Migrating Databases
  141. ///--------------------------
  142. /**
  143. @abstract Returns a Boolean value that indicates if the database managed by the receiver is in need of migration.
  144. */
  145. @property (nonatomic, readonly) BOOL needsMigration;
  146. /**
  147. @abstract Migrates the database managed by the receiver to the specified version, optionally providing progress via a block.
  148. @discussion Migration is performed within a transaction that is rolled back if any errors occur during migration.
  149. @param version The target version to migrate the database to. Pass `UINT64_MAX` to migrate to the latest version.
  150. @param progressBlock An optional block to be invoked each time a migration is applied. The block has no return value and accepts a single `NSProgress` argument. The
  151. progress object can be used to cancel a migration in progress.
  152. @param error A pointer to an error object that is set upon failure to complete the migrations.
  153. @return `YES` if migration was successful, else `NO`.
  154. */
  155. - (BOOL)migrateDatabaseToVersion:(uint64_t)version progress:(void (^)(NSProgress *progress))progressBlock error:(NSError **)error;
  156. @end
  157. //////////////////////////////////////////////////////////////////////////////////////////////////////////////
  158. /**
  159. @abstract The `FMDBMigrating` protocol is adopted by classes that wish to provide migration of SQLite databases accessed via FMDB. The
  160. `FMDBMigrationManager` project ships with a single concrete implementation in the `FMDBFileMigration` class. Non file backed
  161. migrations can be implemented by conforming to the `FMDBMigrating` protocol.
  162. */
  163. @protocol FMDBMigrating <NSObject>
  164. ///-------------------------------------
  165. /// @name Accessing Migration Properties
  166. ///-------------------------------------
  167. /**
  168. @abstract The name of the migration.
  169. */
  170. @property (nonatomic, readonly) NSString *name;
  171. /**
  172. @abstract The numeric version of the migration.
  173. @discussion While monotonically incremented versions are fully supported, it is recommended that to use a timestamp format such as
  174. 201406063106474. Timestamps avoid unnecessary churn in a codebase that is heavily branched.
  175. */
  176. @property (nonatomic, readonly) uint64_t version;
  177. ///--------------------------
  178. /// @name Migrating Databases
  179. ///--------------------------
  180. /**
  181. @abstract Tells the receiver to apply its changes to the given database and return a Boolean value indicating success or failure.
  182. @discussion The `FMDBMigrationManager` manages a transaction while migrations are being applied. Should any call to `migrateDatabase:error` return `NO`,
  183. then the transaction is rolled back.
  184. @param database The database on which to apply the migration.
  185. @param error A pointer to an error object to set should the transaction fail.
  186. @return A Boolean value indicating if the
  187. */
  188. - (BOOL)migrateDatabase:(FMDatabase *)database error:(out NSError *__autoreleasing *)error;
  189. @end
  190. /**
  191. @abstract The `FMDBFileMigration` class provides a concrete implementation of the `FMDBMigrating` protocol that models
  192. a migration stored on disk a SQL file. The filename encodes the name and version of the migration. Conformant filenames are
  193. of the form `[version]_[name].sql`.
  194. */
  195. @interface FMDBFileMigration : NSObject <FMDBMigrating>
  196. ///--------------------------------
  197. /// @name Creating a File Migration
  198. ///--------------------------------
  199. /**
  200. @abstract Creates and returns a new migration with the file at the given path.
  201. @discussion Conformance of filenames can be evaluated with the `FMDBIsMigrationAtPath` utility function.
  202. @param path The path to a file containing a SQL migration with a conformant filename.
  203. */
  204. + (instancetype)migrationWithPath:(NSString *)path;
  205. /**
  206. @abstract The path to the SQL migration file on disk.
  207. */
  208. @property (nonatomic, readonly) NSString *path;
  209. /**
  210. @abstract A convenience accessor for retrieving the SQL from the receiver's path.
  211. */
  212. @property (nonatomic, readonly) NSString *SQL;
  213. @end
  214. //////////////////////////////////////////////////////////////////////////////////////////////////////////////
  215. /**
  216. @abstract The domain for errors created by `FMDBMigrationManager`.
  217. */
  218. extern NSString *const FMDBMigrationManagerErrorDomain;
  219. /**
  220. @abstract A key for an `NSNumber` object in the `userInfo` of an `NSProgress` object specifying the version
  221. that the database was just migrated to.
  222. @see `migrateDatabase:error:`
  223. */
  224. extern NSString *const FMDBMigrationManagerProgressVersionUserInfoKey;
  225. /**
  226. @abstract A key for an `id<FMDBMigrating>` object in the `userInfo` of an `NSProgress` object that identifies
  227. the migration that was just applied to the database.
  228. @see `migrateDatabase:error:`
  229. */
  230. extern NSString *const FMDBMigrationManagerProgressMigrationUserInfoKey;
  231. /**
  232. @abstract Enumerates the errors returned by FMDBMigrationManager
  233. */
  234. typedef NS_ENUM(NSUInteger, FMDBMigrationManagerError) {
  235. /// Indicates that migration was halted due to cancellation
  236. FMDBMigrationManagerErrorMigrationCancelled = 1
  237. };
  238. /**
  239. @abstract Returns a Boolean value that indicates if the file at the given path is an FMDB Migration.
  240. @discussion This function evaluates the last path component of the input string against the regular expression `/\d{1,15}_.+sql$/`.
  241. @param path The path to inspect.
  242. @return `YES` if the path could be identified as a migration, else `NO`.
  243. */
  244. BOOL FMDBIsMigrationAtPath(NSString *path);