clickhouse-activerecord 0.6.2 → 1.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +31 -41
- data/clickhouse-activerecord.gemspec +1 -1
- data/lib/active_record/connection_adapters/clickhouse/schema_creation.rb +1 -5
- data/lib/active_record/connection_adapters/clickhouse/schema_statements.rb +10 -10
- data/lib/active_record/connection_adapters/clickhouse_adapter.rb +9 -20
- data/lib/arel/visitors/clickhouse.rb +12 -1
- data/lib/clickhouse-activerecord/migration.rb +63 -106
- data/lib/clickhouse-activerecord/version.rb +1 -1
- data/lib/core_extensions/active_record/relation.rb +45 -0
- data/lib/tasks/clickhouse.rake +18 -19
- metadata +6 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d56a19e32a58184c6c70b4cc56f30f4e7f742255a9f63441ce8c9b94ec63eebf
|
4
|
+
data.tar.gz: 2bd423580e61443e1b334e2182e4a05a7e712d79141ae6431730edcdc5dee7e3
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 17a5a009eb6ece2d6835f5c37598891bde51fb4cf6c6e7ccb8fdc02cf9b1345d822e39d0f3d825b593606180db020357f4828e89f0c9c4cdf8352349ad6f4104
|
7
|
+
data.tar.gz: edc694713807b818d8f2e9c0b6a0592e0a3862a6b064d4110d12d82ebc1cb40133f7495e9e665007f91f0da5223d129ed110387f4922bf4de710729f462d4509
|
data/README.md
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# Clickhouse::Activerecord
|
2
2
|
|
3
|
-
A Ruby database ActiveRecord driver for ClickHouse. Support Rails >=
|
3
|
+
A Ruby database ActiveRecord driver for ClickHouse. Support Rails >= 7.1.
|
4
4
|
Support ClickHouse version from 22.0 LTS.
|
5
5
|
|
6
6
|
## Installation
|
@@ -50,41 +50,31 @@ class ActionView < ActiveRecord::Base
|
|
50
50
|
end
|
51
51
|
```
|
52
52
|
|
53
|
-
## Usage in Rails
|
53
|
+
## Usage in Rails
|
54
54
|
|
55
55
|
Add your `database.yml` connection information with postfix `_clickhouse` for you environment:
|
56
56
|
|
57
57
|
```yml
|
58
|
-
|
58
|
+
development:
|
59
59
|
adapter: clickhouse
|
60
60
|
database: database
|
61
61
|
```
|
62
62
|
|
63
|
-
|
63
|
+
Your model example:
|
64
64
|
|
65
65
|
```ruby
|
66
66
|
class Action < ActiveRecord::Base
|
67
|
-
establish_connection "#{Rails.env}_clickhouse".to_sym
|
68
67
|
end
|
69
68
|
```
|
70
69
|
|
71
70
|
For materialized view model add:
|
72
71
|
```ruby
|
73
72
|
class ActionView < ActiveRecord::Base
|
74
|
-
establish_connection "#{Rails.env}_clickhouse".to_sym
|
75
73
|
self.is_view = true
|
76
74
|
end
|
77
75
|
```
|
78
76
|
|
79
|
-
|
80
|
-
|
81
|
-
```yml
|
82
|
-
development:
|
83
|
-
adapter: clickhouse
|
84
|
-
database: database
|
85
|
-
```
|
86
|
-
|
87
|
-
## Usage in Rails 6 with second database
|
77
|
+
## Usage in Rails with second database
|
88
78
|
|
89
79
|
Add your `database.yml` connection information for you environment:
|
90
80
|
|
@@ -102,31 +92,31 @@ Connection [Multiple Databases with Active Record](https://guides.rubyonrails.or
|
|
102
92
|
|
103
93
|
```ruby
|
104
94
|
class Action < ActiveRecord::Base
|
105
|
-
|
95
|
+
establish_connection :clickhouse
|
106
96
|
end
|
107
97
|
```
|
108
98
|
|
109
99
|
### Rake tasks
|
110
100
|
|
111
|
-
**Note!** For Rails 6 you can use default rake tasks if you configure `migrations_paths` in your `database.yml`, for example: `rake db:migrate`
|
112
|
-
|
113
101
|
Create / drop / purge / reset database:
|
114
102
|
|
115
|
-
$ rake
|
116
|
-
$ rake
|
117
|
-
$ rake
|
118
|
-
$ rake
|
103
|
+
$ rake db:create
|
104
|
+
$ rake db:drop
|
105
|
+
$ rake db:purge
|
106
|
+
$ rake db:reset
|
119
107
|
|
120
|
-
|
108
|
+
Or with multiple databases:
|
121
109
|
|
122
|
-
$ rake clickhouse
|
123
|
-
$ rake clickhouse
|
110
|
+
$ rake db:create:clickhouse
|
111
|
+
$ rake db:drop:clickhouse
|
112
|
+
$ rake db:purge:clickhouse
|
113
|
+
$ rake db:reset:clickhouse
|
124
114
|
|
125
115
|
Migration:
|
126
116
|
|
127
117
|
$ rails g clickhouse_migration MIGRATION_NAME COLUMNS
|
128
|
-
$ rake
|
129
|
-
$ rake
|
118
|
+
$ rake db:migrate
|
119
|
+
$ rake db:rollback
|
130
120
|
|
131
121
|
### Dump / Load for multiple using databases
|
132
122
|
|
@@ -195,20 +185,20 @@ User.joins(:actions).using(:group_id)
|
|
195
185
|
Integer types are unsigned by default. Specify signed values with `:unsigned =>
|
196
186
|
false`. The default integer is `UInt32`
|
197
187
|
|
198
|
-
| Type (bit size)
|
199
|
-
|
200
|
-
| Int8
|
201
|
-
| Int16
|
202
|
-
| Int32
|
203
|
-
| Int64
|
204
|
-
| Int128
|
205
|
-
| Int256
|
206
|
-
| UInt8
|
207
|
-
| UInt16
|
208
|
-
| UInt32
|
209
|
-
| UInt64
|
210
|
-
| UInt256
|
211
|
-
| Array
|
188
|
+
| Type (bit size) | Range | :limit (byte size) |
|
189
|
+
|:----------------|:--------------------------------------------:|-------------------:|
|
190
|
+
| Int8 | -128 to 127 | 1 |
|
191
|
+
| Int16 | -32768 to 32767 | 2 |
|
192
|
+
| Int32 | -2147483648 to 2,147,483,647 | 3,4 |
|
193
|
+
| Int64 | -9223372036854775808 to 9223372036854775807] | 5,6,7,8 |
|
194
|
+
| Int128 | ... | 9 - 15 |
|
195
|
+
| Int256 | ... | 16+ |
|
196
|
+
| UInt8 | 0 to 255 | 1 |
|
197
|
+
| UInt16 | 0 to 65,535 | 2 |
|
198
|
+
| UInt32 | 0 to 4,294,967,295 | 3,4 |
|
199
|
+
| UInt64 | 0 to 18446744073709551615 | 5,6,7,8 |
|
200
|
+
| UInt256 | 0 to ... | 8+ |
|
201
|
+
| Array | ... | ... |
|
212
202
|
|
213
203
|
Example:
|
214
204
|
|
@@ -24,7 +24,7 @@ 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', '
|
27
|
+
spec.add_runtime_dependency 'activerecord', '>= 7.1'
|
28
28
|
|
29
29
|
spec.add_development_dependency 'rake', '~> 13.0'
|
30
30
|
spec.add_development_dependency 'rspec', '~> 3.4'
|
@@ -125,11 +125,7 @@ module ActiveRecord
|
|
125
125
|
end
|
126
126
|
|
127
127
|
def current_database
|
128
|
-
|
129
|
-
ActiveRecord::Base.connection_db_config.database
|
130
|
-
else
|
131
|
-
ActiveRecord::Base.connection_config[:database]
|
132
|
-
end
|
128
|
+
ActiveRecord::Base.connection_db_config.database
|
133
129
|
end
|
134
130
|
end
|
135
131
|
end
|
@@ -10,13 +10,13 @@ module ActiveRecord
|
|
10
10
|
do_execute(sql, name, settings: settings)
|
11
11
|
end
|
12
12
|
|
13
|
-
def exec_insert(sql, name, _binds, _pk = nil, _sequence_name = nil)
|
13
|
+
def exec_insert(sql, name, _binds, _pk = nil, _sequence_name = nil, returning: nil)
|
14
14
|
new_sql = sql.dup.sub(/ (DEFAULT )?VALUES/, " VALUES")
|
15
15
|
do_execute(new_sql, name, format: nil)
|
16
16
|
true
|
17
17
|
end
|
18
18
|
|
19
|
-
def
|
19
|
+
def internal_exec_query(sql, name = nil, binds = [], prepare: false, async: false)
|
20
20
|
result = do_execute(sql, name)
|
21
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
|
@@ -30,12 +30,16 @@ module ActiveRecord
|
|
30
30
|
true
|
31
31
|
end
|
32
32
|
|
33
|
+
# @link https://clickhouse.com/docs/en/sql-reference/statements/alter/update
|
33
34
|
def exec_update(_sql, _name = nil, _binds = [])
|
34
|
-
|
35
|
+
do_execute(_sql, _name, format: nil)
|
36
|
+
true
|
35
37
|
end
|
36
38
|
|
39
|
+
# @link https://clickhouse.com/docs/en/sql-reference/statements/delete
|
37
40
|
def exec_delete(_sql, _name = nil, _binds = [])
|
38
|
-
|
41
|
+
do_execute(_sql, _name, format: nil)
|
42
|
+
true
|
39
43
|
end
|
40
44
|
|
41
45
|
def tables(name = nil)
|
@@ -137,17 +141,13 @@ module ActiveRecord
|
|
137
141
|
Clickhouse::TableDefinition.new(self, table_name, **options)
|
138
142
|
end
|
139
143
|
|
140
|
-
def new_column_from_field(table_name, field)
|
144
|
+
def new_column_from_field(table_name, field, _definitions)
|
141
145
|
sql_type = field[1]
|
142
146
|
type_metadata = fetch_type_metadata(sql_type)
|
143
147
|
default = field[3]
|
144
148
|
default_value = extract_value_from_default(default)
|
145
149
|
default_function = extract_default_function(default_value, default)
|
146
|
-
|
147
|
-
ClickhouseColumn.new(field[0], default_value, type_metadata, field[1].include?('Nullable'), default_function)
|
148
|
-
else
|
149
|
-
ClickhouseColumn.new(field[0], default_value, type_metadata, field[1].include?('Nullable'), table_name, default_function)
|
150
|
-
end
|
150
|
+
ClickhouseColumn.new(field[0], default_value, type_metadata, field[1].include?('Nullable'), default_function)
|
151
151
|
end
|
152
152
|
|
153
153
|
protected
|
@@ -127,16 +127,17 @@ module ActiveRecord
|
|
127
127
|
@full_config = full_config
|
128
128
|
|
129
129
|
@prepared_statements = false
|
130
|
-
if ActiveRecord::version == Gem::Version.new('6.0.0')
|
131
|
-
@prepared_statement_status = Concurrent::ThreadLocalVar.new(false)
|
132
|
-
end
|
133
130
|
|
134
131
|
connect
|
135
132
|
end
|
136
133
|
|
137
134
|
# Support SchemaMigration from v5.2.2 to v6+
|
138
135
|
def schema_migration # :nodoc:
|
139
|
-
ClickhouseActiverecord::SchemaMigration
|
136
|
+
ClickhouseActiverecord::SchemaMigration.new(self)
|
137
|
+
end
|
138
|
+
|
139
|
+
def internal_metadata # :nodoc:
|
140
|
+
ClickhouseActiverecord::InternalMetadata.new(self)
|
140
141
|
end
|
141
142
|
|
142
143
|
def migrations_paths
|
@@ -144,7 +145,7 @@ module ActiveRecord
|
|
144
145
|
end
|
145
146
|
|
146
147
|
def migration_context # :nodoc:
|
147
|
-
ClickhouseActiverecord::MigrationContext.new(migrations_paths, schema_migration)
|
148
|
+
ClickhouseActiverecord::MigrationContext.new(migrations_paths, schema_migration, internal_metadata)
|
148
149
|
end
|
149
150
|
|
150
151
|
def arel_visitor # :nodoc:
|
@@ -234,30 +235,18 @@ module ActiveRecord
|
|
234
235
|
# Quoting time without microseconds
|
235
236
|
def quoted_date(value)
|
236
237
|
if value.acts_like?(:time)
|
237
|
-
|
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
|
238
|
+
zone_conversion_method = ActiveRecord.default_timezone == :utc ? :getutc : :getlocal
|
242
239
|
|
243
240
|
if value.respond_to?(zone_conversion_method)
|
244
241
|
value = value.send(zone_conversion_method)
|
245
242
|
end
|
246
243
|
end
|
247
244
|
|
248
|
-
|
249
|
-
value.to_fs(:db)
|
250
|
-
else
|
251
|
-
value.to_s(:db)
|
252
|
-
end
|
245
|
+
value.to_fs(:db)
|
253
246
|
end
|
254
247
|
|
255
248
|
def column_name_for_operation(operation, node) # :nodoc:
|
256
|
-
|
257
|
-
visitor.compile(node)
|
258
|
-
else
|
259
|
-
column_name_from_arel_node(node)
|
260
|
-
end
|
249
|
+
visitor.compile(node)
|
261
250
|
end
|
262
251
|
|
263
252
|
# Executes insert +sql+ statement in the context of this connection using
|
@@ -15,7 +15,7 @@ module Arel
|
|
15
15
|
|
16
16
|
def visit_Arel_Table o, collector
|
17
17
|
collector = super
|
18
|
-
collector << ' FINAL
|
18
|
+
collector << ' FINAL' if o.final
|
19
19
|
collector
|
20
20
|
end
|
21
21
|
|
@@ -23,6 +23,17 @@ module Arel
|
|
23
23
|
maybe_visit o.settings, super
|
24
24
|
end
|
25
25
|
|
26
|
+
def visit_Arel_Nodes_UpdateStatement(o, collector)
|
27
|
+
o = prepare_update_statement(o)
|
28
|
+
|
29
|
+
collector << 'ALTER TABLE '
|
30
|
+
collector = visit o.relation, collector
|
31
|
+
collect_nodes_for o.values, collector, ' UPDATE '
|
32
|
+
collect_nodes_for o.wheres, collector, ' WHERE ', ' AND '
|
33
|
+
collect_nodes_for o.orders, collector, ' ORDER BY '
|
34
|
+
maybe_visit o.limit, collector
|
35
|
+
end
|
36
|
+
|
26
37
|
def visit_Arel_Nodes_Settings(o, collector)
|
27
38
|
return collector if o.expr.empty?
|
28
39
|
|
@@ -3,107 +3,96 @@ require 'active_record/migration'
|
|
3
3
|
module ClickhouseActiverecord
|
4
4
|
|
5
5
|
class SchemaMigration < ::ActiveRecord::SchemaMigration
|
6
|
-
|
6
|
+
def create_table
|
7
|
+
return if table_exists?
|
7
8
|
|
8
|
-
|
9
|
-
|
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) || {}
|
10
14
|
|
11
|
-
|
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) || {}
|
15
|
+
if full_config[:distributed_service_tables]
|
16
|
+
table_options.merge!(with_distributed: table_name, sharding_key: 'cityHash64(version)')
|
16
17
|
|
17
|
-
|
18
|
-
table_options.merge!(with_distributed: table_name, sharding_key: 'cityHash64(version)')
|
19
|
-
|
20
|
-
distributed_suffix = "_#{full_config[:distributed_service_tables_suffix] || 'distributed'}"
|
21
|
-
end
|
22
|
-
|
23
|
-
connection.create_table(table_name + distributed_suffix.to_s, **table_options) do |t|
|
24
|
-
t.string :version, **version_options
|
25
|
-
t.column :active, 'Int8', null: false, default: '1'
|
26
|
-
t.datetime :ver, null: false, default: -> { 'now()' }
|
27
|
-
end
|
18
|
+
distributed_suffix = "_#{full_config[:distributed_service_tables_suffix] || 'distributed'}"
|
28
19
|
end
|
29
20
|
|
30
|
-
|
31
|
-
|
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()' }
|
32
25
|
end
|
33
26
|
end
|
34
|
-
end
|
35
27
|
|
36
|
-
|
37
|
-
|
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)])
|
38
35
|
|
39
|
-
|
40
|
-
|
41
|
-
if row.nil? || row.value != value
|
42
|
-
create!(key: key, value: value)
|
43
|
-
end
|
44
|
-
end
|
36
|
+
connection.select_values(sm, "#{self.class} Load")
|
37
|
+
end
|
45
38
|
|
46
|
-
|
47
|
-
|
48
|
-
|
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
|
49
45
|
|
50
|
-
|
51
|
-
return if table_exists?
|
46
|
+
class InternalMetadata < ::ActiveRecord::InternalMetadata
|
52
47
|
|
53
|
-
|
54
|
-
|
55
|
-
id: false,
|
56
|
-
options: connection.adapter_name.downcase == 'clickhouse' ? 'ReplacingMergeTree(created_at) PARTITION BY key ORDER BY key' : '',
|
57
|
-
if_not_exists: true
|
58
|
-
}
|
59
|
-
full_config = connection.instance_variable_get(:@full_config) || {}
|
48
|
+
def create_table
|
49
|
+
return if table_exists? || !enabled?
|
60
50
|
|
61
|
-
|
62
|
-
|
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) || {}
|
63
58
|
|
64
|
-
|
65
|
-
|
59
|
+
if full_config[:distributed_service_tables]
|
60
|
+
table_options.merge!(with_distributed: table_name, sharding_key: 'cityHash64(created_at)')
|
66
61
|
|
67
|
-
|
68
|
-
t.string :key, **key_options
|
69
|
-
t.string :value
|
70
|
-
t.timestamps
|
71
|
-
end
|
62
|
+
distributed_suffix = "_#{full_config[:distributed_service_tables_suffix] || 'distributed'}"
|
72
63
|
end
|
73
|
-
end
|
74
|
-
end
|
75
64
|
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
@schema_migration = schema_migration
|
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
|
82
70
|
end
|
83
71
|
|
84
|
-
|
85
|
-
selected_migrations = if block_given?
|
86
|
-
migrations.select { |m| yield m }
|
87
|
-
else
|
88
|
-
migrations
|
89
|
-
end
|
72
|
+
private
|
90
73
|
|
91
|
-
|
74
|
+
def update_entry(key, new_value)
|
75
|
+
create_entry(key, new_value)
|
92
76
|
end
|
93
77
|
|
94
|
-
def
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
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
|
100
86
|
|
101
|
-
|
87
|
+
connection.select_all(sm, "#{self.class} Load").first
|
102
88
|
end
|
89
|
+
end
|
90
|
+
|
91
|
+
class MigrationContext < ::ActiveRecord::MigrationContext #:nodoc:
|
103
92
|
|
104
93
|
def get_all_versions
|
105
94
|
if schema_migration.table_exists?
|
106
|
-
schema_migration.
|
95
|
+
schema_migration.versions.map(&:to_i)
|
107
96
|
else
|
108
97
|
[]
|
109
98
|
end
|
@@ -111,36 +100,4 @@ module ClickhouseActiverecord
|
|
111
100
|
|
112
101
|
end
|
113
102
|
|
114
|
-
class Migrator < ::ActiveRecord::Migrator
|
115
|
-
|
116
|
-
def initialize(direction, migrations, schema_migration, target_version = nil)
|
117
|
-
@direction = direction
|
118
|
-
@target_version = target_version
|
119
|
-
@migrated_versions = nil
|
120
|
-
@migrations = migrations
|
121
|
-
@schema_migration = schema_migration
|
122
|
-
|
123
|
-
validate(@migrations)
|
124
|
-
|
125
|
-
@schema_migration.create_table
|
126
|
-
ClickhouseActiverecord::InternalMetadata.create_table
|
127
|
-
end
|
128
|
-
|
129
|
-
def record_version_state_after_migrating(version)
|
130
|
-
if down?
|
131
|
-
migrated.delete(version)
|
132
|
-
@schema_migration.create!(version: version.to_s, active: 0)
|
133
|
-
else
|
134
|
-
super
|
135
|
-
end
|
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
|
-
|
145
|
-
end
|
146
103
|
end
|
@@ -11,21 +11,65 @@ module CoreExtensions
|
|
11
11
|
self
|
12
12
|
end
|
13
13
|
|
14
|
+
# Define settings in the SETTINGS clause of the SELECT query. The setting value is applied only to that query and is reset to the default or previous value after the query is executed.
|
15
|
+
# For example:
|
16
|
+
#
|
17
|
+
# users = User.settings(optimize_read_in_order: 1, cast_keep_nullable: 1).where(name: 'John')
|
18
|
+
# # SELECT users.* FROM users WHERE users.name = 'John' SETTINGS optimize_read_in_order = 1, cast_keep_nullable = 1
|
19
|
+
#
|
20
|
+
# An <tt>ActiveRecord::ActiveRecordError</tt> will be raised if database not ClickHouse.
|
14
21
|
# @param [Hash] opts
|
15
22
|
def settings(**opts)
|
23
|
+
spawn.settings!(**opts)
|
24
|
+
end
|
25
|
+
|
26
|
+
# @param [Hash] opts
|
27
|
+
def settings!(**opts)
|
28
|
+
assert_mutability!
|
16
29
|
check_command('SETTINGS')
|
17
30
|
@values[:settings] = (@values[:settings] || {}).merge opts
|
18
31
|
self
|
19
32
|
end
|
20
33
|
|
34
|
+
# When FINAL is specified, ClickHouse fully merges the data before returning the result and thus performs all data transformations that happen during merges for the given table engine.
|
35
|
+
# For example:
|
36
|
+
#
|
37
|
+
# users = User.final.all
|
38
|
+
# # SELECT users.* FROM users FINAL
|
39
|
+
#
|
40
|
+
# An <tt>ActiveRecord::ActiveRecordError</tt> will be raised if database not ClickHouse.
|
21
41
|
# @param [Boolean] final
|
22
42
|
def final(final = true)
|
43
|
+
spawn.final!(final)
|
44
|
+
end
|
45
|
+
|
46
|
+
# @param [Boolean] final
|
47
|
+
def final!(final = true)
|
48
|
+
assert_mutability!
|
23
49
|
check_command('FINAL')
|
24
50
|
@table = @table.dup
|
25
51
|
@table.final = final
|
26
52
|
self
|
27
53
|
end
|
28
54
|
|
55
|
+
# The USING clause specifies one or more columns to join, which establishes the equality of these columns. For example:
|
56
|
+
#
|
57
|
+
# users = User.joins(:joins).using(:event_name, :date)
|
58
|
+
# # SELECT users.* FROM users INNER JOIN joins USING event_name,date
|
59
|
+
#
|
60
|
+
# An <tt>ActiveRecord::ActiveRecordError</tt> will be raised if database not ClickHouse.
|
61
|
+
# @param [Array] opts
|
62
|
+
def using(*opts)
|
63
|
+
spawn.using!(*opts)
|
64
|
+
end
|
65
|
+
|
66
|
+
# @param [Array] opts
|
67
|
+
def using!(*opts)
|
68
|
+
assert_mutability!
|
69
|
+
@values[:using] = opts
|
70
|
+
self
|
71
|
+
end
|
72
|
+
|
29
73
|
private
|
30
74
|
|
31
75
|
def check_command(cmd)
|
@@ -36,6 +80,7 @@ module CoreExtensions
|
|
36
80
|
arel = super
|
37
81
|
|
38
82
|
arel.settings(@values[:settings]) if @values[:settings].present?
|
83
|
+
arel.using(@values[:using]) if @values[:using].present?
|
39
84
|
|
40
85
|
arel
|
41
86
|
end
|
data/lib/tasks/clickhouse.rake
CHANGED
@@ -2,17 +2,13 @@
|
|
2
2
|
|
3
3
|
namespace :clickhouse do
|
4
4
|
task prepare_schema_migration_table: :environment do
|
5
|
-
|
5
|
+
connection = ActiveRecord::Tasks::DatabaseTasks.migration_connection
|
6
|
+
connection.schema_migration.create_table unless ENV['simple'] || ARGV.any? { |a| a.include?('--simple') }
|
6
7
|
end
|
7
8
|
|
8
9
|
task prepare_internal_metadata_table: :environment do
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
task load_config: :environment do
|
13
|
-
ENV['SCHEMA'] = 'db/clickhouse_schema.rb'
|
14
|
-
ActiveRecord::Migrator.migrations_paths = %w[db/migrate_clickhouse]
|
15
|
-
ActiveRecord::Base.establish_connection(:clickhouse)
|
10
|
+
connection = ActiveRecord::Tasks::DatabaseTasks.migration_connection
|
11
|
+
connection.internal_metadata.create_table unless ENV['simple'] || ARGV.any? { |a| a.include?('--simple') }
|
16
12
|
end
|
17
13
|
|
18
14
|
namespace :schema do
|
@@ -37,52 +33,55 @@ namespace :clickhouse do
|
|
37
33
|
|
38
34
|
namespace :structure do
|
39
35
|
desc 'Load database structure'
|
40
|
-
task load: [
|
36
|
+
task load: ['db:check_protected_environments'] do
|
41
37
|
config = ActiveRecord::Base.configurations.configs_for(env_name: Rails.env, name: 'clickhouse')
|
42
38
|
ClickhouseActiverecord::Tasks.new(config).structure_load(Rails.root.join('db/clickhouse_structure.sql'))
|
43
39
|
end
|
44
40
|
|
45
41
|
desc 'Dump database structure'
|
46
|
-
task dump: [
|
42
|
+
task dump: ['db:check_protected_environments'] do
|
47
43
|
config = ActiveRecord::Base.configurations.configs_for(env_name: Rails.env, name: 'clickhouse')
|
48
44
|
ClickhouseActiverecord::Tasks.new(config).structure_dump(Rails.root.join('db/clickhouse_structure.sql'))
|
49
45
|
end
|
50
46
|
end
|
51
47
|
|
52
48
|
desc 'Creates the database from DATABASE_URL or config/database.yml'
|
53
|
-
task create: [
|
49
|
+
task create: [] do
|
54
50
|
config = ActiveRecord::Base.configurations.configs_for(env_name: Rails.env, name: 'clickhouse')
|
55
51
|
ActiveRecord::Tasks::DatabaseTasks.create(config)
|
56
52
|
end
|
57
53
|
|
58
54
|
desc 'Drops the database from DATABASE_URL or config/database.yml'
|
59
|
-
task drop: [
|
55
|
+
task drop: ['db:check_protected_environments'] do
|
60
56
|
config = ActiveRecord::Base.configurations.configs_for(env_name: Rails.env, name: 'clickhouse')
|
61
57
|
ActiveRecord::Tasks::DatabaseTasks.drop(config)
|
62
58
|
end
|
63
59
|
|
64
60
|
desc 'Empty the database from DATABASE_URL or config/database.yml'
|
65
|
-
task purge: [
|
61
|
+
task purge: ['db:check_protected_environments'] do
|
66
62
|
config = ActiveRecord::Base.configurations.configs_for(env_name: Rails.env, name: 'clickhouse')
|
67
63
|
ActiveRecord::Tasks::DatabaseTasks.purge(config)
|
68
64
|
end
|
69
65
|
|
70
66
|
# desc 'Resets your database using your migrations for the current environment'
|
71
|
-
task reset
|
67
|
+
task :reset do
|
72
68
|
Rake::Task['clickhouse:purge'].execute
|
73
69
|
Rake::Task['clickhouse:migrate'].execute
|
74
70
|
end
|
75
71
|
|
76
72
|
desc 'Migrate the clickhouse database'
|
77
|
-
task migrate: %i[
|
78
|
-
Rake::Task['db:migrate'].execute
|
79
|
-
if File.
|
73
|
+
task migrate: %i[prepare_schema_migration_table prepare_internal_metadata_table] do
|
74
|
+
Rake::Task['db:migrate:clickhouse'].execute
|
75
|
+
if File.exist? "#{Rails.root}/db/clickhouse_schema_simple.rb"
|
80
76
|
Rake::Task['clickhouse:schema:dump'].execute(simple: true)
|
81
77
|
end
|
82
78
|
end
|
83
79
|
|
84
80
|
desc 'Rollback the clickhouse database'
|
85
|
-
task rollback: %i[
|
86
|
-
Rake::Task['db:rollback'].execute
|
81
|
+
task rollback: %i[prepare_schema_migration_table prepare_internal_metadata_table] do
|
82
|
+
Rake::Task['db:rollback:clickhouse'].execute
|
83
|
+
if File.exist? "#{Rails.root}/db/clickhouse_schema_simple.rb"
|
84
|
+
Rake::Task['clickhouse:schema:dump'].execute(simple: true)
|
85
|
+
end
|
87
86
|
end
|
88
87
|
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.
|
4
|
+
version: 1.0.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Sergey Odintsov
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2024-01-10 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -28,16 +28,16 @@ dependencies:
|
|
28
28
|
name: activerecord
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
30
30
|
requirements:
|
31
|
-
- - "
|
31
|
+
- - ">="
|
32
32
|
- !ruby/object:Gem::Version
|
33
|
-
version: 7.
|
33
|
+
version: '7.1'
|
34
34
|
type: :runtime
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
|
-
- - "
|
38
|
+
- - ">="
|
39
39
|
- !ruby/object:Gem::Version
|
40
|
-
version: 7.
|
40
|
+
version: '7.1'
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
42
|
name: rake
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|