clickhouse-activerecord 0.5.7 → 0.6.2

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: 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