clickhouse-activerecord 1.5.1 → 1.6.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 +4 -4
- data/CHANGELOG.md +10 -0
- data/README.md +10 -2
- data/lib/active_record/connection_adapters/clickhouse/schema_creation.rb +3 -1
- data/lib/active_record/connection_adapters/clickhouse/schema_statements.rb +38 -0
- data/lib/active_record/connection_adapters/clickhouse_adapter.rb +9 -0
- data/lib/arel/visitors/clickhouse.rb +27 -0
- data/lib/clickhouse-activerecord/minitest.rb +15 -0
- data/lib/clickhouse-activerecord/rspec.rb +1 -3
- data/lib/clickhouse-activerecord/tasks.rb +26 -0
- data/lib/clickhouse-activerecord/version.rb +1 -1
- data/lib/core_extensions/active_record/relation.rb +26 -0
- metadata +7 -3
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: b247a8916fb85dbbc5392da80096fca8d2bea2f822d3e60ed96dced9395d9ebd
|
|
4
|
+
data.tar.gz: dd1ed15c3367463fdc870f5ead578a8af775b9423fe0b851a2eb5be7782e65ba
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: eddec177c686f387fc062689e472a90310a2b8a45f7e20ab416ab865cb39c8f6d52ef5f35433d435372c917ba5bbf211c9a60e00075b50ad6e7cfd7f70322291
|
|
7
|
+
data.tar.gz: acacc953e84579b76da29a1cbf29337de6bd33c72857abdc65484222f0a485b62bc5a9e2305c4c999cd4966ccc99d0c16cbf0d92434ff881d4ce572b10970f46
|
data/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,13 @@
|
|
|
1
|
+
### Version 1.6.0 (Jan 19, 2026)
|
|
2
|
+
|
|
3
|
+
* Support CSE (Common Scalar Expressions) in the `WITH` clause
|
|
4
|
+
* Fix regex to match FROM keyword, not column names containing 'from' #220
|
|
5
|
+
* Add JSON column type support #209
|
|
6
|
+
* Support execute_batch #216
|
|
7
|
+
* Add disconnect method to adapter #186
|
|
8
|
+
* Add check_current_protected_environment! to Tasks #222
|
|
9
|
+
* Do not truncate engines that cannot be truncated #226
|
|
10
|
+
|
|
1
11
|
### Version 1.5.1 (Nov 6, 2025)
|
|
2
12
|
|
|
3
13
|
* Fix rake tasks
|
data/README.md
CHANGED
|
@@ -158,10 +158,18 @@ Structure load from `db/clickhouse_structure.sql` file:
|
|
|
158
158
|
|
|
159
159
|
For auto truncate tables before each test add to `spec/rails_helper.rb` file:
|
|
160
160
|
|
|
161
|
-
```
|
|
161
|
+
```ruby
|
|
162
162
|
require 'clickhouse-activerecord/rspec'
|
|
163
163
|
```
|
|
164
|
-
|
|
164
|
+
|
|
165
|
+
### Minitest
|
|
166
|
+
|
|
167
|
+
For auto truncate tables before each test add to `test/test_helper.rb` file:
|
|
168
|
+
|
|
169
|
+
```ruby
|
|
170
|
+
require 'clickhouse-activerecord/minitest'
|
|
171
|
+
```
|
|
172
|
+
|
|
165
173
|
### Insert and select data
|
|
166
174
|
|
|
167
175
|
```ruby
|
|
@@ -79,7 +79,9 @@ module ActiveRecord
|
|
|
79
79
|
# If you do not specify a database explicitly, ClickHouse will use the "default" database.
|
|
80
80
|
return unless subquery
|
|
81
81
|
|
|
82
|
-
|
|
82
|
+
# Match FROM as a keyword (with word boundary), not as part of a column name
|
|
83
|
+
# \b ensures we only match 'from' as a whole word
|
|
84
|
+
match = subquery.match(/\bfrom\s+(?<database>\w+(?=\.))?(?<table_name>[.\w]+)/i)
|
|
83
85
|
return unless match
|
|
84
86
|
return if match[:database]
|
|
85
87
|
|
|
@@ -47,6 +47,12 @@ module ActiveRecord
|
|
|
47
47
|
end
|
|
48
48
|
end
|
|
49
49
|
|
|
50
|
+
def execute_batch(statements, name = nil, **kwargs)
|
|
51
|
+
statements.each do |statement|
|
|
52
|
+
execute(statement, name, **kwargs)
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
|
|
50
56
|
def exec_insert(sql, name = nil, _binds = [], _pk = nil, _sequence_name = nil, returning: nil)
|
|
51
57
|
new_sql = sql.sub(/ (DEFAULT )?VALUES/, " VALUES")
|
|
52
58
|
with_response_format(nil) { execute(new_sql, name) }
|
|
@@ -293,6 +299,38 @@ module ActiveRecord
|
|
|
293
299
|
.except(*except)
|
|
294
300
|
.to_param
|
|
295
301
|
end
|
|
302
|
+
|
|
303
|
+
# Returns a hash of table names to their engine types
|
|
304
|
+
def table_engines(table_names = nil)
|
|
305
|
+
table_names_sql = if table_names.present?
|
|
306
|
+
"AND name IN (#{table_names.map { |name| "'#{name}'" }.join(', ')})"
|
|
307
|
+
end
|
|
308
|
+
|
|
309
|
+
sql = <<~SQL
|
|
310
|
+
SELECT name, engine FROM system.tables
|
|
311
|
+
WHERE database = currentDatabase()
|
|
312
|
+
#{table_names_sql}
|
|
313
|
+
SQL
|
|
314
|
+
|
|
315
|
+
result = do_system_execute(sql)
|
|
316
|
+
return {} if result.nil?
|
|
317
|
+
|
|
318
|
+
result['data'].to_h { |row| [row[0], row[1]] }
|
|
319
|
+
end
|
|
320
|
+
|
|
321
|
+
# @see https://clickhouse.com/docs/sql-reference/statements/truncate
|
|
322
|
+
# Additionally add 'Dictionary' because it is returned from 'show tables'.
|
|
323
|
+
TRUNCATE_UNSUPPORTED_ENGINES = %w[View File URL Buffer Null Dictionary].freeze
|
|
324
|
+
|
|
325
|
+
def build_truncate_statements(table_names)
|
|
326
|
+
engines = table_engines(table_names)
|
|
327
|
+
tables_to_truncate = table_names.select do |table_name|
|
|
328
|
+
engine = engines[table_name]
|
|
329
|
+
engine.nil? || !TRUNCATE_UNSUPPORTED_ENGINES.include?(engine)
|
|
330
|
+
end
|
|
331
|
+
|
|
332
|
+
super(tables_to_truncate)
|
|
333
|
+
end
|
|
296
334
|
end
|
|
297
335
|
end
|
|
298
336
|
end
|
|
@@ -113,6 +113,8 @@ module ActiveRecord
|
|
|
113
113
|
uint64: { name: 'UInt64' },
|
|
114
114
|
# uint128: { name: 'UInt128' }, not yet implemented in clickhouse
|
|
115
115
|
uint256: { name: 'UInt256' },
|
|
116
|
+
|
|
117
|
+
json: { name: 'JSON' },
|
|
116
118
|
}.freeze
|
|
117
119
|
|
|
118
120
|
include Clickhouse::SchemaStatements
|
|
@@ -147,6 +149,11 @@ module ActiveRecord
|
|
|
147
149
|
connect
|
|
148
150
|
end
|
|
149
151
|
|
|
152
|
+
def disconnect!
|
|
153
|
+
@connection.finish if @connection&.started?
|
|
154
|
+
super
|
|
155
|
+
end
|
|
156
|
+
|
|
150
157
|
# Return ClickHouse server version
|
|
151
158
|
def server_version
|
|
152
159
|
@server_version ||= select_value('SELECT version()')
|
|
@@ -246,6 +253,8 @@ module ActiveRecord
|
|
|
246
253
|
m.register_type(%r(Map)) do |sql_type|
|
|
247
254
|
Clickhouse::OID::Map.new(sql_type)
|
|
248
255
|
end
|
|
256
|
+
|
|
257
|
+
m.register_type %r(JSON)i, ActiveRecord::Type::Json.new
|
|
249
258
|
end
|
|
250
259
|
end
|
|
251
260
|
|
|
@@ -18,6 +18,33 @@ module Arel
|
|
|
18
18
|
end
|
|
19
19
|
end
|
|
20
20
|
|
|
21
|
+
# https://clickhouse.com/docs/sql-reference/statements/select/with
|
|
22
|
+
def visit_Arel_Nodes_Cte(o, collector)
|
|
23
|
+
is_cse = o.relation.is_a?(Symbol)
|
|
24
|
+
|
|
25
|
+
if is_cse && o.name.is_a?(String)
|
|
26
|
+
collector << quote(o.name)
|
|
27
|
+
elsif is_cse && o.name.is_a?(ActiveRecord::Relation)
|
|
28
|
+
visit o.name.arel, collector
|
|
29
|
+
else
|
|
30
|
+
collector << quote_table_name(o.name)
|
|
31
|
+
end
|
|
32
|
+
collector << " AS "
|
|
33
|
+
|
|
34
|
+
case o.materialized
|
|
35
|
+
when true
|
|
36
|
+
collector << "MATERIALIZED "
|
|
37
|
+
when false
|
|
38
|
+
collector << "NOT MATERIALIZED "
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
if is_cse
|
|
42
|
+
collector << o.relation.to_s
|
|
43
|
+
else
|
|
44
|
+
visit o.relation, collector
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
|
|
21
48
|
# https://clickhouse.com/docs/en/sql-reference/statements/delete
|
|
22
49
|
# DELETE and UPDATE in ClickHouse working only without table name
|
|
23
50
|
def visit_Arel_Attributes_Attribute(o, collector)
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ClickhouseActiverecord
|
|
4
|
+
module TestHelper
|
|
5
|
+
def before_setup
|
|
6
|
+
super
|
|
7
|
+
ActiveRecord::Base.configurations.configurations.select { |x| x.env_name == Rails.env && x.adapter == 'clickhouse' }.each do |config|
|
|
8
|
+
ActiveRecord::Base.establish_connection(config)
|
|
9
|
+
ActiveRecord::Base.connection.truncate_tables(*ActiveRecord::Base.connection.tables)
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
ActiveSupport::TestCase.include(ClickhouseActiverecord::TestHelper) if defined?(ActiveSupport::TestCase)
|
|
@@ -4,9 +4,7 @@ RSpec.configure do |config|
|
|
|
4
4
|
config.before do
|
|
5
5
|
ActiveRecord::Base.configurations.configurations.select { |x| x.env_name == Rails.env && x.adapter == 'clickhouse' }.each do |config|
|
|
6
6
|
ActiveRecord::Base.establish_connection(config)
|
|
7
|
-
ActiveRecord::Base.connection.tables
|
|
8
|
-
ActiveRecord::Base.connection.execute("TRUNCATE TABLE #{table}")
|
|
9
|
-
end
|
|
7
|
+
ActiveRecord::Base.connection.truncate_tables(*ActiveRecord::Base.connection.tables)
|
|
10
8
|
end
|
|
11
9
|
end
|
|
12
10
|
end
|
|
@@ -88,8 +88,34 @@ module ClickhouseActiverecord
|
|
|
88
88
|
ActiveRecord::Migration.verbose = verbose_was
|
|
89
89
|
end
|
|
90
90
|
|
|
91
|
+
def check_current_protected_environment!(db_config, migration_class = ActiveRecord::Migration)
|
|
92
|
+
with_temporary_pool(db_config, migration_class) do |pool|
|
|
93
|
+
migration_context = pool.migration_context
|
|
94
|
+
current = migration_context.current_environment
|
|
95
|
+
stored = migration_context.last_stored_environment
|
|
96
|
+
|
|
97
|
+
if migration_context.protected_environment?
|
|
98
|
+
raise ActiveRecord::ProtectedEnvironmentError.new(stored)
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
if stored && stored != current
|
|
102
|
+
raise ActiveRecord::EnvironmentMismatchError.new(current: current, stored: stored)
|
|
103
|
+
end
|
|
104
|
+
rescue ActiveRecord::NoDatabaseError
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
|
|
91
108
|
private
|
|
92
109
|
|
|
110
|
+
def with_temporary_pool(db_config, migration_class, clobber: false)
|
|
111
|
+
original_db_config = migration_class.connection_db_config
|
|
112
|
+
pool = migration_class.connection_handler.establish_connection(db_config, clobber: clobber)
|
|
113
|
+
|
|
114
|
+
yield pool
|
|
115
|
+
ensure
|
|
116
|
+
migration_class.connection_handler.establish_connection(original_db_config, clobber: clobber)
|
|
117
|
+
end
|
|
118
|
+
|
|
93
119
|
def establish_master_connection
|
|
94
120
|
establish_connection @configuration
|
|
95
121
|
end
|
|
@@ -192,6 +192,32 @@ module CoreExtensions
|
|
|
192
192
|
|
|
193
193
|
arel
|
|
194
194
|
end
|
|
195
|
+
|
|
196
|
+
def build_with_value_from_hash(hash)
|
|
197
|
+
return super if ::ActiveRecord::version >= Gem::Version.new('7.2')
|
|
198
|
+
|
|
199
|
+
# Redefine for ActiveRecord < 7.2
|
|
200
|
+
hash.map do |name, value|
|
|
201
|
+
expression =
|
|
202
|
+
case value
|
|
203
|
+
when ::Arel::Nodes::SqlLiteral then ::Arel::Nodes::Grouping.new(value)
|
|
204
|
+
when ::ActiveRecord::Relation then value.arel
|
|
205
|
+
when ::Arel::SelectManager then value
|
|
206
|
+
when Symbol then value
|
|
207
|
+
else
|
|
208
|
+
raise ArgumentError, "Unsupported argument type: `#{value}` #{value.class}"
|
|
209
|
+
end
|
|
210
|
+
::Arel::Nodes::TableAlias.new(expression, name)
|
|
211
|
+
end
|
|
212
|
+
end
|
|
213
|
+
|
|
214
|
+
def build_with_expression_from_value(value, nested = false)
|
|
215
|
+
case value
|
|
216
|
+
when Symbol then value
|
|
217
|
+
else
|
|
218
|
+
super
|
|
219
|
+
end
|
|
220
|
+
end
|
|
195
221
|
end
|
|
196
222
|
end
|
|
197
223
|
end
|
metadata
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: clickhouse-activerecord
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.
|
|
4
|
+
version: 1.6.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Sergey Odintsov
|
|
8
|
+
autorequire:
|
|
8
9
|
bindir: exe
|
|
9
10
|
cert_chain: []
|
|
10
|
-
date:
|
|
11
|
+
date: 2026-01-19 00:00:00.000000000 Z
|
|
11
12
|
dependencies:
|
|
12
13
|
- !ruby/object:Gem::Dependency
|
|
13
14
|
name: bundler
|
|
@@ -133,6 +134,7 @@ files:
|
|
|
133
134
|
- lib/arel/nodes/using.rb
|
|
134
135
|
- lib/arel/visitors/clickhouse.rb
|
|
135
136
|
- lib/clickhouse-activerecord.rb
|
|
137
|
+
- lib/clickhouse-activerecord/minitest.rb
|
|
136
138
|
- lib/clickhouse-activerecord/railtie.rb
|
|
137
139
|
- lib/clickhouse-activerecord/rspec.rb
|
|
138
140
|
- lib/clickhouse-activerecord/schema.rb
|
|
@@ -152,6 +154,7 @@ homepage: https://github.com/pnixx/clickhouse-activerecord
|
|
|
152
154
|
licenses:
|
|
153
155
|
- MIT
|
|
154
156
|
metadata: {}
|
|
157
|
+
post_install_message:
|
|
155
158
|
rdoc_options: []
|
|
156
159
|
require_paths:
|
|
157
160
|
- lib
|
|
@@ -166,7 +169,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
166
169
|
- !ruby/object:Gem::Version
|
|
167
170
|
version: '0'
|
|
168
171
|
requirements: []
|
|
169
|
-
rubygems_version: 3.6
|
|
172
|
+
rubygems_version: 3.1.6
|
|
173
|
+
signing_key:
|
|
170
174
|
specification_version: 4
|
|
171
175
|
summary: ClickHouse ActiveRecord
|
|
172
176
|
test_files: []
|