click_house-client 0.8.8 → 0.10.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/Gemfile.lock +2 -2
- data/README.md +31 -1
- data/lib/click_house/client/arel_extensions/nodes/final.rb +14 -0
- data/lib/click_house/client/arel_visitor.rb +5 -0
- data/lib/click_house/client/query_builder.rb +56 -11
- data/lib/click_house/client/version.rb +1 -1
- metadata +3 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: bef646c7c07c30559265378e9fec3f14f5e41aeadd86058120a31d532d77d10a
|
|
4
|
+
data.tar.gz: e969763ad335eda82d36f3d949207cfcb95ab238646ff0b3e4ec18e5a17bd445
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 9f7883a21fba0ab5534c85ca43686a3490234252c2277e200a244a3bead56fa3a31d7597c8b23deecb0086b5ad3a90dde67c8425f8ce257c289cb29abb8b29ad
|
|
7
|
+
data.tar.gz: 175aaeaefe0f02f0ba1cd9e3828d9d4bb876c450f626765b78568297c0d35d7e2eb7de00f900ea88db84f7240fbb32790fededbd258dcad4afb4d9291bb16f7c
|
data/Gemfile.lock
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
PATH
|
|
2
2
|
remote: .
|
|
3
3
|
specs:
|
|
4
|
-
click_house-client (0.
|
|
4
|
+
click_house-client (0.10.0)
|
|
5
5
|
activerecord (>= 7.0, < 9.0)
|
|
6
6
|
activesupport (>= 7.0, < 9.0)
|
|
7
7
|
addressable (~> 2.8)
|
|
@@ -136,7 +136,7 @@ CHECKSUMS
|
|
|
136
136
|
benchmark (0.4.1) sha256=d4ef40037bba27f03b28013e219b950b82bace296549ec15a78016552f8d2cce
|
|
137
137
|
bigdecimal (3.2.2) sha256=39085f76b495eb39a79ce07af716f3a6829bc35eb44f2195e2753749f2fa5adc
|
|
138
138
|
byebug (12.0.0) sha256=d4a150d291cca40b66ec9ca31f754e93fed8aa266a17335f71bb0afa7fca1a1e
|
|
139
|
-
click_house-client (0.
|
|
139
|
+
click_house-client (0.10.0)
|
|
140
140
|
concurrent-ruby (1.3.5) sha256=813b3e37aca6df2a21a3b9f1d497f8cbab24a2b94cab325bffe65ee0f6cbebc6
|
|
141
141
|
connection_pool (2.5.3) sha256=cfd74a82b9b094d1ce30c4f1a346da23ee19dc8a062a16a85f58eab1ced4305b
|
|
142
142
|
diff-lcs (1.5.0) sha256=49b934001c8c6aedb37ba19daec5c634da27b318a7a3c654ae979d6ba1929b67
|
data/README.md
CHANGED
|
@@ -182,9 +182,28 @@ query
|
|
|
182
182
|
# => "SELECT * FROM `users` WHERE `users`.`active` = 'true' GROUP BY `users`.`department` HAVING `users`.`avg_salary` > 50000"
|
|
183
183
|
```
|
|
184
184
|
|
|
185
|
+
### FINAL Modifier
|
|
186
|
+
|
|
187
|
+
ClickHouse's `FINAL` modifier forces merging of rows during query time for tables in the MergeTree family. Apply it via `.final`:
|
|
188
|
+
|
|
189
|
+
```ruby
|
|
190
|
+
query.final.to_sql
|
|
191
|
+
# => "SELECT * FROM `users` FINAL"
|
|
192
|
+
|
|
193
|
+
query.final.where(active: true).to_sql
|
|
194
|
+
# => "SELECT * FROM `users` FINAL WHERE `users`.`active` = 'true'"
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
> ⚠️ **Warning:** Using `FINAL` in production code can cause excessive I/O and affect ClickHouse availability. Prefer using it only in test environments or behind a feature flag.
|
|
198
|
+
|
|
199
|
+
`FINAL` is currently applied only to the main `FROM` table. When joining, joined tables are not marked `FINAL`. Calling `.final` multiple times is idempotent.
|
|
200
|
+
|
|
185
201
|
### Working with JOINs
|
|
186
202
|
|
|
187
|
-
|
|
203
|
+
`#joins` supports `INNER JOIN` (default) and `LEFT OUTER JOIN` via
|
|
204
|
+
`type: :outer`. The join source can be a table name, an `Arel::Table`, or a
|
|
205
|
+
pre-aliased subquery (`QueryBuilder.new(sub, 'alias').table` or
|
|
206
|
+
`sub.to_arel.as('alias')`).
|
|
188
207
|
|
|
189
208
|
```ruby
|
|
190
209
|
# Join with conditions on joined table
|
|
@@ -201,6 +220,17 @@ query
|
|
|
201
220
|
.having(orders: { total: [100, 200, 300] })
|
|
202
221
|
.to_sql
|
|
203
222
|
# => "SELECT * FROM `users` INNER JOIN `orders` ON `users`.`id` = `orders`.`user_id` GROUP BY `users`.`department` HAVING `orders`.`total` IN (100, 200, 300)"
|
|
223
|
+
|
|
224
|
+
# LEFT OUTER JOIN against a pre-aliased subquery
|
|
225
|
+
orders_sub = ClickHouse::Client::QueryBuilder.new(
|
|
226
|
+
ClickHouse::Client::QueryBuilder.new('orders').select(:id, :user_id),
|
|
227
|
+
'o'
|
|
228
|
+
)
|
|
229
|
+
|
|
230
|
+
query
|
|
231
|
+
.joins(orders_sub.table, { id: :user_id }, type: :outer)
|
|
232
|
+
.to_sql
|
|
233
|
+
# => "SELECT * FROM `users` LEFT OUTER JOIN (SELECT `orders`.`id`, `orders`.`user_id` FROM `orders`) `o` ON `users`.`id` = `o`.`user_id`"
|
|
204
234
|
```
|
|
205
235
|
|
|
206
236
|
### Complete Example
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require 'active_record'
|
|
4
|
+
require_relative 'arel_extensions/nodes/final'
|
|
4
5
|
|
|
5
6
|
module ClickHouse
|
|
6
7
|
module Client
|
|
@@ -159,40 +160,78 @@ module ClickHouse
|
|
|
159
160
|
self
|
|
160
161
|
end
|
|
161
162
|
|
|
163
|
+
# Applies the ClickHouse `FINAL` modifier to the main FROM table.
|
|
164
|
+
# See https://clickhouse.com/docs/en/sql-reference/statements/select/from#final-modifier
|
|
165
|
+
#
|
|
166
|
+
# @example
|
|
167
|
+
# query.final.to_sql
|
|
168
|
+
# # => "SELECT * FROM `test_table` FINAL"
|
|
169
|
+
#
|
|
170
|
+
# Note: `FINAL` only makes sense on a ClickHouse MergeTree-family table.
|
|
171
|
+
#
|
|
172
|
+
# WARNING: Using `FINAL` in production code can cause excessive I/O and
|
|
173
|
+
# affect ClickHouse availability. Prefer using it only in test environments
|
|
174
|
+
# or behind a feature flag.
|
|
175
|
+
#
|
|
176
|
+
# @return [ClickHouse::Client::QueryBuilder] New instance of query builder.
|
|
177
|
+
def final
|
|
178
|
+
clone.tap do |new_instance|
|
|
179
|
+
source = new_instance.manager.source.left
|
|
180
|
+
wrapped = source.is_a?(ArelExtensions::Nodes::Final) ? source : ArelExtensions::Nodes::Final.new(source)
|
|
181
|
+
new_instance.manager.from(wrapped)
|
|
182
|
+
end
|
|
183
|
+
end
|
|
184
|
+
|
|
162
185
|
def from(subquery, alias_name)
|
|
163
186
|
clone.tap do |new_instance|
|
|
164
|
-
if subquery.is_a?(self.class)
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
187
|
+
new_from = if subquery.is_a?(self.class)
|
|
188
|
+
subquery.to_arel.as(alias_name)
|
|
189
|
+
else
|
|
190
|
+
Arel::Nodes::TableAlias.new(subquery, alias_name)
|
|
191
|
+
end
|
|
192
|
+
|
|
193
|
+
if new_instance.manager.source.left.is_a?(ArelExtensions::Nodes::Final)
|
|
194
|
+
new_from = ArelExtensions::Nodes::Final.new(new_from)
|
|
168
195
|
end
|
|
196
|
+
|
|
197
|
+
new_instance.manager.from(new_from)
|
|
169
198
|
end
|
|
170
199
|
end
|
|
171
200
|
|
|
172
|
-
|
|
201
|
+
# Adds a JOIN clause. Pass `type: :outer` for `LEFT OUTER JOIN`.
|
|
202
|
+
# To join a subquery, pre-alias it via `QueryBuilder.new(sub, 'x').table`
|
|
203
|
+
# or `sub.to_arel.as('x')` and pass that.
|
|
204
|
+
# @return [ClickHouse::Client::QueryBuilder] New instance of query builder.
|
|
205
|
+
def joins(source, constraint = nil, type: :inner)
|
|
206
|
+
validate_join_type!(type)
|
|
207
|
+
|
|
173
208
|
clone.tap do |new_instance|
|
|
174
|
-
|
|
209
|
+
join_target = case source
|
|
210
|
+
when Arel::Table, Arel::Nodes::TableAlias then source
|
|
211
|
+
else Arel::Table.new(source)
|
|
212
|
+
end
|
|
213
|
+
join_class = type == :outer ? Arel::Nodes::OuterJoin : Arel::Nodes::InnerJoin
|
|
175
214
|
|
|
176
215
|
join_condition = case constraint
|
|
177
216
|
when Hash
|
|
178
217
|
# Handle hash based constraints like { table1.id: table2.ref_id } or {id: :ref_id}
|
|
179
218
|
constraint_conditions = constraint.map do |left, right|
|
|
180
219
|
left_field = left.is_a?(Arel::Attributes::Attribute) ? left : new_instance.table[left]
|
|
181
|
-
right_field = right.is_a?(Arel::Attributes::Attribute) ? right :
|
|
220
|
+
right_field = right.is_a?(Arel::Attributes::Attribute) ? right : join_target[right]
|
|
182
221
|
left_field.eq(right_field)
|
|
183
222
|
end
|
|
184
223
|
|
|
185
224
|
constraint_conditions.reduce(&:and)
|
|
186
225
|
when Proc
|
|
187
|
-
constraint.call(new_instance.table,
|
|
188
|
-
when Arel::Nodes::Node
|
|
226
|
+
constraint.call(new_instance.table, join_target)
|
|
227
|
+
when Arel::Nodes::Node, Arel::Nodes::SqlLiteral
|
|
189
228
|
constraint
|
|
190
229
|
end
|
|
191
230
|
|
|
192
231
|
if join_condition
|
|
193
|
-
new_instance.manager.join(
|
|
232
|
+
new_instance.manager.join(join_target, join_class).on(join_condition)
|
|
194
233
|
else
|
|
195
|
-
new_instance.manager.join(
|
|
234
|
+
new_instance.manager.join(join_target, join_class)
|
|
196
235
|
end
|
|
197
236
|
end
|
|
198
237
|
end
|
|
@@ -419,6 +458,12 @@ module ClickHouse
|
|
|
419
458
|
|
|
420
459
|
raise ArgumentError, "Invalid order direction '#{direction}'. Must be :asc or :desc"
|
|
421
460
|
end
|
|
461
|
+
|
|
462
|
+
def validate_join_type!(type)
|
|
463
|
+
return if %i[inner outer].include?(type)
|
|
464
|
+
|
|
465
|
+
raise ArgumentError, "Invalid join type '#{type}'. Must be :inner or :outer"
|
|
466
|
+
end
|
|
422
467
|
end
|
|
423
468
|
end
|
|
424
469
|
end
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: click_house-client
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.10.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- group::optimize
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-
|
|
11
|
+
date: 2026-06-04 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: activerecord
|
|
@@ -185,6 +185,7 @@ files:
|
|
|
185
185
|
- gemfiles/Gemfile-rails-8.0
|
|
186
186
|
- lib/click_house/client.rb
|
|
187
187
|
- lib/click_house/client/arel_engine.rb
|
|
188
|
+
- lib/click_house/client/arel_extensions/nodes/final.rb
|
|
188
189
|
- lib/click_house/client/arel_visitor.rb
|
|
189
190
|
- lib/click_house/client/bind_index_manager.rb
|
|
190
191
|
- lib/click_house/client/configuration.rb
|