clickhouse-ruby 0.1.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 +7 -0
- data/CHANGELOG.md +80 -0
- data/LICENSE +21 -0
- data/README.md +251 -0
- data/lib/clickhouse_ruby/active_record/arel_visitor.rb +468 -0
- data/lib/clickhouse_ruby/active_record/connection_adapter.rb +723 -0
- data/lib/clickhouse_ruby/active_record/railtie.rb +192 -0
- data/lib/clickhouse_ruby/active_record/schema_statements.rb +693 -0
- data/lib/clickhouse_ruby/active_record.rb +121 -0
- data/lib/clickhouse_ruby/client.rb +471 -0
- data/lib/clickhouse_ruby/configuration.rb +145 -0
- data/lib/clickhouse_ruby/connection.rb +328 -0
- data/lib/clickhouse_ruby/connection_pool.rb +301 -0
- data/lib/clickhouse_ruby/errors.rb +144 -0
- data/lib/clickhouse_ruby/result.rb +189 -0
- data/lib/clickhouse_ruby/types/array.rb +183 -0
- data/lib/clickhouse_ruby/types/base.rb +77 -0
- data/lib/clickhouse_ruby/types/boolean.rb +68 -0
- data/lib/clickhouse_ruby/types/date_time.rb +163 -0
- data/lib/clickhouse_ruby/types/float.rb +115 -0
- data/lib/clickhouse_ruby/types/integer.rb +157 -0
- data/lib/clickhouse_ruby/types/low_cardinality.rb +58 -0
- data/lib/clickhouse_ruby/types/map.rb +249 -0
- data/lib/clickhouse_ruby/types/nullable.rb +73 -0
- data/lib/clickhouse_ruby/types/parser.rb +244 -0
- data/lib/clickhouse_ruby/types/registry.rb +148 -0
- data/lib/clickhouse_ruby/types/string.rb +83 -0
- data/lib/clickhouse_ruby/types/tuple.rb +206 -0
- data/lib/clickhouse_ruby/types/uuid.rb +84 -0
- data/lib/clickhouse_ruby/types.rb +69 -0
- data/lib/clickhouse_ruby/version.rb +5 -0
- data/lib/clickhouse_ruby.rb +101 -0
- metadata +150 -0
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ClickhouseRuby
|
|
4
|
+
module Types
|
|
5
|
+
# Type handler for ClickHouse UUID type
|
|
6
|
+
#
|
|
7
|
+
# UUIDs are stored as 16-byte values but represented as strings
|
|
8
|
+
# in the format: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
|
|
9
|
+
#
|
|
10
|
+
class UUID < Base
|
|
11
|
+
# UUID regex pattern
|
|
12
|
+
UUID_PATTERN = /\A[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\z/i
|
|
13
|
+
|
|
14
|
+
# Converts a Ruby value to a UUID string
|
|
15
|
+
#
|
|
16
|
+
# @param value [Object] the value to convert
|
|
17
|
+
# @return [String, nil] the UUID string
|
|
18
|
+
# @raise [TypeCastError] if the value is not a valid UUID
|
|
19
|
+
def cast(value)
|
|
20
|
+
return nil if value.nil?
|
|
21
|
+
|
|
22
|
+
str = normalize_uuid(value)
|
|
23
|
+
validate_uuid!(str, value)
|
|
24
|
+
str
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# Converts a value from ClickHouse to a UUID string
|
|
28
|
+
#
|
|
29
|
+
# @param value [Object] the value from ClickHouse
|
|
30
|
+
# @return [String, nil] the UUID string
|
|
31
|
+
def deserialize(value)
|
|
32
|
+
return nil if value.nil?
|
|
33
|
+
|
|
34
|
+
normalize_uuid(value)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
# Converts a UUID to SQL literal
|
|
38
|
+
#
|
|
39
|
+
# @param value [String, nil] the UUID value
|
|
40
|
+
# @return [String] the SQL literal
|
|
41
|
+
def serialize(value)
|
|
42
|
+
return 'NULL' if value.nil?
|
|
43
|
+
|
|
44
|
+
"'#{normalize_uuid(value)}'"
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
private
|
|
48
|
+
|
|
49
|
+
# Normalizes a UUID value to the standard format
|
|
50
|
+
#
|
|
51
|
+
# @param value [Object] the value to normalize
|
|
52
|
+
# @return [String] the normalized UUID
|
|
53
|
+
def normalize_uuid(value)
|
|
54
|
+
str = value.to_s.strip.downcase
|
|
55
|
+
|
|
56
|
+
# Remove braces if present
|
|
57
|
+
str = str.gsub(/[{}]/, '')
|
|
58
|
+
|
|
59
|
+
# If no hyphens, add them
|
|
60
|
+
if str.length == 32 && !str.include?('-')
|
|
61
|
+
str = "#{str[0..7]}-#{str[8..11]}-#{str[12..15]}-#{str[16..19]}-#{str[20..31]}"
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
str
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
# Validates that a string is a valid UUID
|
|
68
|
+
#
|
|
69
|
+
# @param str [String] the normalized UUID string
|
|
70
|
+
# @param original [Object] the original value (for error messages)
|
|
71
|
+
# @raise [TypeCastError] if invalid
|
|
72
|
+
def validate_uuid!(str, original)
|
|
73
|
+
return if str.match?(UUID_PATTERN)
|
|
74
|
+
|
|
75
|
+
raise TypeCastError.new(
|
|
76
|
+
"Invalid UUID format: '#{original}'",
|
|
77
|
+
from_type: original.class.name,
|
|
78
|
+
to_type: name,
|
|
79
|
+
value: original
|
|
80
|
+
)
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
end
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Load base class first, then parser, then specific types, then registry last
|
|
4
|
+
require_relative 'types/base'
|
|
5
|
+
require_relative 'types/parser'
|
|
6
|
+
require_relative 'types/integer'
|
|
7
|
+
require_relative 'types/float'
|
|
8
|
+
require_relative 'types/string'
|
|
9
|
+
require_relative 'types/date_time'
|
|
10
|
+
require_relative 'types/uuid'
|
|
11
|
+
require_relative 'types/boolean'
|
|
12
|
+
require_relative 'types/array'
|
|
13
|
+
require_relative 'types/map'
|
|
14
|
+
require_relative 'types/tuple'
|
|
15
|
+
require_relative 'types/nullable'
|
|
16
|
+
require_relative 'types/low_cardinality'
|
|
17
|
+
require_relative 'types/registry'
|
|
18
|
+
|
|
19
|
+
module ClickhouseRuby
|
|
20
|
+
# Type system for mapping between ClickHouse and Ruby types
|
|
21
|
+
#
|
|
22
|
+
# Key features:
|
|
23
|
+
# - AST-based type parser (handles nested types correctly)
|
|
24
|
+
# - Bidirectional conversion (Ruby ↔ ClickHouse)
|
|
25
|
+
# - Proper handling of complex types (Array, Map, Tuple, Nullable)
|
|
26
|
+
#
|
|
27
|
+
# @example Parse a complex type
|
|
28
|
+
# parser = ClickhouseRuby::Types::Parser.new
|
|
29
|
+
# ast = parser.parse('Array(Tuple(String, UInt64))')
|
|
30
|
+
# # => { type: 'Array', args: [{ type: 'Tuple', args: [...] }] }
|
|
31
|
+
#
|
|
32
|
+
# @example Convert values
|
|
33
|
+
# type = ClickhouseRuby::Types.lookup('UInt64')
|
|
34
|
+
# type.cast(42) # => 42
|
|
35
|
+
# type.serialize(42) # => "42"
|
|
36
|
+
# type.deserialize("42") # => 42
|
|
37
|
+
#
|
|
38
|
+
module Types
|
|
39
|
+
class << self
|
|
40
|
+
# Returns the global type registry
|
|
41
|
+
#
|
|
42
|
+
# @return [Registry] the type registry
|
|
43
|
+
def registry
|
|
44
|
+
@registry ||= Registry.new.tap(&:register_defaults)
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
# Looks up a type by its ClickHouse type string
|
|
48
|
+
#
|
|
49
|
+
# @param type_string [String] the ClickHouse type (e.g., 'Array(String)')
|
|
50
|
+
# @return [Base] the type instance
|
|
51
|
+
def lookup(type_string)
|
|
52
|
+
registry.lookup(type_string)
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
# Parses a ClickHouse type string into an AST
|
|
56
|
+
#
|
|
57
|
+
# @param type_string [String] the ClickHouse type string
|
|
58
|
+
# @return [Hash] the parsed AST
|
|
59
|
+
def parse(type_string)
|
|
60
|
+
Parser.new.parse(type_string)
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
# Resets the registry (useful for testing)
|
|
64
|
+
def reset!
|
|
65
|
+
@registry = nil
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
end
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'clickhouse_ruby/version'
|
|
4
|
+
require_relative 'clickhouse_ruby/errors'
|
|
5
|
+
require_relative 'clickhouse_ruby/configuration'
|
|
6
|
+
require_relative 'clickhouse_ruby/types'
|
|
7
|
+
require_relative 'clickhouse_ruby/result'
|
|
8
|
+
require_relative 'clickhouse_ruby/client'
|
|
9
|
+
require_relative 'clickhouse_ruby/connection'
|
|
10
|
+
require_relative 'clickhouse_ruby/connection_pool'
|
|
11
|
+
|
|
12
|
+
# ClickhouseRuby - Ruby/ActiveRecord integration for ClickHouse
|
|
13
|
+
#
|
|
14
|
+
# A robust, reliable ClickHouse client for Ruby that prioritizes:
|
|
15
|
+
# - No silent failures (proper error handling)
|
|
16
|
+
# - Correct type handling (AST-based parser for complex types)
|
|
17
|
+
# - Performance (bulk operations, connection pooling)
|
|
18
|
+
# - Security (SSL verification enabled by default)
|
|
19
|
+
#
|
|
20
|
+
# @example Basic usage
|
|
21
|
+
# ClickhouseRuby.configure do |config|
|
|
22
|
+
# config.host = 'localhost'
|
|
23
|
+
# config.port = 8123
|
|
24
|
+
# config.database = 'analytics'
|
|
25
|
+
# end
|
|
26
|
+
#
|
|
27
|
+
# client = ClickhouseRuby::Client.new
|
|
28
|
+
# result = client.execute('SELECT * FROM events LIMIT 10')
|
|
29
|
+
# result.each { |row| puts row['name'] }
|
|
30
|
+
#
|
|
31
|
+
# @example Bulk insert
|
|
32
|
+
# client.insert('events', [
|
|
33
|
+
# { id: 1, name: 'click', timestamp: Time.now },
|
|
34
|
+
# { id: 2, name: 'view', timestamp: Time.now }
|
|
35
|
+
# ])
|
|
36
|
+
#
|
|
37
|
+
module ClickhouseRuby
|
|
38
|
+
class << self
|
|
39
|
+
# Returns the global configuration instance
|
|
40
|
+
#
|
|
41
|
+
# @return [Configuration] the configuration object
|
|
42
|
+
def configuration
|
|
43
|
+
@configuration ||= Configuration.new
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
# Allows configuration via a block
|
|
47
|
+
#
|
|
48
|
+
# @yield [Configuration] the configuration object
|
|
49
|
+
# @return [Configuration] the configuration object
|
|
50
|
+
#
|
|
51
|
+
# @example
|
|
52
|
+
# ClickhouseRuby.configure do |config|
|
|
53
|
+
# config.host = 'clickhouse.example.com'
|
|
54
|
+
# config.port = 8443
|
|
55
|
+
# config.ssl = true
|
|
56
|
+
# end
|
|
57
|
+
def configure
|
|
58
|
+
yield(configuration)
|
|
59
|
+
configuration
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
# Resets the configuration to defaults
|
|
63
|
+
# Primarily useful for testing
|
|
64
|
+
#
|
|
65
|
+
# @return [Configuration] a new configuration object
|
|
66
|
+
def reset_configuration!
|
|
67
|
+
@configuration = Configuration.new
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
# Creates a new client with the global configuration
|
|
71
|
+
#
|
|
72
|
+
# @return [Client] a new client instance
|
|
73
|
+
def client
|
|
74
|
+
Client.new(configuration)
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
# Convenience method to execute a query using global configuration
|
|
78
|
+
#
|
|
79
|
+
# @param sql [String] the SQL query to execute
|
|
80
|
+
# @param options [Hash] query options
|
|
81
|
+
# @return [Result] the query result
|
|
82
|
+
def execute(sql, **options)
|
|
83
|
+
client.execute(sql, **options)
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
# Convenience method to insert data using global configuration
|
|
87
|
+
#
|
|
88
|
+
# @param table [String] the table name
|
|
89
|
+
# @param rows [Array<Hash>] the rows to insert
|
|
90
|
+
# @param options [Hash] insert options
|
|
91
|
+
# @return [Result] the insert result
|
|
92
|
+
def insert(table, rows, **options)
|
|
93
|
+
client.insert(table, rows, **options)
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
# Load ActiveRecord integration if ActiveRecord is available
|
|
99
|
+
if defined?(ActiveRecord)
|
|
100
|
+
require_relative 'clickhouse_ruby/active_record'
|
|
101
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: clickhouse-ruby
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.1.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Mohamad Kamar
|
|
8
|
+
autorequire:
|
|
9
|
+
bindir: exe
|
|
10
|
+
cert_chain: []
|
|
11
|
+
date: 2026-01-31 00:00:00.000000000 Z
|
|
12
|
+
dependencies:
|
|
13
|
+
- !ruby/object:Gem::Dependency
|
|
14
|
+
name: rake
|
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
|
16
|
+
requirements:
|
|
17
|
+
- - "~>"
|
|
18
|
+
- !ruby/object:Gem::Version
|
|
19
|
+
version: '13.0'
|
|
20
|
+
type: :development
|
|
21
|
+
prerelease: false
|
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
23
|
+
requirements:
|
|
24
|
+
- - "~>"
|
|
25
|
+
- !ruby/object:Gem::Version
|
|
26
|
+
version: '13.0'
|
|
27
|
+
- !ruby/object:Gem::Dependency
|
|
28
|
+
name: rspec
|
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
|
30
|
+
requirements:
|
|
31
|
+
- - "~>"
|
|
32
|
+
- !ruby/object:Gem::Version
|
|
33
|
+
version: '3.12'
|
|
34
|
+
type: :development
|
|
35
|
+
prerelease: false
|
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
37
|
+
requirements:
|
|
38
|
+
- - "~>"
|
|
39
|
+
- !ruby/object:Gem::Version
|
|
40
|
+
version: '3.12'
|
|
41
|
+
- !ruby/object:Gem::Dependency
|
|
42
|
+
name: simplecov
|
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
|
44
|
+
requirements:
|
|
45
|
+
- - "~>"
|
|
46
|
+
- !ruby/object:Gem::Version
|
|
47
|
+
version: 0.21.0
|
|
48
|
+
type: :development
|
|
49
|
+
prerelease: false
|
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
51
|
+
requirements:
|
|
52
|
+
- - "~>"
|
|
53
|
+
- !ruby/object:Gem::Version
|
|
54
|
+
version: 0.21.0
|
|
55
|
+
- !ruby/object:Gem::Dependency
|
|
56
|
+
name: webmock
|
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
|
58
|
+
requirements:
|
|
59
|
+
- - "~>"
|
|
60
|
+
- !ruby/object:Gem::Version
|
|
61
|
+
version: 3.18.0
|
|
62
|
+
type: :development
|
|
63
|
+
prerelease: false
|
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
65
|
+
requirements:
|
|
66
|
+
- - "~>"
|
|
67
|
+
- !ruby/object:Gem::Version
|
|
68
|
+
version: 3.18.0
|
|
69
|
+
- !ruby/object:Gem::Dependency
|
|
70
|
+
name: yard
|
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
|
72
|
+
requirements:
|
|
73
|
+
- - "~>"
|
|
74
|
+
- !ruby/object:Gem::Version
|
|
75
|
+
version: '0.9'
|
|
76
|
+
type: :development
|
|
77
|
+
prerelease: false
|
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
79
|
+
requirements:
|
|
80
|
+
- - "~>"
|
|
81
|
+
- !ruby/object:Gem::Version
|
|
82
|
+
version: '0.9'
|
|
83
|
+
description: A lightweight Ruby client for ClickHouse with optional ActiveRecord integration.
|
|
84
|
+
Provides a simple interface for querying, inserting, and managing ClickHouse databases.
|
|
85
|
+
email:
|
|
86
|
+
- mohamad.kamar.dev@gmail.com
|
|
87
|
+
executables: []
|
|
88
|
+
extensions: []
|
|
89
|
+
extra_rdoc_files: []
|
|
90
|
+
files:
|
|
91
|
+
- CHANGELOG.md
|
|
92
|
+
- LICENSE
|
|
93
|
+
- README.md
|
|
94
|
+
- lib/clickhouse_ruby.rb
|
|
95
|
+
- lib/clickhouse_ruby/active_record.rb
|
|
96
|
+
- lib/clickhouse_ruby/active_record/arel_visitor.rb
|
|
97
|
+
- lib/clickhouse_ruby/active_record/connection_adapter.rb
|
|
98
|
+
- lib/clickhouse_ruby/active_record/railtie.rb
|
|
99
|
+
- lib/clickhouse_ruby/active_record/schema_statements.rb
|
|
100
|
+
- lib/clickhouse_ruby/client.rb
|
|
101
|
+
- lib/clickhouse_ruby/configuration.rb
|
|
102
|
+
- lib/clickhouse_ruby/connection.rb
|
|
103
|
+
- lib/clickhouse_ruby/connection_pool.rb
|
|
104
|
+
- lib/clickhouse_ruby/errors.rb
|
|
105
|
+
- lib/clickhouse_ruby/result.rb
|
|
106
|
+
- lib/clickhouse_ruby/types.rb
|
|
107
|
+
- lib/clickhouse_ruby/types/array.rb
|
|
108
|
+
- lib/clickhouse_ruby/types/base.rb
|
|
109
|
+
- lib/clickhouse_ruby/types/boolean.rb
|
|
110
|
+
- lib/clickhouse_ruby/types/date_time.rb
|
|
111
|
+
- lib/clickhouse_ruby/types/float.rb
|
|
112
|
+
- lib/clickhouse_ruby/types/integer.rb
|
|
113
|
+
- lib/clickhouse_ruby/types/low_cardinality.rb
|
|
114
|
+
- lib/clickhouse_ruby/types/map.rb
|
|
115
|
+
- lib/clickhouse_ruby/types/nullable.rb
|
|
116
|
+
- lib/clickhouse_ruby/types/parser.rb
|
|
117
|
+
- lib/clickhouse_ruby/types/registry.rb
|
|
118
|
+
- lib/clickhouse_ruby/types/string.rb
|
|
119
|
+
- lib/clickhouse_ruby/types/tuple.rb
|
|
120
|
+
- lib/clickhouse_ruby/types/uuid.rb
|
|
121
|
+
- lib/clickhouse_ruby/version.rb
|
|
122
|
+
homepage: https://github.com/kamardev/clickhouse-ruby
|
|
123
|
+
licenses:
|
|
124
|
+
- MIT
|
|
125
|
+
metadata:
|
|
126
|
+
homepage_uri: https://github.com/kamardev/clickhouse-ruby
|
|
127
|
+
source_code_uri: https://github.com/kamardev/clickhouse-ruby
|
|
128
|
+
changelog_uri: https://github.com/kamardev/clickhouse-ruby/blob/main/CHANGELOG.md
|
|
129
|
+
documentation_uri: https://rubydoc.info/gems/clickhouse-ruby
|
|
130
|
+
rubygems_mfa_required: 'true'
|
|
131
|
+
post_install_message:
|
|
132
|
+
rdoc_options: []
|
|
133
|
+
require_paths:
|
|
134
|
+
- lib
|
|
135
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
136
|
+
requirements:
|
|
137
|
+
- - ">="
|
|
138
|
+
- !ruby/object:Gem::Version
|
|
139
|
+
version: 2.6.0
|
|
140
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
141
|
+
requirements:
|
|
142
|
+
- - ">="
|
|
143
|
+
- !ruby/object:Gem::Version
|
|
144
|
+
version: '0'
|
|
145
|
+
requirements: []
|
|
146
|
+
rubygems_version: 3.0.3.1
|
|
147
|
+
signing_key:
|
|
148
|
+
specification_version: 4
|
|
149
|
+
summary: Ruby/ActiveRecord integration for ClickHouse
|
|
150
|
+
test_files: []
|