csvql 0.1.7 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/lib/csvql/csvql.rb +7 -106
- data/lib/csvql/version.rb +1 -1
- data/lib/csvql.rb +104 -1
- data/spec/csvql_spec.rb +47 -2
- data/spec/sample.csv +6 -0
- data/spec/spec_helper.rb +11 -0
- metadata +5 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 6154147fd2ece48902d1e4494b7d248cd125f9ab
|
4
|
+
data.tar.gz: 492d8e597c6c1e657b2efdfdadc48f9e4c4e6ad5
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 578f5cedc8514c6df4790cfd821b98b075095cce9a6838fc865f8e2d5939864eb1c36151bcd78d6c2ee604ea9263bd56f6552986dde98381f30af8333bd3e624
|
7
|
+
data.tar.gz: 49956fd0295eaf4d7813038ac4f7ab1b5655c2044ce98d193e5143699850eb40798a0a28d11b4d1771b47987351ebea57b86b5fed96c15c7bb923cfe0bd42487
|
data/lib/csvql/csvql.rb
CHANGED
@@ -1,16 +1,13 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
# -*- coding: utf-8 -*-
|
3
3
|
|
4
|
-
require 'csv'
|
5
|
-
require 'nkf'
|
6
4
|
require 'tempfile'
|
7
|
-
require 'optparse'
|
8
5
|
require 'sqlite3'
|
9
6
|
|
10
7
|
module Csvql
|
11
8
|
class TableHandler
|
12
9
|
def initialize(path, console)
|
13
|
-
@db_file = if path && path.size > 0
|
10
|
+
@db_file = if path && path.strip.size > 0
|
14
11
|
path
|
15
12
|
elsif console
|
16
13
|
@tmp_file = Tempfile.new("csvql").path
|
@@ -20,26 +17,11 @@ module Csvql
|
|
20
17
|
@db = SQLite3::Database.new(@db_file)
|
21
18
|
end
|
22
19
|
|
23
|
-
def create_table(
|
24
|
-
@
|
20
|
+
def create_table(schema, table_name="tbl")
|
21
|
+
@col_name = schema.split(",").map {|c| c.split.first.strip }
|
22
|
+
@col_size = @col_name.size
|
25
23
|
@table_name = table_name
|
26
|
-
|
27
|
-
file = File.expand_path(schema)
|
28
|
-
col = if File.exist?(file)
|
29
|
-
File.open(file).read
|
30
|
-
else
|
31
|
-
schema
|
32
|
-
end
|
33
|
-
@col_name = col.split(",").map {|c| c.split.first.strip }
|
34
|
-
else
|
35
|
-
@col_name = if header
|
36
|
-
cols
|
37
|
-
else
|
38
|
-
@col_size.times.map {|i| "c#{i}" }
|
39
|
-
end
|
40
|
-
col = @col_name.map {|c| "#{c} NONE" }.join(",")
|
41
|
-
end
|
42
|
-
sql = "CREATE TABLE IF NOT EXISTS #{@table_name} (#{col})"
|
24
|
+
sql = "CREATE TABLE IF NOT EXISTS #{@table_name} (#{schema})"
|
43
25
|
@db.execute(sql)
|
44
26
|
end
|
45
27
|
|
@@ -62,13 +44,8 @@ module Csvql
|
|
62
44
|
@pre.execute(cols)
|
63
45
|
end
|
64
46
|
|
65
|
-
def exec(sql
|
66
|
-
|
67
|
-
dlm = "\t"
|
68
|
-
end
|
69
|
-
@db.execute(sql) do |row|
|
70
|
-
puts row.join(dlm)
|
71
|
-
end
|
47
|
+
def exec(sql)
|
48
|
+
@db.execute(sql)
|
72
49
|
end
|
73
50
|
|
74
51
|
def open_console
|
@@ -76,80 +53,4 @@ module Csvql
|
|
76
53
|
File.delete(@tmp_file) if @tmp_file
|
77
54
|
end
|
78
55
|
end
|
79
|
-
|
80
|
-
class << self
|
81
|
-
def option_parse(argv)
|
82
|
-
opt = OptionParser.new
|
83
|
-
option = {}
|
84
|
-
|
85
|
-
# default
|
86
|
-
option[:header] = true
|
87
|
-
|
88
|
-
opt.banner = "Usage: csvql [csvfile] [options]"
|
89
|
-
opt.on("--console", "After all commands are run, open sqlite3 console with this data") {|v| option[:console] = v }
|
90
|
-
opt.on("--[no-]header", "Treat file as having the first row as a header row") {|v| option[:header] = v }
|
91
|
-
opt.on('--output-dlm="|"', "Output delimiter (|)") {|v| option[:output_dlm] = v }
|
92
|
-
opt.on("--save-to=FILE", "If set, sqlite3 db is left on disk at this path") {|v| option[:save_to] = v }
|
93
|
-
opt.on("--append", "Append mode (not dropping any tables)") {|v| option[:append] = v }
|
94
|
-
opt.on("--skip-comment", "Skip comment lines start with '#'") {|v| option[:skip_comment] = v }
|
95
|
-
opt.on("--source=FILE", "Source file to load, or defaults to stdin") {|v| option[:source] = v }
|
96
|
-
opt.on("--sql=SQL", "SQL Command(s) to run on the data") {|v| option[:sql] = v }
|
97
|
-
opt.on("--select=COLUMN", "Select column (*)") {|v| option[:select] = v }
|
98
|
-
opt.on("--schema=FILE or STRING", "Specify a table schema") {|v| option[:schema] = v }
|
99
|
-
opt.on("--where=COND", "Where clause") {|v| option[:where] = v }
|
100
|
-
opt.on("--table-name=NAME", "Override the default table name (tbl)") {|v| option[:table_name] = v }
|
101
|
-
opt.on("--verbose", "Enable verbose logging") {|v| option[:verbose] = v }
|
102
|
-
opt.parse!(argv)
|
103
|
-
|
104
|
-
option[:source] ||= argv[0]
|
105
|
-
# option[:where] ||= argv[1]
|
106
|
-
option[:table_name] ||= "tbl"
|
107
|
-
option[:output_dlm] ||= "|"
|
108
|
-
option
|
109
|
-
end
|
110
|
-
|
111
|
-
def run(argv)
|
112
|
-
option = option_parse(argv)
|
113
|
-
if option[:console] && option[:source] == nil
|
114
|
-
puts "Can not open console with pipe input, read a file instead"
|
115
|
-
exit 1
|
116
|
-
end
|
117
|
-
if option[:sql] && (option[:select] || option[:where])
|
118
|
-
puts "Can not use --sql option and --select|--where option at the same time."
|
119
|
-
exit 1
|
120
|
-
end
|
121
|
-
|
122
|
-
csvfile = option[:source] ? File.open(option[:source]) : $stdin
|
123
|
-
|
124
|
-
tbl = TableHandler.new(option[:save_to], option[:console])
|
125
|
-
tbl.drop_table(option[:table_name]) unless option[:append]
|
126
|
-
tbl.exec("PRAGMA synchronous=OFF")
|
127
|
-
tbl.exec("BEGIN TRANSACTION")
|
128
|
-
csvfile.each.with_index(1) do |line,i|
|
129
|
-
line = NKF.nkf('-w', line).strip
|
130
|
-
next if line.size == 0
|
131
|
-
next if option[:skip_comment] && line.start_with?("#")
|
132
|
-
row = line.parse_csv
|
133
|
-
if i == 1
|
134
|
-
tbl.create_table(row, option[:header], option[:table_name], option[:schema])
|
135
|
-
next if option[:header]
|
136
|
-
end
|
137
|
-
tbl.insert(row, i)
|
138
|
-
end
|
139
|
-
tbl.exec("COMMIT TRANSACTION")
|
140
|
-
|
141
|
-
if option[:sql]
|
142
|
-
sql = option[:sql]
|
143
|
-
elsif option[:select] || option[:where]
|
144
|
-
option[:select] ||= "*"
|
145
|
-
sql = "select #{option[:select]} from #{option[:table_name]}"
|
146
|
-
if option[:where]
|
147
|
-
sql += " where (#{option[:where]})"
|
148
|
-
end
|
149
|
-
end
|
150
|
-
|
151
|
-
tbl.exec(sql, option[:output_dlm]) if sql
|
152
|
-
tbl.open_console if option[:console]
|
153
|
-
end
|
154
|
-
end
|
155
56
|
end
|
data/lib/csvql/version.rb
CHANGED
data/lib/csvql.rb
CHANGED
@@ -1,6 +1,109 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
|
3
|
+
require 'csv'
|
4
|
+
require 'nkf'
|
5
|
+
require 'optparse'
|
6
|
+
|
1
7
|
require "csvql/csvql"
|
2
8
|
require "csvql/version"
|
3
9
|
|
4
10
|
module Csvql
|
5
|
-
|
11
|
+
class << self
|
12
|
+
def option_parse(argv)
|
13
|
+
opt = OptionParser.new
|
14
|
+
option = {}
|
15
|
+
|
16
|
+
# default
|
17
|
+
option[:header] = true
|
18
|
+
|
19
|
+
opt.banner = "Usage: csvql [csvfile] [options]"
|
20
|
+
opt.on("--console", "After all commands are run, open sqlite3 console with this data") {|v| option[:console] = v }
|
21
|
+
opt.on("--[no-]header", "Treat file as having the first row as a header row") {|v| option[:header] = v }
|
22
|
+
opt.on('--output-dlm="|"', "Output delimiter (|)") {|v| option[:output_dlm] = v }
|
23
|
+
opt.on("--save-to=FILE", "If set, sqlite3 db is left on disk at this path") {|v| option[:save_to] = v }
|
24
|
+
opt.on("--append", "Append mode (not dropping any tables)") {|v| option[:append] = v }
|
25
|
+
opt.on("--skip-comment", "Skip comment lines start with '#'") {|v| option[:skip_comment] = v }
|
26
|
+
opt.on("--source=FILE", "Source file to load, or defaults to stdin") {|v| option[:source] = v }
|
27
|
+
opt.on("--sql=SQL", "SQL Command(s) to run on the data") {|v| option[:sql] = v }
|
28
|
+
opt.on("--select=COLUMN", "Select column (*)") {|v| option[:select] = v }
|
29
|
+
opt.on("--schema=FILE or STRING", "Specify a table schema") {|v| option[:schema] = v }
|
30
|
+
opt.on("--where=COND", "Where clause") {|v| option[:where] = v }
|
31
|
+
opt.on("--table-name=NAME", "Override the default table name (tbl)") {|v| option[:table_name] = v }
|
32
|
+
opt.on("--verbose", "Enable verbose logging") {|v| option[:verbose] = v }
|
33
|
+
opt.parse!(argv)
|
34
|
+
|
35
|
+
option[:source] ||= argv[0]
|
36
|
+
# option[:where] ||= argv[1]
|
37
|
+
option[:table_name] ||= "tbl"
|
38
|
+
if option[:output_dlm] == 'tab'
|
39
|
+
option[:output_dlm] = "\t"
|
40
|
+
end
|
41
|
+
option[:output_dlm] ||= "|"
|
42
|
+
|
43
|
+
if option[:completion]
|
44
|
+
puts opt.compsys('csvql')
|
45
|
+
exit 0
|
46
|
+
end
|
47
|
+
option
|
48
|
+
end
|
49
|
+
|
50
|
+
def run(argv)
|
51
|
+
option = option_parse(argv)
|
52
|
+
if option[:console] && option[:source] == nil
|
53
|
+
puts "Can not open console with pipe input, read a file instead"
|
54
|
+
exit 1
|
55
|
+
end
|
56
|
+
if option[:sql] && (option[:select] || option[:where])
|
57
|
+
puts "Can not use --sql option and --select|--where option at the same time."
|
58
|
+
exit 1
|
59
|
+
end
|
60
|
+
|
61
|
+
csvfile = option[:source] ? File.open(option[:source]) : $stdin
|
62
|
+
first_line = csvfile.readline
|
63
|
+
|
64
|
+
schema = option[:schema]
|
65
|
+
if schema
|
66
|
+
file = File.expand_path(schema)
|
67
|
+
if File.exist?(file)
|
68
|
+
schema = File.open(file).read
|
69
|
+
end
|
70
|
+
else
|
71
|
+
cols = first_line.parse_csv
|
72
|
+
col_name = if option[:header]
|
73
|
+
cols
|
74
|
+
else
|
75
|
+
cols.size.times.map {|i| "c#{i}" }
|
76
|
+
end
|
77
|
+
schema = col_name.map {|c| "#{c} NONE" }.join(",")
|
78
|
+
end
|
79
|
+
csvfile.rewind unless option[:header]
|
80
|
+
|
81
|
+
tbl = TableHandler.new(option[:save_to], option[:console])
|
82
|
+
tbl.drop_table(option[:table_name]) unless option[:append]
|
83
|
+
tbl.create_table(schema, option[:table_name])
|
84
|
+
tbl.exec("PRAGMA synchronous=OFF")
|
85
|
+
tbl.exec("BEGIN TRANSACTION")
|
86
|
+
csvfile.each.with_index(1) do |line,i|
|
87
|
+
line = NKF.nkf('-w', line).strip
|
88
|
+
next if line.size == 0
|
89
|
+
next if option[:skip_comment] && line.start_with?("#")
|
90
|
+
row = line.parse_csv
|
91
|
+
tbl.insert(row, i)
|
92
|
+
end
|
93
|
+
tbl.exec("COMMIT TRANSACTION")
|
94
|
+
|
95
|
+
if option[:sql]
|
96
|
+
sql = option[:sql]
|
97
|
+
elsif option[:select] || option[:where]
|
98
|
+
option[:select] ||= "*"
|
99
|
+
sql = "select #{option[:select]} from #{option[:table_name]}"
|
100
|
+
if option[:where]
|
101
|
+
sql += " where (#{option[:where]})"
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
tbl.exec(sql).each {|row| puts row.join(option[:output_dlm]) } if sql
|
106
|
+
tbl.open_console if option[:console]
|
107
|
+
end
|
108
|
+
end
|
6
109
|
end
|
data/spec/csvql_spec.rb
CHANGED
@@ -1,11 +1,56 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
|
+
csvfile = File.join(File.expand_path(File.dirname("__FILE__")), "spec/sample.csv")
|
4
|
+
|
3
5
|
describe Csvql do
|
4
6
|
it 'has a version number' do
|
5
7
|
expect(Csvql::VERSION).not_to be nil
|
6
8
|
end
|
7
9
|
|
8
|
-
it '
|
9
|
-
expect(
|
10
|
+
it 'select name' do
|
11
|
+
expect(capture {
|
12
|
+
Csvql.run([csvfile, "--select", "name"])
|
13
|
+
}).to eq(<<EOL)
|
14
|
+
Anne
|
15
|
+
Bob
|
16
|
+
Charry
|
17
|
+
Daniel
|
18
|
+
Edward
|
19
|
+
EOL
|
20
|
+
end
|
21
|
+
|
22
|
+
it 'where age > 40' do
|
23
|
+
expect(capture {
|
24
|
+
Csvql.run([csvfile, "--where", "age > 40"])
|
25
|
+
}).to eq(<<EOL)
|
26
|
+
3|Charry|48
|
27
|
+
5|Edward|52
|
28
|
+
EOL
|
29
|
+
end
|
30
|
+
|
31
|
+
it 'sql option' do
|
32
|
+
expect(capture {
|
33
|
+
Csvql.run([csvfile, "--sql", "select name,age from tbl where age between 20 and 40"])
|
34
|
+
}).to eq(<<EOL)
|
35
|
+
Anne|33
|
36
|
+
Bob|25
|
37
|
+
EOL
|
38
|
+
end
|
39
|
+
|
40
|
+
it 'change output delimiter' do
|
41
|
+
expect(capture {
|
42
|
+
Csvql.run([csvfile, "--where", "id = 3", "--output-dlm", ","])
|
43
|
+
}).to eq(<<EOL)
|
44
|
+
3,Charry,48
|
45
|
+
EOL
|
46
|
+
end
|
47
|
+
|
48
|
+
it 'change table name' do
|
49
|
+
expect(capture {
|
50
|
+
Csvql.run([csvfile, "--sql", "select id,name from user_info where id >= 4", "--table-name", "user_info"])
|
51
|
+
}).to eq(<<EOL)
|
52
|
+
4|Daniel
|
53
|
+
5|Edward
|
54
|
+
EOL
|
10
55
|
end
|
11
56
|
end
|
data/spec/sample.csv
ADDED
data/spec/spec_helper.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: csvql
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- YANO Satoru
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-07-
|
11
|
+
date: 2014-07-14 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: sqlite3
|
@@ -45,6 +45,7 @@ files:
|
|
45
45
|
- lib/csvql/csvql.rb
|
46
46
|
- lib/csvql/version.rb
|
47
47
|
- spec/csvql_spec.rb
|
48
|
+
- spec/sample.csv
|
48
49
|
- spec/spec_helper.rb
|
49
50
|
homepage: ''
|
50
51
|
licenses:
|
@@ -66,10 +67,11 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
66
67
|
version: '0'
|
67
68
|
requirements: []
|
68
69
|
rubyforge_project:
|
69
|
-
rubygems_version: 2.
|
70
|
+
rubygems_version: 2.3.0
|
70
71
|
signing_key:
|
71
72
|
specification_version: 4
|
72
73
|
summary: csvql
|
73
74
|
test_files:
|
74
75
|
- spec/csvql_spec.rb
|
76
|
+
- spec/sample.csv
|
75
77
|
- spec/spec_helper.rb
|