clickhouse-activerecord 1.6.1 → 1.6.2

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: 3e606a08261b2238dab5e6fbd6fb826fb40631ea063d9dcfc25d99aab052d619
4
- data.tar.gz: ad9281c124450ad0784c8442c4b20b3c8603f7028ca94f7aa1dbd73d8b0483d3
3
+ metadata.gz: 1c1e3e82dce96b76db15803ccee1b33d8808d0c4eb542115a080d8aa3854859a
4
+ data.tar.gz: 1ed698d16beeb439179b7d57139328ab47ffd25598ad5a98b314e0612166b1c2
5
5
  SHA512:
6
- metadata.gz: 70e11a39c963dedc824a9b5b54b805902691f9ed335a33796524ffd50c2f1a3bdea65709accc9847239123973c1b3f77fd675ca0ac40022195c54f5c9410ce96
7
- data.tar.gz: 768f6a409b98b66a80a6455d97fe4053f97dc60c35ee64d9a9a0310d64a6dbc00fe4b65bba90e7433114d536ed666f6716f2c1c05f13f34287f3e7f0905ebcb1
6
+ metadata.gz: fc3f56f9f381a583a66ed57db6a8a1457868ce29db354302991920721118415b9850ac02ae4fe7f86dccb618971b87a310c50befb8ba84ca0528b463f9e2d736
7
+ data.tar.gz: c1427aa749177c0f6f97ad34db9508dadbef1f22448d83d1466e61428f8f438aab2fe90851d7f95874399d9019f19ed1dfb74bb05a4a86e183f2ad1467b32763
data/CHANGELOG.md CHANGED
@@ -1,3 +1,7 @@
1
+ ### Version 1.6.2 (Jan 30, 2026)
2
+
3
+ * Add streaming request and save response to tmp file
4
+
1
5
  ### Version 1.6.1 (Jan 20, 2026)
2
6
 
3
7
  * Fix prepare rake task on ClickHouse 24.6
data/README.md CHANGED
@@ -235,6 +235,14 @@ Action.with(ActionView.select(Arel.sql('min(date)')) => :min_date).where(Arel.sq
235
235
  #=> #<ActiveRecord::Relation [#<Action *** >]>
236
236
  ```
237
237
 
238
+ ### Streaming request
239
+
240
+ ```ruby
241
+ path = Action.connection.execute_streaming(Action.where(date: Date.current), format: 'CSVWithNames')
242
+ # Clickhouse Stream (10.3ms) SELECT actions.* FROM actions WHERE actions.date = '2017-11-29'
243
+ file = File.open(path)
244
+ ```
245
+
238
246
 
239
247
  ### Migration Data Types
240
248
 
@@ -53,6 +53,18 @@ module ActiveRecord
53
53
  end
54
54
  end
55
55
 
56
+ # @return [ClickhouseActiverecord::StreamResponse]
57
+ def execute_streaming(sql, name = nil, format: @response_format, settings: {})
58
+ with_response_format(format) do
59
+ log(sql, [adapter_name, 'Stream', name].compact.join(' ')) do
60
+ statement = Statement.new(sql, format: @response_format)
61
+ request(statement, settings: settings) do |response|
62
+ return statement.streaming_response(response)
63
+ end
64
+ end
65
+ end
66
+ end
67
+
56
68
  def exec_insert(sql, name = nil, _binds = [], _pk = nil, _sequence_name = nil, returning: nil)
57
69
  new_sql = sql.sub(/ (DEFAULT )?VALUES/, " VALUES")
58
70
  with_response_format(nil) { execute(new_sql, name) }
@@ -268,8 +280,8 @@ module ActiveRecord
268
280
 
269
281
  def raw_execute(sql, settings: {}, except_params: [])
270
282
  statement = Statement.new(sql, format: @response_format)
271
- statement.response = request(statement, settings: settings, except_params: except_params)
272
- statement.processed_response
283
+ response = request(statement, settings: settings, except_params: except_params)
284
+ statement.processed_response(response)
273
285
  end
274
286
 
275
287
  # Make HTTP request to ClickHouse server
@@ -277,12 +289,13 @@ module ActiveRecord
277
289
  # @param [Hash] settings
278
290
  # @param [Array] except_params
279
291
  # @return [Net::HTTPResponse]
280
- def request(statement, settings: {}, except_params: [])
292
+ def request(statement, settings: {}, except_params: [], &block)
281
293
  @lock.synchronize do
282
- @connection.post("/?#{settings_params(settings, except: except_params)}",
283
- statement.formatted_sql,
284
- 'Content-Type' => 'application/x-www-form-urlencoded',
285
- 'User-Agent' => ClickhouseAdapter::USER_AGENT)
294
+ req = Net::HTTP::Post.new("/?#{settings_params(settings, except: except_params)}", {
295
+ 'Content-Type' => 'application/x-www-form-urlencoded',
296
+ 'User-Agent' => ClickhouseAdapter::USER_AGENT,
297
+ })
298
+ @connection.request(req, statement.formatted_sql, &block)
286
299
  end
287
300
  end
288
301
 
@@ -8,14 +8,18 @@ module ActiveRecord
8
8
 
9
9
  DB_EXCEPTION_REGEXP = /\ACode:\s+\d+\.\s+DB::Exception:/.freeze
10
10
 
11
+ # @param [Net::HTTPResponse] raw_response
12
+ # @param [String, nil] format
13
+ # @param [String] sql
11
14
  def initialize(raw_response, format, sql)
12
15
  @raw_response = raw_response
13
- @body = raw_response.body
14
16
  @format = format
15
17
  @sql = sql
16
18
  end
17
19
 
20
+ # @return [String, Hash, Array, nil]
18
21
  def process
22
+ @body = @raw_response.body
19
23
  if success?
20
24
  process_successful_response
21
25
  else
@@ -25,12 +29,28 @@ module ActiveRecord
25
29
  @body
26
30
  end
27
31
 
32
+ # @return [String, nil]
33
+ def streaming_process
34
+ file = Tempfile.new('clickhouse-activerecord', binmode: true)
35
+ if success?
36
+ @raw_response.read_body do |chunk|
37
+ file.write(chunk)
38
+ end
39
+ file.close
40
+ file.path
41
+ else
42
+ @body = @raw_response.body
43
+ raise_database_error!
44
+ end
45
+ end
46
+
28
47
  private
29
48
 
30
49
  def success?
31
50
  @raw_response.code.to_i == 200
32
51
  end
33
52
 
53
+ # @return [String, Hash, Array]
34
54
  def process_successful_response
35
55
  raise_generic!(@sql) if @body.include?('DB::Exception') && @body.match?(DB_EXCEPTION_REGEXP)
36
56
 
@@ -9,19 +9,27 @@ module ActiveRecord
9
9
  class Statement
10
10
 
11
11
  attr_reader :format
12
- attr_writer :response
13
12
 
14
13
  def initialize(sql, format:)
15
14
  @sql = sql
16
15
  @format = format
17
16
  end
18
17
 
18
+ # @return [String]
19
19
  def formatted_sql
20
20
  @formatted_sql ||= FormatManager.new(@sql, format: @format).apply
21
21
  end
22
22
 
23
- def processed_response
24
- ResponseProcessor.new(@response, @format, @sql).process
23
+ # @param [Net::HTTPResponse] response
24
+ # @return [String, Hash, Array, nil]
25
+ def processed_response(response)
26
+ ResponseProcessor.new(response, @format, @sql).process
27
+ end
28
+
29
+ # @param [Net::HTTPResponse] response
30
+ # @return [String, nil]
31
+ def streaming_response(response)
32
+ ResponseProcessor.new(response, @format, @sql).streaming_process
25
33
  end
26
34
 
27
35
  end
@@ -4,10 +4,13 @@ module ClickhouseActiverecord
4
4
  module TestHelper
5
5
  def before_setup
6
6
  super
7
+ original_connection_config = ActiveRecord::Base.connection_db_config
7
8
  ActiveRecord::Base.configurations.configurations.select { |x| x.env_name == Rails.env && x.adapter == 'clickhouse' }.each do |config|
8
9
  ActiveRecord::Base.establish_connection(config)
9
10
  ActiveRecord::Base.connection.truncate_tables(*ActiveRecord::Base.connection.tables)
10
11
  end
12
+ ensure
13
+ ActiveRecord::Base.establish_connection(original_connection_config) if original_connection_config
11
14
  end
12
15
  end
13
16
  end
@@ -2,9 +2,11 @@
2
2
 
3
3
  RSpec.configure do |config|
4
4
  config.before do
5
- ActiveRecord::Base.configurations.configurations.select { |x| x.env_name == Rails.env && x.adapter == 'clickhouse' }.each do |config|
6
- ActiveRecord::Base.establish_connection(config)
5
+ original_connection_config = ActiveRecord::Base.connection_db_config
6
+ ActiveRecord::Base.configurations.configurations.select { |x| x.env_name == Rails.env && x.adapter == 'clickhouse' }.each do |db_config|
7
+ ActiveRecord::Base.establish_connection(db_config)
7
8
  ActiveRecord::Base.connection.truncate_tables(*ActiveRecord::Base.connection.tables)
8
9
  end
10
+ ActiveRecord::Base.establish_connection(original_connection_config)
9
11
  end
10
12
  end
@@ -1,3 +1,3 @@
1
1
  module ClickhouseActiverecord
2
- VERSION = '1.6.1'
2
+ VERSION = '1.6.2'
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: clickhouse-activerecord
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.6.1
4
+ version: 1.6.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sergey Odintsov
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2026-01-20 00:00:00.000000000 Z
11
+ date: 2026-01-30 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler