/plugins/dbmigrate/dbmigrate.cfc

http://raihan.googlecode.com/ · ColdFusion CFScript · 242 lines · 231 code · 11 blank · 0 comment · 0 complexity · 9fb68b631f00ada21eff5bd17449ee3c MD5 · raw file

  1. <cfcomponent output="false" mixin="none" environment="design,development,maintenance">
  2. <cffunction name="init">
  3. <cfset this.version = "1.0,1.0.1,1.0.2,1.0.3,1.0.4,1.0.5,1.1,1.1.3,1.1.4,1.1.5">
  4. <cfreturn this>
  5. </cffunction>
  6. <cffunction name="migrateTo" access="public" returntype="string" hint="migrates database to a specified version">
  7. <cfargument name="version" type="string" required="false" default="">
  8. <cfset var loc = {}>
  9. <cfset loc.feedback = "">
  10. <cfset loc.versionsCurrentlyMigrated = $getVersionsPreviouslyMigrated()>
  11. <cfset loc.currentVersion = ListLast(loc.versionsCurrentlyMigrated)>
  12. <cfif loc.currentVersion eq arguments.version>
  13. <cfset loc.feedback = "Database is currently at version #arguments.version#. No migration required.#chr(13)#">
  14. <cfelse>
  15. <cfset loc.sqlPath = expandPath("db/sql")>
  16. <cfif not DirectoryExists(loc.sqlPath)>
  17. <cfdirectory action="create" directory="#loc.sqlPath#">
  18. </cfif>
  19. <cfset loc.migrations = getAvailableMigrations()>
  20. <cfif loc.currentVersion gt arguments.version>
  21. <cfset loc.feedback = "Migrating from #loc.currentVersion# down to #arguments.version#.#chr(13)#">
  22. <cfloop index="loc.i" from="#ArrayLen(loc.migrations)#" to="1" step="-1">
  23. <cfset loc.migration = loc.migrations[loc.i]>
  24. <cfif loc.migration.version lte arguments.version><cfbreak></cfif>
  25. <cfif loc.migration.status eq "migrated">
  26. <!--- <cftransaction action="begin"> --->
  27. <cftry>
  28. <cfset loc.feedback = loc.feedback & "#chr(13)#------- " & loc.migration.cfcfile & " #RepeatString("-",Max(5,50-Len(loc.migration.cfcfile)))##chr(13)#">
  29. <cfset Request.migrationOutput = "">
  30. <cfset Request.migrationSQLFile = "#loc.sqlPath#/#loc.migration.cfcfile#_down.sql">
  31. <cffile action="write" file="#Request.migrationSQLFile#" output="">
  32. <cfset loc.migration.cfc.down()>
  33. <cfset loc.feedback = loc.feedback & Request.migrationOutput>
  34. <cfset $removeVersionAsMigrated(loc.migration.version)>
  35. <cfcatch type="any">
  36. <cfset loc.feedback = loc.feedback & "Error migrating to #loc.migration.version#.#chr(13)##CFCATCH.Message##chr(13)##CFCATCH.Detail##chr(13)#">
  37. <!--- <cftransaction action="rollback" /> --->
  38. <cfbreak>
  39. </cfcatch>
  40. </cftry>
  41. <!--- <cftransaction action="commit" />
  42. </cftransaction> --->
  43. </cfif>
  44. </cfloop>
  45. <cfelse>
  46. <cfset loc.feedback = "Migrating from #loc.currentVersion# up to #arguments.version#.#chr(13)#">
  47. <cfloop index="loc.i" from="1" to="#ArrayLen(loc.migrations)#">
  48. <cfset loc.migration = loc.migrations[loc.i]>
  49. <cfif loc.migration.version lte arguments.version and loc.migration.status neq "migrated">
  50. <!--- <cftransaction action="begin"> --->
  51. <cftry>
  52. <cfset loc.feedback = loc.feedback & "#chr(13)#-------- " & loc.migration.cfcfile & " #RepeatString("-",Max(5,50-Len(loc.migration.cfcfile)))##chr(13)#">
  53. <cfset Request.migrationOutput = "">
  54. <cfset Request.migrationSQLFile = "#loc.sqlPath#/#loc.migration.cfcfile#_up.sql">
  55. <cffile action="write" file="#Request.migrationSQLFile#" output="">
  56. <cfset loc.migration.cfc.up()>
  57. <cfset loc.feedback = loc.feedback & Request.migrationOutput>
  58. <cfset $setVersionAsMigrated(loc.migration.version)>
  59. <cfcatch type="any">
  60. <cfset loc.feedback = loc.feedback & "Error migrating to #loc.migration.version#.#chr(13)##CFCATCH.Message##chr(13)##CFCATCH.Detail##chr(13)#">
  61. <!--- <cftransaction action="rollback" /> --->
  62. <cfbreak>
  63. </cfcatch>
  64. </cftry>
  65. <!--- <cftransaction action="commit" />
  66. </cftransaction> --->
  67. <cfelseif loc.migration.version gt arguments.version>
  68. <cfbreak>
  69. </cfif>
  70. </cfloop>
  71. </cfif>
  72. </cfif>
  73. <cfreturn loc.feedback>
  74. </cffunction>
  75. <cffunction name="getCurrentMigrationVersion" access="public" returntype="string" hint="returns current database version">
  76. <cfset var loc = {}>
  77. <cfset loc.listPreviouslyMigratedVersions = $getVersionsPreviouslyMigrated()>
  78. <cfreturn ListLast(loc.listPreviouslyMigratedVersions)>
  79. </cffunction>
  80. <cffunction name="getAvailableMigrations" access="public" returntype="array" hint="searches db/migrate folder for migrations">
  81. <cfset var loc = {}>
  82. <cfset loc.listVersionsPreviouslyMigrated = $getVersionsPreviouslyMigrated()>
  83. <cfset loc.migrations = ArrayNew(1)>
  84. <cfset loc.migrationRE = "^([\d]{3,14})_([^\.]*)\.cfc$">
  85. <cfset loc.migrationsPath = expandPath("db/migrate")>
  86. <cfif not DirectoryExists(loc.migrationsPath)>
  87. <cfdirectory action="create" directory="#loc.migrationsPath#">
  88. </cfif>
  89. <cfdirectory action="list" name="qMigrationFiles" directory="#loc.migrationsPath#" sort="Name" filter="*.cfc" type="file" />
  90. <cfloop query="qMigrationFiles">
  91. <cfif REFind(loc.migrationRE,Name)>
  92. <cfset loc.migration = {}>
  93. <cfset loc.migration.version = REReplace(Name,loc.migrationRE,"\1")>
  94. <cfset loc.migration.name = REReplace(Name,loc.migrationRE,"\2")>
  95. <cfset loc.migration.cfcfile = REReplace(Name,loc.migrationRE,"\1_\2")>
  96. <cfset loc.migration.loadError = "">
  97. <cfset loc.migration.details = "description unavailable">
  98. <cfset loc.migration.status = "">
  99. <cftry>
  100. <cfset loc.migration.cfc = $createObjectFromRoot(path="db.migrate",fileName=loc.migration.cfcfile, method="init")>
  101. <cfset loc.metaData = GetMetaData(loc.migration.cfc)>
  102. <cfif structKeyExists(loc.metaData,"hint")>
  103. <cfset loc.migration.details = loc.metaData.hint>
  104. </cfif>
  105. <cfif ListFind(loc.listVersionsPreviouslyMigrated, loc.migration.version) neq 0>
  106. <cfset loc.migration.status = "migrated">
  107. </cfif>
  108. <cfcatch type="any"><cfset loc.migration.loadError = CFCATCH.Message></cfcatch>
  109. </cftry>
  110. <cfset ArrayAppend(loc.migrations,loc.migration)>
  111. </cfif>
  112. </cfloop>
  113. <cfreturn loc.migrations>
  114. </cffunction>
  115. <cffunction name="createMigration" access="public" returntype="string">
  116. <cfargument name="migrationName" type="string" required="true" />
  117. <cfargument name="templateName" type="string" required="false" default="blank" />
  118. <cfargument name="migrationPrefix" type="string" required="false" default="timestamp" />
  119. <cfif len(trim(arguments.migrationName)) gt 0>
  120. <cfreturn $copyTemplateMigrationAndRename(argumentCollection=arguments)>
  121. <cfelse>
  122. <cfreturn "You must supply a migration name (e.g. 'creates member table')">
  123. </cfif>
  124. </cffunction>
  125. <cffunction name="$setVersionAsMigrated" access="private">
  126. <cfargument name="version" required="true" type="string">
  127. <cfquery datasource="#application.wheels.dataSourceName#" username="#application.wheels.dataSourceUserName#" password="#application.wheels.dataSourcePassword#">
  128. INSERT INTO schemainfo (version) VALUES (<cfqueryparam cfsqltype="cf_sql_varchar" value="#arguments.version#">)
  129. </cfquery>
  130. </cffunction>
  131. <cffunction name="$removeVersionAsMigrated" access="private">
  132. <cfargument name="version" required="true" type="string">
  133. <cfquery datasource="#application.wheels.dataSourceName#" username="#application.wheels.dataSourceUserName#" password="#application.wheels.dataSourcePassword#">
  134. DELETE FROM schemainfo WHERE version = <cfqueryparam cfsqltype="cf_sql_varchar" value="#arguments.version#">
  135. </cfquery>
  136. </cffunction>
  137. <!--- this function copied from /wheels/global/internal.cfm --->
  138. <cffunction name="$createObjectFromRoot" returntype="any" access="private" output="false">
  139. <cfargument name="path" type="string" required="true">
  140. <cfargument name="fileName" type="string" required="true">
  141. <cfargument name="method" type="string" required="true">
  142. <cfscript>
  143. var loc = {};
  144. arguments.returnVariable = "loc.returnValue";
  145. arguments.component = arguments.path & "." & arguments.fileName;
  146. StructDelete(arguments, "path");
  147. StructDelete(arguments, "fileName");
  148. </cfscript>
  149. <cfinclude template="../../root.cfm">
  150. <cfreturn loc.returnValue>
  151. </cffunction>
  152. <cffunction name="$copyTemplateMigrationAndRename" displayname="$copyTemplateMigrationAndRename" access="private" returntype="string">
  153. <cfargument name="migrationName" type="string" required="true" />
  154. <cfargument name="templateName" type="string" required="true" />
  155. <cfargument name="migrationPrefix" type="string" required="false" default="" />
  156. <cfset var loc = {}/>
  157. <cfset loc.migrationsPath = expandPath("db/migrate")/>
  158. <cfset loc.templateFile = expandPath("plugins/dbmigrate/templates") & "/" & arguments.templateName & ".cfc"/>
  159. <cfset loc.extendsPath = "plugins.dbmigrate.Migration"/>
  160. <cfif not FileExists(loc.templateFile)>
  161. <cfreturn "Template #arguments.templateName# could not be found">
  162. </cfif>
  163. <cfif not DirectoryExists(loc.migrationsPath)>
  164. <cfdirectory action="create" directory="#loc.migrationsPath#">
  165. </cfif>
  166. <cftry>
  167. <cffile action="read" file="#loc.templateFile#" variable="loc.templateContent">
  168. <cfif Len(Trim(application.wheels.rootcomponentpath)) GT 0>
  169. <cfset loc.extendsPath = application.wheels.rootcomponentpath & ".plugins.dbmigrate.Migration"/>
  170. </cfif>
  171. <cfset loc.templateContent = replace(loc.templateContent, "[extends]", loc.extendsPath)>
  172. <cfset loc.templateContent = replace(loc.templateContent, "[description]", replace(arguments.migrationName,'"','&quot;','ALL'))>
  173. <cfset loc.migrationFile = REREplace(arguments.migrationName,"[^A-z0-9]+"," ","ALL")>
  174. <cfset loc.migrationFile = REREplace(Trim(loc.migrationFile),"[\s]+","_","ALL")>
  175. <cfset loc.migrationFile = $getNextMigrationNumber(arguments.migrationPrefix) & "_#loc.migrationFile#.cfc">
  176. <cffile action="write" file="#loc.migrationsPath#/#loc.migrationFile#" output="#loc.templateContent#">
  177. <cfcatch type="any">
  178. <cfreturn "There was an error when creating the migration: #cfcatch.message#">
  179. </cfcatch>
  180. </cftry>
  181. <cfreturn "The migration #loc.migrationFile# file was created" />
  182. </cffunction>
  183. <cffunction name="$getNextMigrationNumber" returntype="string">
  184. <cfargument name="migrationPrefix" type="string" required="false" default="" />
  185. <cfscript>
  186. var loc = {};
  187. loc.migrationNumber = dateformat(now(),'yyyymmdd') & timeformat(now(),'HHMMSS');
  188. if(arguments.migrationPrefix != "timestamp") {
  189. loc.migrations = getAvailableMigrations();
  190. if(ArrayLen(loc.migrations) eq 0) {
  191. if(arguments.migrationPrefix == "numeric") {
  192. loc.migrationNumber = "001";
  193. }
  194. } else {
  195. // determine current numbering system
  196. loc.lastMigration = loc.migrations[ArrayLen(loc.migrations)];
  197. if(Len(loc.lastMigration.version) eq 3) {
  198. // use numeric numbering
  199. loc.migrationNumber = NumberFormat(Val(loc.lastMigration.version)+1,"009");
  200. }
  201. }
  202. }
  203. </cfscript>
  204. <cfreturn loc.migrationNumber>
  205. </cffunction>
  206. <cffunction name="$getVersionsPreviouslyMigrated" access="private" returntype="string">
  207. <cfset var loc = {}>
  208. <cftry>
  209. <cfquery name="loc.qMigratedVersions" datasource="#application.wheels.dataSourceName#" username="#application.wheels.dataSourceUserName#" password="#application.wheels.dataSourcePassword#">
  210. SELECT version FROM schemainfo ORDER BY version ASC
  211. </cfquery>
  212. <cfcatch type="database">
  213. <cfquery datasource="#application.wheels.dataSourceName#" username="#application.wheels.dataSourceUserName#" password="#application.wheels.dataSourcePassword#">
  214. CREATE TABLE schemainfo (version VARCHAR(25))
  215. </cfquery>
  216. <cfreturn "0">
  217. </cfcatch>
  218. </cftry>
  219. <cfif loc.qMigratedVersions.recordcount eq 0>
  220. <cfreturn "0">
  221. <cfelse>
  222. <cfreturn ValueList(loc.qMigratedVersions.version)>
  223. </cfif>
  224. </cffunction>
  225. </cfcomponent>