clickhouse-activerecord 1.0.1 → 1.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d56a19e32a58184c6c70b4cc56f30f4e7f742255a9f63441ce8c9b94ec63eebf
4
- data.tar.gz: 2bd423580e61443e1b334e2182e4a05a7e712d79141ae6431730edcdc5dee7e3
3
+ metadata.gz: a41725d372314184b6799381965f0fb299a42d27b5c714bd04ad768ab0f87a11
4
+ data.tar.gz: 12e892096d7d76f039f484e58b045c9f680ee95297790f485f8b19d9ecca7412
5
5
  SHA512:
6
- metadata.gz: 17a5a009eb6ece2d6835f5c37598891bde51fb4cf6c6e7ccb8fdc02cf9b1345d822e39d0f3d825b593606180db020357f4828e89f0c9c4cdf8352349ad6f4104
7
- data.tar.gz: edc694713807b818d8f2e9c0b6a0592e0a3862a6b064d4110d12d82ebc1cb40133f7495e9e665007f91f0da5223d129ed110387f4922bf4de710729f462d4509
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
@@ -68,10 +68,9 @@ module ActiveRecord
68
68
 
69
69
  if options[:precision]
70
70
  kind = :datetime64
71
- options[:value] = options[:precision]
72
71
  end
73
72
 
74
- args.each { |name| column(name, kind, **options.except(:precision)) }
73
+ args.each { |name| column(name, kind, **options) }
75
74
  end
76
75
 
77
76
  def uuid(*args, **options)
@@ -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
 
@@ -1,19 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ClickhouseActiverecord
4
-
5
4
  class Schema < ::ActiveRecord::Schema
6
5
 
7
- def define(info, &block) # :nodoc:
8
- instance_eval(&block)
9
-
10
- if info[:version].present?
11
- connection.schema_migration.create_table
12
- connection.assume_migrated_upto_version(info[:version], ClickhouseActiverecord::Migrator.migrations_paths)
13
- end
14
-
15
- ClickhouseActiverecord::InternalMetadata.create_table
16
- ClickhouseActiverecord::InternalMetadata[:environment] = connection.migration_context.current_environment
17
- end
18
6
  end
19
7
  end
@@ -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.1'
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,14 +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
- task load: %i[load_config prepare_internal_metadata_table] do
17
+ task load: %i[prepare_internal_metadata_table] do
18
18
  simple = ENV['simple'] || ARGV.any? { |a| a.include?('--simple') } ? '_simple' : nil
19
- ClickhouseActiverecord::SchemaMigration.drop_table
19
+ ActiveRecord::Base.establish_connection(:clickhouse)
20
+ ActiveRecord::SchemaMigration.drop_table
20
21
  load(Rails.root.join("db/clickhouse_schema#{simple}.rb"))
21
22
  end
22
23
 
24
+ # TODO: deprecated
23
25
  desc 'Dump database schema'
24
26
  task dump: :environment do |_, args|
25
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.1
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-10 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