clickhouse-activerecord 1.0.2 → 1.0.4
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 +4 -4
- data/README.md +3 -2
- data/lib/active_record/connection_adapters/clickhouse/oid/uuid.rb +36 -0
- data/lib/active_record/connection_adapters/clickhouse/schema_statements.rb +12 -2
- data/lib/active_record/connection_adapters/clickhouse_adapter.rb +19 -28
- data/lib/arel/nodes/final.rb +7 -0
- data/lib/arel/visitors/clickhouse.rb +16 -6
- data/lib/clickhouse-activerecord/tasks.rb +2 -2
- data/lib/clickhouse-activerecord/version.rb +1 -1
- data/lib/clickhouse-activerecord.rb +6 -0
- data/lib/core_extensions/active_record/internal_metadata.rb +46 -0
- data/lib/core_extensions/active_record/relation.rb +5 -7
- data/lib/core_extensions/active_record/schema_migration.rb +48 -0
- data/lib/core_extensions/arel/nodes/select_core.rb +19 -0
- data/lib/core_extensions/arel/select_manager.rb +5 -0
- data/lib/core_extensions/arel/table.rb +0 -2
- data/lib/tasks/clickhouse.rake +4 -3
- metadata +7 -3
- data/lib/clickhouse-activerecord/migration.rb +0 -103
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 194430a00c6f085ec0bf6b1cf18911c06b69ff2e87ef18fc50183b1d7d1f20c7
|
4
|
+
data.tar.gz: 54e418ac9c841e6852e7e87e1ea169335f0c0940537eda3f95d645f3e63ed81e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9d5a90636c9eaa390ce1b75f65581061312dd598fbaf64e0402a40d3e2a1ab6b8079a0ae438ac86e1b03d4df3a1d1ab6308db961cf988320a39b13cf65014cc5
|
7
|
+
data.tar.gz: d961eaf6d2d5798cbf220dc7249330c91b43fe86a68d64ff0371bb0a0f7cba754dd5e95fb1fb666fadcbd74fe71fd89f6ea5b2d85a6d478e7c624e541f00b768
|
data/README.md
CHANGED
@@ -124,11 +124,11 @@ If you using multiple databases, for example: PostgreSQL, Clickhouse.
|
|
124
124
|
|
125
125
|
Schema dump to `db/clickhouse_schema.rb` file:
|
126
126
|
|
127
|
-
$ rake
|
127
|
+
$ rake db:schema:dump:clickhouse
|
128
128
|
|
129
129
|
Schema load from `db/clickhouse_schema.rb` file:
|
130
130
|
|
131
|
-
$ rake
|
131
|
+
$ rake db:schema:load:clickhouse
|
132
132
|
|
133
133
|
For export schema to PostgreSQL, you need use:
|
134
134
|
|
@@ -238,6 +238,7 @@ Donations to this project are going directly to [PNixx](https://github.com/PNixx
|
|
238
238
|
* BTC address: `1H3rhpf7WEF5JmMZ3PVFMQc7Hm29THgUfN`
|
239
239
|
* ETH address: `0x6F094365A70fe7836A633d2eE80A1FA9758234d5`
|
240
240
|
* XMR address: `42gP71qLB5M43RuDnrQ3vSJFFxis9Kw9VMURhpx9NLQRRwNvaZRjm2TFojAMC8Fk1BQhZNKyWhoyJSn5Ak9kppgZPjE17Zh`
|
241
|
+
* TON address: `UQBt0-s1igIpJoEup0B1yAUkZ56rzbpruuAjNhQ26MVCaNlC`
|
241
242
|
|
242
243
|
## Development
|
243
244
|
|
@@ -0,0 +1,36 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActiveRecord
|
4
|
+
module ConnectionAdapters
|
5
|
+
module Clickhouse
|
6
|
+
module OID # :nodoc:
|
7
|
+
class Uuid < Type::Value # :nodoc:
|
8
|
+
ACCEPTABLE_UUID = %r{\A(\{)?([a-fA-F0-9]{4}-?){8}(?(1)\}|)\z}
|
9
|
+
|
10
|
+
alias :serialize :deserialize
|
11
|
+
|
12
|
+
def type
|
13
|
+
:uuid
|
14
|
+
end
|
15
|
+
|
16
|
+
def changed?(old_value, new_value, _)
|
17
|
+
old_value.class != new_value.class ||
|
18
|
+
new_value && old_value.casecmp(new_value) != 0
|
19
|
+
end
|
20
|
+
|
21
|
+
def changed_in_place?(raw_old_value, new_value)
|
22
|
+
raw_old_value.class != new_value.class ||
|
23
|
+
new_value && raw_old_value.casecmp(new_value) != 0
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
def cast_value(value)
|
29
|
+
casted = value.to_s
|
30
|
+
casted if casted.match?(ACCEPTABLE_UUID)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -64,7 +64,7 @@ module ActiveRecord
|
|
64
64
|
|
65
65
|
def do_system_execute(sql, name = nil)
|
66
66
|
log_with_debug(sql, "#{adapter_name} #{name}") do
|
67
|
-
res = @connection.post("/?#{@
|
67
|
+
res = @connection.post("/?#{@connection_config.to_param}", "#{sql} FORMAT JSONCompact", 'User-Agent' => "Clickhouse ActiveRecord #{ClickhouseActiverecord::VERSION}")
|
68
68
|
|
69
69
|
process_response(res)
|
70
70
|
end
|
@@ -73,7 +73,7 @@ module ActiveRecord
|
|
73
73
|
def do_execute(sql, name = nil, format: 'JSONCompact', settings: {})
|
74
74
|
log(sql, "#{adapter_name} #{name}") do
|
75
75
|
formatted_sql = apply_format(sql, format)
|
76
|
-
request_params = @
|
76
|
+
request_params = @connection_config || {}
|
77
77
|
res = @connection.post("/?#{request_params.merge(settings).to_param}", formatted_sql, 'User-Agent' => "Clickhouse ActiveRecord #{ClickhouseActiverecord::VERSION}")
|
78
78
|
|
79
79
|
process_response(res)
|
@@ -100,6 +100,16 @@ module ActiveRecord
|
|
100
100
|
end
|
101
101
|
end
|
102
102
|
|
103
|
+
# Fix insert_all method
|
104
|
+
# https://github.com/PNixx/clickhouse-activerecord/issues/71#issuecomment-1923244983
|
105
|
+
def with_yaml_fallback(value) # :nodoc:
|
106
|
+
if value.is_a?(Array)
|
107
|
+
value
|
108
|
+
else
|
109
|
+
super
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
103
113
|
private
|
104
114
|
|
105
115
|
def apply_format(sql, format)
|
@@ -1,13 +1,14 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require 'arel/visitors/clickhouse'
|
4
|
+
require 'arel/nodes/final'
|
4
5
|
require 'arel/nodes/settings'
|
5
6
|
require 'arel/nodes/using'
|
6
|
-
require 'clickhouse-activerecord/migration'
|
7
7
|
require 'active_record/connection_adapters/clickhouse/oid/array'
|
8
8
|
require 'active_record/connection_adapters/clickhouse/oid/date'
|
9
9
|
require 'active_record/connection_adapters/clickhouse/oid/date_time'
|
10
10
|
require 'active_record/connection_adapters/clickhouse/oid/big_integer'
|
11
|
+
require 'active_record/connection_adapters/clickhouse/oid/uuid'
|
11
12
|
require 'active_record/connection_adapters/clickhouse/schema_definitions'
|
12
13
|
require 'active_record/connection_adapters/clickhouse/schema_creation'
|
13
14
|
require 'active_record/connection_adapters/clickhouse/schema_statements'
|
@@ -44,7 +45,7 @@ module ActiveRecord
|
|
44
45
|
raise ArgumentError, 'No database specified. Missing argument: database.'
|
45
46
|
end
|
46
47
|
|
47
|
-
ConnectionAdapters::ClickhouseAdapter.new(logger, connection,
|
48
|
+
ConnectionAdapters::ClickhouseAdapter.new(logger, connection, config)
|
48
49
|
end
|
49
50
|
end
|
50
51
|
end
|
@@ -63,7 +64,7 @@ module ActiveRecord
|
|
63
64
|
|
64
65
|
module ModelSchema
|
65
66
|
module ClassMethods
|
66
|
-
delegate :final, :settings, to: :all
|
67
|
+
delegate :final, :final!, :settings, :settings!, to: :all
|
67
68
|
|
68
69
|
def is_view
|
69
70
|
@is_view || false
|
@@ -119,33 +120,20 @@ module ActiveRecord
|
|
119
120
|
include Clickhouse::SchemaStatements
|
120
121
|
|
121
122
|
# Initializes and connects a Clickhouse adapter.
|
122
|
-
def initialize(logger, connection_parameters, config
|
123
|
+
def initialize(logger, connection_parameters, config)
|
123
124
|
super(nil, logger)
|
124
125
|
@connection_parameters = connection_parameters
|
126
|
+
@connection_config = { user: config[:username], password: config[:password], database: config[:database] }.compact
|
127
|
+
@debug = config[:debug] || false
|
125
128
|
@config = config
|
126
|
-
@debug = full_config[:debug] || false
|
127
|
-
@full_config = full_config
|
128
129
|
|
129
130
|
@prepared_statements = false
|
130
131
|
|
131
132
|
connect
|
132
133
|
end
|
133
134
|
|
134
|
-
# Support SchemaMigration from v5.2.2 to v6+
|
135
|
-
def schema_migration # :nodoc:
|
136
|
-
ClickhouseActiverecord::SchemaMigration.new(self)
|
137
|
-
end
|
138
|
-
|
139
|
-
def internal_metadata # :nodoc:
|
140
|
-
ClickhouseActiverecord::InternalMetadata.new(self)
|
141
|
-
end
|
142
|
-
|
143
135
|
def migrations_paths
|
144
|
-
@
|
145
|
-
end
|
146
|
-
|
147
|
-
def migration_context # :nodoc:
|
148
|
-
ClickhouseActiverecord::MigrationContext.new(migrations_paths, schema_migration, internal_metadata)
|
136
|
+
@config[:migrations_paths] || 'db/migrate_clickhouse'
|
149
137
|
end
|
150
138
|
|
151
139
|
def arel_visitor # :nodoc:
|
@@ -211,6 +199,9 @@ module ActiveRecord
|
|
211
199
|
register_class_with_limit m, %r(UInt64), Type::UnsignedInteger
|
212
200
|
#register_class_with_limit m, %r(UInt128), Type::UnsignedInteger #not implemnted in clickhouse
|
213
201
|
register_class_with_limit m, %r(UInt256), Type::UnsignedInteger
|
202
|
+
|
203
|
+
m.register_type %r(bool)i, ActiveModel::Type::Boolean.new
|
204
|
+
m.register_type %r{uuid}i, Clickhouse::OID::Uuid.new
|
214
205
|
# register_class_with_limit m, %r(Array), Clickhouse::OID::Array
|
215
206
|
m.register_type(%r(Array)) do |sql_type|
|
216
207
|
Clickhouse::OID::Array.new(sql_type)
|
@@ -275,7 +266,7 @@ module ActiveRecord
|
|
275
266
|
def create_database(name)
|
276
267
|
sql = apply_cluster "CREATE DATABASE #{quote_table_name(name)}"
|
277
268
|
log_with_debug(sql, adapter_name) do
|
278
|
-
res = @connection.post("/?#{@
|
269
|
+
res = @connection.post("/?#{@connection_config.except(:database).to_param}", sql)
|
279
270
|
process_response(res)
|
280
271
|
end
|
281
272
|
end
|
@@ -311,7 +302,7 @@ module ActiveRecord
|
|
311
302
|
raise 'Set a cluster' unless cluster
|
312
303
|
|
313
304
|
distributed_options =
|
314
|
-
"Distributed(#{cluster}, #{@
|
305
|
+
"Distributed(#{cluster}, #{@connection_config[:database]}, #{table_name}, #{sharding_key})"
|
315
306
|
create_table(distributed_table_name, **options.merge(options: distributed_options), &block)
|
316
307
|
end
|
317
308
|
end
|
@@ -320,7 +311,7 @@ module ActiveRecord
|
|
320
311
|
def drop_database(name) #:nodoc:
|
321
312
|
sql = apply_cluster "DROP DATABASE IF EXISTS #{quote_table_name(name)}"
|
322
313
|
log_with_debug(sql, adapter_name) do
|
323
|
-
res = @connection.post("/?#{@
|
314
|
+
res = @connection.post("/?#{@connection_config.except(:database).to_param}", sql)
|
324
315
|
process_response(res)
|
325
316
|
end
|
326
317
|
end
|
@@ -374,15 +365,15 @@ module ActiveRecord
|
|
374
365
|
end
|
375
366
|
|
376
367
|
def cluster
|
377
|
-
@
|
368
|
+
@config[:cluster_name]
|
378
369
|
end
|
379
370
|
|
380
371
|
def replica
|
381
|
-
@
|
372
|
+
@config[:replica_name]
|
382
373
|
end
|
383
374
|
|
384
375
|
def use_default_replicated_merge_tree_params?
|
385
|
-
database_engine_atomic? && @
|
376
|
+
database_engine_atomic? && @config[:use_default_replicated_merge_tree_params]
|
386
377
|
end
|
387
378
|
|
388
379
|
def use_replica?
|
@@ -390,11 +381,11 @@ module ActiveRecord
|
|
390
381
|
end
|
391
382
|
|
392
383
|
def replica_path(table)
|
393
|
-
"/clickhouse/tables/#{cluster}/#{@
|
384
|
+
"/clickhouse/tables/#{cluster}/#{@connection_config[:database]}.#{table}"
|
394
385
|
end
|
395
386
|
|
396
387
|
def database_engine_atomic?
|
397
|
-
current_database_engine = "select engine from system.databases where name = '#{@
|
388
|
+
current_database_engine = "select engine from system.databases where name = '#{@connection_config[:database]}'"
|
398
389
|
res = select_one(current_database_engine)
|
399
390
|
res['engine'] == 'Atomic' if res
|
400
391
|
end
|
@@ -13,12 +13,6 @@ module Arel
|
|
13
13
|
end
|
14
14
|
end
|
15
15
|
|
16
|
-
def visit_Arel_Table o, collector
|
17
|
-
collector = super
|
18
|
-
collector << ' FINAL' if o.final
|
19
|
-
collector
|
20
|
-
end
|
21
|
-
|
22
16
|
def visit_Arel_Nodes_SelectOptions(o, collector)
|
23
17
|
maybe_visit o.settings, super
|
24
18
|
end
|
@@ -34,6 +28,12 @@ module Arel
|
|
34
28
|
maybe_visit o.limit, collector
|
35
29
|
end
|
36
30
|
|
31
|
+
def visit_Arel_Nodes_Final(o, collector)
|
32
|
+
visit o.expr, collector
|
33
|
+
collector << ' FINAL'
|
34
|
+
collector
|
35
|
+
end
|
36
|
+
|
37
37
|
def visit_Arel_Nodes_Settings(o, collector)
|
38
38
|
return collector if o.expr.empty?
|
39
39
|
|
@@ -53,6 +53,16 @@ module Arel
|
|
53
53
|
collector
|
54
54
|
end
|
55
55
|
|
56
|
+
def visit_Arel_Nodes_Matches(o, collector)
|
57
|
+
op = o.case_sensitive ? " LIKE " : " ILIKE "
|
58
|
+
infix_value o, collector, op
|
59
|
+
end
|
60
|
+
|
61
|
+
def visit_Arel_Nodes_DoesNotMatch(o, collector)
|
62
|
+
op = o.case_sensitive ? " NOT LIKE " : " NOT ILIKE "
|
63
|
+
infix_value o, collector, op
|
64
|
+
end
|
65
|
+
|
56
66
|
def sanitize_as_setting_value(value)
|
57
67
|
if value == :default
|
58
68
|
'DEFAULT'
|
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
module ClickhouseActiverecord
|
4
4
|
class Tasks
|
5
|
-
delegate :connection, :establish_connection,
|
5
|
+
delegate :connection, :establish_connection, to: ActiveRecord::Base
|
6
6
|
|
7
7
|
def initialize(configuration)
|
8
8
|
@configuration = configuration.with_indifferent_access
|
@@ -25,7 +25,7 @@ module ClickhouseActiverecord
|
|
25
25
|
end
|
26
26
|
|
27
27
|
def purge
|
28
|
-
clear_active_connections!
|
28
|
+
ActiveRecord::Base.connection_handler.clear_active_connections!(:all)
|
29
29
|
drop
|
30
30
|
create
|
31
31
|
end
|
@@ -2,8 +2,11 @@
|
|
2
2
|
|
3
3
|
require 'active_record/connection_adapters/clickhouse_adapter'
|
4
4
|
|
5
|
+
require 'core_extensions/active_record/internal_metadata'
|
5
6
|
require 'core_extensions/active_record/relation'
|
7
|
+
require 'core_extensions/active_record/schema_migration'
|
6
8
|
|
9
|
+
require 'core_extensions/arel/nodes/select_core'
|
7
10
|
require 'core_extensions/arel/nodes/select_statement'
|
8
11
|
require 'core_extensions/arel/select_manager'
|
9
12
|
require 'core_extensions/arel/table'
|
@@ -21,8 +24,11 @@ end
|
|
21
24
|
|
22
25
|
module ClickhouseActiverecord
|
23
26
|
def self.load
|
27
|
+
ActiveRecord::InternalMetadata.singleton_class.prepend(CoreExtensions::ActiveRecord::InternalMetadata::ClassMethods)
|
24
28
|
ActiveRecord::Relation.prepend(CoreExtensions::ActiveRecord::Relation)
|
29
|
+
ActiveRecord::SchemaMigration.singleton_class.prepend(CoreExtensions::ActiveRecord::SchemaMigration::ClassMethods)
|
25
30
|
|
31
|
+
Arel::Nodes::SelectCore.prepend(CoreExtensions::Arel::Nodes::SelectCore)
|
26
32
|
Arel::Nodes::SelectStatement.prepend(CoreExtensions::Arel::Nodes::SelectStatement)
|
27
33
|
Arel::SelectManager.prepend(CoreExtensions::Arel::SelectManager)
|
28
34
|
Arel::Table.prepend(CoreExtensions::Arel::Table)
|
@@ -0,0 +1,46 @@
|
|
1
|
+
module CoreExtensions
|
2
|
+
module ActiveRecord
|
3
|
+
module InternalMetadata
|
4
|
+
module ClassMethods
|
5
|
+
|
6
|
+
def []=(key, value)
|
7
|
+
row = final.find_by(key: key)
|
8
|
+
if row.nil? || row.value != value
|
9
|
+
create!(key: key, value: value)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
def [](key)
|
14
|
+
final.where(key: key).pluck(:value).first
|
15
|
+
end
|
16
|
+
|
17
|
+
def create_table
|
18
|
+
return super unless connection.is_a?(::ActiveRecord::ConnectionAdapters::ClickhouseAdapter)
|
19
|
+
return if table_exists? || !enabled?
|
20
|
+
|
21
|
+
key_options = connection.internal_string_options_for_primary_key
|
22
|
+
table_options = {
|
23
|
+
id: false,
|
24
|
+
options: connection.adapter_name.downcase == 'clickhouse' ? 'ReplacingMergeTree(created_at) PARTITION BY key ORDER BY key' : '',
|
25
|
+
if_not_exists: true
|
26
|
+
}
|
27
|
+
full_config = connection.instance_variable_get(:@config) || {}
|
28
|
+
|
29
|
+
if full_config[:distributed_service_tables]
|
30
|
+
table_options.merge!(with_distributed: table_name, sharding_key: 'cityHash64(created_at)')
|
31
|
+
|
32
|
+
distributed_suffix = "_#{full_config[:distributed_service_tables_suffix] || 'distributed'}"
|
33
|
+
else
|
34
|
+
distributed_suffix = ''
|
35
|
+
end
|
36
|
+
|
37
|
+
connection.create_table(table_name + distributed_suffix.to_s, **table_options) do |t|
|
38
|
+
t.string :key, **key_options
|
39
|
+
t.string :value
|
40
|
+
t.timestamps
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -38,17 +38,14 @@ module CoreExtensions
|
|
38
38
|
# # SELECT users.* FROM users FINAL
|
39
39
|
#
|
40
40
|
# An <tt>ActiveRecord::ActiveRecordError</tt> will be raised if database not ClickHouse.
|
41
|
-
|
42
|
-
|
43
|
-
spawn.final!(final)
|
41
|
+
def final
|
42
|
+
spawn.final!
|
44
43
|
end
|
45
44
|
|
46
|
-
|
47
|
-
def final!(final = true)
|
45
|
+
def final!
|
48
46
|
assert_mutability!
|
49
47
|
check_command('FINAL')
|
50
|
-
@
|
51
|
-
@table.final = final
|
48
|
+
@values[:final] = true
|
52
49
|
self
|
53
50
|
end
|
54
51
|
|
@@ -79,6 +76,7 @@ module CoreExtensions
|
|
79
76
|
def build_arel(aliases = nil)
|
80
77
|
arel = super
|
81
78
|
|
79
|
+
arel.final! if @values[:final].present?
|
82
80
|
arel.settings(@values[:settings]) if @values[:settings].present?
|
83
81
|
arel.using(@values[:using]) if @values[:using].present?
|
84
82
|
|
@@ -0,0 +1,48 @@
|
|
1
|
+
module CoreExtensions
|
2
|
+
module ActiveRecord
|
3
|
+
module SchemaMigration
|
4
|
+
module ClassMethods
|
5
|
+
|
6
|
+
def create_table
|
7
|
+
return super unless connection.is_a?(::ActiveRecord::ConnectionAdapters::ClickhouseAdapter)
|
8
|
+
|
9
|
+
return if table_exists?
|
10
|
+
|
11
|
+
version_options = connection.internal_string_options_for_primary_key
|
12
|
+
table_options = {
|
13
|
+
id: false, options: 'ReplacingMergeTree(ver) ORDER BY (version)', if_not_exists: true
|
14
|
+
}
|
15
|
+
full_config = connection.instance_variable_get(:@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'}"
|
21
|
+
else
|
22
|
+
distributed_suffix = ''
|
23
|
+
end
|
24
|
+
|
25
|
+
connection.create_table(table_name + distributed_suffix.to_s, **table_options) do |t|
|
26
|
+
t.string :version, **version_options
|
27
|
+
t.column :active, 'Int8', null: false, default: '1'
|
28
|
+
t.datetime :ver, null: false, default: -> { 'now()' }
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def delete_version(version)
|
33
|
+
return super unless connection.is_a?(::ActiveRecord::ConnectionAdapters::ClickhouseAdapter)
|
34
|
+
|
35
|
+
im = Arel::InsertManager.new(arel_table)
|
36
|
+
im.insert(arel_table[primary_key] => version.to_s, arel_table['active'] => 0)
|
37
|
+
connection.insert(im, "#{self.class} Create Rollback Version", primary_key, version)
|
38
|
+
end
|
39
|
+
|
40
|
+
def all_versions
|
41
|
+
return super unless connection.is_a?(::ActiveRecord::ConnectionAdapters::ClickhouseAdapter)
|
42
|
+
|
43
|
+
final.where(active: 1).order(:version).pluck(:version)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module CoreExtensions
|
2
|
+
module Arel # :nodoc: all
|
3
|
+
module Nodes
|
4
|
+
module SelectCore
|
5
|
+
attr_accessor :final
|
6
|
+
|
7
|
+
def source
|
8
|
+
return super unless final
|
9
|
+
|
10
|
+
::Arel::Nodes::Final.new(super)
|
11
|
+
end
|
12
|
+
|
13
|
+
def eql?(other)
|
14
|
+
super && final == other.final
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
data/lib/tasks/clickhouse.rake
CHANGED
@@ -12,15 +12,16 @@ namespace :clickhouse do
|
|
12
12
|
end
|
13
13
|
|
14
14
|
namespace :schema do
|
15
|
-
# TODO:
|
15
|
+
# TODO: deprecated
|
16
16
|
desc 'Load database schema'
|
17
17
|
task load: %i[prepare_internal_metadata_table] do
|
18
18
|
simple = ENV['simple'] || ARGV.any? { |a| a.include?('--simple') } ? '_simple' : nil
|
19
|
-
|
20
|
-
|
19
|
+
ActiveRecord::Base.establish_connection(:clickhouse)
|
20
|
+
ActiveRecord::SchemaMigration.drop_table
|
21
21
|
load(Rails.root.join("db/clickhouse_schema#{simple}.rb"))
|
22
22
|
end
|
23
23
|
|
24
|
+
# TODO: deprecated
|
24
25
|
desc 'Dump database schema'
|
25
26
|
task dump: :environment do |_, args|
|
26
27
|
simple = ENV['simple'] || args[:simple] || ARGV.any? { |a| a.include?('--simple') } ? '_simple' : nil
|
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: 1.0.
|
4
|
+
version: 1.0.4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Sergey Odintsov
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-
|
11
|
+
date: 2024-02-02 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -103,21 +103,25 @@ files:
|
|
103
103
|
- lib/active_record/connection_adapters/clickhouse/oid/big_integer.rb
|
104
104
|
- lib/active_record/connection_adapters/clickhouse/oid/date.rb
|
105
105
|
- lib/active_record/connection_adapters/clickhouse/oid/date_time.rb
|
106
|
+
- lib/active_record/connection_adapters/clickhouse/oid/uuid.rb
|
106
107
|
- lib/active_record/connection_adapters/clickhouse/schema_creation.rb
|
107
108
|
- lib/active_record/connection_adapters/clickhouse/schema_definitions.rb
|
108
109
|
- lib/active_record/connection_adapters/clickhouse/schema_statements.rb
|
109
110
|
- lib/active_record/connection_adapters/clickhouse_adapter.rb
|
111
|
+
- lib/arel/nodes/final.rb
|
110
112
|
- lib/arel/nodes/settings.rb
|
111
113
|
- lib/arel/nodes/using.rb
|
112
114
|
- lib/arel/visitors/clickhouse.rb
|
113
115
|
- lib/clickhouse-activerecord.rb
|
114
|
-
- lib/clickhouse-activerecord/migration.rb
|
115
116
|
- lib/clickhouse-activerecord/railtie.rb
|
116
117
|
- lib/clickhouse-activerecord/schema.rb
|
117
118
|
- lib/clickhouse-activerecord/schema_dumper.rb
|
118
119
|
- lib/clickhouse-activerecord/tasks.rb
|
119
120
|
- lib/clickhouse-activerecord/version.rb
|
121
|
+
- lib/core_extensions/active_record/internal_metadata.rb
|
120
122
|
- lib/core_extensions/active_record/relation.rb
|
123
|
+
- lib/core_extensions/active_record/schema_migration.rb
|
124
|
+
- lib/core_extensions/arel/nodes/select_core.rb
|
121
125
|
- lib/core_extensions/arel/nodes/select_statement.rb
|
122
126
|
- lib/core_extensions/arel/select_manager.rb
|
123
127
|
- lib/core_extensions/arel/table.rb
|
@@ -1,103 +0,0 @@
|
|
1
|
-
require 'active_record/migration'
|
2
|
-
|
3
|
-
module ClickhouseActiverecord
|
4
|
-
|
5
|
-
class SchemaMigration < ::ActiveRecord::SchemaMigration
|
6
|
-
def create_table
|
7
|
-
return if table_exists?
|
8
|
-
|
9
|
-
version_options = connection.internal_string_options_for_primary_key
|
10
|
-
table_options = {
|
11
|
-
id: false, options: 'ReplacingMergeTree(ver) ORDER BY (version)', if_not_exists: true
|
12
|
-
}
|
13
|
-
full_config = connection.instance_variable_get(:@full_config) || {}
|
14
|
-
|
15
|
-
if full_config[:distributed_service_tables]
|
16
|
-
table_options.merge!(with_distributed: table_name, sharding_key: 'cityHash64(version)')
|
17
|
-
|
18
|
-
distributed_suffix = "_#{full_config[:distributed_service_tables_suffix] || 'distributed'}"
|
19
|
-
end
|
20
|
-
|
21
|
-
connection.create_table(table_name + distributed_suffix.to_s, **table_options) do |t|
|
22
|
-
t.string :version, **version_options
|
23
|
-
t.column :active, 'Int8', null: false, default: '1'
|
24
|
-
t.datetime :ver, null: false, default: -> { 'now()' }
|
25
|
-
end
|
26
|
-
end
|
27
|
-
|
28
|
-
def versions
|
29
|
-
table = arel_table.dup
|
30
|
-
table.final = true
|
31
|
-
sm = Arel::SelectManager.new(table)
|
32
|
-
sm.project(arel_table[primary_key])
|
33
|
-
sm.order(arel_table[primary_key].asc)
|
34
|
-
sm.where([arel_table['active'].eq(1)])
|
35
|
-
|
36
|
-
connection.select_values(sm, "#{self.class} Load")
|
37
|
-
end
|
38
|
-
|
39
|
-
def delete_version(version)
|
40
|
-
im = Arel::InsertManager.new(arel_table)
|
41
|
-
im.insert(arel_table[primary_key] => version.to_s, arel_table['active'] => 0)
|
42
|
-
connection.insert(im, "#{self.class} Create Rollback Version", primary_key, version)
|
43
|
-
end
|
44
|
-
end
|
45
|
-
|
46
|
-
class InternalMetadata < ::ActiveRecord::InternalMetadata
|
47
|
-
|
48
|
-
def create_table
|
49
|
-
return if table_exists? || !enabled?
|
50
|
-
|
51
|
-
key_options = connection.internal_string_options_for_primary_key
|
52
|
-
table_options = {
|
53
|
-
id: false,
|
54
|
-
options: connection.adapter_name.downcase == 'clickhouse' ? 'ReplacingMergeTree(created_at) PARTITION BY key ORDER BY key' : '',
|
55
|
-
if_not_exists: true
|
56
|
-
}
|
57
|
-
full_config = connection.instance_variable_get(:@full_config) || {}
|
58
|
-
|
59
|
-
if full_config[:distributed_service_tables]
|
60
|
-
table_options.merge!(with_distributed: table_name, sharding_key: 'cityHash64(created_at)')
|
61
|
-
|
62
|
-
distributed_suffix = "_#{full_config[:distributed_service_tables_suffix] || 'distributed'}"
|
63
|
-
end
|
64
|
-
|
65
|
-
connection.create_table(table_name + distributed_suffix.to_s, **table_options) do |t|
|
66
|
-
t.string :key, **key_options
|
67
|
-
t.string :value
|
68
|
-
t.timestamps
|
69
|
-
end
|
70
|
-
end
|
71
|
-
|
72
|
-
private
|
73
|
-
|
74
|
-
def update_entry(key, new_value)
|
75
|
-
create_entry(key, new_value)
|
76
|
-
end
|
77
|
-
|
78
|
-
def select_entry(key)
|
79
|
-
table = arel_table.dup
|
80
|
-
table.final = true
|
81
|
-
sm = Arel::SelectManager.new(table)
|
82
|
-
sm.project(Arel::Nodes::SqlLiteral.new("*"))
|
83
|
-
sm.where(table[primary_key].eq(Arel::Nodes::BindParam.new(key)))
|
84
|
-
sm.order(table[primary_key].asc)
|
85
|
-
sm.limit = 1
|
86
|
-
|
87
|
-
connection.select_all(sm, "#{self.class} Load").first
|
88
|
-
end
|
89
|
-
end
|
90
|
-
|
91
|
-
class MigrationContext < ::ActiveRecord::MigrationContext #:nodoc:
|
92
|
-
|
93
|
-
def get_all_versions
|
94
|
-
if schema_migration.table_exists?
|
95
|
-
schema_migration.versions.map(&:to_i)
|
96
|
-
else
|
97
|
-
[]
|
98
|
-
end
|
99
|
-
end
|
100
|
-
|
101
|
-
end
|
102
|
-
|
103
|
-
end
|