clickhouse-activerecord 0.5.7 → 0.5.11
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 +8 -2
- data/clickhouse-activerecord.gemspec +1 -1
- data/core_extensions/active_record/migration/command_recorder.rb +0 -9
- data/lib/active_record/connection_adapters/clickhouse/schema_creation.rb +10 -1
- data/lib/active_record/connection_adapters/clickhouse/schema_definitions.rb +32 -0
- data/lib/active_record/connection_adapters/clickhouse/schema_statements.rb +2 -2
- data/lib/active_record/connection_adapters/clickhouse_adapter.rb +65 -29
- data/lib/clickhouse-activerecord/migration.rb +14 -12
- data/lib/clickhouse-activerecord/tasks.rb +1 -1
- data/lib/clickhouse-activerecord/version.rb +1 -1
- metadata +4 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c4828cf36787dd2532b1522cdafc1f221b6088fca1bc394d0b86703355676ebc
|
4
|
+
data.tar.gz: 8d0cf197f79787754461990e8f06599497b4ae9e6eea6419bf30bf83986ad9ac
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 217a21b7456784206a97bd75ed20f337c247973f0d839dec05ef84a60059be43f71b3f7a7a87834be92991262a9272927955cbd0c767adf1d3452496cf511602
|
7
|
+
data.tar.gz: 593dd36a333c07fa7c81a9b011577381cc38a2bad2975a39b9f80c57bc56fab9662501ea25d4e510c06028e62fa46d517942b63e5de902acbabfecb36a6b6bb5
|
data/CHANGELOG.md
CHANGED
@@ -1,10 +1,16 @@
|
|
1
|
+
### Version 0.5.10 (Jun 22, 2022)
|
2
|
+
|
3
|
+
* Fixes to create_table method (#70)
|
4
|
+
* Added support for rails 7 (#65)
|
5
|
+
* Use ClickHouse default KeepAlive timeout of 10 seconds (#67)
|
6
|
+
|
1
7
|
### Version 0.5.6 (Oct 25, 2021)
|
2
|
-
|
8
|
+
|
3
9
|
* Added auto creating service distributed tables and additional options for creating view [@ygreeek](https://github.com/ygreeek)
|
4
10
|
* Added default user agent
|
5
11
|
|
6
12
|
### Version 0.5.3 (Sep 22, 2021)
|
7
|
-
|
13
|
+
|
8
14
|
* Fix replica cluster for a new syntax MergeTree
|
9
15
|
* Fix support rails 5.2 on alter table
|
10
16
|
* Support array type of column
|
@@ -26,7 +26,7 @@ Gem::Specification.new do |spec|
|
|
26
26
|
spec.add_runtime_dependency 'bundler', '>= 1.13.4'
|
27
27
|
spec.add_runtime_dependency 'activerecord', '>= 5.2'
|
28
28
|
|
29
|
-
spec.add_development_dependency 'bundler', '
|
29
|
+
spec.add_development_dependency 'bundler', '>= 1.15'
|
30
30
|
spec.add_development_dependency 'rake', '~> 13.0'
|
31
31
|
spec.add_development_dependency 'rspec', '~> 3.4'
|
32
32
|
spec.add_development_dependency 'pry', '~> 0.12'
|
@@ -2,21 +2,12 @@ module CoreExtensions
|
|
2
2
|
module ActiveRecord
|
3
3
|
module Migration
|
4
4
|
module CommandRecorder
|
5
|
-
def create_table_with_distributed(*args, &block)
|
6
|
-
record(:create_table_with_distributed, args, &block)
|
7
|
-
end
|
8
|
-
|
9
5
|
def create_view(*args, &block)
|
10
6
|
record(:create_view, args, &block)
|
11
7
|
end
|
12
8
|
|
13
9
|
private
|
14
10
|
|
15
|
-
def invert_create_table_with_distributed(args)
|
16
|
-
table_name, options = args
|
17
|
-
[:drop_table_with_distributed, table_name, options]
|
18
|
-
end
|
19
|
-
|
20
11
|
def invert_create_view(args)
|
21
12
|
view_name, options = args
|
22
13
|
[:drop_table, view_name, options]
|
@@ -19,9 +19,18 @@ module ActiveRecord
|
|
19
19
|
end
|
20
20
|
|
21
21
|
def add_column_options!(sql, options)
|
22
|
+
if options[:value]
|
23
|
+
sql.gsub!(/\s+(.*)/, " \\1(#{options[:value]})")
|
24
|
+
end
|
25
|
+
if options[:fixed_string]
|
26
|
+
sql.gsub!(/\s+(.*)/, " FixedString(#{options[:fixed_string]})")
|
27
|
+
end
|
22
28
|
if options[:null] || options[:null].nil?
|
23
29
|
sql.gsub!(/\s+(.*)/, ' Nullable(\1)')
|
24
30
|
end
|
31
|
+
if options[:low_cardinality]
|
32
|
+
sql.gsub!(/\s+(.*)/, ' LowCardinality(\1)')
|
33
|
+
end
|
25
34
|
if options[:array]
|
26
35
|
sql.gsub!(/\s+(.*)/, ' Array(\1)')
|
27
36
|
end
|
@@ -73,7 +82,7 @@ module ActiveRecord
|
|
73
82
|
return unless match
|
74
83
|
return if match[:database]
|
75
84
|
|
76
|
-
create_sql << "TO #{current_database}.#{
|
85
|
+
create_sql << "TO #{current_database}.#{match[:table_name].sub('.', '')}"
|
77
86
|
end
|
78
87
|
|
79
88
|
def visit_TableDefinition(o)
|
@@ -62,6 +62,38 @@ module ActiveRecord
|
|
62
62
|
end
|
63
63
|
args.each { |name| column(name, kind, **options.except(:limit, :unsigned)) }
|
64
64
|
end
|
65
|
+
|
66
|
+
def datetime(*args, **options)
|
67
|
+
kind = :datetime
|
68
|
+
|
69
|
+
if options[:precision]
|
70
|
+
kind = :datetime64
|
71
|
+
options[:value] = options[:precision]
|
72
|
+
end
|
73
|
+
|
74
|
+
args.each { |name| column(name, kind, **options.except(:precision)) }
|
75
|
+
end
|
76
|
+
|
77
|
+
def uuid(*args, **options)
|
78
|
+
args.each { |name| column(name, :uuid, **options) }
|
79
|
+
end
|
80
|
+
|
81
|
+
def enum(*args, **options)
|
82
|
+
kind = :enum8
|
83
|
+
|
84
|
+
unless options[:value].is_a? Hash
|
85
|
+
raise ArgumentError, "Column #{args.first}: option 'value' must be Hash, got: #{options[:value].class}"
|
86
|
+
end
|
87
|
+
|
88
|
+
options[:value] = options[:value].each_with_object([]) { |(k, v), arr| arr.push("'#{k}' = #{v}") }.join(', ')
|
89
|
+
|
90
|
+
if options[:limit]
|
91
|
+
kind = :enum8 if options[:limit] == 1
|
92
|
+
kind = :enum16 if options[:limit] == 2
|
93
|
+
end
|
94
|
+
|
95
|
+
args.each { |name| column(name, kind, **options.except(:limit)) }
|
96
|
+
end
|
65
97
|
end
|
66
98
|
end
|
67
99
|
end
|
@@ -39,7 +39,7 @@ module ActiveRecord
|
|
39
39
|
end
|
40
40
|
|
41
41
|
def tables(name = nil)
|
42
|
-
result = do_system_execute(
|
42
|
+
result = do_system_execute("SHOW TABLES WHERE name NOT LIKE '.inner_id.%'", name)
|
43
43
|
return [] if result.nil?
|
44
44
|
result['data'].flatten
|
45
45
|
end
|
@@ -92,7 +92,7 @@ module ActiveRecord
|
|
92
92
|
if (duplicate = inserting.detect { |v| inserting.count(v) > 1 })
|
93
93
|
raise "Duplicate migration #{duplicate}. Please renumber your migrations to resolve the conflict."
|
94
94
|
end
|
95
|
-
|
95
|
+
do_execute(insert_versions_sql(inserting), nil, settings: {max_partitions_per_insert_block: [100, inserting.size].max})
|
96
96
|
end
|
97
97
|
end
|
98
98
|
|
@@ -32,6 +32,7 @@ module ActiveRecord
|
|
32
32
|
sslca: config[:sslca],
|
33
33
|
read_timeout: config[:read_timeout],
|
34
34
|
write_timeout: config[:write_timeout],
|
35
|
+
keep_alive_timeout: config[:keep_alive_timeout]
|
35
36
|
}
|
36
37
|
end
|
37
38
|
|
@@ -46,20 +47,18 @@ module ActiveRecord
|
|
46
47
|
end
|
47
48
|
end
|
48
49
|
|
49
|
-
|
50
|
-
|
51
|
-
# Replace for only ClickhouseAdapter
|
50
|
+
module ClickhouseRelationReverseOrder
|
52
51
|
def reverse_order!
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
end
|
52
|
+
return super unless connection.is_a?(ConnectionAdapters::ClickhouseAdapter)
|
53
|
+
|
54
|
+
orders = order_values.uniq.compact_blank
|
55
|
+
return super unless orders.empty? && !primary_key
|
56
|
+
|
57
|
+
self.order_values = %w(date created_at).select {|c| column_names.include?(c) }.map{|c| arel_attribute(c).desc }
|
60
58
|
self
|
61
59
|
end
|
62
60
|
end
|
61
|
+
Relation.prepend(ClickhouseRelationReverseOrder)
|
63
62
|
|
64
63
|
module TypeCaster
|
65
64
|
class Map
|
@@ -86,9 +85,8 @@ module ActiveRecord
|
|
86
85
|
def arel_table # :nodoc:
|
87
86
|
@arel_table ||= ClickhouseActiverecord::Arel::Table.new(table_name, type_caster: type_caster)
|
88
87
|
end
|
89
|
-
|
90
88
|
end
|
91
|
-
|
89
|
+
end
|
92
90
|
|
93
91
|
module ConnectionAdapters
|
94
92
|
class ClickhouseColumn < Column
|
@@ -104,8 +102,13 @@ module ActiveRecord
|
|
104
102
|
float: { name: 'Float32' },
|
105
103
|
decimal: { name: 'Decimal' },
|
106
104
|
datetime: { name: 'DateTime' },
|
105
|
+
datetime64: { name: 'DateTime64' },
|
107
106
|
date: { name: 'Date' },
|
108
107
|
boolean: { name: 'UInt8' },
|
108
|
+
uuid: { name: 'UUID' },
|
109
|
+
|
110
|
+
enum8: { name: 'Enum8' },
|
111
|
+
enum16: { name: 'Enum16' },
|
109
112
|
|
110
113
|
int8: { name: 'Int8' },
|
111
114
|
int16: { name: 'Int16' },
|
@@ -182,6 +185,20 @@ module ActiveRecord
|
|
182
185
|
end
|
183
186
|
end
|
184
187
|
|
188
|
+
# `extract_scale` and `extract_precision` are the same as in the Rails abstract base class,
|
189
|
+
# except this permits a space after the comma
|
190
|
+
|
191
|
+
def extract_scale(sql_type)
|
192
|
+
case sql_type
|
193
|
+
when /\((\d+)\)/ then 0
|
194
|
+
when /\((\d+)(,\s?(\d+))\)/ then $3.to_i
|
195
|
+
end
|
196
|
+
end
|
197
|
+
|
198
|
+
def extract_precision(sql_type)
|
199
|
+
$1.to_i if sql_type =~ /\((\d+)(,\s?\d+)?\)/
|
200
|
+
end
|
201
|
+
|
185
202
|
def initialize_type_map(m) # :nodoc:
|
186
203
|
super
|
187
204
|
register_class_with_limit m, %r(String), Type::String
|
@@ -210,14 +227,22 @@ module ActiveRecord
|
|
210
227
|
# Quoting time without microseconds
|
211
228
|
def quoted_date(value)
|
212
229
|
if value.acts_like?(:time)
|
213
|
-
|
230
|
+
if ActiveRecord::version >= Gem::Version.new('7')
|
231
|
+
zone_conversion_method = ActiveRecord.default_timezone == :utc ? :getutc : :getlocal
|
232
|
+
else
|
233
|
+
zone_conversion_method = ActiveRecord::Base.default_timezone == :utc ? :getutc : :getlocal
|
234
|
+
end
|
214
235
|
|
215
236
|
if value.respond_to?(zone_conversion_method)
|
216
237
|
value = value.send(zone_conversion_method)
|
217
238
|
end
|
218
239
|
end
|
219
240
|
|
220
|
-
|
241
|
+
if ActiveRecord::version >= Gem::Version.new('7')
|
242
|
+
value.to_fs(:db)
|
243
|
+
else
|
244
|
+
value.to_s(:db)
|
245
|
+
end
|
221
246
|
end
|
222
247
|
|
223
248
|
def column_name_for_operation(operation, node) # :nodoc:
|
@@ -269,7 +294,7 @@ module ActiveRecord
|
|
269
294
|
drop_table(table_name, options.merge(if_exists: true))
|
270
295
|
end
|
271
296
|
|
272
|
-
|
297
|
+
do_execute(schema_creation.accept(td), format: nil)
|
273
298
|
end
|
274
299
|
|
275
300
|
def create_table(table_name, **options, &block)
|
@@ -281,20 +306,17 @@ module ActiveRecord
|
|
281
306
|
drop_table(table_name, options.merge(if_exists: true))
|
282
307
|
end
|
283
308
|
|
284
|
-
|
285
|
-
end
|
286
|
-
|
287
|
-
def create_table_with_distributed(table_name, **options, &block)
|
288
|
-
sharding_key = options.delete(:sharding_key) || 'rand()'
|
289
|
-
create_table("#{table_name}_distributed", **options, &block)
|
290
|
-
raise 'Set a cluster' unless cluster
|
309
|
+
do_execute(schema_creation.accept(td), format: nil)
|
291
310
|
|
292
|
-
|
293
|
-
|
294
|
-
|
311
|
+
if options[:with_distributed]
|
312
|
+
distributed_table_name = options.delete(:with_distributed)
|
313
|
+
sharding_key = options.delete(:sharding_key) || 'rand()'
|
314
|
+
raise 'Set a cluster' unless cluster
|
295
315
|
|
296
|
-
|
297
|
-
|
316
|
+
distributed_options =
|
317
|
+
"Distributed(#{cluster}, #{@config[:database]}, #{table_name}, #{sharding_key})"
|
318
|
+
create_table(distributed_table_name, **options.merge(options: distributed_options), &block)
|
319
|
+
end
|
298
320
|
end
|
299
321
|
|
300
322
|
# Drops a ClickHouse database.
|
@@ -312,6 +334,11 @@ module ActiveRecord
|
|
312
334
|
|
313
335
|
def drop_table(table_name, options = {}) # :nodoc:
|
314
336
|
do_execute apply_cluster "DROP TABLE#{' IF EXISTS' if options[:if_exists]} #{quote_table_name(table_name)}"
|
337
|
+
|
338
|
+
if options[:with_distributed]
|
339
|
+
distributed_table_name = options.delete(:with_distributed)
|
340
|
+
drop_table(distributed_table_name, **options)
|
341
|
+
end
|
315
342
|
end
|
316
343
|
|
317
344
|
def change_column(table_name, column_name, type, options = {})
|
@@ -351,12 +378,18 @@ module ActiveRecord
|
|
351
378
|
|
352
379
|
def database_engine_atomic?
|
353
380
|
current_database_engine = "select engine from system.databases where name = '#{@config[:database]}'"
|
354
|
-
res =
|
381
|
+
res = select_one(current_database_engine)
|
355
382
|
res['engine'] == 'Atomic' if res
|
356
383
|
end
|
357
384
|
|
358
385
|
def apply_cluster(sql)
|
359
|
-
|
386
|
+
if cluster
|
387
|
+
normalized_cluster_name = cluster.start_with?('{') ? "'#{cluster}'" : cluster
|
388
|
+
|
389
|
+
"#{sql} ON CLUSTER #{normalized_cluster_name}"
|
390
|
+
else
|
391
|
+
sql
|
392
|
+
end
|
360
393
|
end
|
361
394
|
|
362
395
|
def supports_insert_on_duplicate_skip?
|
@@ -393,6 +426,9 @@ module ActiveRecord
|
|
393
426
|
@connection.read_timeout = @connection_parameters[:read_timeout] if @connection_parameters[:read_timeout]
|
394
427
|
@connection.write_timeout = @connection_parameters[:write_timeout] if @connection_parameters[:write_timeout]
|
395
428
|
|
429
|
+
# Use clickhouse default keep_alive_timeout value of 10, rather than Net::HTTP's default of 2
|
430
|
+
@connection.keep_alive_timeout = @connection_parameters[:keep_alive_timeout] || 10
|
431
|
+
|
396
432
|
@connection
|
397
433
|
end
|
398
434
|
|
@@ -12,14 +12,15 @@ module ClickhouseActiverecord
|
|
12
12
|
table_options = {
|
13
13
|
id: false, options: 'ReplacingMergeTree(ver) PARTITION BY version ORDER BY (version)', if_not_exists: true
|
14
14
|
}
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
15
|
+
full_config = connection.instance_variable_get(:@full_config) || {}
|
16
|
+
|
17
|
+
if full_config[:distributed_service_tables]
|
18
|
+
table_options.merge!(with_distributed: table_name, sharding_key: 'cityHash64(version)')
|
19
|
+
|
20
|
+
distributed_suffix = "_#{full_config[:distributed_service_tables_suffix] || 'distributed'}"
|
20
21
|
end
|
21
22
|
|
22
|
-
connection.
|
23
|
+
connection.create_table(table_name + distributed_suffix.to_s, **table_options) do |t|
|
23
24
|
t.string :version, **version_options
|
24
25
|
t.column :active, 'Int8', null: false, default: '1'
|
25
26
|
t.datetime :ver, null: false, default: -> { 'now()' }
|
@@ -43,14 +44,15 @@ module ClickhouseActiverecord
|
|
43
44
|
options: connection.adapter_name.downcase == 'clickhouse' ? 'MergeTree() PARTITION BY toDate(created_at) ORDER BY (created_at)' : '',
|
44
45
|
if_not_exists: true
|
45
46
|
}
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
47
|
+
full_config = connection.instance_variable_get(:@full_config) || {}
|
48
|
+
|
49
|
+
if full_config[:distributed_service_tables]
|
50
|
+
table_options.merge!(with_distributed: table_name, sharding_key: 'cityHash64(created_at)')
|
51
|
+
|
52
|
+
distributed_suffix = "_#{full_config[:distributed_service_tables_suffix] || 'distributed'}"
|
51
53
|
end
|
52
54
|
|
53
|
-
connection.
|
55
|
+
connection.create_table(table_name + distributed_suffix.to_s, **table_options) do |t|
|
54
56
|
t.string :key, **key_options
|
55
57
|
t.string :value
|
56
58
|
t.timestamps
|
@@ -14,7 +14,7 @@ module ClickhouseActiverecord
|
|
14
14
|
connection.create_database @configuration["database"]
|
15
15
|
rescue ActiveRecord::StatementInvalid => e
|
16
16
|
if e.cause.to_s.include?('already exists')
|
17
|
-
raise ActiveRecord::
|
17
|
+
raise ActiveRecord::DatabaseAlreadyExists
|
18
18
|
else
|
19
19
|
raise
|
20
20
|
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: 0.5.
|
4
|
+
version: 0.5.11
|
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: 2022-08-16 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -42,14 +42,14 @@ dependencies:
|
|
42
42
|
name: bundler
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
44
44
|
requirements:
|
45
|
-
- - "
|
45
|
+
- - ">="
|
46
46
|
- !ruby/object:Gem::Version
|
47
47
|
version: '1.15'
|
48
48
|
type: :development
|
49
49
|
prerelease: false
|
50
50
|
version_requirements: !ruby/object:Gem::Requirement
|
51
51
|
requirements:
|
52
|
-
- - "
|
52
|
+
- - ">="
|
53
53
|
- !ruby/object:Gem::Version
|
54
54
|
version: '1.15'
|
55
55
|
- !ruby/object:Gem::Dependency
|