clickhouse-activerecord 1.0.13 → 1.1.2
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/.github/workflows/testing.yml +28 -8
- data/.gitignore +1 -0
- data/CHANGELOG.md +12 -0
- data/README.md +1 -0
- data/clickhouse-activerecord.gemspec +1 -1
- data/lib/active_record/connection_adapters/clickhouse/oid/map.rb +68 -0
- data/lib/active_record/connection_adapters/clickhouse/quoting.rb +19 -0
- data/lib/active_record/connection_adapters/clickhouse/schema_creation.rb +3 -0
- data/lib/active_record/connection_adapters/clickhouse/schema_definitions.rb +1 -1
- data/lib/active_record/connection_adapters/clickhouse/schema_statements.rb +45 -28
- data/lib/active_record/connection_adapters/clickhouse_adapter.rb +61 -32
- data/lib/arel/visitors/clickhouse.rb +8 -0
- data/lib/clickhouse-activerecord/schema.rb +7 -1
- data/lib/clickhouse-activerecord/schema_dumper.rb +5 -19
- data/lib/clickhouse-activerecord/version.rb +1 -1
- data/lib/core_extensions/active_record/internal_metadata.rb +24 -7
- data/lib/core_extensions/active_record/relation.rb +24 -5
- data/lib/core_extensions/active_record/schema_migration.rb +8 -0
- data/lib/core_extensions/arel/select_manager.rb +12 -0
- metadata +6 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7c1652d6cf040ebaf43559441ccfa2496f4c408b232ca6d3d1c71da9bbc20209
|
4
|
+
data.tar.gz: 900d15344b100536b6f1e0ac1dd3e1df03b890fe5c1b6f7b6f9218283156c17f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d9a74ee0ca1e865f821d5c8a1f8612e30dcd790cd79725344ab568b246d30d895711a69f5f05542ff1989f194605a70d7906ec432c90bf2c0688f5a5b120b3ab
|
7
|
+
data.tar.gz: ddb82724f85c5a2cfb6da4db250d20f9fc6eb7cddf91a8bada889f07eb717f8657ad011d71e2ab19cb625b83b74dad91a8adeb1bcf94886f33fbd2cd999cc833
|
@@ -19,8 +19,16 @@ jobs:
|
|
19
19
|
fail-fast: true
|
20
20
|
max-parallel: 1
|
21
21
|
matrix:
|
22
|
-
|
23
|
-
|
22
|
+
version:
|
23
|
+
- ruby: 2.7
|
24
|
+
rails: 7.1.3
|
25
|
+
- ruby: 3.0
|
26
|
+
rails: 7.1.3
|
27
|
+
- ruby: 3.2
|
28
|
+
rails: 7.1.3
|
29
|
+
- ruby: 3.2
|
30
|
+
rails: 7.2.0
|
31
|
+
clickhouse: [ '22.1', '24.6' ]
|
24
32
|
|
25
33
|
steps:
|
26
34
|
- uses: actions/checkout@v4
|
@@ -33,10 +41,12 @@ jobs:
|
|
33
41
|
compose-file: '.docker/docker-compose.yml'
|
34
42
|
down-flags: '--volumes'
|
35
43
|
|
36
|
-
-
|
44
|
+
- run: echo 'gem "activerecord", "~> ${{ matrix.version.rails }}"' >> Gemfile
|
45
|
+
|
46
|
+
- name: Set up Ruby ${{ matrix.version.ruby }}
|
37
47
|
uses: ruby/setup-ruby@v1
|
38
48
|
with:
|
39
|
-
ruby-version: ${{ matrix.ruby
|
49
|
+
ruby-version: ${{ matrix.version.ruby }}
|
40
50
|
bundler-cache: true
|
41
51
|
|
42
52
|
- run: bundle exec rspec spec/single
|
@@ -54,8 +64,16 @@ jobs:
|
|
54
64
|
fail-fast: true
|
55
65
|
max-parallel: 1
|
56
66
|
matrix:
|
57
|
-
|
58
|
-
|
67
|
+
version:
|
68
|
+
- ruby: 2.7
|
69
|
+
rails: 7.1.3
|
70
|
+
- ruby: 3.0
|
71
|
+
rails: 7.1.3
|
72
|
+
- ruby: 3.2
|
73
|
+
rails: 7.1.3
|
74
|
+
- ruby: 3.2
|
75
|
+
rails: 7.2.0
|
76
|
+
clickhouse: [ '22.1', '24.6' ]
|
59
77
|
|
60
78
|
steps:
|
61
79
|
- uses: actions/checkout@v4
|
@@ -68,10 +86,12 @@ jobs:
|
|
68
86
|
compose-file: '.docker/docker-compose.cluster.yml'
|
69
87
|
down-flags: '--volumes'
|
70
88
|
|
71
|
-
-
|
89
|
+
- run: echo 'gem "activerecord", "~> ${{ matrix.version.rails }}"' >> Gemfile
|
90
|
+
|
91
|
+
- name: Set up Ruby ${{ matrix.version.ruby }}
|
72
92
|
uses: ruby/setup-ruby@v1
|
73
93
|
with:
|
74
|
-
ruby-version: ${{ matrix.ruby
|
94
|
+
ruby-version: ${{ matrix.version.ruby }}
|
75
95
|
bundler-cache: true
|
76
96
|
|
77
97
|
- run: bundle exec rspec spec/cluster
|
data/.gitignore
CHANGED
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,15 @@
|
|
1
|
+
### Version 1.1.2 (Aug 27, 2024)
|
2
|
+
* 🎉 Support for rails 7.2 #156
|
3
|
+
* Add method `views` for getting table `View` list in #152
|
4
|
+
* Add support for Map datatype in #144
|
5
|
+
* Add support window named functions
|
6
|
+
* Fix schema dumper default values for number
|
7
|
+
* Normalize table name in schema dump in #148
|
8
|
+
* Noop savepoint functionality in #150
|
9
|
+
* Fix `#find_by` in #153
|
10
|
+
* Add RSpec configure
|
11
|
+
* Fix detect model primary key
|
12
|
+
|
1
13
|
### Version 1.0.7 (Apr 27, 2024)
|
2
14
|
|
3
15
|
* Support table indexes
|
data/README.md
CHANGED
@@ -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'
|
@@ -0,0 +1,68 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActiveRecord
|
4
|
+
module ConnectionAdapters
|
5
|
+
module Clickhouse
|
6
|
+
module OID # :nodoc:
|
7
|
+
class Map < Type::Value # :nodoc:
|
8
|
+
|
9
|
+
def initialize(sql_type)
|
10
|
+
@subtype = case sql_type
|
11
|
+
when /U?Int\d+/
|
12
|
+
:integer
|
13
|
+
when /DateTime/
|
14
|
+
:datetime
|
15
|
+
when /Date/
|
16
|
+
:date
|
17
|
+
else
|
18
|
+
:string
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def type
|
23
|
+
@subtype
|
24
|
+
end
|
25
|
+
|
26
|
+
def deserialize(value)
|
27
|
+
if value.is_a?(::Hash)
|
28
|
+
value.map { |k, item| [k.to_s, deserialize(item)] }.to_h
|
29
|
+
else
|
30
|
+
return value if value.nil?
|
31
|
+
case @subtype
|
32
|
+
when :integer
|
33
|
+
value.to_i
|
34
|
+
when :datetime
|
35
|
+
::DateTime.parse(value)
|
36
|
+
when :date
|
37
|
+
::Date.parse(value)
|
38
|
+
else
|
39
|
+
super
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def serialize(value)
|
45
|
+
if value.is_a?(::Hash)
|
46
|
+
value.map { |k, item| [k.to_s, serialize(item)] }.to_h
|
47
|
+
else
|
48
|
+
return value if value.nil?
|
49
|
+
case @subtype
|
50
|
+
when :integer
|
51
|
+
value.to_i
|
52
|
+
when :datetime
|
53
|
+
DateTime.new.serialize(value)
|
54
|
+
when :date
|
55
|
+
Date.new.serialize(value)
|
56
|
+
when :string
|
57
|
+
value.to_s
|
58
|
+
else
|
59
|
+
super
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module ActiveRecord
|
2
|
+
module ConnectionAdapters
|
3
|
+
module Clickhouse
|
4
|
+
module Quoting
|
5
|
+
extend ActiveSupport::Concern
|
6
|
+
|
7
|
+
module ClassMethods # :nodoc:
|
8
|
+
def quote_column_name(name)
|
9
|
+
name.to_s.include?('.') ? "`#{name}`" : name.to_s
|
10
|
+
end
|
11
|
+
|
12
|
+
def quote_table_name(name)
|
13
|
+
name.to_s
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -33,6 +33,9 @@ module ActiveRecord
|
|
33
33
|
if options[:array]
|
34
34
|
sql.gsub!(/\s+(.*)/, ' Array(\1)')
|
35
35
|
end
|
36
|
+
if options[:map]
|
37
|
+
sql.gsub!(/\s+(.*)/, ' Map(String, \1)')
|
38
|
+
end
|
36
39
|
sql.gsub!(/(\sString)\(\d+\)/, '\1')
|
37
40
|
sql << " DEFAULT #{quote_default_expression(options[:default], options[:column])}" if options_include_default?(options)
|
38
41
|
sql
|
@@ -18,9 +18,16 @@ module ActiveRecord
|
|
18
18
|
true
|
19
19
|
end
|
20
20
|
|
21
|
-
def internal_exec_query(sql, name = nil, binds = [], prepare: false, async: false)
|
21
|
+
def internal_exec_query(sql, name = nil, binds = [], prepare: false, async: false, allow_retry: false)
|
22
22
|
result = do_execute(sql, name)
|
23
|
-
|
23
|
+
columns = result['meta'].map { |m| m['name'] }
|
24
|
+
types = {}
|
25
|
+
result['meta'].each_with_index do |m, i|
|
26
|
+
# need use column name and index after commit in 7.2:
|
27
|
+
# https://github.com/rails/rails/commit/24dbf7637b1d5cd6eb3d7100b8d0f6872c3fee3c
|
28
|
+
types[m['name']] = types[i] = type_map.lookup(m['type'])
|
29
|
+
end
|
30
|
+
ActiveRecord::Result.new(columns, result['data'], types)
|
24
31
|
rescue ActiveRecord::ActiveRecordError => e
|
25
32
|
raise e
|
26
33
|
rescue StandardError => e
|
@@ -57,6 +64,12 @@ module ActiveRecord
|
|
57
64
|
result['data'].flatten
|
58
65
|
end
|
59
66
|
|
67
|
+
def views(name = nil)
|
68
|
+
result = do_system_execute("SHOW TABLES WHERE engine = 'View'", name)
|
69
|
+
return [] if result.nil?
|
70
|
+
result['data'].flatten
|
71
|
+
end
|
72
|
+
|
60
73
|
def functions
|
61
74
|
result = do_system_execute("SELECT name FROM system.functions WHERE origin = 'SQLUserDefined'")
|
62
75
|
return [] if result.nil?
|
@@ -103,6 +116,20 @@ module ActiveRecord
|
|
103
116
|
end
|
104
117
|
end
|
105
118
|
|
119
|
+
if ::ActiveRecord::version >= Gem::Version.new('7.2')
|
120
|
+
def schema_migration
|
121
|
+
pool.schema_migration
|
122
|
+
end
|
123
|
+
|
124
|
+
def migration_context
|
125
|
+
pool.migration_context
|
126
|
+
end
|
127
|
+
|
128
|
+
def internal_metadata
|
129
|
+
pool.internal_metadata
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
106
133
|
def assume_migrated_upto_version(version, migrations_paths = nil)
|
107
134
|
version = version.to_i
|
108
135
|
sm_table = quote_table_name(schema_migration.table_name)
|
@@ -126,7 +153,7 @@ module ActiveRecord
|
|
126
153
|
# Fix insert_all method
|
127
154
|
# https://github.com/PNixx/clickhouse-activerecord/issues/71#issuecomment-1923244983
|
128
155
|
def with_yaml_fallback(value) # :nodoc:
|
129
|
-
if value.is_a?(Array)
|
156
|
+
if value.is_a?(Array) || value.is_a?(Hash)
|
130
157
|
value
|
131
158
|
else
|
132
159
|
super
|
@@ -191,9 +218,9 @@ module ActiveRecord
|
|
191
218
|
def new_column_from_field(table_name, field, _definitions)
|
192
219
|
sql_type = field[1]
|
193
220
|
type_metadata = fetch_type_metadata(sql_type)
|
194
|
-
|
195
|
-
|
196
|
-
|
221
|
+
default_value = extract_value_from_default(field[3], field[2])
|
222
|
+
default_function = extract_default_function(field[3])
|
223
|
+
default_value = lookup_cast_type(sql_type).cast(default_value)
|
197
224
|
ClickhouseColumn.new(field[0], default_value, type_metadata, field[1].include?('Nullable'), default_function)
|
198
225
|
end
|
199
226
|
|
@@ -212,32 +239,22 @@ module ActiveRecord
|
|
212
239
|
private
|
213
240
|
|
214
241
|
# Extracts the value from a PostgreSQL column default definition.
|
215
|
-
def extract_value_from_default(
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
# Object identifier types
|
224
|
-
when "''"
|
225
|
-
''
|
226
|
-
when /\A-?\d+\z/
|
227
|
-
$1
|
228
|
-
else
|
229
|
-
# Anything else is blank, some user type, or some function
|
230
|
-
# and we can't know the value of that, so return nil.
|
231
|
-
nil
|
232
|
-
end
|
242
|
+
def extract_value_from_default(default_expression, default_type)
|
243
|
+
return nil if default_type != 'DEFAULT' || default_expression.blank?
|
244
|
+
return nil if has_default_function?(default_expression)
|
245
|
+
|
246
|
+
# Convert string
|
247
|
+
return $1 if default_expression.match(/^'(.*?)'$/)
|
248
|
+
|
249
|
+
default_expression
|
233
250
|
end
|
234
251
|
|
235
|
-
def extract_default_function(
|
236
|
-
default if has_default_function?(
|
252
|
+
def extract_default_function(default) # :nodoc:
|
253
|
+
default if has_default_function?(default)
|
237
254
|
end
|
238
255
|
|
239
|
-
def has_default_function?(
|
240
|
-
|
256
|
+
def has_default_function?(default) # :nodoc:
|
257
|
+
(%r{\w+\(.*\)} === default)
|
241
258
|
end
|
242
259
|
|
243
260
|
def format_body_response(body, format)
|
@@ -8,7 +8,9 @@ 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/map'
|
11
12
|
require 'active_record/connection_adapters/clickhouse/oid/uuid'
|
13
|
+
require 'active_record/connection_adapters/clickhouse/quoting'
|
12
14
|
require 'active_record/connection_adapters/clickhouse/schema_definitions'
|
13
15
|
require 'active_record/connection_adapters/clickhouse/schema_creation'
|
14
16
|
require 'active_record/connection_adapters/clickhouse/schema_statements'
|
@@ -22,30 +24,11 @@ module ActiveRecord
|
|
22
24
|
def clickhouse_connection(config)
|
23
25
|
config = config.symbolize_keys
|
24
26
|
|
25
|
-
|
26
|
-
connection = {
|
27
|
-
connection: config[:connection]
|
28
|
-
}
|
29
|
-
else
|
30
|
-
port = config[:port] || 8123
|
31
|
-
connection = {
|
32
|
-
host: config[:host] || 'localhost',
|
33
|
-
port: port,
|
34
|
-
ssl: config[:ssl].present? ? config[:ssl] : port == 443,
|
35
|
-
sslca: config[:sslca],
|
36
|
-
read_timeout: config[:read_timeout],
|
37
|
-
write_timeout: config[:write_timeout],
|
38
|
-
keep_alive_timeout: config[:keep_alive_timeout]
|
39
|
-
}
|
40
|
-
end
|
41
|
-
|
42
|
-
if config.key?(:database)
|
43
|
-
database = config[:database]
|
44
|
-
else
|
27
|
+
unless config.key?(:database)
|
45
28
|
raise ArgumentError, 'No database specified. Missing argument: database.'
|
46
29
|
end
|
47
30
|
|
48
|
-
ConnectionAdapters::ClickhouseAdapter.new(
|
31
|
+
ConnectionAdapters::ClickhouseAdapter.new(config)
|
49
32
|
end
|
50
33
|
end
|
51
34
|
end
|
@@ -64,7 +47,7 @@ module ActiveRecord
|
|
64
47
|
|
65
48
|
module ModelSchema
|
66
49
|
module ClassMethods
|
67
|
-
delegate :final, :final!, :settings, :settings!, to: :all
|
50
|
+
delegate :final, :final!, :settings, :settings!, :window, :window!, to: :all
|
68
51
|
|
69
52
|
def is_view
|
70
53
|
@is_view || false
|
@@ -82,11 +65,21 @@ module ActiveRecord
|
|
82
65
|
end
|
83
66
|
|
84
67
|
module ConnectionAdapters
|
85
|
-
class ClickhouseColumn < Column
|
86
68
|
|
69
|
+
if ActiveRecord::version >= Gem::Version.new('7.2')
|
70
|
+
register "clickhouse", "ActiveRecord::ConnectionAdapters::ClickhouseAdapter", "active_record/connection_adapters/clickhouse_adapter"
|
71
|
+
end
|
72
|
+
|
73
|
+
class ClickhouseColumn < Column
|
74
|
+
private
|
75
|
+
def deduplicated
|
76
|
+
self
|
77
|
+
end
|
87
78
|
end
|
88
79
|
|
89
80
|
class ClickhouseAdapter < AbstractAdapter
|
81
|
+
include Clickhouse::Quoting
|
82
|
+
|
90
83
|
ADAPTER_NAME = 'Clickhouse'.freeze
|
91
84
|
NATIVE_DATABASE_TYPES = {
|
92
85
|
string: { name: 'String' },
|
@@ -121,18 +114,39 @@ module ActiveRecord
|
|
121
114
|
include Clickhouse::SchemaStatements
|
122
115
|
|
123
116
|
# Initializes and connects a Clickhouse adapter.
|
124
|
-
def initialize(
|
125
|
-
super
|
126
|
-
@
|
127
|
-
|
128
|
-
|
129
|
-
|
117
|
+
def initialize(config_or_deprecated_connection, deprecated_logger = nil, deprecated_connection_options = nil, deprecated_config = nil)
|
118
|
+
super
|
119
|
+
if @config[:connection]
|
120
|
+
connection = {
|
121
|
+
connection: @config[:connection]
|
122
|
+
}
|
123
|
+
else
|
124
|
+
port = @config[:port] || 8123
|
125
|
+
connection = {
|
126
|
+
host: @config[:host] || 'localhost',
|
127
|
+
port: port,
|
128
|
+
ssl: @config[:ssl].present? ? @config[:ssl] : port == 443,
|
129
|
+
sslca: @config[:sslca],
|
130
|
+
read_timeout: @config[:read_timeout],
|
131
|
+
write_timeout: @config[:write_timeout],
|
132
|
+
keep_alive_timeout: @config[:keep_alive_timeout]
|
133
|
+
}
|
134
|
+
end
|
135
|
+
@connection_parameters = connection
|
136
|
+
|
137
|
+
@connection_config = { user: @config[:username], password: @config[:password], database: @config[:database] }.compact
|
138
|
+
@debug = @config[:debug] || false
|
130
139
|
|
131
140
|
@prepared_statements = false
|
132
141
|
|
133
142
|
connect
|
134
143
|
end
|
135
144
|
|
145
|
+
# Return ClickHouse server version
|
146
|
+
def server_version
|
147
|
+
@server_version ||= do_system_execute('SELECT version()')['data'][0][0]
|
148
|
+
end
|
149
|
+
|
136
150
|
# Savepoints are not supported, noop
|
137
151
|
def create_savepoint(name)
|
138
152
|
end
|
@@ -221,6 +235,10 @@ module ActiveRecord
|
|
221
235
|
m.register_type(%r(Array)) do |sql_type|
|
222
236
|
Clickhouse::OID::Array.new(sql_type)
|
223
237
|
end
|
238
|
+
|
239
|
+
m.register_type(%r(Map)) do |sql_type|
|
240
|
+
Clickhouse::OID::Map.new(sql_type)
|
241
|
+
end
|
224
242
|
end
|
225
243
|
end
|
226
244
|
|
@@ -233,6 +251,8 @@ module ActiveRecord
|
|
233
251
|
case value
|
234
252
|
when Array
|
235
253
|
'[' + value.map { |v| quote(v) }.join(', ') + ']'
|
254
|
+
when Hash
|
255
|
+
'{' + value.map { |k, v| "#{quote(k)}: #{quote(v)}" }.join(', ') + '}'
|
236
256
|
else
|
237
257
|
super
|
238
258
|
end
|
@@ -261,10 +281,15 @@ module ActiveRecord
|
|
261
281
|
|
262
282
|
# SCHEMA STATEMENTS ========================================
|
263
283
|
|
264
|
-
def
|
284
|
+
def primary_keys(table_name)
|
285
|
+
if server_version.to_f >= 23.4
|
286
|
+
structure = do_system_execute("SHOW COLUMNS FROM `#{table_name}`")
|
287
|
+
return structure['data'].select {|m| m[3]&.include?('PRI') }.pluck(0)
|
288
|
+
end
|
289
|
+
|
265
290
|
pk = table_structure(table_name).first
|
266
|
-
return 'id' if pk.present? && pk[0] == 'id'
|
267
|
-
|
291
|
+
return ['id'] if pk.present? && pk[0] == 'id'
|
292
|
+
[]
|
268
293
|
end
|
269
294
|
|
270
295
|
def create_schema_dumper(options) # :nodoc:
|
@@ -517,6 +542,10 @@ module ActiveRecord
|
|
517
542
|
@connection
|
518
543
|
end
|
519
544
|
|
545
|
+
def reconnect
|
546
|
+
connect
|
547
|
+
end
|
548
|
+
|
520
549
|
def apply_replica(table, options)
|
521
550
|
if use_replica? && options[:options]
|
522
551
|
if options[:options].match(/^Replicated/)
|
@@ -74,6 +74,14 @@ module Arel
|
|
74
74
|
infix_value o, collector, op
|
75
75
|
end
|
76
76
|
|
77
|
+
def visit_Arel_Nodes_Rows(o, collector)
|
78
|
+
if o.expr.is_a?(String)
|
79
|
+
collector << "ROWS #{o.expr}"
|
80
|
+
else
|
81
|
+
super
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
77
85
|
def sanitize_as_setting_value(value)
|
78
86
|
if value == :default
|
79
87
|
'DEFAULT'
|
@@ -2,6 +2,12 @@
|
|
2
2
|
|
3
3
|
module ClickhouseActiverecord
|
4
4
|
class Schema < ::ActiveRecord::Schema
|
5
|
-
|
5
|
+
def define(...)
|
6
|
+
ActiveRecord.deprecator.warn(<<~MSG)
|
7
|
+
ClickhouseActiverecord::Schema is deprecated
|
8
|
+
and will be removed in 1.2 version. Use ActiveRecord::Schema instead.
|
9
|
+
MSG
|
10
|
+
super
|
11
|
+
end
|
6
12
|
end
|
7
13
|
end
|
@@ -14,25 +14,6 @@ module ClickhouseActiverecord
|
|
14
14
|
|
15
15
|
private
|
16
16
|
|
17
|
-
def header(stream)
|
18
|
-
stream.puts <<HEADER
|
19
|
-
# This file is auto-generated from the current state of the database. Instead
|
20
|
-
# of editing this file, please use the migrations feature of Active Record to
|
21
|
-
# incrementally modify your database, and then regenerate this schema definition.
|
22
|
-
#
|
23
|
-
# This file is the source Rails uses to define your schema when running `rails
|
24
|
-
# #{simple ? 'db' : 'clickhouse'}:schema:load`. When creating a new database, `rails #{simple ? 'db' : 'clickhouse'}:schema:load` tends to
|
25
|
-
# be faster and is potentially less error prone than running all of your
|
26
|
-
# migrations from scratch. Old migrations may fail to apply correctly if those
|
27
|
-
# migrations use external dependencies or application code.
|
28
|
-
#
|
29
|
-
# It's strongly recommended that you check this file into your version control system.
|
30
|
-
|
31
|
-
#{simple ? 'ActiveRecord' : 'ClickhouseActiverecord'}::Schema.define(#{define_params}) do
|
32
|
-
|
33
|
-
HEADER
|
34
|
-
end
|
35
|
-
|
36
17
|
def tables(stream)
|
37
18
|
functions = @connection.functions
|
38
19
|
functions.each do |function|
|
@@ -168,6 +149,10 @@ HEADER
|
|
168
149
|
(column.sql_type =~ /Array?\(/).nil? ? nil : true
|
169
150
|
end
|
170
151
|
|
152
|
+
def schema_map(column)
|
153
|
+
(column.sql_type =~ /Map?\(/).nil? ? nil : true
|
154
|
+
end
|
155
|
+
|
171
156
|
def schema_low_cardinality(column)
|
172
157
|
(column.sql_type =~ /LowCardinality?\(/).nil? ? nil : true
|
173
158
|
end
|
@@ -176,6 +161,7 @@ HEADER
|
|
176
161
|
spec = {}
|
177
162
|
spec[:unsigned] = schema_unsigned(column)
|
178
163
|
spec[:array] = schema_array(column)
|
164
|
+
spec[:map] = schema_map(column)
|
179
165
|
spec[:low_cardinality] = schema_low_cardinality(column)
|
180
166
|
spec.merge(super).compact
|
181
167
|
end
|
@@ -4,7 +4,7 @@ module CoreExtensions
|
|
4
4
|
|
5
5
|
def create_table
|
6
6
|
return super unless connection.is_a?(::ActiveRecord::ConnectionAdapters::ClickhouseAdapter)
|
7
|
-
return if
|
7
|
+
return if !enabled? || table_exists?
|
8
8
|
|
9
9
|
key_options = connection.internal_string_options_for_primary_key
|
10
10
|
table_options = {
|
@@ -31,14 +31,23 @@ module CoreExtensions
|
|
31
31
|
|
32
32
|
private
|
33
33
|
|
34
|
-
def update_entry(
|
35
|
-
|
36
|
-
|
37
|
-
|
34
|
+
def update_entry(connection_or_key, key_or_new_value, new_value = nil)
|
35
|
+
if ::ActiveRecord::version >= Gem::Version.new('7.2')
|
36
|
+
return super unless connection.is_a?(::ActiveRecord::ConnectionAdapters::ClickhouseAdapter)
|
37
|
+
create_entry(connection_or_key, key_or_new_value, new_value)
|
38
|
+
else
|
39
|
+
return super(connection_or_key, key_or_new_value) unless connection.is_a?(::ActiveRecord::ConnectionAdapters::ClickhouseAdapter)
|
40
|
+
create_entry(connection_or_key, key_or_new_value)
|
41
|
+
end
|
38
42
|
end
|
39
43
|
|
40
|
-
def select_entry(key)
|
41
|
-
|
44
|
+
def select_entry(connection_or_key, key = nil)
|
45
|
+
if ::ActiveRecord::version >= Gem::Version.new('7.2')
|
46
|
+
return super unless connection.is_a?(::ActiveRecord::ConnectionAdapters::ClickhouseAdapter)
|
47
|
+
else
|
48
|
+
key = connection_or_key
|
49
|
+
return super(key) unless connection.is_a?(::ActiveRecord::ConnectionAdapters::ClickhouseAdapter)
|
50
|
+
end
|
42
51
|
|
43
52
|
sm = ::Arel::SelectManager.new(arel_table)
|
44
53
|
sm.final! if connection.table_options(table_name)[:options] =~ /^ReplacingMergeTree/
|
@@ -49,6 +58,14 @@ module CoreExtensions
|
|
49
58
|
|
50
59
|
connection.select_one(sm, "#{self.class} Load")
|
51
60
|
end
|
61
|
+
|
62
|
+
def connection
|
63
|
+
if ::ActiveRecord::version >= Gem::Version.new('7.2')
|
64
|
+
@pool.lease_connection
|
65
|
+
else
|
66
|
+
super
|
67
|
+
end
|
68
|
+
end
|
52
69
|
end
|
53
70
|
end
|
54
71
|
end
|
@@ -25,7 +25,6 @@ module CoreExtensions
|
|
25
25
|
|
26
26
|
# @param [Hash] opts
|
27
27
|
def settings!(**opts)
|
28
|
-
assert_mutability!
|
29
28
|
check_command('SETTINGS')
|
30
29
|
@values[:settings] = (@values[:settings] || {}).merge opts
|
31
30
|
self
|
@@ -43,7 +42,6 @@ module CoreExtensions
|
|
43
42
|
end
|
44
43
|
|
45
44
|
def final!
|
46
|
-
assert_mutability!
|
47
45
|
check_command('FINAL')
|
48
46
|
@values[:final] = true
|
49
47
|
self
|
@@ -62,23 +60,44 @@ module CoreExtensions
|
|
62
60
|
|
63
61
|
# @param [Array] opts
|
64
62
|
def using!(*opts)
|
65
|
-
assert_mutability!
|
66
63
|
@values[:using] = opts
|
67
64
|
self
|
68
65
|
end
|
69
66
|
|
67
|
+
# Windows functions let you perform calculations across a set of rows that are related to the current row. For example:
|
68
|
+
#
|
69
|
+
# users = User.window('x', order: 'date', partition: 'name', rows: 'UNBOUNDED PRECEDING').select('sum(value) OVER x')
|
70
|
+
# # SELECT sum(value) OVER x FROM users WINDOW x AS (PARTITION BY name ORDER BY date ROWS UNBOUNDED PRECEDING)
|
71
|
+
#
|
72
|
+
# @param [String] name
|
73
|
+
# @param [Hash] opts
|
74
|
+
def window(name, **opts)
|
75
|
+
spawn.window!(name, **opts)
|
76
|
+
end
|
77
|
+
|
78
|
+
def window!(name, **opts)
|
79
|
+
@values[:windows] = [] unless @values[:windows]
|
80
|
+
@values[:windows] << [name, opts]
|
81
|
+
self
|
82
|
+
end
|
83
|
+
|
70
84
|
private
|
71
85
|
|
72
86
|
def check_command(cmd)
|
73
87
|
raise ::ActiveRecord::ActiveRecordError, cmd + ' is a ClickHouse specific query clause' unless connection.is_a?(::ActiveRecord::ConnectionAdapters::ClickhouseAdapter)
|
74
88
|
end
|
75
89
|
|
76
|
-
def build_arel(aliases = nil)
|
77
|
-
|
90
|
+
def build_arel(connection_or_aliases = nil, aliases = nil)
|
91
|
+
if ::ActiveRecord::version >= Gem::Version.new('7.2')
|
92
|
+
arel = super
|
93
|
+
else
|
94
|
+
arel = super(connection_or_aliases)
|
95
|
+
end
|
78
96
|
|
79
97
|
arel.final! if @values[:final].present?
|
80
98
|
arel.settings(@values[:settings]) if @values[:settings].present?
|
81
99
|
arel.using(@values[:using]) if @values[:using].present?
|
100
|
+
arel.windows(@values[:windows]) if @values[:windows].present?
|
82
101
|
|
83
102
|
arel
|
84
103
|
end
|
@@ -13,6 +13,18 @@ module CoreExtensions
|
|
13
13
|
self
|
14
14
|
end
|
15
15
|
|
16
|
+
# @param [Array] windows
|
17
|
+
def windows(windows)
|
18
|
+
@ctx.windows = windows.map do |name, opts|
|
19
|
+
# https://github.com/rails/rails/blob/main/activerecord/test/cases/arel/select_manager_test.rb#L790
|
20
|
+
window = ::Arel::Nodes::NamedWindow.new(name)
|
21
|
+
opts.each do |key, value|
|
22
|
+
window.send(key, value)
|
23
|
+
end
|
24
|
+
window
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
16
28
|
def using(*exprs)
|
17
29
|
@ctx.source.right.last.right = ::Arel::Nodes::Using.new(::Arel.sql(exprs.join(',')))
|
18
30
|
self
|
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.
|
4
|
+
version: 1.1.2
|
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-08-
|
11
|
+
date: 2024-08-27 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -28,14 +28,14 @@ dependencies:
|
|
28
28
|
name: activerecord
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
30
30
|
requirements:
|
31
|
-
- - "
|
31
|
+
- - "~>"
|
32
32
|
- !ruby/object:Gem::Version
|
33
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
40
|
version: '7.1'
|
41
41
|
- !ruby/object:Gem::Dependency
|
@@ -110,7 +110,9 @@ files:
|
|
110
110
|
- lib/active_record/connection_adapters/clickhouse/oid/big_integer.rb
|
111
111
|
- lib/active_record/connection_adapters/clickhouse/oid/date.rb
|
112
112
|
- lib/active_record/connection_adapters/clickhouse/oid/date_time.rb
|
113
|
+
- lib/active_record/connection_adapters/clickhouse/oid/map.rb
|
113
114
|
- lib/active_record/connection_adapters/clickhouse/oid/uuid.rb
|
115
|
+
- lib/active_record/connection_adapters/clickhouse/quoting.rb
|
114
116
|
- lib/active_record/connection_adapters/clickhouse/schema_creation.rb
|
115
117
|
- lib/active_record/connection_adapters/clickhouse/schema_definitions.rb
|
116
118
|
- lib/active_record/connection_adapters/clickhouse/schema_statements.rb
|