20131223032112_switch_to_json_serialization.rb 2.5 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677
  1. class SwitchToJsonSerialization < ActiveRecord::Migration
  2. FIELDS = {
  3. :agents => [:options, :memory],
  4. :events => [:payload]
  5. }
  6. def up
  7. if data_exists?
  8. puts "This migration will update tables to use UTF-8 encoding and will update Agent and Event storage from YAML to JSON."
  9. puts "It should work, but please make a backup before proceeding!"
  10. print "Continue? (y/n) "
  11. STDOUT.flush
  12. exit unless STDIN.gets =~ /^y/i
  13. set_to_utf8
  14. translate YAML, JSON
  15. end
  16. end
  17. def down
  18. if data_exists?
  19. translate JSON, YAML
  20. end
  21. end
  22. def set_to_utf8
  23. if mysql?
  24. %w[agent_logs agents delayed_jobs events links users].each do |table_name|
  25. quoted_table_name = ActiveRecord::Base.connection.quote_table_name(table_name)
  26. execute "ALTER TABLE #{quoted_table_name} CONVERT TO CHARACTER SET utf8"
  27. end
  28. end
  29. end
  30. def mysql?
  31. ActiveRecord::Base.connection.adapter_name =~ /mysql/i
  32. end
  33. def data_exists?
  34. events = ActiveRecord::Base.connection.select_rows("SELECT count(*) FROM #{ActiveRecord::Base.connection.quote_table_name("events")}").first.first.to_i
  35. agents = ActiveRecord::Base.connection.select_rows("SELECT count(*) FROM #{ActiveRecord::Base.connection.quote_table_name("agents")}").first.first.to_i
  36. agents + events > 0
  37. end
  38. def translate(from, to)
  39. FIELDS.each do |table, fields|
  40. quoted_table_name = ActiveRecord::Base.connection.quote_table_name(table)
  41. fields = fields.map { |f| ActiveRecord::Base.connection.quote_column_name(f) }
  42. page_start = 0
  43. page_size = 1000
  44. page_end = page_start + page_size
  45. begin
  46. rows = ActiveRecord::Base.connection.select_rows("SELECT id, #{fields.join(", ")} FROM #{quoted_table_name} WHERE id >= #{page_start} AND id < #{page_end}")
  47. puts "Grabbing rows of #{table} from #{page_start} to #{page_end}"
  48. rows.each do |row|
  49. id, *field_data = row
  50. yaml_fields = field_data.map { |f| from.load(f) }.map { |f| to.dump(f) }
  51. yaml_fields.map! {|f| f.encode('utf-8', 'binary', invalid: :replace, undef: :replace, replace: '??') }
  52. update_sql = "UPDATE #{quoted_table_name} SET #{fields.map {|f| "#{f}=?"}.join(", ")} WHERE id = ?"
  53. sanitized_update_sql = ActiveRecord::Base.send :sanitize_sql_array, [update_sql, *yaml_fields, id]
  54. ActiveRecord::Base.connection.execute sanitized_update_sql
  55. end
  56. page_start += page_size
  57. page_end += page_size
  58. end until rows.count == 0
  59. end
  60. end
  61. end