clickhouse 0.1.4 → 0.1.8
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.codeclimate.yml +22 -0
- data/.travis.yml +0 -2
- data/CHANGELOG.md +21 -0
- data/README.md +28 -1
- data/VERSION +1 -1
- data/bin/clickhouse +9 -0
- data/clickhouse.gemspec +7 -2
- data/lib/clickhouse.rb +7 -2
- data/lib/clickhouse/cli.rb +55 -0
- data/lib/clickhouse/cli/client.rb +149 -0
- data/lib/clickhouse/cli/console.rb +73 -0
- data/lib/clickhouse/cli/server.rb +36 -0
- data/lib/clickhouse/cli/server/assets/css/clickhouse.css +177 -0
- data/lib/clickhouse/cli/server/assets/css/codemirror.css +341 -0
- data/lib/clickhouse/cli/server/assets/css/datatables.css +1 -0
- data/lib/clickhouse/cli/server/assets/css/normalize.css +427 -0
- data/lib/clickhouse/cli/server/assets/css/skeleton.css +418 -0
- data/lib/clickhouse/cli/server/assets/js/clickhouse.js +188 -0
- data/lib/clickhouse/cli/server/assets/js/codemirror.js +9096 -0
- data/lib/clickhouse/cli/server/assets/js/datatables.js +166 -0
- data/lib/clickhouse/cli/server/assets/js/disableswipeback.js +97 -0
- data/lib/clickhouse/cli/server/assets/js/jquery.js +11015 -0
- data/lib/clickhouse/cli/server/assets/js/sql.js +232 -0
- data/lib/clickhouse/cli/server/views/index.erb +46 -0
- data/lib/clickhouse/cluster.rb +2 -1
- data/lib/clickhouse/connection.rb +2 -2
- data/lib/clickhouse/connection/client.rb +79 -10
- data/lib/clickhouse/connection/query.rb +7 -8
- data/lib/clickhouse/connection/query/result_set.rb +2 -0
- data/lib/clickhouse/utils.rb +23 -0
- data/lib/clickhouse/version.rb +1 -1
- data/test/unit/connection/test_client.rb +75 -10
- data/test/unit/connection/test_cluster.rb +1 -1
- data/test/unit/connection/test_logger.rb +3 -3
- data/test/unit/connection/test_query.rb +25 -23
- data/test/unit/test_utils.rb +39 -0
- metadata +86 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: acc4fd253999310d9bfa4038d1dddeddf60e9108
|
4
|
+
data.tar.gz: c03da9aecb9d94b1fffbc190df88a57c6ac49979
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 93016aef4dd65ceb1c60a61c857a0f3bd2452e3476760f283d1c8374ed9b402d410e1e6d8e7c09216dc257adf8ba2d1cec4db7eadb9b9b8d283b3ea8b137606c
|
7
|
+
data.tar.gz: 248da8918613ba751cdf80db2667e8cb3b3a83f64b7e6973da57dfb80cfa76eb40647af4db0442aeec7984647f6a187c9857350418ee03313f6d5ea7ed1ece5f
|
data/.codeclimate.yml
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
---
|
2
|
+
engines:
|
3
|
+
csslint:
|
4
|
+
enabled: false
|
5
|
+
duplication:
|
6
|
+
enabled: true
|
7
|
+
config:
|
8
|
+
languages:
|
9
|
+
- ruby
|
10
|
+
eslint:
|
11
|
+
enabled: true
|
12
|
+
fixme:
|
13
|
+
enabled: true
|
14
|
+
rubocop:
|
15
|
+
enabled: true
|
16
|
+
ratings:
|
17
|
+
paths:
|
18
|
+
- "**.rb"
|
19
|
+
exclude_paths:
|
20
|
+
- lib/clickhouse/cli/
|
21
|
+
- script/
|
22
|
+
- test/
|
data/.travis.yml
CHANGED
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,26 @@
|
|
1
1
|
## Clickhouse CHANGELOG
|
2
2
|
|
3
|
+
### Version 0.1.8 (November 6, 2016)
|
4
|
+
|
5
|
+
* Fixed loading URLs in GUI
|
6
|
+
|
7
|
+
### Version 0.1.7 (November 6, 2016) [YANKED]
|
8
|
+
|
9
|
+
* Corrected interpolating nested patterns
|
10
|
+
* Only upcasing whole (preserved) words
|
11
|
+
|
12
|
+
### Version 0.1.6 (November 5, 2016) [YANKED]
|
13
|
+
|
14
|
+
* Improved clickhouse console and server
|
15
|
+
* Using Bundler >= 1.13.4
|
16
|
+
* Changed cluster timeout to 5 seconds
|
17
|
+
|
18
|
+
### Version 0.1.5 (November 4, 2016) [YANKED]
|
19
|
+
|
20
|
+
* Added command line interface `clickhouse c`
|
21
|
+
* Being able to start a Sinatra based Clickhouse client using `clickhouse s`
|
22
|
+
* Using :debug to log queries
|
23
|
+
|
3
24
|
### Version 0.1.4 (October 25, 2016)
|
4
25
|
|
5
26
|
* Ensured that Clickhouse does not modify the passed cluster config
|
data/README.md
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# Clickhouse [![Build Status](https://travis-ci.org/archan937/clickhouse.svg?branch=master)](https://travis-ci.org/archan937/clickhouse) [![Code Climate](https://codeclimate.com/github/archan937/clickhouse/badges/gpa.svg)](https://codeclimate.com/github/archan937/clickhouse) [![Test Coverage](https://codeclimate.com/github/archan937/clickhouse/badges/coverage.svg)](https://codeclimate.com/github/archan937/clickhouse/coverage) [![Gem](https://img.shields.io/gem/v/clickhouse.svg)](https://rubygems.org/gems/clickhouse) [![Gem](https://img.shields.io/gem/dt/clickhouse.svg)](https://rubygems.org/gems/clickhouse)
|
2
2
|
|
3
|
-
A Ruby database driver for ClickHouse.
|
3
|
+
A Ruby database driver for ClickHouse (also Clickhouse CLI and web GUI included).
|
4
4
|
|
5
5
|
## Introduction
|
6
6
|
|
@@ -179,6 +179,33 @@ If all the connections failed, it will just return `nil`.
|
|
179
179
|
|
180
180
|
To see what more the `Clickhouse` gem has to offer, please take a look at the unit tests ( [test/unit/connection/test_query.rb](https://github.com/archan937/clickhouse/blob/master/test/unit/connection/test_query.rb) for instance).
|
181
181
|
|
182
|
+
## Using the Sinatra-based browser GUI and Pry-based CLI
|
183
|
+
|
184
|
+
As of `Clickhouse v0.1.7`, the gem is provided with both a Sinatra-based GUI and a Pry-based CLI. Starting either of them is very easy:
|
185
|
+
|
186
|
+
* `clickhouse s localhost:8123` - (the `s` stands for server as we know from `rails s`)
|
187
|
+
* `clickhouse c localhost:8123` - (the `c` stands for console as we know from `rails c`)
|
188
|
+
|
189
|
+
Multiple connections should be passed comma separated:
|
190
|
+
|
191
|
+
`clickhouse s https://myserver.com:8123,https://myserver.com:8124`
|
192
|
+
|
193
|
+
Use `clickhouse help` to:
|
194
|
+
|
195
|
+
```
|
196
|
+
$ clickhouse help server
|
197
|
+
Usage:
|
198
|
+
clickhouse server [HOSTS]
|
199
|
+
|
200
|
+
Options:
|
201
|
+
-p, [--port=N]
|
202
|
+
# Default: 1982
|
203
|
+
-u, [--username=USERNAME]
|
204
|
+
-P, [--password=PASSWORD]
|
205
|
+
|
206
|
+
Start a Sinatra server as ClickHouse client (HOSTS should be comma separated URIs)
|
207
|
+
```
|
208
|
+
|
182
209
|
## Using the console
|
183
210
|
|
184
211
|
As you probably already noticed, the `Clickhouse` repo is provided with a `script/console` file which you can use for development / testing purposes. Please note that you need to have a ClickHouse server running.
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.1.
|
1
|
+
0.1.8
|
data/bin/clickhouse
ADDED
data/clickhouse.gemspec
CHANGED
@@ -4,8 +4,8 @@ require File.expand_path("../lib/clickhouse/version", __FILE__)
|
|
4
4
|
Gem::Specification.new do |gem|
|
5
5
|
gem.authors = ["Paul Engel"]
|
6
6
|
gem.email = ["pm_engel@icloud.com"]
|
7
|
-
gem.summary = %q{A Ruby database driver for ClickHouse}
|
8
|
-
gem.description = %q{A Ruby database driver for ClickHouse}
|
7
|
+
gem.summary = %q{A Ruby database driver for ClickHouse (also Clickhouse CLI and web GUI included)}
|
8
|
+
gem.description = %q{A Ruby database driver for ClickHouse (also Clickhouse CLI and web GUI included)}
|
9
9
|
gem.homepage = "https://github.com/archan937/clickhouse"
|
10
10
|
|
11
11
|
gem.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
@@ -14,9 +14,14 @@ Gem::Specification.new do |gem|
|
|
14
14
|
gem.name = "clickhouse"
|
15
15
|
gem.require_paths = ["lib"]
|
16
16
|
gem.version = Clickhouse::VERSION
|
17
|
+
gem.licenses = ["MIT"]
|
17
18
|
|
19
|
+
gem.add_dependency "bundler", ">= 1.13.4"
|
18
20
|
gem.add_dependency "faraday"
|
19
21
|
gem.add_dependency "pond"
|
22
|
+
gem.add_dependency "activesupport", ">= 4.1.8"
|
23
|
+
gem.add_dependency "thor"
|
24
|
+
gem.add_dependency "launchy"
|
20
25
|
|
21
26
|
gem.add_development_dependency "rake"
|
22
27
|
gem.add_development_dependency "pry"
|
data/lib/clickhouse.rb
CHANGED
@@ -1,12 +1,17 @@
|
|
1
1
|
require "uri"
|
2
2
|
require "forwardable"
|
3
3
|
require "csv"
|
4
|
+
require "json"
|
4
5
|
|
5
6
|
require "faraday"
|
6
7
|
require "pond"
|
8
|
+
require "active_support/dependencies/autoload"
|
9
|
+
require "active_support/number_helper"
|
10
|
+
require "active_support/core_ext/string/inflections"
|
7
11
|
|
8
12
|
require "clickhouse/cluster"
|
9
13
|
require "clickhouse/connection"
|
14
|
+
require "clickhouse/utils"
|
10
15
|
require "clickhouse/error"
|
11
16
|
require "clickhouse/version"
|
12
17
|
|
@@ -37,12 +42,12 @@ module Clickhouse
|
|
37
42
|
end
|
38
43
|
end
|
39
44
|
|
40
|
-
def self.connect!(config)
|
45
|
+
def self.connect!(config = {})
|
41
46
|
@connection = connect(config)
|
42
47
|
@connection.connect!
|
43
48
|
end
|
44
49
|
|
45
|
-
def self.connect(config)
|
50
|
+
def self.connect(config = {})
|
46
51
|
klass = (config[:urls] || config["urls"]) ? Cluster : Connection
|
47
52
|
klass.new(config)
|
48
53
|
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
require "thor"
|
2
|
+
require "launchy"
|
3
|
+
require "clickhouse"
|
4
|
+
|
5
|
+
module Clickhouse
|
6
|
+
class CLI < Thor
|
7
|
+
|
8
|
+
DEPENDENCIES = {:server => "sinatra"}
|
9
|
+
DEFAULT_URLS = "http://localhost:8123"
|
10
|
+
|
11
|
+
desc "server [HOSTS]", "Start a Sinatra server as ClickHouse client (HOSTS should be comma separated URIs)"
|
12
|
+
method_options [:port, "-p"] => 1982, [:username, "-u"] => :string, [:password, "-P"] => :string
|
13
|
+
def server(urls = DEFAULT_URLS)
|
14
|
+
run! :server, urls, options do
|
15
|
+
Launchy.open "http://localhost:#{options[:port]}"
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
desc "console [HOSTS]", "Start a Pry console as ClickHouse client (HOSTS should be comma separated URIs)"
|
20
|
+
method_options [:username, "-u"] => :string, [:password, "-P"] => :string
|
21
|
+
def console(urls = DEFAULT_URLS)
|
22
|
+
run! :console, urls, options
|
23
|
+
end
|
24
|
+
|
25
|
+
map "s" => :server
|
26
|
+
map "c" => :console
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
def run!(const, urls, options, &block)
|
31
|
+
require! DEPENDENCIES[const]
|
32
|
+
require_relative "cli/client"
|
33
|
+
require_relative "cli/#{const}"
|
34
|
+
connect! urls, options
|
35
|
+
self.class.const_get(const.to_s.capitalize).run!(:port => options["port"], &block)
|
36
|
+
end
|
37
|
+
|
38
|
+
def require!(name)
|
39
|
+
require(name) if name
|
40
|
+
rescue LoadError
|
41
|
+
puts "fatal: #{name.capitalize} not available. Please run `gem install #{name}` first."
|
42
|
+
exit!
|
43
|
+
end
|
44
|
+
|
45
|
+
def connect!(urls, options)
|
46
|
+
config = options.merge(:urls => urls.split(",")).inject({}){|h, (k, v)| h[k.to_sym] = v; h}
|
47
|
+
Clickhouse.establish_connection config
|
48
|
+
end
|
49
|
+
|
50
|
+
def method_missing(method, *_args)
|
51
|
+
raise Error, "Unrecognized command \"#{method}\". Please consult `clickhouse help`."
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,149 @@
|
|
1
|
+
module Clickhouse
|
2
|
+
class CLI < Thor
|
3
|
+
module Client
|
4
|
+
|
5
|
+
HISTORY_FILE = "#{ENV["HOME"]}/.clickhouse_history"
|
6
|
+
|
7
|
+
def self.included(base)
|
8
|
+
extended(base)
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.extended(base)
|
12
|
+
Clickhouse.logger = self
|
13
|
+
load_history
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.debug(message = nil)
|
17
|
+
@log = message.split("\n").detect{|line| line.include?("36m")}
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.log
|
21
|
+
@log
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.load_history
|
25
|
+
File.readlines(HISTORY_FILE).each do |line|
|
26
|
+
Readline::HISTORY.push line.gsub(";;", "\n").strip
|
27
|
+
end if File.exists?(HISTORY_FILE)
|
28
|
+
end
|
29
|
+
|
30
|
+
def alter_history(sql, current = true)
|
31
|
+
(Readline::HISTORY.to_a.count{|line| line[-1] != ";"} + (current ? 1 : 0)).times do
|
32
|
+
Readline::HISTORY.pop
|
33
|
+
end
|
34
|
+
unless Readline::HISTORY.to_a[-1] == sql
|
35
|
+
Readline::HISTORY.push(sql)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def dump_history
|
40
|
+
File.open(HISTORY_FILE, "w+") do |file|
|
41
|
+
Readline::HISTORY.each do |line|
|
42
|
+
file.puts line.strip.gsub("\n", ";;")
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def prettify(sql)
|
48
|
+
sql, replaced = numerize_patterns(sql)
|
49
|
+
|
50
|
+
preserved_words = %w(
|
51
|
+
USE
|
52
|
+
SHOW
|
53
|
+
DATABASES
|
54
|
+
TABLES
|
55
|
+
PROCESSLIST
|
56
|
+
INSERT
|
57
|
+
INTO
|
58
|
+
FORMAT
|
59
|
+
SELECT
|
60
|
+
COUNT
|
61
|
+
DISTINCT
|
62
|
+
SAMPLE
|
63
|
+
AS
|
64
|
+
FROM
|
65
|
+
JOIN
|
66
|
+
UNION
|
67
|
+
ALL
|
68
|
+
PREWHERE
|
69
|
+
WHERE
|
70
|
+
AND
|
71
|
+
OR
|
72
|
+
NOT
|
73
|
+
IN
|
74
|
+
GROUP
|
75
|
+
BY
|
76
|
+
HAVING
|
77
|
+
ORDER
|
78
|
+
LIMIT
|
79
|
+
CREATE
|
80
|
+
DESCRIBE
|
81
|
+
ALTER
|
82
|
+
RENAME
|
83
|
+
DROP
|
84
|
+
DETACH
|
85
|
+
ATTACH
|
86
|
+
TABLE
|
87
|
+
VIEW
|
88
|
+
PARTITION
|
89
|
+
EXISTS
|
90
|
+
SET
|
91
|
+
OPTIMIZE
|
92
|
+
WITH
|
93
|
+
TOTALS
|
94
|
+
).sort{|a, b| [b.size, a] <=> [a.size, b]}
|
95
|
+
|
96
|
+
sql.gsub!(/(\b)(#{preserved_words.join("|")})(\b)/i) do
|
97
|
+
"#{$1}#{$2.upcase}#{$3}"
|
98
|
+
end
|
99
|
+
|
100
|
+
interpolate_patterns(sql, replaced)
|
101
|
+
end
|
102
|
+
|
103
|
+
def numerize_patterns(sql, replaced = [])
|
104
|
+
sql = sql.gsub(/(["'])(?:(?=(\\?))\2.)*?\1/) do |match|
|
105
|
+
replaced << match
|
106
|
+
"${#{replaced.size - 1}}"
|
107
|
+
end
|
108
|
+
|
109
|
+
parenthesized = false
|
110
|
+
|
111
|
+
sql = sql.gsub(/\([^\(\)]*?\)/) do |match|
|
112
|
+
parenthesized = true
|
113
|
+
replaced << match
|
114
|
+
"%{#{replaced.size - 1}}"
|
115
|
+
end
|
116
|
+
|
117
|
+
parenthesized ? numerize_patterns(sql, replaced) : [sql, replaced]
|
118
|
+
end
|
119
|
+
|
120
|
+
def interpolate_patterns(sql, replaced)
|
121
|
+
matched = false
|
122
|
+
|
123
|
+
sql = sql.gsub(/(\$|%)\{(\d+)\}/) do |match|
|
124
|
+
matched = true
|
125
|
+
replaced[$2.to_i]
|
126
|
+
end
|
127
|
+
|
128
|
+
matched ? interpolate_patterns(sql, replaced) : sql
|
129
|
+
end
|
130
|
+
|
131
|
+
def execute(sql, &block)
|
132
|
+
if sql[-1] == ";"
|
133
|
+
dump_history
|
134
|
+
method = sql.match(/^(SELECT|SHOW|DESCRIBE)/i) ? :query : :execute
|
135
|
+
result = Clickhouse.connection.send(method, sql[0..-2])
|
136
|
+
|
137
|
+
if block_given?
|
138
|
+
block.call(result, Client.log)
|
139
|
+
else
|
140
|
+
process_result(result, Client.log)
|
141
|
+
end
|
142
|
+
else
|
143
|
+
sql
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
end
|
148
|
+
end
|
149
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
require "readline"
|
2
|
+
|
3
|
+
module Clickhouse
|
4
|
+
class CLI < Thor
|
5
|
+
module Console
|
6
|
+
extend self
|
7
|
+
extend Client
|
8
|
+
|
9
|
+
CLR = "\r\e[A\e[K"
|
10
|
+
|
11
|
+
def run!(options = {})
|
12
|
+
readline
|
13
|
+
end
|
14
|
+
|
15
|
+
# private
|
16
|
+
|
17
|
+
def readline(buffer = nil)
|
18
|
+
prompt = buffer ? ":-] " : ":) "
|
19
|
+
line = Readline.readline(prompt, true)
|
20
|
+
|
21
|
+
exit! unless line && !%w(exit quit).include?(line = line.strip)
|
22
|
+
|
23
|
+
line = prettify(line)
|
24
|
+
sql = [buffer, line].compact.join("\n").gsub(/\s+;$/, ";")
|
25
|
+
|
26
|
+
puts "#{CLR}#{prompt}#{line}"
|
27
|
+
alter_history(sql)
|
28
|
+
|
29
|
+
buffer = begin
|
30
|
+
execute(sql)
|
31
|
+
rescue Clickhouse::Error => e
|
32
|
+
puts "ERROR: #{e.message}"
|
33
|
+
end
|
34
|
+
|
35
|
+
readline buffer
|
36
|
+
end
|
37
|
+
|
38
|
+
def process_result(result, log)
|
39
|
+
if result.is_a?(Clickhouse::Connection::Query::ResultSet)
|
40
|
+
if result.size > 0
|
41
|
+
array = [result.names].concat(result.to_a)
|
42
|
+
lengths = array.inject([]) do |lengths, row|
|
43
|
+
row.each_with_index do |value, index|
|
44
|
+
length = value.to_s.strip.length
|
45
|
+
lengths[index] = [lengths[index].to_i, length].max
|
46
|
+
end
|
47
|
+
lengths
|
48
|
+
end
|
49
|
+
puts
|
50
|
+
array.each_with_index do |row, i|
|
51
|
+
values = [nil]
|
52
|
+
lengths.each_with_index do |length, index|
|
53
|
+
values << row[index].to_s.ljust(length, " ")
|
54
|
+
end
|
55
|
+
values << nil
|
56
|
+
separator = (i == 0) ? "+" : "|"
|
57
|
+
puts values.join(" #{separator} ")
|
58
|
+
end
|
59
|
+
end
|
60
|
+
else
|
61
|
+
puts result == true ? "Ok." : (result || "Fail.")
|
62
|
+
end
|
63
|
+
|
64
|
+
if log
|
65
|
+
puts
|
66
|
+
puts log.strip
|
67
|
+
end
|
68
|
+
puts
|
69
|
+
end
|
70
|
+
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|