clickhouse-activerecord 0.5.7 → 0.6.2

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: 6533de4ab9b2415a4df22e72e7ef551d4fb58c5bdc6713ec355b85dbad1174fd
4
+ data.tar.gz: 1c0a898f494c6dc7511897617ea86c2f12aa42461cf693e28e78124bde3638c6
5
5
  SHA512:
6
- metadata.gz: 840b01cc1d5b88eee5e031fada23cfd35f7a8d948a35a767b2f5baf1596af665f28c56778e699960511004367b16ee3130be1f5bd2ee713a9a0d5053a9b58a37
7
- data.tar.gz: 4e613a19c51b362a05634da06d9528809ed09f01ebec4eac9984656156e9b877bdc4ce321e24bea63ae4e49d85609fa9dda781db60159e60b6d6ee06e8a99a3e
6
+ metadata.gz: f6c5c2eba18dce09211cc6b8e5448aaaf2c40ca3eb5cc411aa76874ad6efc00ae257880e5997256808d02771aec7947559edfca941187c219b6724f09b9488f0
7
+ data.tar.gz: 7b7e5f520c4189b4b5a1587789d0ea2ef8b74e86c077611ac18515c45105306c6e17cfd824623ab574420beeb45d205a9f301e2db9f3befd3c3b92c4d303b978
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
data/README.md CHANGED
@@ -1,7 +1,7 @@
1
1
  # Clickhouse::Activerecord
2
2
 
3
3
  A Ruby database ActiveRecord driver for ClickHouse. Support Rails >= 5.2.
4
- Support ClickHouse version from 20.9 LTS.
4
+ Support ClickHouse version from 22.0 LTS.
5
5
 
6
6
  ## Installation
7
7
 
@@ -165,7 +165,7 @@ Structure load from `db/clickhouse_structure.sql` file:
165
165
 
166
166
  ```ruby
167
167
  Action.where(url: 'http://example.com', date: Date.current).where.not(name: nil).order(created_at: :desc).limit(10)
168
- # Clickhouse Action Load (10.3ms) SELECT actions.* FROM actions WHERE actions.date = '2017-11-29' AND actions.url = 'http://example.com' AND (actions.name IS NOT NULL) ORDER BY actions.created_at DESC LIMIT 10
168
+ # Clickhouse Action Load (10.3ms) SELECT actions.* FROM actions WHERE actions.date = '2017-11-29' AND actions.url = 'http://example.com' AND (actions.name IS NOT NULL) ORDER BY actions.created_at DESC LIMIT 10
169
169
  #=> #<ActiveRecord::Relation [#<Action *** >]>
170
170
 
171
171
  Action.create(url: 'http://example.com', date: Date.yesterday)
@@ -175,6 +175,18 @@ Action.create(url: 'http://example.com', date: Date.yesterday)
175
175
  ActionView.maximum(:date)
176
176
  # Clickhouse (10.3ms) SELECT maxMerge(actions.date) FROM actions
177
177
  #=> 'Wed, 29 Nov 2017'
178
+
179
+ Action.where(date: Date.current).final.limit(10)
180
+ # Clickhouse Action Load (10.3ms) SELECT actions.* FROM actions FINAL WHERE actions.date = '2017-11-29' LIMIT 10
181
+ #=> #<ActiveRecord::Relation [#<Action *** >]>
182
+
183
+ Action.settings(optimize_read_in_order: 1).where(date: Date.current).limit(10)
184
+ # Clickhouse Action Load (10.3ms) SELECT actions.* FROM actions FINAL WHERE actions.date = '2017-11-29' LIMIT 10 SETTINGS optimize_read_in_order = 1
185
+ #=> #<ActiveRecord::Relation [#<Action *** >]>
186
+
187
+ User.joins(:actions).using(:group_id)
188
+ # Clickhouse User Load (10.3ms) SELECT users.* FROM users INNER JOIN actions USING group_id
189
+ #=> #<ActiveRecord::Relation [#<Action *** >]>
178
190
  ```
179
191
 
180
192
 
@@ -24,9 +24,8 @@ 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', '>= 5.2'
27
+ spec.add_runtime_dependency 'activerecord', '~> 7.0.0'
28
28
 
29
- spec.add_development_dependency 'bundler', '~> 1.15'
30
29
  spec.add_development_dependency 'rake', '~> 13.0'
31
30
  spec.add_development_dependency 'rspec', '~> 3.4'
32
31
  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]
@@ -23,6 +23,42 @@ module ActiveRecord
23
23
  @subtype
24
24
  end
25
25
 
26
+ def deserialize(value)
27
+ if value.is_a?(::Array)
28
+ value.map { |item| deserialize(item) }
29
+ else
30
+ return value if value.nil?
31
+ case @subtype
32
+ when :integer
33
+ value.to_i
34
+ when :datetime
35
+ ::DateTime.parse(value)
36
+ when :date
37
+ ::Date.parse(value)
38
+ else
39
+ super
40
+ end
41
+ end
42
+ end
43
+
44
+ def serialize(value)
45
+ if value.is_a?(::Array)
46
+ value.map { |item| serialize(item) }
47
+ else
48
+ return value if value.nil?
49
+ case @subtype
50
+ when :integer
51
+ value.to_i
52
+ when :datetime
53
+ DateTime.new.serialize(value)
54
+ when :date
55
+ Date.new.serialize(value)
56
+ else
57
+ super
58
+ end
59
+ end
60
+ end
61
+
26
62
  end
27
63
  end
28
64
  end
@@ -9,9 +9,8 @@ module ActiveRecord
9
9
  def serialize(value)
10
10
  value = super
11
11
  return unless value
12
- return value.strftime('%Y-%m-%d %H:%M:%S') unless value.acts_like?(:time)
13
12
 
14
- value.to_time.strftime('%Y-%m-%d %H:%M:%S')
13
+ value.strftime('%Y-%m-%d %H:%M:%S' + (@precision.present? && @precision > 0 ? ".%#{@precision}N" : ''))
15
14
  end
16
15
 
17
16
  def type_cast_from_database(value)
@@ -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)
@@ -116,7 +125,11 @@ module ActiveRecord
116
125
  end
117
126
 
118
127
  def current_database
119
- ActiveRecord::Base.connection_db_config.database
128
+ if ActiveRecord::version >= Gem::Version.new('6.1')
129
+ ActiveRecord::Base.connection_db_config.database
130
+ else
131
+ ActiveRecord::Base.connection_config[:database]
132
+ end
120
133
  end
121
134
  end
122
135
  end
@@ -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
@@ -6,8 +6,8 @@ module ActiveRecord
6
6
  module ConnectionAdapters
7
7
  module Clickhouse
8
8
  module SchemaStatements
9
- def execute(sql, name = nil)
10
- do_execute(sql, name)
9
+ def execute(sql, name = nil, settings: {})
10
+ do_execute(sql, name, settings: settings)
11
11
  end
12
12
 
13
13
  def exec_insert(sql, name, _binds, _pk = nil, _sequence_name = nil)
@@ -18,7 +18,7 @@ module ActiveRecord
18
18
 
19
19
  def exec_query(sql, name = nil, binds = [], prepare: false)
20
20
  result = do_execute(sql, name)
21
- ActiveRecord::Result.new(result['meta'].map { |m| m['name'] }, result['data'])
21
+ ActiveRecord::Result.new(result['meta'].map { |m| m['name'] }, result['data'], result['meta'].map { |m| [m['name'], type_map.lookup(m['type'])] }.to_h)
22
22
  rescue ActiveRecord::ActiveRecordError => e
23
23
  raise e
24
24
  rescue StandardError => e
@@ -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
 
@@ -105,10 +105,20 @@ module ActiveRecord
105
105
  def process_response(res)
106
106
  case res.code.to_i
107
107
  when 200
108
- res.body.presence && JSON.parse(res.body)
108
+ if res.body.to_s.include?("DB::Exception")
109
+ raise ActiveRecord::ActiveRecordError, "Response code: #{res.code}:\n#{res.body}"
110
+ else
111
+ res.body.presence && JSON.parse(res.body)
112
+ end
109
113
  else
110
- raise ActiveRecord::ActiveRecordError,
111
- "Response code: #{res.code}:\n#{res.body}"
114
+ case res.body
115
+ when /DB::Exception:.*\(UNKNOWN_DATABASE\)/
116
+ raise ActiveRecord::NoDatabaseError
117
+ when /DB::Exception:.*\(DATABASE_ALREADY_EXISTS\)/
118
+ raise ActiveRecord::DatabaseAlreadyExists
119
+ else
120
+ raise ActiveRecord::ActiveRecordError, "Response code: #{res.code}:\n#{res.body}"
121
+ end
112
122
  end
113
123
  rescue JSON::ParserError
114
124
  res.body
@@ -1,7 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'clickhouse-activerecord/arel/visitors/to_sql'
4
- require 'clickhouse-activerecord/arel/table'
3
+ require 'arel/visitors/clickhouse'
4
+ require 'arel/nodes/settings'
5
+ require 'arel/nodes/using'
5
6
  require 'clickhouse-activerecord/migration'
6
7
  require 'active_record/connection_adapters/clickhouse/oid/array'
7
8
  require 'active_record/connection_adapters/clickhouse/oid/date'
@@ -11,6 +12,7 @@ require 'active_record/connection_adapters/clickhouse/schema_definitions'
11
12
  require 'active_record/connection_adapters/clickhouse/schema_creation'
12
13
  require 'active_record/connection_adapters/clickhouse/schema_statements'
13
14
  require 'net/http'
15
+ require 'openssl'
14
16
 
15
17
  module ActiveRecord
16
18
  class Base
@@ -32,6 +34,7 @@ module ActiveRecord
32
34
  sslca: config[:sslca],
33
35
  read_timeout: config[:read_timeout],
34
36
  write_timeout: config[:write_timeout],
37
+ keep_alive_timeout: config[:keep_alive_timeout]
35
38
  }
36
39
  end
37
40
 
@@ -46,21 +49,6 @@ module ActiveRecord
46
49
  end
47
50
  end
48
51
 
49
- class Relation
50
-
51
- # Replace for only ClickhouseAdapter
52
- 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
60
- self
61
- end
62
- end
63
-
64
52
  module TypeCaster
65
53
  class Map
66
54
  def is_view
@@ -74,7 +62,9 @@ module ActiveRecord
74
62
  end
75
63
 
76
64
  module ModelSchema
77
- module ClassMethods
65
+ module ClassMethods
66
+ delegate :final, :settings, to: :all
67
+
78
68
  def is_view
79
69
  @is_view || false
80
70
  end
@@ -82,13 +72,12 @@ module ActiveRecord
82
72
  def is_view=(value)
83
73
  @is_view = value
84
74
  end
85
-
86
- def arel_table # :nodoc:
87
- @arel_table ||= ClickhouseActiverecord::Arel::Table.new(table_name, type_caster: type_caster)
88
- end
89
-
75
+ #
76
+ # def arel_table # :nodoc:
77
+ # @arel_table ||= Arel::Table.new(table_name, type_caster: type_caster)
78
+ # end
90
79
  end
91
- end
80
+ end
92
81
 
93
82
  module ConnectionAdapters
94
83
  class ClickhouseColumn < Column
@@ -104,8 +93,13 @@ module ActiveRecord
104
93
  float: { name: 'Float32' },
105
94
  decimal: { name: 'Decimal' },
106
95
  datetime: { name: 'DateTime' },
96
+ datetime64: { name: 'DateTime64' },
107
97
  date: { name: 'Date' },
108
- boolean: { name: 'UInt8' },
98
+ boolean: { name: 'Bool' },
99
+ uuid: { name: 'UUID' },
100
+
101
+ enum8: { name: 'Enum8' },
102
+ enum16: { name: 'Enum16' },
109
103
 
110
104
  int8: { name: 'Int8' },
111
105
  int16: { name: 'Int16' },
@@ -154,7 +148,7 @@ module ActiveRecord
154
148
  end
155
149
 
156
150
  def arel_visitor # :nodoc:
157
- ClickhouseActiverecord::Arel::Visitors::ToSql.new(self)
151
+ Arel::Visitors::Clickhouse.new(self)
158
152
  end
159
153
 
160
154
  def native_database_types #:nodoc:
@@ -165,59 +159,97 @@ module ActiveRecord
165
159
  !native_database_types[type].nil?
166
160
  end
167
161
 
168
- def extract_limit(sql_type) # :nodoc:
169
- case sql_type
170
- when /(Nullable)?\(?String\)?/
171
- super('String')
172
- when /(Nullable)?\(?U?Int8\)?/
173
- 1
174
- when /(Nullable)?\(?U?Int16\)?/
175
- 2
176
- when /(Nullable)?\(?U?Int32\)?/
177
- nil
178
- when /(Nullable)?\(?U?Int64\)?/
179
- 8
180
- else
181
- super
162
+ class << self
163
+ def extract_limit(sql_type) # :nodoc:
164
+ case sql_type
165
+ when /(Nullable)?\(?String\)?/
166
+ super('String')
167
+ when /(Nullable)?\(?U?Int8\)?/
168
+ 1
169
+ when /(Nullable)?\(?U?Int16\)?/
170
+ 2
171
+ when /(Nullable)?\(?U?Int32\)?/
172
+ nil
173
+ when /(Nullable)?\(?U?Int64\)?/
174
+ 8
175
+ else
176
+ super
177
+ end
178
+ end
179
+
180
+ # `extract_scale` and `extract_precision` are the same as in the Rails abstract base class,
181
+ # except this permits a space after the comma
182
+
183
+ def extract_scale(sql_type)
184
+ case sql_type
185
+ when /\((\d+)\)/ then 0
186
+ when /\((\d+)(,\s?(\d+))\)/ then $3.to_i
187
+ end
182
188
  end
189
+
190
+ def extract_precision(sql_type)
191
+ $1.to_i if sql_type =~ /\((\d+)(,\s?\d+)?\)/
192
+ end
193
+
194
+ def initialize_type_map(m) # :nodoc:
195
+ super
196
+ register_class_with_limit m, %r(String), Type::String
197
+ register_class_with_limit m, 'Date', Clickhouse::OID::Date
198
+ register_class_with_precision m, %r(datetime)i, Clickhouse::OID::DateTime
199
+
200
+ register_class_with_limit m, %r(Int8), Type::Integer
201
+ register_class_with_limit m, %r(Int16), Type::Integer
202
+ register_class_with_limit m, %r(Int32), Type::Integer
203
+ register_class_with_limit m, %r(Int64), Type::Integer
204
+ register_class_with_limit m, %r(Int128), Type::Integer
205
+ register_class_with_limit m, %r(Int256), Type::Integer
206
+
207
+ register_class_with_limit m, %r(UInt8), Type::UnsignedInteger
208
+ register_class_with_limit m, %r(UInt16), Type::UnsignedInteger
209
+ register_class_with_limit m, %r(UInt32), Type::UnsignedInteger
210
+ register_class_with_limit m, %r(UInt64), Type::UnsignedInteger
211
+ #register_class_with_limit m, %r(UInt128), Type::UnsignedInteger #not implemnted in clickhouse
212
+ register_class_with_limit m, %r(UInt256), Type::UnsignedInteger
213
+ # register_class_with_limit m, %r(Array), Clickhouse::OID::Array
214
+ m.register_type(%r(Array)) do |sql_type|
215
+ Clickhouse::OID::Array.new(sql_type)
216
+ end
217
+ end
218
+ end
219
+
220
+ # In Rails 7 used constant TYPE_MAP, we need redefine method
221
+ def type_map
222
+ @type_map ||= Type::TypeMap.new.tap { |m| ClickhouseAdapter.initialize_type_map(m) }
183
223
  end
184
224
 
185
- def initialize_type_map(m) # :nodoc:
186
- super
187
- register_class_with_limit m, %r(String), Type::String
188
- register_class_with_limit m, 'Date', Clickhouse::OID::Date
189
- register_class_with_limit m, 'DateTime', Clickhouse::OID::DateTime
190
-
191
- register_class_with_limit m, %r(Int8), Type::Integer
192
- register_class_with_limit m, %r(Int16), Type::Integer
193
- register_class_with_limit m, %r(Int32), Type::Integer
194
- register_class_with_limit m, %r(Int64), Type::Integer
195
- register_class_with_limit m, %r(Int128), Type::Integer
196
- register_class_with_limit m, %r(Int256), Type::Integer
197
-
198
- register_class_with_limit m, %r(UInt8), Type::UnsignedInteger
199
- register_class_with_limit m, %r(UInt16), Type::UnsignedInteger
200
- register_class_with_limit m, %r(UInt32), Type::UnsignedInteger
201
- register_class_with_limit m, %r(UInt64), Type::UnsignedInteger
202
- #register_class_with_limit m, %r(UInt128), Type::UnsignedInteger #not implemnted in clickhouse
203
- register_class_with_limit m, %r(UInt256), Type::UnsignedInteger
204
- # register_class_with_limit m, %r(Array), Clickhouse::OID::Array
205
- m.register_type(%r(Array)) do |sql_type|
206
- Clickhouse::OID::Array.new(sql_type)
225
+ def quote(value)
226
+ case value
227
+ when Array
228
+ '[' + value.map { |v| quote(v) }.join(', ') + ']'
229
+ else
230
+ super
207
231
  end
208
232
  end
209
233
 
210
234
  # Quoting time without microseconds
211
235
  def quoted_date(value)
212
236
  if value.acts_like?(:time)
213
- zone_conversion_method = ActiveRecord::Base.default_timezone == :utc ? :getutc : :getlocal
237
+ if ActiveRecord::version >= Gem::Version.new('7')
238
+ zone_conversion_method = ActiveRecord.default_timezone == :utc ? :getutc : :getlocal
239
+ else
240
+ zone_conversion_method = ActiveRecord::Base.default_timezone == :utc ? :getutc : :getlocal
241
+ end
214
242
 
215
243
  if value.respond_to?(zone_conversion_method)
216
244
  value = value.send(zone_conversion_method)
217
245
  end
218
246
  end
219
247
 
220
- value.to_s(:db)
248
+ if ActiveRecord::version >= Gem::Version.new('7')
249
+ value.to_fs(:db)
250
+ else
251
+ value.to_s(:db)
252
+ end
221
253
  end
222
254
 
223
255
  def column_name_for_operation(operation, node) # :nodoc:
@@ -269,32 +301,30 @@ module ActiveRecord
269
301
  drop_table(table_name, options.merge(if_exists: true))
270
302
  end
271
303
 
272
- execute schema_creation.accept td
304
+ do_execute(schema_creation.accept(td), format: nil)
273
305
  end
274
306
 
275
307
  def create_table(table_name, **options, &block)
276
308
  options = apply_replica(table_name, options)
277
309
  td = create_table_definition(apply_cluster(table_name), **options)
278
310
  block.call td if block_given?
311
+ td.column(:id, options[:id], null: false) if options[:id].present? && td[:id].blank?
279
312
 
280
313
  if options[:force]
281
314
  drop_table(table_name, options.merge(if_exists: true))
282
315
  end
283
316
 
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
317
+ do_execute(schema_creation.accept(td), format: nil)
291
318
 
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
319
+ if options[:with_distributed]
320
+ distributed_table_name = options.delete(:with_distributed)
321
+ sharding_key = options.delete(:sharding_key) || 'rand()'
322
+ raise 'Set a cluster' unless cluster
295
323
 
296
- def drop_table_with_distributed(table_name, **options)
297
- ["#{table_name}_distributed", table_name].each { |name| drop_table(name, **options) }
324
+ distributed_options =
325
+ "Distributed(#{cluster}, #{@config[:database]}, #{table_name}, #{sharding_key})"
326
+ create_table(distributed_table_name, **options.merge(options: distributed_options), &block)
327
+ end
298
328
  end
299
329
 
300
330
  # Drops a ClickHouse database.
@@ -311,11 +341,36 @@ module ActiveRecord
311
341
  end
312
342
 
313
343
  def drop_table(table_name, options = {}) # :nodoc:
314
- do_execute apply_cluster "DROP TABLE#{' IF EXISTS' if options[:if_exists]} #{quote_table_name(table_name)}"
344
+ query = "DROP TABLE"
345
+ query = "#{query} IF EXISTS " if options[:if_exists]
346
+ query = "#{query} #{quote_table_name(table_name)}"
347
+ query = apply_cluster(query)
348
+ query = "#{query} SYNC" if options[:sync]
349
+
350
+ do_execute(query)
351
+
352
+ if options[:with_distributed]
353
+ distributed_table_name = options.delete(:with_distributed)
354
+ drop_table(distributed_table_name, **options)
355
+ end
356
+ end
357
+
358
+ def add_column(table_name, column_name, type, **options)
359
+ return if options[:if_not_exists] == true && column_exists?(table_name, column_name, type)
360
+
361
+ at = create_alter_table table_name
362
+ at.add_column(column_name, type, **options)
363
+ execute(schema_creation.accept(at), nil, settings: {wait_end_of_query: 1, send_progress_in_http_headers: 1})
364
+ end
365
+
366
+ def remove_column(table_name, column_name, type = nil, **options)
367
+ return if options[:if_exists] == true && !column_exists?(table_name, column_name)
368
+
369
+ execute("ALTER TABLE #{quote_table_name(table_name)} #{remove_column_for_alter(table_name, column_name, type, **options)}", nil, settings: {wait_end_of_query: 1, send_progress_in_http_headers: 1})
315
370
  end
316
371
 
317
372
  def change_column(table_name, column_name, type, options = {})
318
- result = do_execute "ALTER TABLE #{quote_table_name(table_name)} #{change_column_for_alter(table_name, column_name, type, options)}"
373
+ result = do_execute("ALTER TABLE #{quote_table_name(table_name)} #{change_column_for_alter(table_name, column_name, type, options)}", nil, settings: {wait_end_of_query: 1, send_progress_in_http_headers: 1})
319
374
  raise "Error parse json response: #{result}" if result.presence && !result.is_a?(Hash)
320
375
  end
321
376
 
@@ -351,12 +406,18 @@ module ActiveRecord
351
406
 
352
407
  def database_engine_atomic?
353
408
  current_database_engine = "select engine from system.databases where name = '#{@config[:database]}'"
354
- res = ActiveRecord::Base.connection.select_one(current_database_engine)
409
+ res = select_one(current_database_engine)
355
410
  res['engine'] == 'Atomic' if res
356
411
  end
357
412
 
358
413
  def apply_cluster(sql)
359
- cluster ? "#{sql} ON CLUSTER #{cluster}" : sql
414
+ if cluster
415
+ normalized_cluster_name = cluster.start_with?('{') ? "'#{cluster}'" : cluster
416
+
417
+ "#{sql} ON CLUSTER #{normalized_cluster_name}"
418
+ else
419
+ sql
420
+ end
360
421
  end
361
422
 
362
423
  def supports_insert_on_duplicate_skip?
@@ -393,6 +454,9 @@ module ActiveRecord
393
454
  @connection.read_timeout = @connection_parameters[:read_timeout] if @connection_parameters[:read_timeout]
394
455
  @connection.write_timeout = @connection_parameters[:write_timeout] if @connection_parameters[:write_timeout]
395
456
 
457
+ # Use clickhouse default keep_alive_timeout value of 10, rather than Net::HTTP's default of 2
458
+ @connection.keep_alive_timeout = @connection_parameters[:keep_alive_timeout] || 10
459
+
396
460
  @connection
397
461
  end
398
462
 
@@ -0,0 +1,11 @@
1
+ module Arel # :nodoc: all
2
+ module Nodes
3
+ class Settings < Arel::Nodes::Unary
4
+ def initialize(expr)
5
+ raise ArgumentError, 'Settings must be a Hash' unless expr.is_a?(Hash)
6
+
7
+ super
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,6 @@
1
+ module Arel # :nodoc: all
2
+ module Nodes
3
+ class Using < Arel::Nodes::Unary
4
+ end
5
+ end
6
+ end
@@ -0,0 +1,60 @@
1
+ require 'arel/visitors/to_sql'
2
+
3
+ module Arel
4
+ module Visitors
5
+ class Clickhouse < ::Arel::Visitors::ToSql
6
+
7
+ def aggregate(name, o, collector)
8
+ # replacing function name for materialized view
9
+ if o.expressions.first && o.expressions.first != '*' && !o.expressions.first.is_a?(String) && o.expressions.first.relation&.is_view
10
+ super("#{name.downcase}Merge", o, collector)
11
+ else
12
+ super
13
+ end
14
+ end
15
+
16
+ def visit_Arel_Table o, collector
17
+ collector = super
18
+ collector << ' FINAL ' if o.final
19
+ collector
20
+ end
21
+
22
+ def visit_Arel_Nodes_SelectOptions(o, collector)
23
+ maybe_visit o.settings, super
24
+ end
25
+
26
+ def visit_Arel_Nodes_Settings(o, collector)
27
+ return collector if o.expr.empty?
28
+
29
+ collector << "SETTINGS "
30
+ o.expr.each_with_index do |(key, value), i|
31
+ collector << ", " if i > 0
32
+ collector << key.to_s.gsub(/\W+/, "")
33
+ collector << " = "
34
+ collector << sanitize_as_setting_value(value)
35
+ end
36
+ collector
37
+ end
38
+
39
+ def visit_Arel_Nodes_Using o, collector
40
+ collector << "USING "
41
+ visit o.expr, collector
42
+ collector
43
+ end
44
+
45
+ def sanitize_as_setting_value(value)
46
+ if value == :default
47
+ 'DEFAULT'
48
+ else
49
+ quote(value)
50
+ end
51
+ end
52
+
53
+ def sanitize_as_setting_name(value)
54
+ return value if Arel::Nodes::SqlLiteral === value
55
+ @connection.sanitize_as_setting_name(value)
56
+ end
57
+
58
+ end
59
+ end
60
+ end
@@ -10,16 +10,17 @@ module ClickhouseActiverecord
10
10
 
11
11
  version_options = connection.internal_string_options_for_primary_key
12
12
  table_options = {
13
- id: false, options: 'ReplacingMergeTree(ver) PARTITION BY version ORDER BY (version)', if_not_exists: true
13
+ id: false, options: 'ReplacingMergeTree(ver) 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()' }
@@ -27,30 +28,43 @@ module ClickhouseActiverecord
27
28
  end
28
29
 
29
30
  def all_versions
30
- from("#{table_name} FINAL").where(active: 1).order(:version).pluck(:version)
31
+ final.where(active: 1).order(:version).pluck(:version)
31
32
  end
32
33
  end
33
34
  end
34
35
 
35
36
  class InternalMetadata < ::ActiveRecord::InternalMetadata
36
37
  class << self
38
+
39
+ def []=(key, value)
40
+ row = final.find_by(key: key)
41
+ if row.nil? || row.value != value
42
+ create!(key: key, value: value)
43
+ end
44
+ end
45
+
46
+ def [](key)
47
+ final.where(key: key).pluck(:value).first
48
+ end
49
+
37
50
  def create_table
38
51
  return if table_exists?
39
52
 
40
53
  key_options = connection.internal_string_options_for_primary_key
41
54
  table_options = {
42
55
  id: false,
43
- options: connection.adapter_name.downcase == 'clickhouse' ? 'MergeTree() PARTITION BY toDate(created_at) ORDER BY (created_at)' : '',
56
+ options: connection.adapter_name.downcase == 'clickhouse' ? 'ReplacingMergeTree(created_at) PARTITION BY key ORDER BY key' : '',
44
57
  if_not_exists: true
45
58
  }
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'
59
+ full_config = connection.instance_variable_get(:@full_config) || {}
60
+
61
+ if full_config[:distributed_service_tables]
62
+ table_options.merge!(with_distributed: table_name, sharding_key: 'cityHash64(created_at)')
63
+
64
+ distributed_suffix = "_#{full_config[:distributed_service_tables_suffix] || 'distributed'}"
51
65
  end
52
66
 
53
- connection.public_send(table_creation_method, table_name, **table_options) do |t|
67
+ connection.create_table(table_name + distributed_suffix.to_s, **table_options) do |t|
54
68
  t.string :key, **key_options
55
69
  t.string :value
56
70
  t.timestamps
@@ -120,5 +134,13 @@ module ClickhouseActiverecord
120
134
  super
121
135
  end
122
136
  end
137
+
138
+ private
139
+
140
+ def record_environment
141
+ return if down?
142
+ ClickhouseActiverecord::InternalMetadata[:environment] = ActiveRecord::Base.connection.migration_context.current_environment
143
+ end
144
+
123
145
  end
124
146
  end
@@ -4,6 +4,12 @@ module ClickhouseActiverecord
4
4
  require 'rails'
5
5
 
6
6
  class Railtie < Rails::Railtie
7
+ initializer "clickhouse.load" do
8
+ ActiveSupport.on_load :active_record do
9
+ ClickhouseActiverecord.load
10
+ end
11
+ end
12
+
7
13
  rake_tasks { load 'tasks/clickhouse.rake' }
8
14
  end
9
15
  end
@@ -2,7 +2,6 @@
2
2
 
3
3
  module ClickhouseActiverecord
4
4
  class Tasks
5
-
6
5
  delegate :connection, :establish_connection, :clear_active_connections!, to: ActiveRecord::Base
7
6
 
8
7
  def initialize(configuration)
@@ -11,10 +10,10 @@ module ClickhouseActiverecord
11
10
 
12
11
  def create
13
12
  establish_master_connection
14
- connection.create_database @configuration["database"]
13
+ connection.create_database @configuration['database']
15
14
  rescue ActiveRecord::StatementInvalid => e
16
15
  if e.cause.to_s.include?('already exists')
17
- raise ActiveRecord::Tasks::DatabaseAlreadyExists
16
+ raise ActiveRecord::DatabaseAlreadyExists
18
17
  else
19
18
  raise
20
19
  end
@@ -22,7 +21,7 @@ module ClickhouseActiverecord
22
21
 
23
22
  def drop
24
23
  establish_master_connection
25
- connection.drop_database @configuration["database"]
24
+ connection.drop_database @configuration['database']
26
25
  end
27
26
 
28
27
  def purge
@@ -1,3 +1,3 @@
1
1
  module ClickhouseActiverecord
2
- VERSION = '0.5.7'
2
+ VERSION = '0.6.2'
3
3
  end
@@ -2,6 +2,12 @@
2
2
 
3
3
  require 'active_record/connection_adapters/clickhouse_adapter'
4
4
 
5
+ require 'core_extensions/active_record/relation'
6
+
7
+ require 'core_extensions/arel/nodes/select_statement'
8
+ require 'core_extensions/arel/select_manager'
9
+ require 'core_extensions/arel/table'
10
+
5
11
  require_relative '../core_extensions/active_record/migration/command_recorder'
6
12
  ActiveRecord::Migration::CommandRecorder.include CoreExtensions::ActiveRecord::Migration::CommandRecorder
7
13
 
@@ -14,5 +20,11 @@ if defined?(Rails::Railtie)
14
20
  end
15
21
 
16
22
  module ClickhouseActiverecord
23
+ def self.load
24
+ ActiveRecord::Relation.prepend(CoreExtensions::ActiveRecord::Relation)
17
25
 
26
+ Arel::Nodes::SelectStatement.prepend(CoreExtensions::Arel::Nodes::SelectStatement)
27
+ Arel::SelectManager.prepend(CoreExtensions::Arel::SelectManager)
28
+ Arel::Table.prepend(CoreExtensions::Arel::Table)
29
+ end
18
30
  end
@@ -0,0 +1,44 @@
1
+ module CoreExtensions
2
+ module ActiveRecord
3
+ module Relation
4
+ def reverse_order!
5
+ return super unless connection.is_a?(::ActiveRecord::ConnectionAdapters::ClickhouseAdapter)
6
+
7
+ orders = order_values.uniq.reject(&:blank?)
8
+ return super unless orders.empty? && !primary_key
9
+
10
+ self.order_values = (column_names & %w[date created_at]).map { |c| arel_table[c].desc }
11
+ self
12
+ end
13
+
14
+ # @param [Hash] opts
15
+ def settings(**opts)
16
+ check_command('SETTINGS')
17
+ @values[:settings] = (@values[:settings] || {}).merge opts
18
+ self
19
+ end
20
+
21
+ # @param [Boolean] final
22
+ def final(final = true)
23
+ check_command('FINAL')
24
+ @table = @table.dup
25
+ @table.final = final
26
+ self
27
+ end
28
+
29
+ private
30
+
31
+ def check_command(cmd)
32
+ raise ::ActiveRecord::ActiveRecordError, cmd + ' is a ClickHouse specific query clause' unless connection.is_a?(::ActiveRecord::ConnectionAdapters::ClickhouseAdapter)
33
+ end
34
+
35
+ def build_arel(aliases = nil)
36
+ arel = super
37
+
38
+ arel.settings(@values[:settings]) if @values[:settings].present?
39
+
40
+ arel
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,18 @@
1
+ module CoreExtensions
2
+ module Arel # :nodoc: all
3
+ module Nodes
4
+ module SelectStatement
5
+ attr_accessor :settings
6
+
7
+ def initialize(relation = nil)
8
+ super
9
+ @settings = nil
10
+ end
11
+
12
+ def eql?(other)
13
+ super && settings == other.settings
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,17 @@
1
+ module CoreExtensions
2
+ module Arel
3
+ module SelectManager
4
+
5
+ # @param [Hash] values
6
+ def settings(values)
7
+ @ast.settings = ::Arel::Nodes::Settings.new(values)
8
+ self
9
+ end
10
+
11
+ def using(*exprs)
12
+ @ctx.source.right.last.right = ::Arel::Nodes::Using.new(::Arel.sql(exprs.join(',')))
13
+ self
14
+ end
15
+ end
16
+ end
17
+ end
@@ -1,6 +1,8 @@
1
- module ClickhouseActiverecord
1
+ module CoreExtensions
2
2
  module Arel
3
- class Table < ::Arel::Table
3
+ module Table
4
+ attr_accessor :final
5
+
4
6
  def is_view
5
7
  type_caster.is_view
6
8
  end
@@ -1,68 +1,70 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  namespace :clickhouse do
4
-
5
4
  task prepare_schema_migration_table: :environment do
6
- ClickhouseActiverecord::SchemaMigration.create_table unless ENV['simple'] || ARGV.map{|a| a.include?('--simple') ? true : nil}.compact.any?
5
+ ClickhouseActiverecord::SchemaMigration.create_table unless ENV['simple'] || ARGV.any? { |a| a.include?('--simple') }
7
6
  end
8
7
 
9
8
  task prepare_internal_metadata_table: :environment do
10
- ClickhouseActiverecord::InternalMetadata.create_table unless ENV['simple'] || ARGV.map{|a| a.include?('--simple') ? true : nil}.compact.any?
9
+ ClickhouseActiverecord::InternalMetadata.create_table unless ENV['simple'] || ARGV.any? { |a| a.include?('--simple') }
11
10
  end
12
11
 
13
12
  task load_config: :environment do
14
- ENV['SCHEMA'] = "db/clickhouse_schema.rb"
15
- ActiveRecord::Migrator.migrations_paths = ["db/migrate_clickhouse"]
16
- ActiveRecord::Base.establish_connection(:"#{Rails.env}_clickhouse")
13
+ ENV['SCHEMA'] = 'db/clickhouse_schema.rb'
14
+ ActiveRecord::Migrator.migrations_paths = %w[db/migrate_clickhouse]
15
+ ActiveRecord::Base.establish_connection(:clickhouse)
17
16
  end
18
17
 
19
18
  namespace :schema do
20
-
21
- # todo not testing
19
+ # TODO: not testing
22
20
  desc 'Load database schema'
23
- task load: [:load_config, :prepare_internal_metadata_table] do |t, args|
24
- simple = ENV['simple'] || ARGV.map{|a| a.include?('--simple') ? true : nil}.compact.any? ? '_simple' : nil
21
+ task load: %i[load_config prepare_internal_metadata_table] do
22
+ simple = ENV['simple'] || ARGV.any? { |a| a.include?('--simple') } ? '_simple' : nil
25
23
  ClickhouseActiverecord::SchemaMigration.drop_table
26
- load("#{Rails.root}/db/clickhouse_schema#{simple}.rb")
24
+ load(Rails.root.join("db/clickhouse_schema#{simple}.rb"))
27
25
  end
28
26
 
29
27
  desc 'Dump database schema'
30
- task dump: :environment do |t, args|
31
- simple = ENV['simple'] || args[:simple] || ARGV.map{|a| a.include?('--simple') ? true : nil}.compact.any? ? '_simple' : nil
32
- filename = "#{Rails.root}/db/clickhouse_schema#{simple}.rb"
28
+ task dump: :environment do |_, args|
29
+ simple = ENV['simple'] || args[:simple] || ARGV.any? { |a| a.include?('--simple') } ? '_simple' : nil
30
+ filename = Rails.root.join("db/clickhouse_schema#{simple}.rb")
33
31
  File.open(filename, 'w:utf-8') do |file|
34
- ActiveRecord::Base.establish_connection(:"#{Rails.env}_clickhouse")
35
- ClickhouseActiverecord::SchemaDumper.dump(ActiveRecord::Base.connection, file, ActiveRecord::Base, !!simple)
32
+ ActiveRecord::Base.establish_connection(:clickhouse)
33
+ ClickhouseActiverecord::SchemaDumper.dump(ActiveRecord::Base.connection, file, ActiveRecord::Base, simple.present?)
36
34
  end
37
35
  end
38
-
39
36
  end
40
37
 
41
38
  namespace :structure do
42
39
  desc 'Load database structure'
43
40
  task load: [:load_config, 'db:check_protected_environments'] do
44
- ClickhouseActiverecord::Tasks.new(ActiveRecord::Base.configurations["#{Rails.env}_clickhouse"]).structure_load("#{Rails.root}/db/clickhouse_structure.sql")
41
+ config = ActiveRecord::Base.configurations.configs_for(env_name: Rails.env, name: 'clickhouse')
42
+ ClickhouseActiverecord::Tasks.new(config).structure_load(Rails.root.join('db/clickhouse_structure.sql'))
45
43
  end
46
44
 
47
45
  desc 'Dump database structure'
48
46
  task dump: [:load_config, 'db:check_protected_environments'] do
49
- ClickhouseActiverecord::Tasks.new(ActiveRecord::Base.configurations["#{Rails.env}_clickhouse"]).structure_dump("#{Rails.root}/db/clickhouse_structure.sql")
47
+ config = ActiveRecord::Base.configurations.configs_for(env_name: Rails.env, name: 'clickhouse')
48
+ ClickhouseActiverecord::Tasks.new(config).structure_dump(Rails.root.join('db/clickhouse_structure.sql'))
50
49
  end
51
50
  end
52
51
 
53
52
  desc 'Creates the database from DATABASE_URL or config/database.yml'
54
53
  task create: [:load_config] do
55
- ActiveRecord::Tasks::DatabaseTasks.create(ActiveRecord::Base.configurations["#{Rails.env}_clickhouse"])
54
+ config = ActiveRecord::Base.configurations.configs_for(env_name: Rails.env, name: 'clickhouse')
55
+ ActiveRecord::Tasks::DatabaseTasks.create(config)
56
56
  end
57
57
 
58
58
  desc 'Drops the database from DATABASE_URL or config/database.yml'
59
59
  task drop: [:load_config, 'db:check_protected_environments'] do
60
- ActiveRecord::Tasks::DatabaseTasks.drop(ActiveRecord::Base.configurations["#{Rails.env}_clickhouse"])
60
+ config = ActiveRecord::Base.configurations.configs_for(env_name: Rails.env, name: 'clickhouse')
61
+ ActiveRecord::Tasks::DatabaseTasks.drop(config)
61
62
  end
62
63
 
63
64
  desc 'Empty the database from DATABASE_URL or config/database.yml'
64
65
  task purge: [:load_config, 'db:check_protected_environments'] do
65
- ActiveRecord::Tasks::DatabaseTasks.purge(ActiveRecord::Base.configurations["#{Rails.env}_clickhouse"])
66
+ config = ActiveRecord::Base.configurations.configs_for(env_name: Rails.env, name: 'clickhouse')
67
+ ActiveRecord::Tasks::DatabaseTasks.purge(config)
66
68
  end
67
69
 
68
70
  # desc 'Resets your database using your migrations for the current environment'
@@ -72,7 +74,7 @@ namespace :clickhouse do
72
74
  end
73
75
 
74
76
  desc 'Migrate the clickhouse database'
75
- task migrate: [:load_config, :prepare_schema_migration_table, :prepare_internal_metadata_table] do
77
+ task migrate: %i[load_config prepare_schema_migration_table prepare_internal_metadata_table] do
76
78
  Rake::Task['db:migrate'].execute
77
79
  if File.exists? "#{Rails.root}/db/clickhouse_schema_simple.rb"
78
80
  Rake::Task['clickhouse:schema:dump'].execute(simple: true)
@@ -80,7 +82,7 @@ namespace :clickhouse do
80
82
  end
81
83
 
82
84
  desc 'Rollback the clickhouse database'
83
- task rollback: [:load_config, :prepare_schema_migration_table, :prepare_internal_metadata_table] do
85
+ task rollback: %i[load_config prepare_schema_migration_table prepare_internal_metadata_table] do
84
86
  Rake::Task['db:rollback'].execute
85
87
  end
86
88
  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.6.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sergey Odintsov
8
- autorequire:
8
+ autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2021-10-25 00:00:00.000000000 Z
11
+ date: 2023-11-29 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -26,32 +26,18 @@ dependencies:
26
26
  version: 1.13.4
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: activerecord
29
- requirement: !ruby/object:Gem::Requirement
30
- requirements:
31
- - - ">="
32
- - !ruby/object:Gem::Version
33
- version: '5.2'
34
- type: :runtime
35
- prerelease: false
36
- version_requirements: !ruby/object:Gem::Requirement
37
- requirements:
38
- - - ">="
39
- - !ruby/object:Gem::Version
40
- version: '5.2'
41
- - !ruby/object:Gem::Dependency
42
- name: bundler
43
29
  requirement: !ruby/object:Gem::Requirement
44
30
  requirements:
45
31
  - - "~>"
46
32
  - !ruby/object:Gem::Version
47
- version: '1.15'
48
- type: :development
33
+ version: 7.0.0
34
+ type: :runtime
49
35
  prerelease: false
50
36
  version_requirements: !ruby/object:Gem::Requirement
51
37
  requirements:
52
38
  - - "~>"
53
39
  - !ruby/object:Gem::Version
54
- version: '1.15'
40
+ version: 7.0.0
55
41
  - !ruby/object:Gem::Dependency
56
42
  name: rake
57
43
  requirement: !ruby/object:Gem::Requirement
@@ -121,22 +107,27 @@ files:
121
107
  - lib/active_record/connection_adapters/clickhouse/schema_definitions.rb
122
108
  - lib/active_record/connection_adapters/clickhouse/schema_statements.rb
123
109
  - lib/active_record/connection_adapters/clickhouse_adapter.rb
110
+ - lib/arel/nodes/settings.rb
111
+ - lib/arel/nodes/using.rb
112
+ - lib/arel/visitors/clickhouse.rb
124
113
  - lib/clickhouse-activerecord.rb
125
- - lib/clickhouse-activerecord/arel/table.rb
126
- - lib/clickhouse-activerecord/arel/visitors/to_sql.rb
127
114
  - lib/clickhouse-activerecord/migration.rb
128
115
  - lib/clickhouse-activerecord/railtie.rb
129
116
  - lib/clickhouse-activerecord/schema.rb
130
117
  - lib/clickhouse-activerecord/schema_dumper.rb
131
118
  - lib/clickhouse-activerecord/tasks.rb
132
119
  - lib/clickhouse-activerecord/version.rb
120
+ - lib/core_extensions/active_record/relation.rb
121
+ - lib/core_extensions/arel/nodes/select_statement.rb
122
+ - lib/core_extensions/arel/select_manager.rb
123
+ - lib/core_extensions/arel/table.rb
133
124
  - lib/generators/clickhouse_migration_generator.rb
134
125
  - lib/tasks/clickhouse.rake
135
126
  homepage: https://github.com/pnixx/clickhouse-activerecord
136
127
  licenses:
137
128
  - MIT
138
129
  metadata: {}
139
- post_install_message:
130
+ post_install_message:
140
131
  rdoc_options: []
141
132
  require_paths:
142
133
  - lib
@@ -151,8 +142,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
151
142
  - !ruby/object:Gem::Version
152
143
  version: '0'
153
144
  requirements: []
154
- rubygems_version: 3.0.3
155
- signing_key:
145
+ rubygems_version: 3.1.6
146
+ signing_key:
156
147
  specification_version: 4
157
148
  summary: ClickHouse ActiveRecord
158
149
  test_files: []
@@ -1,20 +0,0 @@
1
- require 'arel/visitors/to_sql'
2
-
3
- module ClickhouseActiverecord
4
- module Arel
5
- module Visitors
6
- class ToSql < ::Arel::Visitors::ToSql
7
-
8
- def aggregate(name, o, collector)
9
- # replacing function name for materialized view
10
- if o.expressions.first && o.expressions.first != '*' && !o.expressions.first.is_a?(String) && o.expressions.first.relation&.is_view
11
- super("#{name.downcase}Merge", o, collector)
12
- else
13
- super
14
- end
15
- end
16
-
17
- end
18
- end
19
- end
20
- end