Skip to content

Instantly share code, notes, and snippets.

@w00lf
Forked from boblail/sql_rocket
Created December 12, 2013 19:40
Show Gist options
  • Select an option

  • Save w00lf/7934093 to your computer and use it in GitHub Desktop.

Select an option

Save w00lf/7934093 to your computer and use it in GitHub Desktop.
#!/usr/bin/env ruby
require 'benchmark'
require 'nokogiri'
require 'progressbar'
require 'tempfile'
# require 'pry'
class SqlRocket
class << self
def dump(database_name, output_file=nil)
$stderr.print "Dumping #{database_name}..."
command = "mysqldump --skip-triggers --ignore-table #{database_name}.schema_migrations --xml #{ARGV[2..-1].join(" ")} #{database_name}"
command << " > #{output_file}" if output_file
ms = Benchmark.realtime do
output = `#{command}`
puts output unless output_file
end
$stderr.puts " \e[32mfinished in #{"%.2f seconds" % ms}\e[0m"
end
def convert(xml_file)
xml = File.open(xml_file)
puts SqlRocket.new(xml).convert
end
def rocket(database_name)
xml_file = Tempfile.new(database_name)
dump(database_name, xml_file.path)
begin
puts SqlRocket.new(xml_file.open).convert
ensure
xml_file.close
xml_file.unlink
end
end
end
def initialize(io)
@io = io
@reader = Nokogiri::XML::Reader(io)
@schema_by_table = {}
@inserts = []
@mode = :continue
end
attr_reader :io, :reader
def convert
progress = ProgressBar.new("Converting", io.size)
reader.each do |node|
figure_it_out(node)
progress.set io.pos
end
progress.finish
@inserts.join("\n")
end
def figure_it_out(node)
if node.node_type == Nokogiri::XML::Reader::TYPE_END_ELEMENT
case node.name
when 'table_structure'
@mode = :continue
when 'table_data'
table_name = node.attribute('name')
@inserts << "INSERT INTO \"#{table_name}\" VALUES #{@rows.join(",")};" if @rows.any?
@mode = :continue
when 'row'
@rows << "(#{@values.join(",")})"
end
end
if node.node_type == Nokogiri::XML::Reader::TYPE_ELEMENT
case node.name
when 'table_structure'
@mode = :read_structure
table_name = node.attribute('name')
@current_schema = @schema_by_table[table_name] = {}
when 'table_data'
@mode = :read_data
table_name = node.attribute('name')
@current_schema = @schema_by_table[table_name] # readonly
@rows = []
when 'row'
@values = []
when 'field'
add_field_to_table_structure(node) if @mode == :read_structure
add_value_to_table_row(node) if @mode == :read_data
end
end
end
def add_field_to_table_structure(node)
@current_schema[node.attribute('Field')] = node.attribute('Type')
end
def add_value_to_table_row(node)
type = @current_schema[node.attribute('name')]
@values << output_postgres_value(node, type)
end
def output_postgres_value(node, type)
return "NULL" if node.attribute('xsi:nil') == "true"
value = Nokogiri::XML.fragment(node.inner_xml).text
return value if type =~ /^int/
return output_quoted(value) if type =~ /^(decimal|varchar|text|date|datetime|time)/
return output_postgres_boolean(value) if type == "tinyint(1)"
raise NotImplementedError, "Field type #{type} is unhandled"
end
def output_quoted(value)
escaped = value.gsub(/'/, "''")
"'#{escaped}'"
end
def output_postgres_boolean(value)
output_quoted(value == '1' ? 't' : 'f')
end
end
command, arg = ARGV
case command
when "dump"; SqlRocket.dump arg
when "convert"; SqlRocket.convert arg
when "rocket"; SqlRocket.rocket arg
else puts "Uh... Don't know #{command}. Please say dump, convert or rocket!"
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment