clickhouse-activerecord 1.0.2 → 1.0.3

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: e5a31d2ddff53bb618e1624e325c0be76f2f602655565a74a96106674f3a375e
4
- data.tar.gz: 762b41c929d36e7ff0f0ceff557da5b85d0b231e8df3e6c6bd41b6085eb940b0
3
+ metadata.gz: a41725d372314184b6799381965f0fb299a42d27b5c714bd04ad768ab0f87a11
4
+ data.tar.gz: 12e892096d7d76f039f484e58b045c9f680ee95297790f485f8b19d9ecca7412
5
5
  SHA512:
6
- metadata.gz: a6f8143c312efbc6f276b0fdc2b9e5ea8ab811f06a865d54ff82dcaa0614c30b26c44f65f60098f0895dcf935aa97c86e8b168e7e1bc5b7e54899662b71ad86a
7
- data.tar.gz: d9f4ba22a3f13bd157a225870e4d5eb025ca6a35ac21dc73731c19a8bb0d7464e479479109007d117d29f1aa788bb2f3cd5ae0350f1fa13596c36da3ed99a766
6
+ metadata.gz: 35afbee04dec61f61685ec9edd70a305df94fa2198933c9271f3221258f069be06a684dfd10a0a203798712ab1113f91b4f773d8c1f85f1455e4511b6437d6be
7
+ data.tar.gz: 0b9adb5b3da648ab1ca1b44352b58fc91d7d09b0c29cbc816e23faa39b060ca3e7a8f35e67eb87e92332cae513592f19378fcf993c098684463ffbcd817d64e2
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 clickhouse:schema:dump
127
+ $ rake db:schema:dump:clickhouse
128
128
 
129
129
  Schema load from `db/clickhouse_schema.rb` file:
130
130
 
131
- $ rake clickhouse:schema:load
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
@@ -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'
@@ -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
@@ -131,23 +132,10 @@ module ActiveRecord
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
136
  @full_config[:migrations_paths] || 'db/migrate_clickhouse'
145
137
  end
146
138
 
147
- def migration_context # :nodoc:
148
- ClickhouseActiverecord::MigrationContext.new(migrations_paths, schema_migration, internal_metadata)
149
- end
150
-
151
139
  def arel_visitor # :nodoc:
152
140
  Arel::Visitors::Clickhouse.new(self)
153
141
  end
@@ -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)
@@ -0,0 +1,7 @@
1
+ module Arel # :nodoc: all
2
+ module Nodes
3
+ class Final < Arel::Nodes::Unary
4
+ delegate :empty?, to: :expr
5
+ end
6
+ end
7
+ 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
 
@@ -2,7 +2,7 @@
2
2
 
3
3
  module ClickhouseActiverecord
4
4
  class Tasks
5
- delegate :connection, :establish_connection, :clear_active_connections!, to: ActiveRecord::Base
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
@@ -1,3 +1,3 @@
1
1
  module ClickhouseActiverecord
2
- VERSION = '1.0.2'
2
+ VERSION = '1.0.3'
3
3
  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(:@full_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
- # @param [Boolean] final
42
- def final(final = true)
43
- spawn.final!(final)
41
+ def final
42
+ spawn.final!
44
43
  end
45
44
 
46
- # @param [Boolean] final
47
- def final!(final = true)
45
+ def final!
48
46
  assert_mutability!
49
47
  check_command('FINAL')
50
- @table = @table.dup
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(:@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'}"
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
@@ -2,6 +2,11 @@ module CoreExtensions
2
2
  module Arel
3
3
  module SelectManager
4
4
 
5
+ def final!
6
+ @ctx.final = true
7
+ self
8
+ end
9
+
5
10
  # @param [Hash] values
6
11
  def settings(values)
7
12
  @ast.settings = ::Arel::Nodes::Settings.new(values)
@@ -1,8 +1,6 @@
1
1
  module CoreExtensions
2
2
  module Arel
3
3
  module Table
4
- attr_accessor :final
5
-
6
4
  def is_view
7
5
  type_caster.is_view
8
6
  end
@@ -12,15 +12,16 @@ namespace :clickhouse do
12
12
  end
13
13
 
14
14
  namespace :schema do
15
- # TODO: not testing
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
- config = ActiveRecord::Base.configurations.configs_for(env_name: Rails.env, name: 'clickhouse')
20
- ClickhouseActiverecord::SchemaMigration.new(ActiveRecord::Base.establish_connection(config).connection).drop_table
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.2
4
+ version: 1.0.3
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-01-12 00:00:00.000000000 Z
11
+ date: 2024-01-22 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