clickhouse-activerecord 1.2.0 → 1.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 +4 -4
- data/.github/workflows/testing.yml +9 -5
- data/README.md +5 -1
- data/clickhouse-activerecord.gemspec +1 -1
- data/lib/active_record/connection_adapters/clickhouse/oid/map.rb +23 -9
- data/lib/active_record/connection_adapters/clickhouse/schema_statements.rb +13 -5
- data/lib/active_record/connection_adapters/clickhouse/table_definition.rb +1 -1
- data/lib/active_record/connection_adapters/clickhouse_adapter.rb +10 -6
- data/lib/arel/visitors/clickhouse.rb +16 -6
- data/lib/clickhouse-activerecord/schema_dumper.rb +15 -3
- data/lib/clickhouse-activerecord/tasks.rb +3 -3
- data/lib/clickhouse-activerecord/version.rb +1 -1
- metadata +10 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 8e47b553f60d06d69a8e1f7c70bbc495c32502f0ea75f3ede9c8ac2bafb063ee
|
4
|
+
data.tar.gz: 42ec02c29478ae577fda1f1a726d003e925b2c36faa02fd6a92797a4e05f960c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 405f20e2c53352ae702b4b462165ccbf5306da2e72733d5c8f80588ba5392637e6b8d2c95d6d61cc9f777d0fc49e48a3578ee6db2fdb292668cb4ec0c52d0471
|
7
|
+
data.tar.gz: 8b3bebc0c3672112c26889555d1eb0b280cbc331758cbf1d5d1733273983e10178929bec76fda6793a654d1ff2c0048b44a58eca533f3ec0a2e8481e7d928314
|
@@ -28,13 +28,15 @@ jobs:
|
|
28
28
|
rails: 7.1.3
|
29
29
|
- ruby: 3.2
|
30
30
|
rails: 7.2.1
|
31
|
-
|
31
|
+
- ruby: 3.2
|
32
|
+
rails: 8.0.1
|
33
|
+
clickhouse: [ '22.1', '24.9' ]
|
32
34
|
|
33
35
|
steps:
|
34
36
|
- uses: actions/checkout@v4
|
35
37
|
|
36
38
|
- name: Start ClickHouse ${{ matrix.clickhouse }}
|
37
|
-
uses:
|
39
|
+
uses: hoverkraft-tech/compose-action@v2.1.0
|
38
40
|
env:
|
39
41
|
CLICKHOUSE_VERSION: ${{ matrix.clickhouse }}
|
40
42
|
with:
|
@@ -72,14 +74,16 @@ jobs:
|
|
72
74
|
- ruby: 3.2
|
73
75
|
rails: 7.1.3
|
74
76
|
- ruby: 3.2
|
75
|
-
rails: 7.2.
|
76
|
-
|
77
|
+
rails: 7.2.1
|
78
|
+
- ruby: 3.2
|
79
|
+
rails: 8.0.1
|
80
|
+
clickhouse: [ '22.1', '24.9' ]
|
77
81
|
|
78
82
|
steps:
|
79
83
|
- uses: actions/checkout@v4
|
80
84
|
|
81
85
|
- name: Start ClickHouse Cluster ${{ matrix.clickhouse }}
|
82
|
-
uses:
|
86
|
+
uses: hoverkraft-tech/compose-action@v2.1.0
|
83
87
|
env:
|
84
88
|
CLICKHOUSE_VERSION: ${{ matrix.clickhouse }}
|
85
89
|
with:
|
data/README.md
CHANGED
@@ -184,7 +184,11 @@ Action.settings(optimize_read_in_order: 1).where(date: Date.current).limit(10)
|
|
184
184
|
|
185
185
|
User.joins(:actions).using(:group_id)
|
186
186
|
# Clickhouse User Load (10.3ms) SELECT users.* FROM users INNER JOIN actions USING group_id
|
187
|
-
#=> #<ActiveRecord::Relation [#<
|
187
|
+
#=> #<ActiveRecord::Relation [#<User *** >]>
|
188
|
+
|
189
|
+
User.window('x', order: 'date', partition: 'name', rows: 'UNBOUNDED PRECEDING').select('sum(value) OVER x')
|
190
|
+
# SELECT sum(value) OVER x FROM users WINDOW x AS (PARTITION BY name ORDER BY date ROWS UNBOUNDED PRECEDING)
|
191
|
+
#=> #<ActiveRecord::Relation [#<User *** >]>
|
188
192
|
```
|
189
193
|
|
190
194
|
|
@@ -24,7 +24,7 @@ Gem::Specification.new do |spec|
|
|
24
24
|
spec.require_paths = ['lib']
|
25
25
|
|
26
26
|
spec.add_runtime_dependency 'bundler', '>= 1.13.4'
|
27
|
-
spec.add_runtime_dependency 'activerecord', '
|
27
|
+
spec.add_runtime_dependency 'activerecord', '>= 7.1', '< 9.0'
|
28
28
|
|
29
29
|
spec.add_development_dependency 'rake', '~> 13.0'
|
30
30
|
spec.add_development_dependency 'rspec', '~> 3.4'
|
@@ -7,15 +7,16 @@ module ActiveRecord
|
|
7
7
|
class Map < Type::Value # :nodoc:
|
8
8
|
|
9
9
|
def initialize(sql_type)
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
10
|
+
case sql_type
|
11
|
+
when /U?Int(\d+)/
|
12
|
+
@subtype = :integer
|
13
|
+
@limit = bits_to_limit(Regexp.last_match(1)&.to_i)
|
14
|
+
when /DateTime/
|
15
|
+
@subtype = :datetime
|
16
|
+
when /Date/
|
17
|
+
@subtype = :date
|
18
|
+
else
|
19
|
+
@subtype = :string
|
19
20
|
end
|
20
21
|
end
|
21
22
|
|
@@ -65,6 +66,19 @@ module ActiveRecord
|
|
65
66
|
end
|
66
67
|
end
|
67
68
|
|
69
|
+
private
|
70
|
+
|
71
|
+
def bits_to_limit(bits)
|
72
|
+
case bits
|
73
|
+
when 8 then 1
|
74
|
+
when 16 then 2
|
75
|
+
when 32 then 4
|
76
|
+
when 64 then 8
|
77
|
+
when 128 then 16
|
78
|
+
when 256 then 32
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
68
82
|
end
|
69
83
|
end
|
70
84
|
end
|
@@ -72,8 +72,14 @@ module ActiveRecord
|
|
72
72
|
result['data'].flatten
|
73
73
|
end
|
74
74
|
|
75
|
+
def materialized_views(name = nil)
|
76
|
+
result = do_system_execute("SHOW TABLES WHERE engine = 'MaterializedView'", name)
|
77
|
+
return [] if result.nil?
|
78
|
+
result['data'].flatten
|
79
|
+
end
|
80
|
+
|
75
81
|
def functions
|
76
|
-
result = do_system_execute("SELECT name FROM system.functions WHERE origin = 'SQLUserDefined'")
|
82
|
+
result = do_system_execute("SELECT name FROM system.functions WHERE origin = 'SQLUserDefined' ORDER BY name")
|
77
83
|
return [] if result.nil?
|
78
84
|
result['data'].flatten
|
79
85
|
end
|
@@ -172,10 +178,12 @@ module ActiveRecord
|
|
172
178
|
def request(sql, format = nil, settings = {})
|
173
179
|
formatted_sql = apply_format(sql, format)
|
174
180
|
request_params = @connection_config || {}
|
175
|
-
@
|
176
|
-
|
177
|
-
|
178
|
-
|
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
|
179
187
|
end
|
180
188
|
|
181
189
|
def apply_format(sql, format)
|
@@ -102,7 +102,7 @@ module ActiveRecord
|
|
102
102
|
private
|
103
103
|
|
104
104
|
def valid_column_definition_options
|
105
|
-
super + [:array, :low_cardinality, :fixed_string, :value, :type, :map, :codec]
|
105
|
+
super + [:array, :low_cardinality, :fixed_string, :value, :type, :map, :codec, :unsigned]
|
106
106
|
end
|
107
107
|
end
|
108
108
|
|
@@ -191,6 +191,8 @@ module ActiveRecord
|
|
191
191
|
nil
|
192
192
|
when /(Nullable)?\(?U?Int64\)?/
|
193
193
|
8
|
194
|
+
when /(Nullable)?\(?U?Int128\)?/
|
195
|
+
16
|
194
196
|
else
|
195
197
|
super
|
196
198
|
end
|
@@ -298,9 +300,11 @@ module ActiveRecord
|
|
298
300
|
end
|
299
301
|
|
300
302
|
# @param [String] table
|
303
|
+
# @option [Boolean] single_line
|
301
304
|
# @return [String]
|
302
|
-
def show_create_table(table)
|
303
|
-
do_system_execute("SHOW CREATE TABLE `#{table}`")['data'].try(:first).try(:first)
|
305
|
+
def show_create_table(table, single_line: true)
|
306
|
+
sql = do_system_execute("SHOW CREATE TABLE `#{table}`")['data'].try(:first).try(:first)
|
307
|
+
single_line ? sql.squish : sql
|
304
308
|
end
|
305
309
|
|
306
310
|
# Create a new ClickHouse database.
|
@@ -312,7 +316,7 @@ module ActiveRecord
|
|
312
316
|
end
|
313
317
|
end
|
314
318
|
|
315
|
-
def create_view(table_name, **options)
|
319
|
+
def create_view(table_name, request_settings: {}, **options)
|
316
320
|
options.merge!(view: true)
|
317
321
|
options = apply_replica(table_name, options)
|
318
322
|
td = create_table_definition(apply_cluster(table_name), **options)
|
@@ -322,10 +326,10 @@ module ActiveRecord
|
|
322
326
|
drop_table(table_name, options.merge(if_exists: true))
|
323
327
|
end
|
324
328
|
|
325
|
-
do_execute(schema_creation.accept(td), format: nil)
|
329
|
+
do_execute(schema_creation.accept(td), format: nil, settings: request_settings)
|
326
330
|
end
|
327
331
|
|
328
|
-
def create_table(table_name, **options, &block)
|
332
|
+
def create_table(table_name, request_settings: {}, **options, &block)
|
329
333
|
options = apply_replica(table_name, options)
|
330
334
|
td = create_table_definition(apply_cluster(table_name), **options)
|
331
335
|
block.call td if block_given?
|
@@ -339,7 +343,7 @@ module ActiveRecord
|
|
339
343
|
drop_table(table_name, options.merge(if_exists: true))
|
340
344
|
end
|
341
345
|
|
342
|
-
do_execute(schema_creation.accept(td), format: nil)
|
346
|
+
do_execute(schema_creation.accept(td), format: nil, settings: request_settings)
|
343
347
|
|
344
348
|
if options[:with_distributed]
|
345
349
|
distributed_table_name = options.delete(:with_distributed)
|
@@ -4,6 +4,11 @@ module Arel
|
|
4
4
|
module Visitors
|
5
5
|
class Clickhouse < ::Arel::Visitors::ToSql
|
6
6
|
|
7
|
+
def compile(node, collector = Arel::Collectors::SQLString.new)
|
8
|
+
@delete_or_update = false
|
9
|
+
super
|
10
|
+
end
|
11
|
+
|
7
12
|
def aggregate(name, o, collector)
|
8
13
|
# replacing function name for materialized view
|
9
14
|
if o.expressions.first && o.expressions.first != '*' && !o.expressions.first.is_a?(String) && o.expressions.first.relation&.is_view
|
@@ -16,12 +21,11 @@ module Arel
|
|
16
21
|
# https://clickhouse.com/docs/en/sql-reference/statements/delete
|
17
22
|
# DELETE and UPDATE in ClickHouse working only without table name
|
18
23
|
def visit_Arel_Attributes_Attribute(o, collector)
|
19
|
-
|
20
|
-
|
21
|
-
collector <<
|
22
|
-
else
|
23
|
-
super
|
24
|
+
unless @delete_or_update
|
25
|
+
join_name = o.relation.table_alias || o.relation.name
|
26
|
+
collector << quote_table_name(join_name) << '.'
|
24
27
|
end
|
28
|
+
collector << quote_column_name(o.name)
|
25
29
|
end
|
26
30
|
|
27
31
|
def visit_Arel_Nodes_SelectOptions(o, collector)
|
@@ -30,6 +34,7 @@ module Arel
|
|
30
34
|
end
|
31
35
|
|
32
36
|
def visit_Arel_Nodes_UpdateStatement(o, collector)
|
37
|
+
@delete_or_update = true
|
33
38
|
o = prepare_update_statement(o)
|
34
39
|
|
35
40
|
collector << 'ALTER TABLE '
|
@@ -40,6 +45,11 @@ module Arel
|
|
40
45
|
maybe_visit o.limit, collector
|
41
46
|
end
|
42
47
|
|
48
|
+
def visit_Arel_Nodes_DeleteStatement(o, collector)
|
49
|
+
@delete_or_update = true
|
50
|
+
super
|
51
|
+
end
|
52
|
+
|
43
53
|
def visit_Arel_Nodes_Final(o, collector)
|
44
54
|
visit o.expr, collector
|
45
55
|
collector << ' FINAL'
|
@@ -64,7 +74,7 @@ module Arel
|
|
64
74
|
collector
|
65
75
|
end
|
66
76
|
|
67
|
-
def visit_Arel_Nodes_Using
|
77
|
+
def visit_Arel_Nodes_Using(o, collector)
|
68
78
|
collector << "USING "
|
69
79
|
visit o.expr, collector
|
70
80
|
collector
|
@@ -15,13 +15,16 @@ module ClickhouseActiverecord
|
|
15
15
|
private
|
16
16
|
|
17
17
|
def tables(stream)
|
18
|
-
functions = @connection.functions
|
18
|
+
functions = @connection.functions.sort
|
19
19
|
functions.each do |function|
|
20
20
|
function(function, stream)
|
21
21
|
end
|
22
22
|
|
23
|
-
|
24
|
-
|
23
|
+
view_tables = @connection.views.sort
|
24
|
+
materialized_view_tables = @connection.materialized_views.sort
|
25
|
+
sorted_tables = @connection.tables.sort - view_tables - materialized_view_tables
|
26
|
+
|
27
|
+
(sorted_tables + view_tables + materialized_view_tables).each do |table_name|
|
25
28
|
table(table_name, stream) unless ignored?(table_name)
|
26
29
|
end
|
27
30
|
end
|
@@ -109,6 +112,15 @@ module ClickhouseActiverecord
|
|
109
112
|
end
|
110
113
|
end
|
111
114
|
|
115
|
+
def column_spec_for_primary_key(column)
|
116
|
+
spec = super
|
117
|
+
|
118
|
+
id = ActiveRecord::ConnectionAdapters::ClickhouseAdapter::NATIVE_DATABASE_TYPES.invert[{name: column.sql_type.gsub(/\(\d+\)/, "")}]
|
119
|
+
spec[:id] = id.inspect if id.present?
|
120
|
+
|
121
|
+
spec.except!(:limit, :unsigned) # This can be removed at some date, it is only here to clean up existing schemas which have dumped these values already
|
122
|
+
end
|
123
|
+
|
112
124
|
def function(function, stream)
|
113
125
|
stream.puts " # FUNCTION: #{function}"
|
114
126
|
sql = @connection.show_create_function(function)
|
@@ -40,19 +40,19 @@ module ClickhouseActiverecord
|
|
40
40
|
# get all tables
|
41
41
|
tables = connection.execute("SHOW TABLES FROM #{@configuration.database} WHERE name NOT LIKE '.inner_id.%'")['data'].flatten.map do |table|
|
42
42
|
next if %w[schema_migrations ar_internal_metadata].include?(table)
|
43
|
-
connection.show_create_table(table).gsub("#{@configuration.database}.", '')
|
43
|
+
connection.show_create_table(table, single_line: false).gsub("#{@configuration.database}.", '')
|
44
44
|
end.compact
|
45
45
|
|
46
46
|
# sort view to last
|
47
47
|
tables.sort_by! {|table| table.match(/^CREATE\s+(MATERIALIZED\s+)?VIEW/) ? 1 : 0}
|
48
48
|
|
49
49
|
# get all functions
|
50
|
-
functions = connection.execute("SELECT create_query FROM system.functions WHERE origin = 'SQLUserDefined'")['data'].flatten
|
50
|
+
functions = connection.execute("SELECT create_query FROM system.functions WHERE origin = 'SQLUserDefined' ORDER BY name")['data'].flatten
|
51
51
|
|
52
52
|
# put to file
|
53
53
|
File.open(args.first, 'w:utf-8') do |file|
|
54
54
|
functions.each do |function|
|
55
|
-
file.puts function + ";\n\n"
|
55
|
+
file.puts function.gsub('\\n', "\n") + ";\n\n"
|
56
56
|
end
|
57
57
|
|
58
58
|
tables.each do |table|
|
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.
|
4
|
+
version: 1.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Sergey Odintsov
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2025-01-24 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -28,16 +28,22 @@ dependencies:
|
|
28
28
|
name: activerecord
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
30
30
|
requirements:
|
31
|
-
- - "
|
31
|
+
- - ">="
|
32
32
|
- !ruby/object:Gem::Version
|
33
33
|
version: '7.1'
|
34
|
+
- - "<"
|
35
|
+
- !ruby/object:Gem::Version
|
36
|
+
version: '9.0'
|
34
37
|
type: :runtime
|
35
38
|
prerelease: false
|
36
39
|
version_requirements: !ruby/object:Gem::Requirement
|
37
40
|
requirements:
|
38
|
-
- - "
|
41
|
+
- - ">="
|
39
42
|
- !ruby/object:Gem::Version
|
40
43
|
version: '7.1'
|
44
|
+
- - "<"
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
version: '9.0'
|
41
47
|
- !ruby/object:Gem::Dependency
|
42
48
|
name: rake
|
43
49
|
requirement: !ruby/object:Gem::Requirement
|