-
Notifications
You must be signed in to change notification settings - Fork 37
/
Copy pathtest_query.rb
417 lines (379 loc) · 14.3 KB
/
test_query.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
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
require_relative "../../test_helper"
module Unit
module Connection
class TestQuery < MiniTest::Test
class Connection < SimpleConnection
include Clickhouse::Connection::Query
include Clickhouse::Connection::Logger
end
describe Clickhouse::Connection::Query do
before do
@connection = Connection.new
@connection.stubs(:parse_stats)
@connection.stubs(:write_log)
end
describe "#execute" do
it "sends a POST request" do
@connection.expects(:post).with("sql", nil).returns("")
assert_equal true, @connection.execute("sql")
end
describe "when server returns a non-empty body" do
it "returns the body of the response" do
@connection.expects(:post).with("sql", "body").returns("Ok.")
assert_equal "Ok.", @connection.execute("sql", "body")
end
end
end
describe "#query" do
it "sends a GET request requesting a TSV response including names and types" do
@connection.expects(:get).with("sql FORMAT JSONCompact")
@connection.stubs(:parse_data)
assert_equal [], @connection.query("sql").to_a
end
end
describe "#databases" do
it "sends a 'SHOW DATABASES' query" do
@connection.expects(:get).with("SHOW DATABASES FORMAT JSONCompact")
@connection.stubs(:parse_data).returns([])
assert_equal [], @connection.databases
end
end
describe "#tables" do
it "sends a 'SHOW TABLES' query" do
@connection.expects(:get).with("SHOW TABLES FORMAT JSONCompact")
@connection.stubs(:parse_data).returns([])
@connection.tables
end
end
describe "#create_table" do
it "sends a 'CREATE TABLE' query" do
sql = <<-SQL
CREATE TABLE logs_test (
id UInt8,
price Float32,
name String,
date Date,
time DateTime,
hex_id FixedString(8)
)
ENGINE = MergeTree(date, 8192)
SQL
@connection.expects(:post).with(sql.strip, nil).returns("")
@connection.create_table("logs_test") do |t|
t.uint8 :id
t.float32 :price
t.string :name
t.date :date
t.date_time :time
t.fixed_string :hex_id, 8
t.engine "MergeTree(date, 8192)"
end
end
end
describe "#describe_table" do
it "sends a 'DESCRIBE TABLE <name>' query" do
@connection.expects(:get).with("DESCRIBE TABLE logs FORMAT JSONCompact")
@connection.stubs(:parse_data)
@connection.describe_table("logs")
end
end
describe "#rename_table" do
describe "when passing an array with an even number of names" do
it "sends a POST request containing a RENAME TABLE statement" do
@connection.expects(:post).with("RENAME TABLE foo TO bar, baz TO qux", nil).returns("").twice
assert_equal true, @connection.rename_table("foo", "bar", "baz", "qux")
assert_equal true, @connection.rename_table(["foo", "bar"], ["baz", "qux"])
end
end
describe "when passing an array with an odd number of names" do
it "raises an Clickhouse::InvalidQueryError" do
assert_raises Clickhouse::InvalidQueryError do
@connection.rename_table "foo"
end
assert_raises Clickhouse::InvalidQueryError do
@connection.rename_table ["foo"]
end
end
end
describe "when passing a hash" do
it "sends a POST request containing a RENAME TABLE statement" do
@connection.expects(:post).with("RENAME TABLE foo TO bar, baz TO qux", nil).returns("")
assert_equal true, @connection.rename_table(:foo => "bar", :baz => "qux")
end
end
end
describe "#drop_table" do
it "sends a POST request containing a 'DROP TABLE' statement" do
@connection.expects(:post).with("DROP TABLE logs", nil).returns("")
assert_equal true, @connection.drop_table("logs")
end
end
describe "#exists_table" do
it "sends a POST request containing a 'EXISTS TABLE' statement" do
@connection.expects(:post).with("EXISTS TABLE logs", nil).returns("1")
assert_equal true, @connection.exists_table("logs")
end
end
describe "#insert_rows" do
before do
@csv = <<-CSV
id,first_name,last_name
12345,Paul,Engel
67890,Bruce,Wayne
CSV
@csv.gsub!(/^\s+/, "")
end
describe "when using hashes" do
it "sends a POST request containing a 'INSERT INTO' statement using CSV" do
@connection.expects(:post).with("INSERT INTO logs FORMAT CSVWithNames", @csv).returns("")
assert_equal true, @connection.insert_rows("logs") { |rows|
rows << {:id => 12345, :first_name => "Paul", :last_name => "Engel"}
rows << {:id => 67890, :first_name => "Bruce", :last_name => "Wayne"}
}
end
end
describe "when using arrays" do
it "sends a POST request containing a 'INSERT INTO' statement using CSV" do
@connection.expects(:post).with("INSERT INTO logs FORMAT CSVWithNames", @csv).returns("")
assert_equal true, @connection.insert_rows("logs", :names => %w(id first_name last_name)) { |rows|
rows << [12345, "Paul", "Engel"]
rows << [67890, "Bruce", "Wayne"]
}
end
end
end
describe "#select_rows" do
it "sends a GET request and parses the result set" do
body = <<-JAVASCRIPT
{
"meta": [
{"name": "year", "type": "UInt16"},
{"name": "name", "type": "String"}
],
"data": [
[1982, "Paul"],
[1947, "Anna"]
]
}
JAVASCRIPT
@connection.expects(:to_select_query).with(options = {:from => "logs"}).returns("")
@connection.expects(:get).returns(JSON.parse(body))
assert_equal [
[1982, "Paul"],
[1947, "Anna"]
], @connection.select_rows(options).to_a
end
end
describe "#select_row" do
it "returns an empty array" do
@connection.expects(:select_rows).returns([["Paul", "Engel"], ["Bruce", "Wayne"]])
assert_equal ["Paul", "Engel"], @connection.select_row({})
end
end
describe "#select_values" do
describe "when empty result set" do
it "returns an empty array" do
@connection.expects(:to_select_query).returns("")
@connection.expects(:get).returns(stub(:body => ""))
@connection.stubs(:parse_data).returns([])
assert_equal [], @connection.select_values({})
end
end
describe "when getting data" do
it "returns every first value of every row" do
body = <<-JAVASCRIPT
{
"meta": [
{"name": "year", "type": "UInt16"},
{"name": "name", "type": "String"}
],
"data": [
[1982, "Paul"],
[1947, "Anna"]
]
}
JAVASCRIPT
@connection.expects(:to_select_query).returns("")
@connection.expects(:get).returns(JSON.parse(body))
assert_equal [
1982,
1947
], @connection.select_values({})
end
end
end
describe "#select_value" do
describe "when empty result set" do
it "returns nil" do
@connection.expects(:select_values).with(options = {:foo => "bar"}).returns([])
assert_nil @connection.select_value(options)
end
end
describe "when getting data" do
it "returns the first value of the first row" do
@connection.expects(:select_values).with(options = {:foo => "bar"}).returns([1982])
assert_equal 1982, @connection.select_value(options)
end
end
end
describe "#count" do
it "returns the first value of the first row" do
@connection.expects(:select_value).with(:select => "COUNT(*)", :from => "logs").returns(1982)
assert_equal 1982, @connection.count(:from => "logs")
end
end
describe "#to_select_query" do
describe "when passing :from option" do
it "generates a simple 'SELECT * FROM <table>' query" do
query = <<-SQL
SELECT *
FROM logs
SQL
options = {
:from => "logs"
}
assert_query(query, @connection.send(:to_select_query, options))
end
end
describe "when passing :from and :select option" do
describe "when passing a single column" do
it "respects the single column in the SELECT statement" do
query = <<-SQL
SELECT MIN(date)
FROM logs
SQL
options = {
:select => "MIN(date)",
:from => "logs"
}
assert_query(query, @connection.send(:to_select_query, options))
end
end
describe "when passing multiple columns" do
it "only includes the passed columns in the SELECT statement" do
query = <<-SQL
SELECT MIN(date), MAX(date)
FROM logs
SQL
options = {
:select => ["MIN(date)", "MAX(date)"],
:from => "logs"
}
assert_query(query, @connection.send(:to_select_query, options))
end
end
describe "when filtering on value is empty" do
it "uses the empty() function in the WHERE statement" do
query = <<-SQL
SELECT *
FROM logs
WHERE empty(parent_id)
SQL
options = {
:from => "logs",
:where => {
:parent_id => :empty
}
}
assert_query(query, @connection.send(:to_select_query, options))
end
end
describe "when filtering on value is within a certain range" do
it "includes the range in the WHERE statement" do
query = <<-SQL
SELECT *
FROM logs
WHERE code >= 6 AND code <= 10
SQL
options = {
:from => "logs",
:where => {
:code => 6..10
}
}
assert_query(query, @connection.send(:to_select_query, options))
end
end
describe "when filtering on value in array" do
it "uses an IN operator in the WHERE statement" do
query = <<-SQL
SELECT *
FROM logs
WHERE code IN (6, 7, 8, 9, 10)
SQL
options = {
:from => "logs",
:where => {
:code => [6, 7, 8, 9, 10]
}
}
assert_query(query, @connection.send(:to_select_query, options))
end
end
describe "when filtering using backticks" do
it "uses the specified SQL as is" do
query = <<-SQL
SELECT *
FROM logs
WHERE id != 'cb5a67d2932911e6'
SQL
options = {
:from => "logs",
:where => {
:id => "`!= 'cb5a67d2932911e6'`"
}
}
assert_query(query, @connection.send(:to_select_query, options))
end
end
describe "when filtering on a string" do
it "uses a single quoted string" do
query = <<-SQL
SELECT *
FROM logs
WHERE id = 'cb5a67d2932911e6'
SQL
options = {
:from => "logs",
:where => {
:id => "cb5a67d2932911e6"
}
}
assert_query(query, @connection.send(:to_select_query, options))
end
end
describe "when using all options" do
it "generates the complex query" do
query = <<-SQL
SELECT date, COUNT(id), groupUniqArray(severity), SUM(clicks)
FROM logs
WHERE date >= '2016-08-01' AND hidden = 0
GROUP BY date
HAVING MIN(severity) = 2
ORDER BY MIN(time) DESC
LIMIT 120, 60
SQL
options = {
:select => ["date", "COUNT(id)", "groupUniqArray(severity)", "SUM(clicks)"],
:from => "logs",
:where => {
:date => "`>= '2016-08-01'`",
:hidden => 0
},
:group => "date",
:having => {
"MIN(severity)" => 2
},
:order => "MIN(time) DESC",
:limit => 60,
:offset => 120
}
assert_query(query, @connection.send(:to_select_query, options))
end
end
end
end
end
end
end
end