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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 27b81975f1f934997c497bd13dd33b01e9808f96640bea06bd79dbedb1fdaafd
4
- data.tar.gz: 92534981627408a2654ea29bca98746e99c205064c3a8ecd863bc54a84662f9a
3
+ metadata.gz: 0e2ca8f0530ff3f9a48853609325b69481909e1bf4b6d208cee425430cc3b3e0
4
+ data.tar.gz: 98c2180b46b3345de18d3525c070fc59f7acd88897a6f8e593a8b7b97e69d2d0
5
5
  SHA512:
6
- metadata.gz: 730256f383d8a48927fe2a9a13541dacf6c63300a899c68e647d5457757b32db840e43eab17f2b19b02bed86582e5dbec8bbaa32d24215c5efa9f0c528b32169
7
- data.tar.gz: '09e366a8fbb135fe7bb6eee89f7cdb98c3e91a70b350045cfebaeb2ec4db1bc459343e38d5eb902cc30ee1cba7b9ed2ddb10cb17e9726f791abe7dc1800e132b'
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.2)
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.2)
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
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'active_record'
4
+
3
5
  module ClickHouse
4
6
  module Client
5
7
  class ArelVisitor < Arel::Visitors::ToSql
@@ -29,8 +29,15 @@ module ClickHouse
29
29
  Arel::Nodes::As
30
30
  ].freeze
31
31
 
32
- def initialize(table_name)
33
- @table = Arel::Table.new(table_name)
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::Redactor.redact(self, bind_index_manager)
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
@@ -2,6 +2,6 @@
2
2
 
3
3
  module ClickHouse
4
4
  module Client
5
- VERSION = "0.8.2"
5
+ VERSION = "0.8.4"
6
6
  end
7
7
  end
@@ -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/redactor"
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.2
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-10-13 00:00:00.000000000 Z
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