clickhouse-activerecord 1.3.1 → 1.5.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: a8710787ab541533a020ea884090c897bd47a7bfefb0cd93bf552e6776ae2583
4
- data.tar.gz: 104eb3ec2a235899004ecc1c0ff01432da4b1fccfdfa5144e041351aaedc9dfd
3
+ metadata.gz: b8c5d0d360ba85375ca8f11600f5a6c3e37cd24504ede6255dbced8e3652319b
4
+ data.tar.gz: d2491e138a661eeb1c9952064e55125eed84f61fdebab88279e3ba73d881d19f
5
5
  SHA512:
6
- metadata.gz: 8346c11c2f912c78f82129f66f0fcc09e4c6689fe64a3d31cbb951c4046ae7102e7e33cd05258eb8b8136c4aa41a0f0297562b2228b8540480a8a6d6d4554edf
7
- data.tar.gz: bae4f5c8a7b2ceb3eb9341f0b64cd1c57694791db3aecbdb3b92b4ed98ac4b76246ca0941e19f1f66ca28c96b51b82dbbde98d7552e7c856978de7abf3667db6
6
+ metadata.gz: 3904eff8d2ff017cdb2572483baa5e201e8280799a4ab8753e5545ac27795365bec5174b183b2128cdbe7c8cf075c935f9d9f0b83045c5569db397a22ece8ad7
7
+ data.tar.gz: 80aed33044ca6ebf12280f352e41584a7c9582f7e934f44370a49148ad292a3af499f2d5132621142756fd384cea51111c59bed8f4a188539ed80b552befba7e
@@ -15,15 +15,16 @@
15
15
  <tmp_path>/var/lib/clickhouse/tmp/</tmp_path>
16
16
  <user_files_path>/var/lib/clickhouse/user_files/</user_files_path>
17
17
  <access_control_path>/var/lib/clickhouse/access/</access_control_path>
18
+ <format_schema_path>/var/lib/clickhouse/format_schemas/</format_schema_path>
18
19
  <keep_alive_timeout>3</keep_alive_timeout>
19
20
 
20
21
  <logger>
21
- <level>debug</level>
22
+ <level>error</level>
22
23
  <log>/var/log/clickhouse-server/clickhouse-server.log</log>
23
24
  <errorlog>/var/log/clickhouse-server/clickhouse-server.err.log</errorlog>
24
25
  <size>1000M</size>
25
26
  <count>10</count>
26
- <console>1</console>
27
+ <console>0</console>
27
28
  </logger>
28
29
 
29
30
  <remote_servers>
@@ -114,4 +115,11 @@
114
115
  <value>86400</value>
115
116
  </header>
116
117
  </http_options_response>
118
+
119
+ <user_directories>
120
+ <users_xml>
121
+ <path>users.xml</path>
122
+ </users_xml>
123
+ </user_directories>
124
+
117
125
  </clickhouse>
@@ -15,15 +15,16 @@
15
15
  <tmp_path>/var/lib/clickhouse/tmp/</tmp_path>
16
16
  <user_files_path>/var/lib/clickhouse/user_files/</user_files_path>
17
17
  <access_control_path>/var/lib/clickhouse/access/</access_control_path>
18
+ <format_schema_path>/var/lib/clickhouse/format_schemas/</format_schema_path>
18
19
  <keep_alive_timeout>3</keep_alive_timeout>
19
20
 
20
21
  <logger>
21
- <level>debug</level>
22
+ <level>error</level>
22
23
  <log>/var/log/clickhouse-server/clickhouse-server.log</log>
23
24
  <errorlog>/var/log/clickhouse-server/clickhouse-server.err.log</errorlog>
24
25
  <size>1000M</size>
25
26
  <count>10</count>
26
- <console>1</console>
27
+ <console>0</console>
27
28
  </logger>
28
29
 
29
30
  <remote_servers>
@@ -114,4 +115,11 @@
114
115
  <value>86400</value>
115
116
  </header>
116
117
  </http_options_response>
118
+
119
+ <user_directories>
120
+ <users_xml>
121
+ <path>users.xml</path>
122
+ </users_xml>
123
+ </user_directories>
124
+
117
125
  </clickhouse>
@@ -14,15 +14,16 @@
14
14
  <tmp_path>/var/lib/clickhouse/tmp/</tmp_path>
15
15
  <user_files_path>/var/lib/clickhouse/user_files/</user_files_path>
16
16
  <access_control_path>/var/lib/clickhouse/access/</access_control_path>
17
+ <format_schema_path>/var/lib/clickhouse/format_schemas/</format_schema_path>
17
18
  <keep_alive_timeout>3</keep_alive_timeout>
18
19
 
19
20
  <logger>
20
- <level>debug</level>
21
+ <level>error</level>
21
22
  <log>/var/log/clickhouse-server/clickhouse-server.log</log>
22
23
  <errorlog>/var/log/clickhouse-server/clickhouse-server.err.log</errorlog>
23
24
  <size>1000M</size>
24
25
  <count>10</count>
25
- <console>1</console>
26
+ <console>0</console>
26
27
  </logger>
27
28
 
28
29
  <query_log>
@@ -51,4 +52,10 @@
51
52
  </header>
52
53
  </http_options_response>
53
54
 
55
+ <user_directories>
56
+ <users_xml>
57
+ <path>users.xml</path>
58
+ </users_xml>
59
+ </user_directories>
60
+
54
61
  </clickhouse>
@@ -8,8 +8,17 @@
8
8
  </profiles>
9
9
 
10
10
  <users>
11
+ <test>
12
+ <password>test</password>
13
+ <networks>
14
+ <ip>::/0</ip>
15
+ </networks>
16
+ <profile>default</profile>
17
+ <quota>default</quota>
18
+ <access_management>1</access_management>
19
+ </test>
11
20
  <default>
12
- <password></password>
21
+ <password />
13
22
  <networks>
14
23
  <ip>::/0</ip>
15
24
  </networks>
@@ -1,6 +1,6 @@
1
1
  services:
2
2
  clickhouse1:
3
- image: 'clickhouse/clickhouse-server:${CLICKHOUSE_VERSION-23.11-alpine}'
3
+ image: 'clickhouse/clickhouse-server:${CLICKHOUSE_VERSION}'
4
4
  ulimits:
5
5
  nofile:
6
6
  soft: 262144
@@ -18,7 +18,7 @@ services:
18
18
  interval: 5s
19
19
 
20
20
  clickhouse2:
21
- image: 'clickhouse/clickhouse-server:${CLICKHOUSE_VERSION-23.11-alpine}'
21
+ image: 'clickhouse/clickhouse-server:${CLICKHOUSE_VERSION}'
22
22
  ulimits:
23
23
  nofile:
24
24
  soft: 262144
@@ -1,6 +1,6 @@
1
1
  services:
2
2
  clickhouse:
3
- image: 'clickhouse/clickhouse-server:${CLICKHOUSE_VERSION-23.11-alpine}'
3
+ image: 'clickhouse/clickhouse-server:${CLICKHOUSE_VERSION}'
4
4
  container_name: 'clickhouse-activerecord-clickhouse-server'
5
5
  ports:
6
6
  - '18123:8123'
@@ -14,6 +14,8 @@ jobs:
14
14
  env:
15
15
  CLICKHOUSE_PORT: 18123
16
16
  CLICKHOUSE_DATABASE: default
17
+ CLICKHOUSE_USER: test
18
+ CLICKHOUSE_PASSWORD: test
17
19
 
18
20
  strategy:
19
21
  fail-fast: true
@@ -22,21 +24,21 @@ jobs:
22
24
  version:
23
25
  - ruby: 2.7
24
26
  rails: 7.1.3
25
- - ruby: 3.0
26
- rails: 7.1.3
27
27
  - ruby: 3.2
28
28
  rails: 7.1.3
29
29
  - ruby: 3.2
30
30
  rails: 7.2.1
31
31
  - ruby: 3.2
32
32
  rails: 8.0.1
33
- clickhouse: [ '22.1', '24.9' ]
33
+ - ruby: 3.2
34
+ rails: 8.1
35
+ clickhouse: [ '24.9', '25.10' ]
34
36
 
35
37
  steps:
36
38
  - uses: actions/checkout@v4
37
39
 
38
40
  - name: Start ClickHouse ${{ matrix.clickhouse }}
39
- uses: hoverkraft-tech/compose-action@v2.1.0
41
+ uses: hoverkraft-tech/compose-action@v2.4.1
40
42
  env:
41
43
  CLICKHOUSE_VERSION: ${{ matrix.clickhouse }}
42
44
  with:
@@ -61,6 +63,8 @@ jobs:
61
63
  CLICKHOUSE_PORT: 28123
62
64
  CLICKHOUSE_DATABASE: default
63
65
  CLICKHOUSE_CLUSTER: test_cluster
66
+ CLICKHOUSE_USER: test
67
+ CLICKHOUSE_PASSWORD: test
64
68
 
65
69
  strategy:
66
70
  fail-fast: true
@@ -69,21 +73,21 @@ jobs:
69
73
  version:
70
74
  - ruby: 2.7
71
75
  rails: 7.1.3
72
- - ruby: 3.0
73
- rails: 7.1.3
74
76
  - ruby: 3.2
75
77
  rails: 7.1.3
76
78
  - ruby: 3.2
77
79
  rails: 7.2.1
78
80
  - ruby: 3.2
79
81
  rails: 8.0.1
80
- clickhouse: [ '22.1', '24.9' ]
82
+ - ruby: 3.2
83
+ rails: 8.1
84
+ clickhouse: [ '24.9', '25.10' ]
81
85
 
82
86
  steps:
83
87
  - uses: actions/checkout@v4
84
88
 
85
89
  - name: Start ClickHouse Cluster ${{ matrix.clickhouse }}
86
- uses: hoverkraft-tech/compose-action@v2.1.0
90
+ uses: hoverkraft-tech/compose-action@v2.4.1
87
91
  env:
88
92
  CLICKHOUSE_VERSION: ${{ matrix.clickhouse }}
89
93
  with:
data/CHANGELOG.md CHANGED
@@ -1,4 +1,54 @@
1
- ### Version 1.1.2 (Aug 27, 2024)
1
+ ### Version 1.5.0 (Nov 5, 2025)
2
+
3
+ * 🎉 Support for Rails 8.1
4
+ * Fix sql structure dump with `schema_migrations` #138
5
+ * Remove old tasks
6
+
7
+ ### Version 1.4.0 (Sep 18, 2025)
8
+
9
+ * `CREATE OR REPLACE FUNCTION` in SQL schema dumps in #198
10
+ * Added shard config to handle replica path with shard in #201
11
+ * Add support for simple schema dumping in #203
12
+ * Unscope :final and :settings in #208
13
+ * Encapsulate format logic within Statement and helper classes in #162
14
+
15
+ ### Version 1.3.1 (Feb 12, 2025)
16
+
17
+ * Restore replace database from dump schema table creation
18
+
19
+ ### Version 1.3.0 (Jan 24, 2025)
20
+
21
+ * 🎉 Support for Rails 8.0 #189
22
+ * Restore multi-line table definitions in structure dumps #187
23
+ * Use a flag to track updates/deletes in SQL visitor #188
24
+
25
+ ### Version 1.2.1 (Nov 18, 2024)
26
+
27
+ * Maintain primary key type specificity in #183
28
+ * Reliably sort functions, views, and materialized views in schema in #181
29
+ * Improve function dumps in #179
30
+ * Add support for integer limits in map type in #178
31
+ * Add support for `request_settings` in create_table
32
+
33
+ ### Version 1.2.0 (Oct 23, 2024)
34
+
35
+ * Fix for function creation in `structure.sql` #166
36
+ * Add `group_by_grouping_sets` query method #161
37
+ * Add support for `CREATE FUNCTION` and `CREATE OR REPLACE FUNCTION`; the later in schema loading #146
38
+ * Add support for `LIMIT BY` clause #169
39
+ * Include column definitions in schema dump if the column name is not `id` #173
40
+ * Add blank line after create_function in schema #170
41
+ * Improve DB::Exception error handling #164
42
+ * SchemaDumper adds materialized view destination #159
43
+ * Add Array support to Map #158
44
+ * Add support codec compression parameter #135
45
+
46
+ ### Version 1.1.3 (Sep 27, 2024)
47
+
48
+ * Fix schema dumper #163
49
+
50
+ ### Version 1.1.2 (Aug 27, 2024)
51
+
2
52
  * 🎉 Support for rails 7.2 #156
3
53
  * Add method `views` for getting table `View` list in #152
4
54
  * Add support for Map datatype in #144
data/README.md CHANGED
@@ -33,6 +33,9 @@ default: &default
33
33
  migrations_paths: db/clickhouse # optional, default: db/migrate_clickhouse
34
34
  cluster_name: 'cluster_name' # optional for creating tables in cluster
35
35
  replica_name: '{replica}' # replica macros name, optional for creating replicated tables
36
+ read_timeout: 300 # change network timeouts, by default 60 seconds
37
+ write_timeout: 300
38
+ keep_alive_timeout: 300
36
39
  ```
37
40
 
38
41
  Alternatively if you wish to pass a custom `Net::HTTP` transport (or any other
@@ -282,10 +285,10 @@ Engines `MergeTree` and all support replication engines will be replaced to `Rep
282
285
 
283
286
  Donations to this project are going directly to [PNixx](https://github.com/PNixx), the original author of this project:
284
287
 
285
- * BTC address: `1H3rhpf7WEF5JmMZ3PVFMQc7Hm29THgUfN`
288
+ * BTC address: `bc1qr73vls0kv2ujk4ugqmpqj6j0qtqvdr3nx25xdl`
286
289
  * ETH address: `0x6F094365A70fe7836A633d2eE80A1FA9758234d5`
287
290
  * XMR address: `42gP71qLB5M43RuDnrQ3vSJFFxis9Kw9VMURhpx9NLQRRwNvaZRjm2TFojAMC8Fk1BQhZNKyWhoyJSn5Ak9kppgZPjE17Zh`
288
- * TON address: `UQBt0-s1igIpJoEup0B1yAUkZ56rzbpruuAjNhQ26MVCaNlC`
291
+ * TON address: `UQBCnxOfBsHPZ3PesGgMedVMEf5UHnm0jrSq-042pMWw08Ux`
289
292
 
290
293
  ## Development
291
294
 
@@ -5,7 +5,7 @@ module ActiveRecord
5
5
 
6
6
  attr_reader :codec
7
7
 
8
- def initialize(name, default, sql_type_metadata = nil, null = true, default_function = nil, codec: nil, **args)
8
+ def initialize(*, codec: nil, **)
9
9
  super
10
10
  @codec = codec
11
11
  end
@@ -43,7 +43,12 @@ module ActiveRecord
43
43
  sql.gsub!(/\s+(.*)/, " \\1 CODEC(#{options[:codec]})")
44
44
  end
45
45
  sql.gsub!(/(\sString)\(\d+\)/, '\1')
46
- sql << " DEFAULT #{quote_default_expression(options[:default], options[:column])}" if options_include_default?(options)
46
+
47
+ if ::ActiveRecord::version >= Gem::Version.new('8.1')
48
+ sql << " DEFAULT #{quote_default_expression_for_column_definition(options[:default], options[:column])}" if options_include_default?(options)
49
+ else
50
+ sql << " DEFAULT #{quote_default_expression(options[:default], options[:column])}" if options_include_default?(options)
51
+ end
47
52
  sql
48
53
  end
49
54
 
@@ -6,22 +6,55 @@ module ActiveRecord
6
6
  module ConnectionAdapters
7
7
  module Clickhouse
8
8
  module SchemaStatements
9
- DEFAULT_RESPONSE_FORMAT = 'JSONCompactEachRowWithNamesAndTypes'.freeze
10
9
 
11
- DB_EXCEPTION_REGEXP = /\ACode:\s+\d+\.\s+DB::Exception:/.freeze
10
+ def with_settings(**settings)
11
+ @block_settings ||= {}
12
+ prev_settings = @block_settings
13
+ @block_settings = @block_settings.merge(settings)
14
+ yield
15
+ ensure
16
+ @block_settings = prev_settings
17
+ end
12
18
 
13
- def execute(sql, name = nil, settings: {})
14
- do_execute(sql, name, settings: settings)
19
+ # Request a specific format for the duration of the provided block.
20
+ # Pass `nil` to explicitly send the SQL statement without a `FORMAT` clause.
21
+ # @param [String, nil] format
22
+ #
23
+ # @example Specify CSVWithNamesAndTypes format
24
+ # with_response_format('CSVWithNamesAndTypes') do
25
+ # Table.connection.execute('SELECT * FROM table')
26
+ # end
27
+ # # sends and executes "SELECT * FROM table FORMAT CSVWithNamesAndTypes"
28
+ #
29
+ # @example Specify no format
30
+ # with_response_format(nil) do
31
+ # Table.connection.execute('SELECT * FROM table')
32
+ # end
33
+ # # sends and executes "SELECT * FROM table"
34
+ def with_response_format(format)
35
+ prev_format = @response_format
36
+ @response_format = format
37
+ yield
38
+ ensure
39
+ @response_format = prev_format
40
+ end
41
+
42
+ def execute(sql, name = nil, format: @response_format, settings: {})
43
+ with_response_format(format) do
44
+ log(sql, [adapter_name, name].compact.join(' ')) do
45
+ raw_execute(sql, settings: settings)
46
+ end
47
+ end
15
48
  end
16
49
 
17
- def exec_insert(sql, name, _binds, _pk = nil, _sequence_name = nil, returning: nil)
18
- new_sql = sql.dup.sub(/ (DEFAULT )?VALUES/, " VALUES")
19
- do_execute(new_sql, name, format: nil)
50
+ def exec_insert(sql, name = nil, _binds = [], _pk = nil, _sequence_name = nil, returning: nil)
51
+ new_sql = sql.sub(/ (DEFAULT )?VALUES/, " VALUES")
52
+ with_response_format(nil) { execute(new_sql, name) }
20
53
  true
21
54
  end
22
55
 
23
56
  def internal_exec_query(sql, name = nil, binds = [], prepare: false, async: false, allow_retry: false)
24
- result = do_execute(sql, name)
57
+ result = execute(sql, name)
25
58
  columns = result['meta'].map { |m| m['name'] }
26
59
  types = {}
27
60
  result['meta'].each_with_index do |m, i|
@@ -37,24 +70,25 @@ module ActiveRecord
37
70
  end
38
71
 
39
72
  def exec_insert_all(sql, name)
40
- do_execute(sql, name, format: nil)
73
+ with_response_format(nil) { execute(sql, name) }
41
74
  true
42
75
  end
43
76
 
44
77
  # @link https://clickhouse.com/docs/en/sql-reference/statements/alter/update
45
- def exec_update(_sql, _name = nil, _binds = [])
46
- do_execute(_sql, _name, format: nil)
78
+ def exec_update(sql, name = nil, _binds = [])
79
+ execute(sql, name)
47
80
  0
48
81
  end
49
82
 
50
83
  # @link https://clickhouse.com/docs/en/sql-reference/statements/delete
51
- def exec_delete(_sql, _name = nil, _binds = [])
52
- log(_sql, "#{adapter_name} #{_name}") do
53
- res = request(_sql)
84
+ def exec_delete(sql, name = nil, _binds = [])
85
+ log(sql, "#{adapter_name} #{name}") do
86
+ statement = Statement.new(sql, format: @response_format)
87
+ res = request(statement)
54
88
  begin
55
89
  data = JSON.parse(res.header['x-clickhouse-summary'])
56
90
  data['result_rows'].to_i
57
- rescue JSONError
91
+ rescue JSON::ParserError
58
92
  0
59
93
  end
60
94
  end
@@ -85,7 +119,10 @@ module ActiveRecord
85
119
  end
86
120
 
87
121
  def show_create_function(function)
88
- do_execute("SELECT create_query FROM system.functions WHERE origin = 'SQLUserDefined' AND name = '#{function}'", format: nil)
122
+ result = do_system_execute("SELECT create_query FROM system.functions WHERE origin = 'SQLUserDefined' AND name = '#{function}'")
123
+ return if result.nil?
124
+
125
+ result['data'].flatten.first.sub(/\ACREATE FUNCTION/, 'CREATE OR REPLACE FUNCTION')
89
126
  end
90
127
 
91
128
  def table_options(table)
@@ -110,18 +147,18 @@ module ActiveRecord
110
147
  tables
111
148
  end
112
149
 
113
- def do_system_execute(sql, name = nil)
114
- log_with_debug(sql, "#{adapter_name} #{name}") do
115
- res = request(sql, DEFAULT_RESPONSE_FORMAT)
116
- process_response(res, DEFAULT_RESPONSE_FORMAT, sql)
150
+ def do_system_execute(sql, name = nil, except_params: [])
151
+ log_with_debug(sql, [adapter_name, name].compact.join(' ')) do
152
+ raw_execute(sql, except_params: except_params)
117
153
  end
118
154
  end
119
155
 
120
156
  def do_execute(sql, name = nil, format: DEFAULT_RESPONSE_FORMAT, settings: {})
121
- log(sql, "#{adapter_name} #{name}") do
122
- res = request(sql, format, settings)
123
- process_response(res, format, sql)
124
- end
157
+ ActiveRecord.deprecator.warn(<<~MSG.squish)
158
+ `do_execute` is deprecated and will be removed in an upcoming release.
159
+ Please use `execute` instead.
160
+ MSG
161
+ execute(sql, name, format: format, settings: settings)
125
162
  end
126
163
 
127
164
  if ::ActiveRecord::version >= Gem::Version.new('7.2')
@@ -154,7 +191,7 @@ module ActiveRecord
154
191
  if (duplicate = inserting.detect { |v| inserting.count(v) > 1 })
155
192
  raise "Duplicate migration #{duplicate}. Please renumber your migrations to resolve the conflict."
156
193
  end
157
- do_execute(insert_versions_sql(inserting), nil, format: nil, settings: {max_partitions_per_insert_block: [100, inserting.size].max})
194
+ execute(insert_versions_sql(inserting), nil, settings: {max_partitions_per_insert_block: [100, inserting.size].max})
158
195
  end
159
196
  end
160
197
 
@@ -168,56 +205,19 @@ module ActiveRecord
168
205
  end
169
206
  end
170
207
 
171
- private
172
-
173
- # Make HTTP request to ClickHouse server
174
- # @param [String] sql
175
- # @param [String, nil] format
176
- # @param [Hash] settings
177
- # @return [Net::HTTPResponse]
178
- def request(sql, format = nil, settings = {})
179
- formatted_sql = apply_format(sql, format)
180
- request_params = @connection_config || {}
181
- @lock.synchronize do
182
- @connection.post("/?#{request_params.merge(settings).to_param}", formatted_sql, {
183
- 'User-Agent' => "Clickhouse ActiveRecord #{ClickhouseActiverecord::VERSION}",
184
- 'Content-Type' => 'application/x-www-form-urlencoded',
185
- })
186
- end
187
- end
208
+ protected
188
209
 
189
- def apply_format(sql, format)
190
- format ? "#{sql} FORMAT #{format}" : sql
191
- end
210
+ def table_structure(table_name)
211
+ result = do_system_execute("DESCRIBE TABLE `#{table_name}`", table_name)
212
+ data = result['data']
192
213
 
193
- def process_response(res, format, sql = nil)
194
- case res.code.to_i
195
- when 200
196
- body = res.body
214
+ return data unless data.empty?
197
215
 
198
- if body.include?("DB::Exception") && body.match?(DB_EXCEPTION_REGEXP)
199
- raise ActiveRecord::ActiveRecordError, "Response code: #{res.code}:\n#{res.body}#{sql ? "\nQuery: #{sql}" : ''}"
200
- else
201
- format_body_response(res.body, format)
202
- end
203
- else
204
- case res.body
205
- when /DB::Exception:.*\(UNKNOWN_DATABASE\)/
206
- raise ActiveRecord::NoDatabaseError
207
- when /DB::Exception:.*\(DATABASE_ALREADY_EXISTS\)/
208
- raise ActiveRecord::DatabaseAlreadyExists
209
- else
210
- raise ActiveRecord::ActiveRecordError, "Response code: #{res.code}:\n#{res.body}"
211
- end
212
- end
213
- rescue JSON::ParserError
214
- res.body
216
+ raise ActiveRecord::StatementInvalid, "Could not find table '#{table_name}'"
215
217
  end
218
+ alias column_definitions table_structure
216
219
 
217
- def log_with_debug(sql, name = nil)
218
- return yield unless @debug
219
- log(sql, "#{name} (system)") { yield }
220
- end
220
+ private
221
221
 
222
222
  def schema_creation
223
223
  Clickhouse::SchemaCreation.new(self)
@@ -228,29 +228,20 @@ module ActiveRecord
228
228
  end
229
229
 
230
230
  def new_column_from_field(table_name, field, _definitions)
231
- sql_type = field[1]
231
+ column_name, sql_type, default_type, default_expression = field
232
232
  type_metadata = fetch_type_metadata(sql_type)
233
- default_value = extract_value_from_default(field[3], field[2])
234
- default_function = extract_default_function(field[3])
235
- default_value = lookup_cast_type(sql_type).cast(default_value)
236
- Clickhouse::Column.new(field[0], default_value, type_metadata, field[1].include?('Nullable'), default_function, codec: field[5].presence)
237
- end
238
-
239
- protected
240
-
241
- def table_structure(table_name)
242
- result = do_system_execute("DESCRIBE TABLE `#{table_name}`", table_name)
243
- data = result['data']
233
+ default_value = extract_value_from_default(default_expression, default_type)
234
+ default_function = extract_default_function(default_expression)
235
+ cast_type = lookup_cast_type(sql_type)
236
+ default_value = cast_type.cast(default_value)
244
237
 
245
- return data unless data.empty?
238
+ args = [column_name]
239
+ args << cast_type if ::ActiveRecord::version >= Gem::Version.new('8.1')
240
+ args += [default_value, type_metadata, field[1].include?('Nullable'), default_function]
246
241
 
247
- raise ActiveRecord::StatementInvalid, "Could not find table '#{table_name}'"
242
+ Clickhouse::Column.new(*args, codec: field[5].presence)
248
243
  end
249
- alias column_definitions table_structure
250
-
251
- private
252
244
 
253
- # Extracts the value from a PostgreSQL column default definition.
254
245
  def extract_value_from_default(default_expression, default_type)
255
246
  return nil if default_type != 'DEFAULT' || default_expression.blank?
256
247
  return nil if has_default_function?(default_expression)
@@ -269,42 +260,38 @@ module ActiveRecord
269
260
  (%r{\w+\(.*\)} === default)
270
261
  end
271
262
 
272
- def format_body_response(body, format)
273
- return body if body.blank?
274
-
275
- case format
276
- when 'JSONCompact'
277
- format_from_json_compact(body)
278
- when 'JSONCompactEachRowWithNamesAndTypes'
279
- format_from_json_compact_each_row_with_names_and_types(body)
280
- else
281
- body
282
- end
283
- end
284
-
285
- def format_from_json_compact(body)
286
- parse_json_payload(body)
263
+ def raw_execute(sql, settings: {}, except_params: [])
264
+ statement = Statement.new(sql, format: @response_format)
265
+ statement.response = request(statement, settings: settings, except_params: except_params)
266
+ statement.processed_response
287
267
  end
288
268
 
289
- def format_from_json_compact_each_row_with_names_and_types(body)
290
- rows = body.split("\n").map { |row| parse_json_payload(row) }
291
- names, types, *data = rows
292
-
293
- meta = names.zip(types).map do |name, type|
294
- {
295
- 'name' => name,
296
- 'type' => type
297
- }
269
+ # Make HTTP request to ClickHouse server
270
+ # @param [ActiveRecord::ConnectionAdapters::Clickhouse::Statement] statement
271
+ # @param [Hash] settings
272
+ # @param [Array] except_params
273
+ # @return [Net::HTTPResponse]
274
+ def request(statement, settings: {}, except_params: [])
275
+ @lock.synchronize do
276
+ @connection.post("/?#{settings_params(settings, except: except_params)}",
277
+ statement.formatted_sql,
278
+ 'Content-Type' => 'application/x-www-form-urlencoded',
279
+ 'User-Agent' => ClickhouseAdapter::USER_AGENT)
298
280
  end
281
+ end
299
282
 
300
- {
301
- 'meta' => meta,
302
- 'data' => data
303
- }
283
+ def log_with_debug(sql, name = nil)
284
+ return yield unless @debug
285
+ log(sql, "#{name} (system)") { yield }
304
286
  end
305
287
 
306
- def parse_json_payload(payload)
307
- JSON.parse(payload, decimal_class: BigDecimal)
288
+ def settings_params(settings = {}, except: [])
289
+ request_params = @connection_config || {}
290
+ block_settings = @block_settings || {}
291
+ request_params.merge(block_settings)
292
+ .merge(settings)
293
+ .except(*except)
294
+ .to_param
308
295
  end
309
296
  end
310
297
  end