click_house-client 0.8.2 → 0.8.4
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/.gitignore +1 -0
- data/Gemfile.lock +2 -2
- data/lib/click_house/client/arel_visitor.rb +2 -0
- data/lib/click_house/client/query_builder.rb +35 -3
- data/lib/click_house/client/to_redacted_sql_visitor.rb +118 -0
- data/lib/click_house/client/version.rb +1 -1
- data/lib/click_house/client.rb +2 -2
- metadata +4 -3
- data/lib/click_house/client/redactor.rb +0 -93
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 0e2ca8f0530ff3f9a48853609325b69481909e1bf4b6d208cee425430cc3b3e0
|
|
4
|
+
data.tar.gz: 98c2180b46b3345de18d3525c070fc59f7acd88897a6f8e593a8b7b97e69d2d0
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 23f2d4022f33b6795030e73a68f0549d06d1925221f08d49f0a314e87096de83fdd641d12597960f71e08656d81bf2af5b46483f8e113464d6e8bcd83fe2a7a4
|
|
7
|
+
data.tar.gz: 5492a6fb7bbb700722fbf4684570ba4d4d220be2c5316a98461e9e6b857cb549b5bfa7fb1173dc38beae27b9df9f945eca1ed1568a061774f3fd6b3b6b2aca63
|
data/.gitignore
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
.rspec_status
|
data/Gemfile.lock
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
PATH
|
|
2
2
|
remote: .
|
|
3
3
|
specs:
|
|
4
|
-
click_house-client (0.8.
|
|
4
|
+
click_house-client (0.8.4)
|
|
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.8.
|
|
139
|
+
click_house-client (0.8.4)
|
|
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
|
|
@@ -29,8 +29,15 @@ module ClickHouse
|
|
|
29
29
|
Arel::Nodes::As
|
|
30
30
|
].freeze
|
|
31
31
|
|
|
32
|
-
|
|
33
|
-
|
|
32
|
+
delegate :[], to: :table
|
|
33
|
+
|
|
34
|
+
def initialize(table, alias_name = nil)
|
|
35
|
+
@table = if table.is_a?(self.class) # subquery
|
|
36
|
+
Arel::Nodes::TableAlias.new(table.to_arel, alias_name)
|
|
37
|
+
else
|
|
38
|
+
Arel::Table.new(table)
|
|
39
|
+
end
|
|
40
|
+
|
|
34
41
|
@manager = Arel::SelectManager.new(Arel::Table.engine).from(@table).project(Arel.star)
|
|
35
42
|
end
|
|
36
43
|
|
|
@@ -63,6 +70,15 @@ module ClickHouse
|
|
|
63
70
|
end
|
|
64
71
|
end
|
|
65
72
|
|
|
73
|
+
# Evaluates given block in scope of current builder. Example:
|
|
74
|
+
# query = ClickHouse::Client::QueryBuilder.new('test_table').build do
|
|
75
|
+
# select(named_func('argMax', [table[:id], table[:timestamp]]).as('max_id')).limit(10)
|
|
76
|
+
# end
|
|
77
|
+
# @return [ClickHouse::QueryBuilder] New instance of query builder.
|
|
78
|
+
def build(&)
|
|
79
|
+
instance_eval(&)
|
|
80
|
+
end
|
|
81
|
+
|
|
66
82
|
# The `having` method applies constraints to the HAVING clause, similar to how
|
|
67
83
|
# `where` applies constraints to the WHERE clause. It supports the same constraint types.
|
|
68
84
|
# Correct usage:
|
|
@@ -181,6 +197,20 @@ module ClickHouse
|
|
|
181
197
|
end
|
|
182
198
|
end
|
|
183
199
|
|
|
200
|
+
# Shortcut for `Arel::Nodes::NamedFunction.new`
|
|
201
|
+
# @return [Arel::Nodes::NamedFunction]
|
|
202
|
+
def named_func(*args)
|
|
203
|
+
Arel::Nodes::NamedFunction.new(*args)
|
|
204
|
+
end
|
|
205
|
+
|
|
206
|
+
# Shortcut for `Arel::Nodes.build_quoted`
|
|
207
|
+
# @return [Arel::Nodes::Node]
|
|
208
|
+
def quote(...)
|
|
209
|
+
Arel::Nodes.build_quoted(...)
|
|
210
|
+
end
|
|
211
|
+
|
|
212
|
+
alias_method :func, :named_func
|
|
213
|
+
|
|
184
214
|
# Aggregation helper methods
|
|
185
215
|
|
|
186
216
|
# Creates an AVG aggregate function node
|
|
@@ -315,7 +345,9 @@ module ClickHouse
|
|
|
315
345
|
end
|
|
316
346
|
|
|
317
347
|
def to_redacted_sql(bind_index_manager = ClickHouse::Client::BindIndexManager.new)
|
|
318
|
-
ClickHouse::Client::
|
|
348
|
+
visitor = ClickHouse::Client::ToRedactedSqlVisitor.new(ClickHouse::Client::ArelEngine.new,
|
|
349
|
+
bind_manager: bind_index_manager)
|
|
350
|
+
visitor.accept(manager.ast, Arel::Collectors::SQLString.new).value
|
|
319
351
|
end
|
|
320
352
|
|
|
321
353
|
def to_arel
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ClickHouse
|
|
4
|
+
module Client
|
|
5
|
+
# Redacts the SQL query represented by the query builder.
|
|
6
|
+
#
|
|
7
|
+
# Example:
|
|
8
|
+
# query_builder = ClickHouse::QueryBuilder.new('users').where(name: 'John Doe')
|
|
9
|
+
# redacted_query = query_builder.to_redacted_sql
|
|
10
|
+
# # The redacted_query will contain the SQL query with values replaced by placeholders.
|
|
11
|
+
# output: "SELECT * FROM \"users\" WHERE \"users\".\"name\" = $1"
|
|
12
|
+
class ToRedactedSqlVisitor < ArelVisitor
|
|
13
|
+
attr_reader :bind_manager
|
|
14
|
+
|
|
15
|
+
def initialize(*args, bind_manager: ClickHouse::Client::BindIndexManager.new)
|
|
16
|
+
@bind_manager = bind_manager
|
|
17
|
+
super(*args)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
private
|
|
21
|
+
|
|
22
|
+
def redaction_enabled?
|
|
23
|
+
!!@redaction_enabled
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def collect_nodes_for(nodes, collector, spacer, *args)
|
|
27
|
+
return super unless spacer.strip.in?(%w[WHERE HAVING])
|
|
28
|
+
|
|
29
|
+
redaction_was = @redaction_enabled
|
|
30
|
+
@redaction_enabled = true
|
|
31
|
+
super
|
|
32
|
+
@redaction_enabled = redaction_was
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
# rubocop:disable Naming/MethodName -- following Arel::Visitors::Visitor pattern
|
|
36
|
+
# rubocop:disable Naming/MethodParameterName -- following Arel::Visitors::Visitor pattern
|
|
37
|
+
|
|
38
|
+
def visit_Arel_Nodes_In(o, collector)
|
|
39
|
+
return super unless redaction_enabled?
|
|
40
|
+
|
|
41
|
+
return super if o.right.is_a? Arel::Nodes::SelectStatement
|
|
42
|
+
|
|
43
|
+
super(redact_right(o), collector)
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def visit_Arel_Nodes_Equality(o, collector)
|
|
47
|
+
return super unless redaction_enabled?
|
|
48
|
+
|
|
49
|
+
super(redact_right(o), collector)
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def visit_Arel_Nodes_LessThan(o, collector)
|
|
53
|
+
return super unless redaction_enabled?
|
|
54
|
+
|
|
55
|
+
super(redact_right(o), collector)
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def visit_Arel_Nodes_LessThanOrEqual(o, collector)
|
|
59
|
+
return super unless redaction_enabled?
|
|
60
|
+
|
|
61
|
+
super(redact_right(o), collector)
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def visit_Arel_Nodes_GreaterThan(o, collector)
|
|
65
|
+
return super unless redaction_enabled?
|
|
66
|
+
|
|
67
|
+
super(redact_right(o), collector)
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def visit_Arel_Nodes_GreaterThanOrEqual(o, collector)
|
|
71
|
+
return super unless redaction_enabled?
|
|
72
|
+
|
|
73
|
+
super(redact_right(o), collector)
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def visit_Arel_Nodes_NamedFunction(o, collector)
|
|
77
|
+
return super unless redaction_enabled?
|
|
78
|
+
|
|
79
|
+
redacted_o = Arel::Nodes::NamedFunction.new(o.name, o.expressions.dup)
|
|
80
|
+
|
|
81
|
+
case redacted_o.name
|
|
82
|
+
when 'startsWith'
|
|
83
|
+
redacted_o.expressions[1] = Arel.sql(bind_manager.next_bind_str)
|
|
84
|
+
else
|
|
85
|
+
redacted_o.expressions = redacted_o.expressions.map do
|
|
86
|
+
Arel.sql(bind_manager.next_bind_str)
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
super(redacted_o, collector)
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
def visit_Arel_Nodes_Matches(o, collector)
|
|
94
|
+
return super unless redaction_enabled?
|
|
95
|
+
|
|
96
|
+
super(redact_right(o), collector)
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
def redact_right(o)
|
|
100
|
+
cloned_o = o.clone
|
|
101
|
+
|
|
102
|
+
redacted_right = if o.right.is_a?(Array)
|
|
103
|
+
Array.new(o.right.size) do
|
|
104
|
+
Arel.sql(bind_manager.next_bind_str)
|
|
105
|
+
end
|
|
106
|
+
else
|
|
107
|
+
Arel.sql(bind_manager.next_bind_str)
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
cloned_o.right = redacted_right
|
|
111
|
+
cloned_o
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
# rubocop:enable Naming/MethodName
|
|
115
|
+
# rubocop:enable Naming/MethodParameterName
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
end
|
data/lib/click_house/client.rb
CHANGED
|
@@ -15,11 +15,11 @@ require_relative "client/quoting"
|
|
|
15
15
|
require_relative "client/arel_engine"
|
|
16
16
|
require_relative "client/query_like"
|
|
17
17
|
require_relative "client/query"
|
|
18
|
-
require_relative "client/
|
|
18
|
+
require_relative "client/arel_visitor"
|
|
19
|
+
require_relative "client/to_redacted_sql_visitor"
|
|
19
20
|
require_relative "client/query_builder"
|
|
20
21
|
require_relative "client/formatter"
|
|
21
22
|
require_relative "client/response"
|
|
22
|
-
require_relative "client/arel_visitor"
|
|
23
23
|
|
|
24
24
|
module ClickHouse
|
|
25
25
|
module Client
|
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.8.
|
|
4
|
+
version: 0.8.4
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- group::optimize
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2025-
|
|
11
|
+
date: 2025-12-03 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: activerecord
|
|
@@ -170,6 +170,7 @@ executables: []
|
|
|
170
170
|
extensions: []
|
|
171
171
|
extra_rdoc_files: []
|
|
172
172
|
files:
|
|
173
|
+
- ".gitignore"
|
|
173
174
|
- ".gitlab-ci.yml"
|
|
174
175
|
- ".rspec"
|
|
175
176
|
- ".rubocop.yml"
|
|
@@ -193,8 +194,8 @@ files:
|
|
|
193
194
|
- lib/click_house/client/query_builder.rb
|
|
194
195
|
- lib/click_house/client/query_like.rb
|
|
195
196
|
- lib/click_house/client/quoting.rb
|
|
196
|
-
- lib/click_house/client/redactor.rb
|
|
197
197
|
- lib/click_house/client/response.rb
|
|
198
|
+
- lib/click_house/client/to_redacted_sql_visitor.rb
|
|
198
199
|
- lib/click_house/client/version.rb
|
|
199
200
|
homepage: https://gitlab.com/gitlab-org/ruby/gems/clickhouse-client
|
|
200
201
|
licenses:
|
|
@@ -1,93 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module ClickHouse
|
|
4
|
-
module Client
|
|
5
|
-
module Redactor
|
|
6
|
-
class << self
|
|
7
|
-
# Redacts the SQL query represented by the query builder.
|
|
8
|
-
#
|
|
9
|
-
# @param query_builder [::ClickHouse::Querybuilder] The query builder object to be redacted.
|
|
10
|
-
# @return [String] The redacted SQL query as a string.
|
|
11
|
-
# @raise [ArgumentError] when the condition in the query is of an unsupported type.
|
|
12
|
-
#
|
|
13
|
-
# Example:
|
|
14
|
-
# query_builder = ClickHouse::QueryBuilder.new('users').where(name: 'John Doe')
|
|
15
|
-
# redacted_query = ClickHouse::Redactor.redact(query_builder)
|
|
16
|
-
# # The redacted_query will contain the SQL query with values replaced by placeholders.
|
|
17
|
-
# output: "SELECT * FROM \"users\" WHERE \"users\".\"name\" = $1"
|
|
18
|
-
def redact(query_builder, bind_manager = ClickHouse::Client::BindIndexManager.new)
|
|
19
|
-
redacted_constraints = query_builder.manager.constraints.map do |constraint|
|
|
20
|
-
redact_constraint(constraint, bind_manager)
|
|
21
|
-
end
|
|
22
|
-
|
|
23
|
-
cloned_query_builder = query_builder.clone
|
|
24
|
-
|
|
25
|
-
cloned_query_builder.manager.constraints.clear
|
|
26
|
-
redacted_constraints.each do |constraint|
|
|
27
|
-
cloned_query_builder.manager.where(constraint)
|
|
28
|
-
end
|
|
29
|
-
|
|
30
|
-
cloned_query_builder.to_sql
|
|
31
|
-
end
|
|
32
|
-
|
|
33
|
-
private
|
|
34
|
-
|
|
35
|
-
def redact_constraint(constraint, bind_manager)
|
|
36
|
-
case constraint
|
|
37
|
-
when Arel::Nodes::In
|
|
38
|
-
if constraint.right.is_a? Arel::Nodes::SelectStatement
|
|
39
|
-
constraint.left.in(redact_select_statement(constraint.right, bind_manager))
|
|
40
|
-
else
|
|
41
|
-
constraint.left.in(Array.new(constraint.right.size) { Arel.sql(bind_manager.next_bind_str) })
|
|
42
|
-
end
|
|
43
|
-
when Arel::Nodes::Equality
|
|
44
|
-
constraint.left.eq(Arel.sql(bind_manager.next_bind_str))
|
|
45
|
-
when Arel::Nodes::LessThan
|
|
46
|
-
constraint.left.lt(Arel.sql(bind_manager.next_bind_str))
|
|
47
|
-
when Arel::Nodes::LessThanOrEqual
|
|
48
|
-
constraint.left.lteq(Arel.sql(bind_manager.next_bind_str))
|
|
49
|
-
when Arel::Nodes::GreaterThan
|
|
50
|
-
constraint.left.gt(Arel.sql(bind_manager.next_bind_str))
|
|
51
|
-
when Arel::Nodes::GreaterThanOrEqual
|
|
52
|
-
constraint.left.gteq(Arel.sql(bind_manager.next_bind_str))
|
|
53
|
-
when Arel::Nodes::NamedFunction
|
|
54
|
-
redact_named_function(constraint, bind_manager)
|
|
55
|
-
when Arel::Nodes::Matches
|
|
56
|
-
constraint.left.matches(Arel.sql(bind_manager.next_bind_str), constraint.escape, constraint.case_sensitive)
|
|
57
|
-
else
|
|
58
|
-
raise ArgumentError, "Unsupported Arel node type for Redactor: #{constraint.class}"
|
|
59
|
-
end
|
|
60
|
-
end
|
|
61
|
-
|
|
62
|
-
def redact_named_function(constraint, bind_manager)
|
|
63
|
-
redacted_constraint =
|
|
64
|
-
Arel::Nodes::NamedFunction.new(constraint.name, constraint.expressions.dup)
|
|
65
|
-
|
|
66
|
-
case redacted_constraint.name
|
|
67
|
-
when 'startsWith'
|
|
68
|
-
redacted_constraint.expressions[1] = Arel.sql(bind_manager.next_bind_str)
|
|
69
|
-
else
|
|
70
|
-
redacted_constraint.expressions = redacted_constraint.expressions.map do
|
|
71
|
-
Arel.sql(bind_manager.next_bind_str)
|
|
72
|
-
end
|
|
73
|
-
end
|
|
74
|
-
|
|
75
|
-
redacted_constraint
|
|
76
|
-
end
|
|
77
|
-
|
|
78
|
-
def redact_select_statement(select_statement, bind_manager)
|
|
79
|
-
cloned_statement = select_statement.clone
|
|
80
|
-
cloned_statement.cores.map! do |select_core|
|
|
81
|
-
select_core.wheres = select_core.wheres.map do |where|
|
|
82
|
-
redact_constraint(where, bind_manager)
|
|
83
|
-
end
|
|
84
|
-
|
|
85
|
-
select_core
|
|
86
|
-
end
|
|
87
|
-
|
|
88
|
-
cloned_statement
|
|
89
|
-
end
|
|
90
|
-
end
|
|
91
|
-
end
|
|
92
|
-
end
|
|
93
|
-
end
|