clickhouse-activerecord 0.5.7 → 0.5.11

Sign up to get free protection for your applications and to get access to all the features.
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