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