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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 79b43575e5eef93daa11e1a2aca2984f696cbef59021f55d1470b2dc0a5d1e3d
4
- data.tar.gz: b79cccb85d8a07fa4dc533d7b7e9042eeaa2afead26214c075b3131e1843394b
3
+ metadata.gz: c4828cf36787dd2532b1522cdafc1f221b6088fca1bc394d0b86703355676ebc
4
+ data.tar.gz: 8d0cf197f79787754461990e8f06599497b4ae9e6eea6419bf30bf83986ad9ac
5
5
  SHA512:
6
- metadata.gz: 840b01cc1d5b88eee5e031fada23cfd35f7a8d948a35a767b2f5baf1596af665f28c56778e699960511004367b16ee3130be1f5bd2ee713a9a0d5053a9b58a37
7
- data.tar.gz: 4e613a19c51b362a05634da06d9528809ed09f01ebec4eac9984656156e9b877bdc4ce321e24bea63ae4e49d85609fa9dda781db60159e60b6d6ee06e8a99a3e
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', '~> 1.15'
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}.#{options.to.sub('.', '')} "
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('SHOW TABLES', name)
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
- execute insert_versions_sql(inserting)
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
- class Relation
50
-
51
- # Replace for only ClickhouseAdapter
50
+ module ClickhouseRelationReverseOrder
52
51
  def reverse_order!
53
- orders = order_values.uniq
54
- orders.reject!(&:blank?)
55
- if self.connection.is_a?(ConnectionAdapters::ClickhouseAdapter) && orders.empty? && !primary_key
56
- self.order_values = %w(date created_at).select {|c| column_names.include?(c) }.map{|c| arel_attribute(c).desc }
57
- else
58
- self.order_values = reverse_sql_order(orders)
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
- end
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
- zone_conversion_method = ActiveRecord::Base.default_timezone == :utc ? :getutc : :getlocal
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
- value.to_s(:db)
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
- execute schema_creation.accept td
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
- execute schema_creation.accept td
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
- distributed_options = "Distributed(#{cluster},#{@config[:database]},#{table_name}_distributed,#{sharding_key})"
293
- create_table(table_name, **options.merge(options: distributed_options), &block)
294
- end
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
- def drop_table_with_distributed(table_name, **options)
297
- ["#{table_name}_distributed", table_name].each { |name| drop_table(name, **options) }
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 = ActiveRecord::Base.connection.select_one(current_database_engine)
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
- cluster ? "#{sql} ON CLUSTER #{cluster}" : sql
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
- if connection.instance_variable_get(:@full_config)[:distributed_service_tables]
16
- table_options.merge!(sharding_key: 'cityHash64(version)')
17
- table_creation_method = 'create_table_with_distributed'
18
- else
19
- table_creation_method = 'create_table'
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.public_send(table_creation_method, table_name, **table_options) do |t|
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
- if connection.instance_variable_get(:@full_config).try(:[], :distributed_service_tables)
47
- table_options.merge!(sharding_key: 'cityHash64(created_at)')
48
- table_creation_method = 'create_table_with_distributed'
49
- else
50
- table_creation_method = 'create_table'
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.public_send(table_creation_method, table_name, **table_options) do |t|
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::Tasks::DatabaseAlreadyExists
17
+ raise ActiveRecord::DatabaseAlreadyExists
18
18
  else
19
19
  raise
20
20
  end
@@ -1,3 +1,3 @@
1
1
  module ClickhouseActiverecord
2
- VERSION = '0.5.7'
2
+ VERSION = '0.5.11'
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: 0.5.7
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: 2021-10-25 00:00:00.000000000 Z
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