clickhouse-rb 0.2.0 → 0.3.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 1ccbd22754bea20c9ca9fed064ef2b4e7a5cf936b9e6c8c3f819f732adfc25e7
4
- data.tar.gz: 9537ffae5678fa0bf7ffbdd7b0cbb505aa66a8471072271765e98e99456a7f9d
3
+ metadata.gz: b3665b6f44472615341970c7b6169eaa3e54e7df0132ad866d24b8e24cb170df
4
+ data.tar.gz: b5948f2b40a4e969ed35daf92dfb24f3da23215fa113b86b168c3ab911f20335
5
5
  SHA512:
6
- metadata.gz: a289ee47e7d21efdf378737f101dca5efb11a0938ff41b0be804a39a2b0e2d6a694a78e57b0037d65a0e9fe136042d32f3d4377f5320bf9f63d0119d7bb51d33
7
- data.tar.gz: e836803b9664e2e5548af277978ed7170b8f18e79b767d908f2bbe8005464bc016fb6261bec5fa84d60774c632b6a42578e1d56899a63f90309c49941050750f
6
+ metadata.gz: 777e4c17a2d3278fb7a1221ed16f5d4ea01f16eeaac31c5e24ba52f81a2b5dc81613ea379c1e722b9d75f55c3850f6761a1ad5e45265e3be4a2199449777f68c
7
+ data.tar.gz: af8ac7a565c217142e5234d4d83dc9caf70cf2aca86714985b76155ecf8434076f27b9716794f09090b392033a49a7b14097645bc8b180190d089df4e45616c5
data/CHANGELOG.md CHANGED
@@ -1,3 +1,9 @@
1
+ ## Unreleased
2
+
3
+ ## [0.3.0] - 2025-12-31
4
+
5
+ - Make responses more user-friendly ([#1](https://github.com/kukicola/clickhouse-rb/pull/1))
6
+
1
7
  ## [0.2.0] - 2025-12-28
2
8
 
3
9
  - Added instrumentation support with configurable `instrumenter` (defaults to `NullInstrumenter`)
data/README.md CHANGED
@@ -51,10 +51,10 @@ end
51
51
 
52
52
  ```ruby
53
53
  conn = Clickhouse::Connection.new
54
- response = conn.query("SELECT * FROM users WHERE id = 1")
54
+ response = conn.query("SELECT id, name FROM users WHERE active = true")
55
55
 
56
- response.rows.each do |row|
57
- puts row.inspect
56
+ response.each do |row|
57
+ puts "#{row[:id]}: #{row[:name]}"
58
58
  end
59
59
  ```
60
60
 
@@ -83,19 +83,29 @@ end
83
83
 
84
84
  ### Working with Results
85
85
 
86
+ Response objects implement `Enumerable`, allowing direct iteration:
87
+
86
88
  ```ruby
87
89
  response = conn.query("SELECT id, name, created_at FROM users")
88
90
 
91
+ # Iterate over rows as hashes with symbol keys
92
+ response.each { |row| puts row[:name] }
93
+
94
+ # Use any Enumerable method
95
+ response.map { |row| row[:id] }
96
+ response.select { |row| row[:id] > 10 }
97
+ response.first # => {id: 1, name: "Alice", created_at: 2024-01-01 00:00:00 UTC}
98
+
89
99
  # Access raw rows (arrays)
90
100
  response.rows # => [[1, "Alice", 2024-01-01 00:00:00 UTC], ...]
91
- response.columns # => ["id", "name", "created_at"]
92
- response.types # => ["UInt64", "String", "DateTime"]
101
+ response.columns # => [:id, :name, :created_at]
102
+ response.types # => [:UInt64, :String, :DateTime]
93
103
 
94
104
  # Convert to array of hashes
95
- response.to_a # => [{"id" => 1, "name" => "Alice", ...}, ...]
105
+ response.to_a # => [{id: 1, name: "Alice", ...}, ...]
96
106
 
97
- # Query summary from ClickHouse
98
- response.summary # => {"read_rows" => "1", "read_bytes" => "42", ...}
107
+ # Query summary from ClickHouse (symbol keys)
108
+ response.summary # => {read_rows: "1", read_bytes: "42", ...}
99
109
  ```
100
110
 
101
111
  ### Query Parameters
@@ -36,7 +36,7 @@ module Clickhouse
36
36
  query_params = {database: @config.database}.merge(options[:params] || {})
37
37
  response = @http_client.post("/", params: query_params, body: sql, headers: @default_headers)
38
38
 
39
- summary = JSON.parse(response.headers["X-ClickHouse-Summary"])
39
+ summary = JSON.parse(response.headers["X-ClickHouse-Summary"], symbolize_names: true)
40
40
 
41
41
  raise QueryError, response.body.to_s unless response.status.success?
42
42
 
@@ -46,8 +46,8 @@ module Clickhouse
46
46
  col_type = read_string
47
47
 
48
48
  if @columns.length < num_columns
49
- @columns << col_name
50
- @types << col_type
49
+ @columns << col_name.to_sym
50
+ @types << col_type.to_sym
51
51
  end
52
52
 
53
53
  columns_data << read_column(col_type, num_rows)
@@ -65,14 +65,14 @@ module Clickhouse
65
65
  when "UInt16" then read_uint16_column(num_rows)
66
66
  when "UInt32" then read_uint32_column(num_rows)
67
67
  when "UInt64" then read_uint64_column(num_rows)
68
- when "UInt128" then Array.new(num_rows) { read_uint128 }
69
- when "UInt256" then Array.new(num_rows) { read_uint256 }
68
+ when "UInt128" then read_uint128_column(num_rows)
69
+ when "UInt256" then read_uint256_column(num_rows)
70
70
  when "Int8" then read_int8_column(num_rows)
71
71
  when "Int16" then read_int16_column(num_rows)
72
72
  when "Int32" then read_int32_column(num_rows)
73
73
  when "Int64" then read_int64_column(num_rows)
74
- when "Int128" then Array.new(num_rows) { read_int128 }
75
- when "Int256" then Array.new(num_rows) { read_int256 }
74
+ when "Int128" then read_int128_column(num_rows)
75
+ when "Int256" then read_int256_column(num_rows)
76
76
 
77
77
  # Floats
78
78
  when "Float32" then read_float32_column(num_rows)
@@ -82,7 +82,7 @@ module Clickhouse
82
82
  when "Bool" then read_bool_column(num_rows)
83
83
 
84
84
  # Strings
85
- when "String" then Array.new(num_rows) { read_string }
85
+ when "String" then read_string_column(num_rows)
86
86
  when /^FixedString\((\d+)\)$/ then read_fixed_string_column($1.to_i, num_rows)
87
87
 
88
88
  # Dates and Times
@@ -92,11 +92,11 @@ module Clickhouse
92
92
  when /^DateTime64\((\d+)(?:,.*)?\)$/ then read_datetime64_column($1.to_i, num_rows)
93
93
 
94
94
  # UUID
95
- when "UUID" then Array.new(num_rows) { read_uuid }
95
+ when "UUID" then read_uuid_column(num_rows)
96
96
 
97
97
  # IP addresses
98
- when "IPv4" then Array.new(num_rows) { read_ipv4 }
99
- when "IPv6" then Array.new(num_rows) { read_ipv6 }
98
+ when "IPv4" then read_ipv4_column(num_rows)
99
+ when "IPv6" then read_ipv6_column(num_rows)
100
100
 
101
101
  # Decimals - ClickHouse always returns Decimal(precision, scale)
102
102
  when /^Decimal\((\d+),\s*(\d+)\)$/ then read_decimal_column($1.to_i, $2.to_i, num_rows)
@@ -145,6 +145,14 @@ module Clickhouse
145
145
  @reader.read(num_rows * 8).unpack("Q<*")
146
146
  end
147
147
 
148
+ def read_uint128_column(num_rows)
149
+ Array.new(num_rows) { read_le_bytes(16) }
150
+ end
151
+
152
+ def read_uint256_column(num_rows)
153
+ Array.new(num_rows) { read_le_bytes(32) }
154
+ end
155
+
148
156
  def read_int8_column(num_rows)
149
157
  @reader.read(num_rows).unpack("c*")
150
158
  end
@@ -161,6 +169,14 @@ module Clickhouse
161
169
  @reader.read(num_rows * 8).unpack("q<*")
162
170
  end
163
171
 
172
+ def read_int128_column(num_rows)
173
+ Array.new(num_rows) { read_signed_le_bytes(16) }
174
+ end
175
+
176
+ def read_int256_column(num_rows)
177
+ Array.new(num_rows) { read_signed_le_bytes(32) }
178
+ end
179
+
164
180
  def read_float32_column(num_rows)
165
181
  @reader.read(num_rows * 4).unpack("e*")
166
182
  end
@@ -173,6 +189,10 @@ module Clickhouse
173
189
  @reader.read(num_rows).bytes.map { |b| b == 1 }
174
190
  end
175
191
 
192
+ def read_string_column(num_rows)
193
+ Array.new(num_rows) { read_string }
194
+ end
195
+
176
196
  def read_fixed_string_column(length, num_rows)
177
197
  Array.new(num_rows) { @reader.read(length).force_encoding(Encoding::UTF_8) }
178
198
  end
@@ -197,6 +217,18 @@ module Clickhouse
197
217
  end
198
218
  end
199
219
 
220
+ def read_uuid_column(num_rows)
221
+ Array.new(num_rows) { read_uuid }
222
+ end
223
+
224
+ def read_ipv4_column(num_rows)
225
+ Array.new(num_rows) { read_ipv4 }
226
+ end
227
+
228
+ def read_ipv6_column(num_rows)
229
+ Array.new(num_rows) { read_ipv6 }
230
+ end
231
+
200
232
  def read_decimal_column(precision, scale, num_rows)
201
233
  divisor = 10**scale
202
234
  if precision <= 9
@@ -230,10 +262,6 @@ module Clickhouse
230
262
  end
231
263
 
232
264
  def read_uint64 = @reader.read(8).unpack1("Q<")
233
- def read_uint128 = read_le_bytes(16)
234
- def read_uint256 = read_le_bytes(32)
235
- def read_int128 = read_signed_le_bytes(16)
236
- def read_int256 = read_signed_le_bytes(32)
237
265
 
238
266
  def read_uuid
239
267
  first_half = @reader.read(8).bytes.reverse
@@ -5,20 +5,27 @@ module Clickhouse
5
5
  #
6
6
  # @example
7
7
  # response = conn.query("SELECT id, name FROM users")
8
- # response.columns # => ["id", "name"]
8
+ # response.columns # => [:id, :name]
9
9
  # response.rows # => [[1, "Alice"], [2, "Bob"]]
10
- # response.to_a # => [{"id" => 1, "name" => "Alice"}, ...]
10
+ # response.each { |row| puts row[:name] }
11
11
  Response = Data.define(:columns, :types, :rows, :summary) do
12
- # @param columns [Array<String>] column names
13
- # @param types [Array<String>] column types
12
+ include Enumerable
13
+
14
+ # @param columns [Array<Symbol>] column names
15
+ # @param types [Array<Symbol>] column types
14
16
  # @param rows [Array<Array>] row data
15
- # @param summary [Hash, nil] ClickHouse query summary
17
+ # @param summary [Hash, nil] ClickHouse query summary with symbol keys
16
18
  def initialize(columns: [], types: [], rows: [], summary: nil)
17
19
  super
18
20
  end
19
21
 
20
- # Converts rows to an array of hashes.
21
- # @return [Array<Hash>] rows as hashes with column names as keys
22
- def to_a = rows.map { |row| columns.zip(row).to_h }
22
+ # Iterates over rows as hashes with symbol keys.
23
+ # @yield [Hash] each row as a hash
24
+ # @return [Enumerator] if no block given
25
+ def each
26
+ return to_enum(:each) unless block_given?
27
+
28
+ rows.each { |row| yield columns.zip(row).to_h }
29
+ end
23
30
  end
24
31
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Clickhouse
4
- VERSION = "0.2.0"
4
+ VERSION = "0.3.0"
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: clickhouse-rb
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Karol Bąk