clickhouse-activerecord 1.1.3 → 1.2.0
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 +3 -3
- data/README.md +2 -1
- data/lib/active_record/connection_adapters/clickhouse/column.rb +21 -0
- data/lib/active_record/connection_adapters/clickhouse/oid/map.rb +4 -0
- data/lib/active_record/connection_adapters/clickhouse/schema_creation.rb +7 -1
- data/lib/active_record/connection_adapters/clickhouse/schema_statements.rb +6 -2
- data/lib/active_record/connection_adapters/clickhouse/{schema_definitions.rb → table_definition.rb} +6 -1
- data/lib/active_record/connection_adapters/clickhouse_adapter.rb +12 -11
- data/lib/arel/nodes/grouping_sets.rb +32 -0
- data/lib/arel/nodes/limit_by.rb +17 -0
- data/lib/arel/visitors/clickhouse.rb +30 -0
- data/lib/clickhouse-activerecord/schema_dumper.rb +23 -10
- data/lib/clickhouse-activerecord/tasks.rb +2 -0
- data/lib/clickhouse-activerecord/version.rb +1 -1
- data/lib/core_extensions/active_record/relation.rb +46 -0
- data/lib/core_extensions/arel/nodes/select_statement.rb +5 -2
- data/lib/core_extensions/arel/select_manager.rb +5 -0
- metadata +6 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c3301f161ab9a3c94507a1d3a989e32710628746eeeac895966e64dd25579dd4
|
4
|
+
data.tar.gz: 2ab20aabc31a593987fb8905b5c58f023025a24cf447d0d922644ebbcff63397
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f2d35a1489031909c0a2b8c2f8feb5624b93854bfbcabd9e39e006874033d0b534d568e72deabb795fcbeb1c0a3416966df8168b6f10d1ef64a3bb36d96b838f
|
7
|
+
data.tar.gz: 9c6be79ff71e1207cd2b162e909db8b7db8a2276ce17351b2b2fc38097dc47f8c4dd8386cfacdc37bd1ccd3cfc28072dc45bd09b155737ef28b63a3a55f35def
|
@@ -27,7 +27,7 @@ jobs:
|
|
27
27
|
- ruby: 3.2
|
28
28
|
rails: 7.1.3
|
29
29
|
- ruby: 3.2
|
30
|
-
rails: 7.2.
|
30
|
+
rails: 7.2.1
|
31
31
|
clickhouse: [ '22.1', '24.6' ]
|
32
32
|
|
33
33
|
steps:
|
@@ -49,7 +49,7 @@ jobs:
|
|
49
49
|
ruby-version: ${{ matrix.version.ruby }}
|
50
50
|
bundler-cache: true
|
51
51
|
|
52
|
-
- run: bundle exec rspec spec/single
|
52
|
+
- run: bundle exec rspec spec/single --format progress
|
53
53
|
|
54
54
|
tests_cluster:
|
55
55
|
name: Testing cluster server
|
@@ -94,4 +94,4 @@ jobs:
|
|
94
94
|
ruby-version: ${{ matrix.version.ruby }}
|
95
95
|
bundler-cache: true
|
96
96
|
|
97
|
-
- run: bundle exec rspec spec/cluster
|
97
|
+
- run: bundle exec rspec spec/cluster --format progress
|
data/README.md
CHANGED
@@ -237,12 +237,13 @@ class CreateDataItems < ActiveRecord::Migration[7.1]
|
|
237
237
|
end
|
238
238
|
```
|
239
239
|
|
240
|
-
Create table with custom column structure:
|
240
|
+
Create table with custom column structure and codec compression:
|
241
241
|
|
242
242
|
```ruby
|
243
243
|
class CreateDataItems < ActiveRecord::Migration[7.1]
|
244
244
|
def change
|
245
245
|
create_table "data_items", id: false, options: "MergeTree PARTITION BY toYYYYMM(timestamp) ORDER BY timestamp", force: :cascade do |t|
|
246
|
+
t.integer :user_id, limit: 8, codec: 'DoubleDelta, LZ4'
|
246
247
|
t.column "timestamp", "DateTime('UTC') CODEC(DoubleDelta, LZ4)"
|
247
248
|
end
|
248
249
|
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module ActiveRecord
|
2
|
+
module ConnectionAdapters
|
3
|
+
module Clickhouse
|
4
|
+
class Column < ActiveRecord::ConnectionAdapters::Column
|
5
|
+
|
6
|
+
attr_reader :codec
|
7
|
+
|
8
|
+
def initialize(name, default, sql_type_metadata = nil, null = true, default_function = nil, codec: nil, **args)
|
9
|
+
super
|
10
|
+
@codec = codec
|
11
|
+
end
|
12
|
+
|
13
|
+
private
|
14
|
+
|
15
|
+
def deduplicated
|
16
|
+
self
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -26,6 +26,8 @@ module ActiveRecord
|
|
26
26
|
def deserialize(value)
|
27
27
|
if value.is_a?(::Hash)
|
28
28
|
value.map { |k, item| [k.to_s, deserialize(item)] }.to_h
|
29
|
+
elsif value.is_a?(::Array)
|
30
|
+
value.map { |item| deserialize(item) }
|
29
31
|
else
|
30
32
|
return value if value.nil?
|
31
33
|
case @subtype
|
@@ -44,6 +46,8 @@ module ActiveRecord
|
|
44
46
|
def serialize(value)
|
45
47
|
if value.is_a?(::Hash)
|
46
48
|
value.map { |k, item| [k.to_s, serialize(item)] }.to_h
|
49
|
+
elsif value.is_a?(::Array)
|
50
|
+
value.map { |item| serialize(item) }
|
47
51
|
else
|
48
52
|
return value if value.nil?
|
49
53
|
case @subtype
|
@@ -33,9 +33,15 @@ module ActiveRecord
|
|
33
33
|
if options[:array]
|
34
34
|
sql.gsub!(/\s+(.*)/, ' Array(\1)')
|
35
35
|
end
|
36
|
-
if options[:map]
|
36
|
+
if options[:map] == :array
|
37
|
+
sql.gsub!(/\s+(.*)/, ' Map(String, Array(\1))')
|
38
|
+
end
|
39
|
+
if options[:map] == true
|
37
40
|
sql.gsub!(/\s+(.*)/, ' Map(String, \1)')
|
38
41
|
end
|
42
|
+
if options[:codec]
|
43
|
+
sql.gsub!(/\s+(.*)/, " \\1 CODEC(#{options[:codec]})")
|
44
|
+
end
|
39
45
|
sql.gsub!(/(\sString)\(\d+\)/, '\1')
|
40
46
|
sql << " DEFAULT #{quote_default_expression(options[:default], options[:column])}" if options_include_default?(options)
|
41
47
|
sql
|
@@ -8,6 +8,8 @@ module ActiveRecord
|
|
8
8
|
module SchemaStatements
|
9
9
|
DEFAULT_RESPONSE_FORMAT = 'JSONCompactEachRowWithNamesAndTypes'.freeze
|
10
10
|
|
11
|
+
DB_EXCEPTION_REGEXP = /\ACode:\s+\d+\.\s+DB::Exception:/.freeze
|
12
|
+
|
11
13
|
def execute(sql, name = nil, settings: {})
|
12
14
|
do_execute(sql, name, settings: settings)
|
13
15
|
end
|
@@ -183,7 +185,9 @@ module ActiveRecord
|
|
183
185
|
def process_response(res, format, sql = nil)
|
184
186
|
case res.code.to_i
|
185
187
|
when 200
|
186
|
-
|
188
|
+
body = res.body
|
189
|
+
|
190
|
+
if body.include?("DB::Exception") && body.match?(DB_EXCEPTION_REGEXP)
|
187
191
|
raise ActiveRecord::ActiveRecordError, "Response code: #{res.code}:\n#{res.body}#{sql ? "\nQuery: #{sql}" : ''}"
|
188
192
|
else
|
189
193
|
format_body_response(res.body, format)
|
@@ -221,7 +225,7 @@ module ActiveRecord
|
|
221
225
|
default_value = extract_value_from_default(field[3], field[2])
|
222
226
|
default_function = extract_default_function(field[3])
|
223
227
|
default_value = lookup_cast_type(sql_type).cast(default_value)
|
224
|
-
|
228
|
+
Clickhouse::Column.new(field[0], default_value, type_metadata, field[1].include?('Nullable'), default_function, codec: field[5].presence)
|
225
229
|
end
|
226
230
|
|
227
231
|
protected
|
data/lib/active_record/connection_adapters/clickhouse/{schema_definitions.rb → table_definition.rb}
RENAMED
@@ -94,10 +94,15 @@ module ActiveRecord
|
|
94
94
|
args.each { |name| column(name, kind, **options.except(:limit)) }
|
95
95
|
end
|
96
96
|
|
97
|
+
def column(name, type, index: nil, **options)
|
98
|
+
options[:null] = false if type.match?(/Nullable\([^)]+\)/)
|
99
|
+
super(name, type, index: index, **options)
|
100
|
+
end
|
101
|
+
|
97
102
|
private
|
98
103
|
|
99
104
|
def valid_column_definition_options
|
100
|
-
super + [:array, :low_cardinality, :fixed_string, :value, :type, :map]
|
105
|
+
super + [:array, :low_cardinality, :fixed_string, :value, :type, :map, :codec]
|
101
106
|
end
|
102
107
|
end
|
103
108
|
|
@@ -2,18 +2,21 @@
|
|
2
2
|
|
3
3
|
require 'arel/visitors/clickhouse'
|
4
4
|
require 'arel/nodes/final'
|
5
|
+
require 'arel/nodes/grouping_sets'
|
5
6
|
require 'arel/nodes/settings'
|
6
7
|
require 'arel/nodes/using'
|
8
|
+
require 'arel/nodes/limit_by'
|
7
9
|
require 'active_record/connection_adapters/clickhouse/oid/array'
|
8
10
|
require 'active_record/connection_adapters/clickhouse/oid/date'
|
9
11
|
require 'active_record/connection_adapters/clickhouse/oid/date_time'
|
10
12
|
require 'active_record/connection_adapters/clickhouse/oid/big_integer'
|
11
13
|
require 'active_record/connection_adapters/clickhouse/oid/map'
|
12
14
|
require 'active_record/connection_adapters/clickhouse/oid/uuid'
|
15
|
+
require 'active_record/connection_adapters/clickhouse/column'
|
13
16
|
require 'active_record/connection_adapters/clickhouse/quoting'
|
14
|
-
require 'active_record/connection_adapters/clickhouse/schema_definitions'
|
15
17
|
require 'active_record/connection_adapters/clickhouse/schema_creation'
|
16
18
|
require 'active_record/connection_adapters/clickhouse/schema_statements'
|
19
|
+
require 'active_record/connection_adapters/clickhouse/table_definition'
|
17
20
|
require 'net/http'
|
18
21
|
require 'openssl'
|
19
22
|
|
@@ -47,7 +50,12 @@ module ActiveRecord
|
|
47
50
|
|
48
51
|
module ModelSchema
|
49
52
|
module ClassMethods
|
50
|
-
delegate :final, :final!,
|
53
|
+
delegate :final, :final!,
|
54
|
+
:group_by_grouping_sets, :group_by_grouping_sets!,
|
55
|
+
:settings, :settings!,
|
56
|
+
:window, :window!,
|
57
|
+
:limit_by, :limit_by!,
|
58
|
+
to: :all
|
51
59
|
|
52
60
|
def is_view
|
53
61
|
@is_view || false
|
@@ -70,13 +78,6 @@ module ActiveRecord
|
|
70
78
|
register "clickhouse", "ActiveRecord::ConnectionAdapters::ClickhouseAdapter", "active_record/connection_adapters/clickhouse_adapter"
|
71
79
|
end
|
72
80
|
|
73
|
-
class ClickhouseColumn < Column
|
74
|
-
private
|
75
|
-
def deduplicated
|
76
|
-
self
|
77
|
-
end
|
78
|
-
end
|
79
|
-
|
80
81
|
class ClickhouseAdapter < AbstractAdapter
|
81
82
|
include Clickhouse::Quoting
|
82
83
|
|
@@ -351,8 +352,8 @@ module ActiveRecord
|
|
351
352
|
end
|
352
353
|
end
|
353
354
|
|
354
|
-
def create_function(name, body)
|
355
|
-
fd = "CREATE FUNCTION #{apply_cluster(quote_table_name(name))} AS #{body}"
|
355
|
+
def create_function(name, body, **options)
|
356
|
+
fd = "CREATE#{' OR REPLACE' if options[:force]} FUNCTION #{apply_cluster(quote_table_name(name))} AS #{body}"
|
356
357
|
do_execute(fd, format: nil)
|
357
358
|
end
|
358
359
|
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Arel # :nodoc: all
|
4
|
+
module Nodes
|
5
|
+
class GroupingSets < Arel::Nodes::Unary
|
6
|
+
|
7
|
+
def initialize(expr)
|
8
|
+
super
|
9
|
+
@expr = wrap_grouping_sets(expr)
|
10
|
+
end
|
11
|
+
|
12
|
+
private
|
13
|
+
|
14
|
+
def wrap_grouping_sets(sets)
|
15
|
+
sets.map do |element|
|
16
|
+
# See Arel::SelectManager#group
|
17
|
+
case element
|
18
|
+
when Array
|
19
|
+
wrap_grouping_sets(element)
|
20
|
+
when String
|
21
|
+
::Arel::Nodes::SqlLiteral.new(element)
|
22
|
+
when Symbol
|
23
|
+
::Arel::Nodes::SqlLiteral.new(element.to_s)
|
24
|
+
else
|
25
|
+
element
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module Arel # :nodoc: all
|
2
|
+
module Nodes
|
3
|
+
class LimitBy < Arel::Nodes::Unary
|
4
|
+
attr_reader :column
|
5
|
+
|
6
|
+
def initialize(limit, column)
|
7
|
+
raise ArgumentError, 'Limit should be an integer' unless limit.is_a?(Integer)
|
8
|
+
raise ArgumentError, 'Limit should be a positive integer' unless limit >= 0
|
9
|
+
raise ArgumentError, 'Column should be a Symbol or String' unless column.is_a?(String) || column.is_a?(Symbol)
|
10
|
+
|
11
|
+
@column = column
|
12
|
+
|
13
|
+
super(limit)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -25,6 +25,7 @@ module Arel
|
|
25
25
|
end
|
26
26
|
|
27
27
|
def visit_Arel_Nodes_SelectOptions(o, collector)
|
28
|
+
maybe_visit o.limit_by, collector
|
28
29
|
maybe_visit o.settings, super
|
29
30
|
end
|
30
31
|
|
@@ -45,6 +46,11 @@ module Arel
|
|
45
46
|
collector
|
46
47
|
end
|
47
48
|
|
49
|
+
def visit_Arel_Nodes_GroupingSets(o, collector)
|
50
|
+
collector << 'GROUPING SETS '
|
51
|
+
grouping_array_or_grouping_element(o.expr, collector)
|
52
|
+
end
|
53
|
+
|
48
54
|
def visit_Arel_Nodes_Settings(o, collector)
|
49
55
|
return collector if o.expr.empty?
|
50
56
|
|
@@ -64,6 +70,11 @@ module Arel
|
|
64
70
|
collector
|
65
71
|
end
|
66
72
|
|
73
|
+
def visit_Arel_Nodes_LimitBy(o, collector)
|
74
|
+
collector << "LIMIT #{o.expr} BY #{o.column}"
|
75
|
+
collector
|
76
|
+
end
|
77
|
+
|
67
78
|
def visit_Arel_Nodes_Matches(o, collector)
|
68
79
|
op = o.case_sensitive ? " LIKE " : " ILIKE "
|
69
80
|
infix_value o, collector, op
|
@@ -95,6 +106,25 @@ module Arel
|
|
95
106
|
@connection.sanitize_as_setting_name(value)
|
96
107
|
end
|
97
108
|
|
109
|
+
private
|
110
|
+
|
111
|
+
# Utilized by GroupingSet, Cube & RollUp visitors to
|
112
|
+
# handle grouping aggregation semantics
|
113
|
+
def grouping_array_or_grouping_element(o, collector)
|
114
|
+
if o.is_a? Array
|
115
|
+
collector << '( '
|
116
|
+
o.each_with_index do |el, i|
|
117
|
+
collector << ', ' if i > 0
|
118
|
+
grouping_array_or_grouping_element el, collector
|
119
|
+
end
|
120
|
+
collector << ' )'
|
121
|
+
elsif o.respond_to? :expr
|
122
|
+
visit o.expr, collector
|
123
|
+
else
|
124
|
+
visit o, collector
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
98
128
|
end
|
99
129
|
end
|
100
130
|
end
|
@@ -35,7 +35,7 @@ module ClickhouseActiverecord
|
|
35
35
|
# super(table.gsub(/^\.inner\./, ''), stream)
|
36
36
|
|
37
37
|
# detect view table
|
38
|
-
|
38
|
+
view_match = sql.match(/^CREATE\s+(MATERIALIZED\s+)?VIEW\s+\S+\s+(?:TO (\S+))?/)
|
39
39
|
end
|
40
40
|
|
41
41
|
# Copy from original dumper
|
@@ -50,8 +50,9 @@ module ClickhouseActiverecord
|
|
50
50
|
|
51
51
|
unless simple
|
52
52
|
# Add materialize flag
|
53
|
-
tbl.print ', view: true' if
|
54
|
-
tbl.print ', materialized: true' if
|
53
|
+
tbl.print ', view: true' if view_match
|
54
|
+
tbl.print ', materialized: true' if view_match && view_match[1].presence
|
55
|
+
tbl.print ", to: \"#{view_match[2]}\"" if view_match && view_match[2].presence
|
55
56
|
end
|
56
57
|
|
57
58
|
if (id = columns.detect { |c| c.name == 'id' })
|
@@ -75,10 +76,10 @@ module ClickhouseActiverecord
|
|
75
76
|
tbl.puts ", force: :cascade do |t|"
|
76
77
|
|
77
78
|
# then dump all non-primary key columns
|
78
|
-
if simple || !
|
79
|
+
if simple || !view_match
|
79
80
|
columns.each do |column|
|
80
81
|
raise StandardError, "Unknown type '#{column.sql_type}' for column '#{column.name}'" unless @connection.valid_type?(column.type)
|
81
|
-
next if column.name == pk
|
82
|
+
next if column.name == pk && column.name == "id"
|
82
83
|
type, colspec = column_spec(column)
|
83
84
|
name = column.name =~ (/\./) ? "\"`#{column.name}`\"" : column.name.inspect
|
84
85
|
tbl.print " t.#{type} #{name}"
|
@@ -111,8 +112,11 @@ module ClickhouseActiverecord
|
|
111
112
|
def function(function, stream)
|
112
113
|
stream.puts " # FUNCTION: #{function}"
|
113
114
|
sql = @connection.show_create_function(function)
|
114
|
-
|
115
|
-
|
115
|
+
if sql
|
116
|
+
stream.puts " # SQL: #{sql}"
|
117
|
+
stream.puts " create_function \"#{function}\", \"#{sql.gsub(/^CREATE FUNCTION (.*?) AS/, '').strip}\", force: true"
|
118
|
+
stream.puts
|
119
|
+
end
|
116
120
|
end
|
117
121
|
|
118
122
|
def format_options(options)
|
@@ -141,23 +145,32 @@ module ClickhouseActiverecord
|
|
141
145
|
end
|
142
146
|
|
143
147
|
def schema_array(column)
|
144
|
-
(column.sql_type =~ /Array
|
148
|
+
(column.sql_type =~ /Array\(/).nil? ? nil : true
|
145
149
|
end
|
146
150
|
|
147
151
|
def schema_map(column)
|
148
|
-
|
152
|
+
if column.sql_type =~ /Map\(([^,]+),\s*(Array)\)/
|
153
|
+
return :array
|
154
|
+
end
|
155
|
+
|
156
|
+
(column.sql_type =~ /Map\(/).nil? ? nil : true
|
149
157
|
end
|
150
158
|
|
151
159
|
def schema_low_cardinality(column)
|
152
|
-
(column.sql_type =~ /LowCardinality
|
160
|
+
(column.sql_type =~ /LowCardinality\(/).nil? ? nil : true
|
153
161
|
end
|
154
162
|
|
163
|
+
# @param [ActiveRecord::ConnectionAdapters::Clickhouse::Column] column
|
155
164
|
def prepare_column_options(column)
|
156
165
|
spec = {}
|
157
166
|
spec[:unsigned] = schema_unsigned(column)
|
158
167
|
spec[:array] = schema_array(column)
|
159
168
|
spec[:map] = schema_map(column)
|
169
|
+
if spec[:map] == :array
|
170
|
+
spec[:array] = nil
|
171
|
+
end
|
160
172
|
spec[:low_cardinality] = schema_low_cardinality(column)
|
173
|
+
spec[:codec] = column.codec.inspect if column.codec
|
161
174
|
spec.merge(super).compact
|
162
175
|
end
|
163
176
|
|
@@ -47,6 +47,33 @@ module CoreExtensions
|
|
47
47
|
self
|
48
48
|
end
|
49
49
|
|
50
|
+
# GROUPING SETS allows you to specify multiple groupings in the GROUP BY clause.
|
51
|
+
# Whereas GROUP BY CUBE generates all possible groupings, GROUP BY GROUPING SETS generates only the specified groupings.
|
52
|
+
# For example:
|
53
|
+
#
|
54
|
+
# users = User.group_by_grouping_sets([], [:name], [:name, :age]).select(:name, :age, 'count(*)')
|
55
|
+
# # SELECT name, age, count(*) FROM users GROUP BY GROUPING SETS ( (), (name), (name, age) )
|
56
|
+
#
|
57
|
+
# which is generally equivalent to:
|
58
|
+
# # SELECT NULL, NULL, count(*) FROM users
|
59
|
+
# # UNION ALL
|
60
|
+
# # SELECT name, NULL, count(*) FROM users GROUP BY name
|
61
|
+
# # UNION ALL
|
62
|
+
# # SELECT name, age, count(*) FROM users GROUP BY name, age
|
63
|
+
#
|
64
|
+
# Raises <tt>ArgumentError</tt> if no grouping sets are specified are provided.
|
65
|
+
def group_by_grouping_sets(*grouping_sets)
|
66
|
+
raise ArgumentError, 'The method .group_by_grouping_sets() must contain arguments.' if grouping_sets.blank?
|
67
|
+
|
68
|
+
spawn.group_by_grouping_sets!(*grouping_sets)
|
69
|
+
end
|
70
|
+
|
71
|
+
def group_by_grouping_sets!(*grouping_sets) # :nodoc:
|
72
|
+
grouping_sets = grouping_sets.map { |set| arel_columns(set) }
|
73
|
+
self.group_values += [::Arel::Nodes::GroupingSets.new(grouping_sets)]
|
74
|
+
self
|
75
|
+
end
|
76
|
+
|
50
77
|
# The USING clause specifies one or more columns to join, which establishes the equality of these columns. For example:
|
51
78
|
#
|
52
79
|
# users = User.joins(:joins).using(:event_name, :date)
|
@@ -81,6 +108,24 @@ module CoreExtensions
|
|
81
108
|
self
|
82
109
|
end
|
83
110
|
|
111
|
+
# The LIMIT BY clause permit to improve deduplication based on a unique key, it has better performances than
|
112
|
+
# the GROUP BY clause
|
113
|
+
#
|
114
|
+
# users = User.limit_by(1, id)
|
115
|
+
# # SELECT users.* FROM users LIMIT 1 BY id
|
116
|
+
#
|
117
|
+
# An <tt>ActiveRecord::ActiveRecordError</tt> will be reaised if database is not Clickhouse.
|
118
|
+
# @param [Array] opts
|
119
|
+
def limit_by(*opts)
|
120
|
+
spawn.limit_by!(*opts)
|
121
|
+
end
|
122
|
+
|
123
|
+
# @param [Array] opts
|
124
|
+
def limit_by!(*opts)
|
125
|
+
@values[:limit_by] = *opts
|
126
|
+
self
|
127
|
+
end
|
128
|
+
|
84
129
|
private
|
85
130
|
|
86
131
|
def check_command(cmd)
|
@@ -95,6 +140,7 @@ module CoreExtensions
|
|
95
140
|
end
|
96
141
|
|
97
142
|
arel.final! if @values[:final].present?
|
143
|
+
arel.limit_by(*@values[:limit_by]) if @values[:limit_by].present?
|
98
144
|
arel.settings(@values[:settings]) if @values[:settings].present?
|
99
145
|
arel.using(@values[:using]) if @values[:using].present?
|
100
146
|
arel.windows(@values[:windows]) if @values[:windows].present?
|
@@ -2,15 +2,18 @@ module CoreExtensions
|
|
2
2
|
module Arel # :nodoc: all
|
3
3
|
module Nodes
|
4
4
|
module SelectStatement
|
5
|
-
attr_accessor :settings
|
5
|
+
attr_accessor :limit_by, :settings
|
6
6
|
|
7
7
|
def initialize(relation = nil)
|
8
8
|
super
|
9
|
+
@limit_by = nil
|
9
10
|
@settings = nil
|
10
11
|
end
|
11
12
|
|
12
13
|
def eql?(other)
|
13
|
-
super &&
|
14
|
+
super &&
|
15
|
+
limit_by == other.limit_by &&
|
16
|
+
settings == other.settings
|
14
17
|
end
|
15
18
|
end
|
16
19
|
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: 1.
|
4
|
+
version: 1.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Sergey Odintsov
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-
|
11
|
+
date: 2024-10-23 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -106,6 +106,7 @@ files:
|
|
106
106
|
- bin/console
|
107
107
|
- bin/setup
|
108
108
|
- clickhouse-activerecord.gemspec
|
109
|
+
- lib/active_record/connection_adapters/clickhouse/column.rb
|
109
110
|
- lib/active_record/connection_adapters/clickhouse/oid/array.rb
|
110
111
|
- lib/active_record/connection_adapters/clickhouse/oid/big_integer.rb
|
111
112
|
- lib/active_record/connection_adapters/clickhouse/oid/date.rb
|
@@ -114,10 +115,12 @@ files:
|
|
114
115
|
- lib/active_record/connection_adapters/clickhouse/oid/uuid.rb
|
115
116
|
- lib/active_record/connection_adapters/clickhouse/quoting.rb
|
116
117
|
- lib/active_record/connection_adapters/clickhouse/schema_creation.rb
|
117
|
-
- lib/active_record/connection_adapters/clickhouse/schema_definitions.rb
|
118
118
|
- lib/active_record/connection_adapters/clickhouse/schema_statements.rb
|
119
|
+
- lib/active_record/connection_adapters/clickhouse/table_definition.rb
|
119
120
|
- lib/active_record/connection_adapters/clickhouse_adapter.rb
|
120
121
|
- lib/arel/nodes/final.rb
|
122
|
+
- lib/arel/nodes/grouping_sets.rb
|
123
|
+
- lib/arel/nodes/limit_by.rb
|
121
124
|
- lib/arel/nodes/settings.rb
|
122
125
|
- lib/arel/nodes/using.rb
|
123
126
|
- lib/arel/visitors/clickhouse.rb
|