clickhouse-activerecord 0.5.7 → 0.5.8

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: c8f8d01291f12197d0b5d0b8f791631fe144cdc5d855de9dd9c8498e170fafdf
4
+ data.tar.gz: 2ebd5b6068a179a7c87679db0fe9c34bbe2f9197c6230da9aad1a6935fc9c7d9
5
5
  SHA512:
6
- metadata.gz: 840b01cc1d5b88eee5e031fada23cfd35f7a8d948a35a767b2f5baf1596af665f28c56778e699960511004367b16ee3130be1f5bd2ee713a9a0d5053a9b58a37
7
- data.tar.gz: 4e613a19c51b362a05634da06d9528809ed09f01ebec4eac9984656156e9b877bdc4ce321e24bea63ae4e49d85609fa9dda781db60159e60b6d6ee06e8a99a3e
6
+ metadata.gz: 94abbef81ff0000ecbc05092dd3558dc90e53223fd0025c5a57549f4b5f98431a01ee9fceee5587887c9fa6fcc3411420e936ad942150caa8edc5146c7db54df
7
+ data.tar.gz: '03871bfe2bec394c9612928229cc3380615acd795b25c058072a3aec62a7852e06711f2811286438ab278c838d4ed65736bd47ae449bc0ca5bbad6763692a927'
@@ -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
@@ -46,20 +46,18 @@ module ActiveRecord
46
46
  end
47
47
  end
48
48
 
49
- class Relation
50
-
51
- # Replace for only ClickhouseAdapter
49
+ module ClickhouseRelationReverseOrder
52
50
  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
51
+ return super unless connection.is_a?(ConnectionAdapters::ClickhouseAdapter)
52
+
53
+ orders = order_values.uniq.compact_blank
54
+ return super unless orders.empty? && !primary_key
55
+
56
+ self.order_values = %w(date created_at).select {|c| column_names.include?(c) }.map{|c| arel_attribute(c).desc }
60
57
  self
61
58
  end
62
59
  end
60
+ Relation.prepend(ClickhouseRelationReverseOrder)
63
61
 
64
62
  module TypeCaster
65
63
  class Map
@@ -82,13 +80,19 @@ module ActiveRecord
82
80
  def is_view=(value)
83
81
  @is_view = value
84
82
  end
83
+ end
84
+ end
85
85
 
86
- def arel_table # :nodoc:
87
- @arel_table ||= ClickhouseActiverecord::Arel::Table.new(table_name, type_caster: type_caster)
88
- end
89
-
86
+ ActiveRecord::Core::ClassMethods.module_eval do
87
+ def arel_table
88
+ @arel_table ||=
89
+ if self.connection.is_a?(ConnectionAdapters::ClickhouseAdapter)
90
+ ClickhouseActiverecord::Arel::Table.new(table_name, type_caster: type_caster)
91
+ else
92
+ Arel::Table.new(table_name, klass: self)
93
+ end
90
94
  end
91
- end
95
+ end
92
96
 
93
97
  module ConnectionAdapters
94
98
  class ClickhouseColumn < Column
@@ -104,8 +108,13 @@ module ActiveRecord
104
108
  float: { name: 'Float32' },
105
109
  decimal: { name: 'Decimal' },
106
110
  datetime: { name: 'DateTime' },
111
+ datetime64: { name: 'DateTime64' },
107
112
  date: { name: 'Date' },
108
113
  boolean: { name: 'UInt8' },
114
+ uuid: { name: 'UUID' },
115
+
116
+ enum8: { name: 'Enum8' },
117
+ enum16: { name: 'Enum16' },
109
118
 
110
119
  int8: { name: 'Int8' },
111
120
  int16: { name: 'Int16' },
@@ -182,6 +191,20 @@ module ActiveRecord
182
191
  end
183
192
  end
184
193
 
194
+ # `extract_scale` and `extract_precision` are the same as in the Rails abstract base class,
195
+ # except this permits a space after the comma
196
+
197
+ def extract_scale(sql_type)
198
+ case sql_type
199
+ when /\((\d+)\)/ then 0
200
+ when /\((\d+)(,\s?(\d+))\)/ then $3.to_i
201
+ end
202
+ end
203
+
204
+ def extract_precision(sql_type)
205
+ $1.to_i if sql_type =~ /\((\d+)(,\s?\d+)?\)/
206
+ end
207
+
185
208
  def initialize_type_map(m) # :nodoc:
186
209
  super
187
210
  register_class_with_limit m, %r(String), Type::String
@@ -282,19 +305,16 @@ module ActiveRecord
282
305
  end
283
306
 
284
307
  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
291
308
 
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
309
+ if options[:with_distributed]
310
+ distributed_table_name = options.delete(:with_distributed)
311
+ sharding_key = options.delete(:sharding_key) || 'rand()'
312
+ raise 'Set a cluster' unless cluster
295
313
 
296
- def drop_table_with_distributed(table_name, **options)
297
- ["#{table_name}_distributed", table_name].each { |name| drop_table(name, **options) }
314
+ distributed_options =
315
+ "Distributed(#{cluster}, #{@config[:database]}, #{table_name}, #{sharding_key})"
316
+ create_table(distributed_table_name, **options.merge(options: distributed_options), &block)
317
+ end
298
318
  end
299
319
 
300
320
  # Drops a ClickHouse database.
@@ -312,6 +332,11 @@ module ActiveRecord
312
332
 
313
333
  def drop_table(table_name, options = {}) # :nodoc:
314
334
  do_execute apply_cluster "DROP TABLE#{' IF EXISTS' if options[:if_exists]} #{quote_table_name(table_name)}"
335
+
336
+ if options[:with_distributed]
337
+ distributed_table_name = options.delete(:with_distributed)
338
+ drop_table(distributed_table_name, **options)
339
+ end
315
340
  end
316
341
 
317
342
  def change_column(table_name, column_name, type, options = {})
@@ -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.8'
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.8
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-05-26 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