-
Notifications
You must be signed in to change notification settings - Fork 7
/
Copy pathcsv2sqlite.rb
145 lines (125 loc) · 3.83 KB
/
csv2sqlite.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
#!/usr/bin/env ruby
require 'csv'
require 'rubygems'
require 'sequel'
require 'trollop'
require 'tempfile'
require 'sqlite3'
# ruby 1.8 FasterCSV compatibility
if CSV.const_defined? :Reader
require 'fastercsv'
Object.send(:remove_const, :CSV)
CSV = FasterCSV
end
OPTIONS = Trollop::options do
banner <<-EOS
Usage:
csv2sqlite [options] TABLENAME.csv [...]
where [options] are:
EOS
opt :irb_console, "Open an IRB session after loading FILES into an in-memory DB"
opt :sqlite_console, "Execute 'sqlite3 FILENAME.db' afterwards"
opt :output, "FILENAME.db where to save the sqlite database", :type => :string
end
def getDatabase(filename)
puts "Connecting to sqlite://#{filename}"
database = Sequel.sqlite(filename)
# database.test_connection # saves blank file
return database
end
def populateTableFromCSV(database,filename)
options = { :headers => true,
:header_converters => :symbol,
:converters => :all }
data = CSV.table(filename, options)
headers = data.headers
tablename = File.basename(filename, '.csv').gsub(/[^0-9a-zA-Z_]/,'_').to_sym
puts "Dropping and re-creating table #{tablename}"
DB.drop_table? tablename
DB.create_table tablename do
# see http://sequel.rubyforge.org/rdoc/files/doc/schema_modification_rdoc.html
# primary_key :id
# Float :price
data.by_col!.each do |columnName,rows|
columnType = getCommonClass(rows) || String
column columnName, columnType
end
end
data.by_row!.each do |row|
database[tablename].insert(row.to_hash)
end
end
#
# :call-seq:
# getCommonClass([1,2,3]) => FixNum
# getCommonClass([1,"bob",3]) => String
#
# Returns the class of each element in +rows+ if same for all elements, otherwise returns nil
#
def getCommonClass(rows)
return rows.inject(rows[0].class) { |klass, el| break if klass != el.class ; klass }
end
def launchConsole(database)
require 'irb'
require 'pp'
require 'yaml'
puts "Launching IRB Console.\n\n"
puts "You can now interact with the database via DB. Examples:"
puts " DB.tables #=> SHOW tables"
puts " ds = DB[:posts] #=> SELECT * FROM posts"
puts " ds = DB[:posts].where(:id => 1) #==> SELECT * FROM posts WHERE id => 1"
puts " puts DB[:posts].all ; nil #=> executes query, pretty prints results"
puts ""
puts "See http://sequel.rubyforge.org/rdoc/files/doc/dataset_basics_rdoc.html"
puts "To launch sqlite3 console, type 'sqlite3'"
puts ""
puts "Available tables: "
database.tables.each do |table|
puts " DB[:#{table.to_s}] - #{DB[table].count.to_s} records"
end
puts ""
IRB.start
catch :IRB_EXIT do
# IRB.start should trap this but doesn't
exit
end
end
def sqlite3()
launchSqliteConsole()
end
def launchSqliteConsole()
File.exists?(DB_PATH) or Trollop.die "Unable to launch sqlite3; invalid file: #{DB_PATH}"
puts "Launching 'sqlite3 #{DB_PATH}'. Table schema:\n"
# NB: Using Kernel.system instead of Kernel.exec to allow Tempfile cleanup
system("sqlite3 #{DB_PATH} '.schema'")
puts ""
system("sqlite3 #{DB_PATH}")
exit
end
if OPTIONS[:output]
DB_PATH = OPTIONS[:output]
else
DB_TMP = Tempfile.new(['csv2sqlite','.sqlite3'])
DB_PATH = DB_TMP.path
end
DB = getDatabase(DB_PATH)
Trollop.die "Missing CSV file argument(s)" unless ARGV.count > 0
until ARGV.empty? do
file = ARGV.shift
File.exists?(file) or Trollop.die "Invalid file: #{file}"
puts "Parsing file #{file}"
populateTableFromCSV(DB, file)
end
launchSqliteConsole() if OPTIONS[:sqlite_console]
launchConsole(DB) if OPTIONS[:irb_console] || ! OPTIONS[:output]
__END__
"year","name","percent","sex"
1880,"John",0.081541,"boy"
1880,"William",0.080511,"boy"
1880,"James",0.050057,"boy"
1880,"Charles",0.045167,"boy"
1880,"George",0.043292,"boy"
1880,"Frank",0.02738,"boy"
1880,"Joseph",0.022229,"boy"
1880,"Thomas",0.021401,"boy"
1880,"Henry",0.020641,"boy"