Advertisement
Guest User

Untitled

a guest
Dec 28th, 2016
85
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 8.09 KB | None | 0 0
  1. #!/usr/bin/env ruby
  2. =begin
  3. Migrate TT-RSS data from MySQL to Postgres
  4.  
  5. Author: Bryce Chidester <bryce@cobryce.com>
  6. Provided as-is, no warranty. Make backups!
  7.  
  8. This script may work in the reverse direction... but probably not. And seeing as
  9. TT-RSS doesn't officially support any other database backend at this time, there's
  10. really no point in migrating to or from any other database.
  11. Really this script was just the quickest and easiest way for me to "convert" my
  12. TT-RSS instance from the legacy MySQL instance to Postgres, without losing all
  13. the histories, entries, starred entries, tags etc. I found an old Java migrator,
  14. which wasn't an option for many reasons, and I messed around with pgloader for
  15. awhile but it required too much configuration and coaxing. In the end, it was
  16. much simpler to write my own migrator/converter. This script is largely based
  17. off sequel's --copy-database mode.
  18.  
  19. Basic Usage:
  20. 1. Create the target database, and a user. If you don't know how to do this,
  21. I refer you to Postgres' documentation.
  22. 2. Load the bare TT-RSS Postgres schema.
  23. psql <connect string> -f <path to schema/ttrss_schema_pgsql.sql>
  24. 3. Make sure update-daemon2.php is stopped
  25. 4. Run this script.
  26. ttrss-migrator.rb <MySQL connect string> <Postgres connect string>
  27. Ex: 'mysql2://localhost/tt-rss?user=<user>&password=<password>&encoding=utf8'
  28. Note: I used the mysql2 connector and explicitly specified encoding=utf8 to
  29. avoid some encoding issues I encountered early on. If newlines appear as
  30. "\012" then you probably messed up this part.
  31. Ex: 'postgres://localhost/ttrss?user=<user>&password=<password>&encoding=utf8'
  32. Note: Again, I explicitly specified encoding=utf8 to ensure everything is clear.
  33. 5. Update your TT-RSS config.php with the Postgres connection details.
  34.  
  35.  
  36. License:
  37. Copyright (c) 2016, Bryce Chidester <bryce@cobryce.com>
  38. All rights reserved.
  39.  
  40. Redistribution and use in source and binary forms, with or without
  41. modification, are permitted provided that the following conditions are met:
  42.  
  43. 1. Redistributions of source code must retain the above copyright notice, this
  44. list of conditions and the following disclaimer.
  45. 2. Redistributions in binary form must reproduce the above copyright notice,
  46. this list of conditions and the following disclaimer in the documentation
  47. and/or other materials provided with the distribution.
  48.  
  49. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
  50. ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
  51. WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  52. DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
  53. ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
  54. (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  55. LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  56. ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  57. (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  58. SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  59.  
  60. The views and conclusions contained in the software and documentation are those
  61. of the authors and should not be interpreted as representing official policies,
  62. either expressed or implied, of the FreeBSD Project.
  63. =end
  64.  
  65. require 'logger'
  66. require 'sequel'
  67.  
  68. def usage
  69. "Usage:\n" +
  70. "#{$0} <MySQL connect string> <Postgres connect string>\n" +
  71. "See file header for more information."
  72. end
  73. abort usage if ARGV.count < 2
  74.  
  75. start_time = Time.now
  76.  
  77. Srcloggers = []
  78. Srcloggers << Logger.new($stderr)
  79. Srcloggers.each { |l| l.progname="SourceDB" }
  80.  
  81. Tgtloggers = []
  82. Tgtloggers << Logger.new($stderr)
  83. Tgtloggers.each { |l| l.progname="TargetDB" }
  84.  
  85. SourceDB = Sequel.connect(ARGV[0])
  86. SourceDB.loggers = Srcloggers
  87. SourceDB.test_connection
  88.  
  89. TargetDB = Sequel.connect(ARGV[1])
  90. TargetDB.loggers = Tgtloggers
  91. TargetDB.test_connection
  92.  
  93. =begin
  94. 1. Drop foreign_keys from TargetDB
  95. 2. Make any custom table modifications to avoid constraints
  96. 3. Copy data
  97. 4. Make any custom table modifications to reset constraints
  98. 5. Re-add foreign_keys to TargetDB
  99. =end
  100.  
  101. Sequel.extension :migration
  102. TargetDB.extension :schema_dumper
  103.  
  104. #index_migration = eval(TargetDB.dump_indexes_migration(:same_db=>true, :index_names=>true))
  105. fk_migration = eval(TargetDB.dump_foreign_key_migration(:same_db=>true))
  106.  
  107. SourceDB.transaction do
  108. TargetDB.transaction do
  109. puts "Begin removing foreign key constraints"
  110. fk_migration.apply(TargetDB, :down)
  111. puts "Finished removing foreign key constraints"
  112.  
  113. # Temporarily allow null in section_name from ttrss_prefs_sections
  114. TargetDB.alter_table(:ttrss_prefs_sections) do
  115. set_column_allow_null :section_name
  116. end
  117.  
  118. SourceDB.tables.each do |table|
  119. puts "Truncating TargetDB table: #{table}"
  120. TargetDB[table].truncate
  121. puts "Begin copying records for table: #{table}"
  122. time = Time.now
  123. to_ds = TargetDB.from(table)
  124. j = 0
  125. SourceDB.from(table).each do |record|
  126. if Time.now - time > 5
  127. puts "Status: #{j} records copied"
  128. time = Time.now
  129. end
  130. to_ds.insert(record)
  131. j += 1
  132. end
  133. puts "Finished copying #{j} records for table: #{table}"
  134. end
  135. puts "Finished copying data"
  136.  
  137. # Apply schema updates to ttrss_prefs_sections and reset section_name allowing nulls
  138. TargetDB[:ttrss_prefs_sections].where(:id=>1).update(:section_name => 'General')
  139. TargetDB[:ttrss_prefs_sections].where(:id=>2).update(:section_name => 'Interface')
  140. TargetDB[:ttrss_prefs_sections].where(:id=>3).update(:section_name => 'Advanced')
  141. TargetDB[:ttrss_prefs_sections].where(:id=>4).update(:section_name => 'Digest')
  142. TargetDB.alter_table(:ttrss_prefs_sections) do
  143. set_column_not_null :section_name
  144. end
  145.  
  146. puts "Begin adding foreign key constraints"
  147. fk_migration.apply(TargetDB, :up)
  148. puts "Finished adding foreign key constraints"
  149.  
  150. TargetDB.tables.each{|t| TargetDB.reset_primary_key_sequence(t)}
  151. puts "Primary key sequences reset successfully"
  152.  
  153. puts "Database copy finished in #{Time.now - start_time} seconds"
  154. end
  155. end
  156. puts "Finished copying data"
  157.  
  158. exit
  159.  
  160. =begin
  161. For reference:
  162. if copy_databases
  163. Sequel.extension :migration
  164. DB.extension :schema_dumper
  165.  
  166. db2 = ARGV.shift
  167. error_proc["Error: Must specify database connection string or path to yaml file as second argument for database you want to copy to"] if db2.nil? || db2.empty?
  168. extra_proc.call
  169. start_time = Time.now
  170. TO_DB = connect_proc[db2]
  171. same_db = DB.database_type==TO_DB.database_type
  172. index_opts = {:same_db=>same_db}
  173. index_opts[:index_names] = :namespace if !DB.global_index_namespace? && TO_DB.global_index_namespace?
  174.  
  175. puts "Databases connections successful"
  176. schema_migration = eval(DB.dump_schema_migration(:indexes=>false, :same_db=>same_db))
  177. index_migration = eval(DB.dump_indexes_migration(index_opts))
  178. fk_migration = eval(DB.dump_foreign_key_migration(:same_db=>same_db))
  179. puts "Migrations dumped successfully"
  180.  
  181. schema_migration.apply(TO_DB, :up)
  182. puts "Tables created"
  183.  
  184. puts "Begin copying data"
  185. DB.transaction do
  186. TO_DB.transaction do
  187. DB.tables.each do |table|
  188. puts "Begin copying records for table: #{table}"
  189. time = Time.now
  190. to_ds = TO_DB.from(table)
  191. j = 0
  192. DB.from(table).each do |record|
  193. if Time.now - time > 5
  194. puts "Status: #{j} records copied"
  195. time = Time.now
  196. end
  197. to_ds.insert(record)
  198. j += 1
  199. end
  200. puts "Finished copying #{j} records for table: #{table}"
  201. end
  202. end
  203. end
  204. puts "Finished copying data"
  205.  
  206. puts "Begin creating indexes"
  207. index_migration.apply(TO_DB, :up)
  208. puts "Finished creating indexes"
  209.  
  210. puts "Begin adding foreign key constraints"
  211. fk_migration.apply(TO_DB, :up)
  212. puts "Finished adding foreign key constraints"
  213.  
  214. if TO_DB.database_type == :postgres
  215. TO_DB.tables.each{|t| TO_DB.reset_primary_key_sequence(t)}
  216. puts "Primary key sequences reset successfully"
  217. end
  218. puts "Database copy finished in #{Time.now - start_time} seconds"
  219. exit
  220. end
  221. =end
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement