clickhouse-ruby 0.1.0 → 0.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/CHANGELOG.md +74 -1
- data/README.md +165 -79
- data/lib/clickhouse_ruby/active_record/arel_visitor.rb +205 -76
- data/lib/clickhouse_ruby/active_record/connection_adapter.rb +103 -98
- data/lib/clickhouse_ruby/active_record/railtie.rb +20 -15
- data/lib/clickhouse_ruby/active_record/relation_extensions.rb +398 -0
- data/lib/clickhouse_ruby/active_record/schema_statements.rb +90 -104
- data/lib/clickhouse_ruby/active_record.rb +24 -10
- data/lib/clickhouse_ruby/client.rb +181 -74
- data/lib/clickhouse_ruby/configuration.rb +51 -10
- data/lib/clickhouse_ruby/connection.rb +180 -64
- data/lib/clickhouse_ruby/connection_pool.rb +25 -19
- data/lib/clickhouse_ruby/errors.rb +13 -1
- data/lib/clickhouse_ruby/result.rb +11 -16
- data/lib/clickhouse_ruby/retry_handler.rb +172 -0
- data/lib/clickhouse_ruby/streaming_result.rb +309 -0
- data/lib/clickhouse_ruby/types/array.rb +11 -64
- data/lib/clickhouse_ruby/types/base.rb +59 -0
- data/lib/clickhouse_ruby/types/boolean.rb +28 -25
- data/lib/clickhouse_ruby/types/date_time.rb +10 -27
- data/lib/clickhouse_ruby/types/decimal.rb +173 -0
- data/lib/clickhouse_ruby/types/enum.rb +262 -0
- data/lib/clickhouse_ruby/types/float.rb +14 -28
- data/lib/clickhouse_ruby/types/integer.rb +21 -43
- data/lib/clickhouse_ruby/types/low_cardinality.rb +1 -1
- data/lib/clickhouse_ruby/types/map.rb +21 -36
- data/lib/clickhouse_ruby/types/null_safe.rb +81 -0
- data/lib/clickhouse_ruby/types/nullable.rb +2 -2
- data/lib/clickhouse_ruby/types/parser.rb +28 -18
- data/lib/clickhouse_ruby/types/registry.rb +40 -29
- data/lib/clickhouse_ruby/types/string.rb +9 -13
- data/lib/clickhouse_ruby/types/string_parser.rb +135 -0
- data/lib/clickhouse_ruby/types/tuple.rb +11 -68
- data/lib/clickhouse_ruby/types/uuid.rb +15 -22
- data/lib/clickhouse_ruby/types.rb +19 -15
- data/lib/clickhouse_ruby/version.rb +1 -1
- data/lib/clickhouse_ruby.rb +11 -11
- metadata +41 -6
|
@@ -36,7 +36,7 @@ module ClickhouseRuby
|
|
|
36
36
|
# @param type_class [Class] the type class
|
|
37
37
|
def register(name, type_class)
|
|
38
38
|
@types[name] = type_class
|
|
39
|
-
@cache.clear
|
|
39
|
+
@cache.clear # Invalidate cache when types change
|
|
40
40
|
end
|
|
41
41
|
|
|
42
42
|
# Looks up a type by its ClickHouse type string
|
|
@@ -62,43 +62,55 @@ module ClickhouseRuby
|
|
|
62
62
|
# Registers all default ClickHouse types
|
|
63
63
|
def register_defaults
|
|
64
64
|
# Integer types
|
|
65
|
-
register(
|
|
66
|
-
register(
|
|
67
|
-
register(
|
|
68
|
-
register(
|
|
69
|
-
register(
|
|
70
|
-
register(
|
|
71
|
-
register(
|
|
72
|
-
register(
|
|
73
|
-
register(
|
|
74
|
-
register(
|
|
75
|
-
register(
|
|
76
|
-
register(
|
|
65
|
+
register("Int8", Integer)
|
|
66
|
+
register("Int16", Integer)
|
|
67
|
+
register("Int32", Integer)
|
|
68
|
+
register("Int64", Integer)
|
|
69
|
+
register("Int128", Integer)
|
|
70
|
+
register("Int256", Integer)
|
|
71
|
+
register("UInt8", Integer)
|
|
72
|
+
register("UInt16", Integer)
|
|
73
|
+
register("UInt32", Integer)
|
|
74
|
+
register("UInt64", Integer)
|
|
75
|
+
register("UInt128", Integer)
|
|
76
|
+
register("UInt256", Integer)
|
|
77
77
|
|
|
78
78
|
# Float types
|
|
79
|
-
register(
|
|
80
|
-
register(
|
|
79
|
+
register("Float32", Float)
|
|
80
|
+
register("Float64", Float)
|
|
81
|
+
|
|
82
|
+
# Decimal types
|
|
83
|
+
register("Decimal", Decimal)
|
|
84
|
+
register("Decimal32", Decimal)
|
|
85
|
+
register("Decimal64", Decimal)
|
|
86
|
+
register("Decimal128", Decimal)
|
|
87
|
+
register("Decimal256", Decimal)
|
|
81
88
|
|
|
82
89
|
# String types
|
|
83
|
-
register(
|
|
84
|
-
register(
|
|
90
|
+
register("String", String)
|
|
91
|
+
register("FixedString", String)
|
|
85
92
|
|
|
86
93
|
# Date/Time types
|
|
87
|
-
register(
|
|
88
|
-
register(
|
|
89
|
-
register(
|
|
90
|
-
register(
|
|
94
|
+
register("Date", DateTime)
|
|
95
|
+
register("Date32", DateTime)
|
|
96
|
+
register("DateTime", DateTime)
|
|
97
|
+
register("DateTime64", DateTime)
|
|
91
98
|
|
|
92
99
|
# Other basic types
|
|
93
|
-
register(
|
|
94
|
-
register(
|
|
100
|
+
register("UUID", UUID)
|
|
101
|
+
register("Bool", Boolean)
|
|
95
102
|
|
|
96
103
|
# Complex/wrapper types
|
|
97
|
-
register(
|
|
98
|
-
register(
|
|
99
|
-
register(
|
|
100
|
-
register(
|
|
101
|
-
register(
|
|
104
|
+
register("Array", Array)
|
|
105
|
+
register("Map", Map)
|
|
106
|
+
register("Tuple", Tuple)
|
|
107
|
+
register("Nullable", Nullable)
|
|
108
|
+
register("LowCardinality", LowCardinality)
|
|
109
|
+
|
|
110
|
+
# Enum types
|
|
111
|
+
register("Enum", Enum)
|
|
112
|
+
register("Enum8", Enum)
|
|
113
|
+
register("Enum16", Enum)
|
|
102
114
|
end
|
|
103
115
|
|
|
104
116
|
private
|
|
@@ -142,7 +154,6 @@ module ClickhouseRuby
|
|
|
142
154
|
type_class.new(type_name)
|
|
143
155
|
end
|
|
144
156
|
end
|
|
145
|
-
|
|
146
157
|
end
|
|
147
158
|
end
|
|
148
159
|
end
|
|
@@ -29,9 +29,7 @@ module ClickhouseRuby
|
|
|
29
29
|
str = value.to_s
|
|
30
30
|
|
|
31
31
|
# For FixedString, pad or truncate to length
|
|
32
|
-
if @length
|
|
33
|
-
str = str.ljust(@length, "\0")[0, @length]
|
|
34
|
-
end
|
|
32
|
+
str = str.ljust(@length, "\0")[0, @length] if @length
|
|
35
33
|
|
|
36
34
|
str
|
|
37
35
|
end
|
|
@@ -46,9 +44,7 @@ module ClickhouseRuby
|
|
|
46
44
|
str = value.to_s
|
|
47
45
|
|
|
48
46
|
# For FixedString, remove trailing null bytes
|
|
49
|
-
if @length
|
|
50
|
-
str = str.gsub(/\0+\z/, '')
|
|
51
|
-
end
|
|
47
|
+
str = str.gsub(/\0+\z/, "") if @length
|
|
52
48
|
|
|
53
49
|
str
|
|
54
50
|
end
|
|
@@ -58,7 +54,7 @@ module ClickhouseRuby
|
|
|
58
54
|
# @param value [String, nil] the value to serialize
|
|
59
55
|
# @return [String] the SQL literal
|
|
60
56
|
def serialize(value)
|
|
61
|
-
return
|
|
57
|
+
return "NULL" if value.nil?
|
|
62
58
|
|
|
63
59
|
escaped = escape_string(value.to_s)
|
|
64
60
|
"'#{escaped}'"
|
|
@@ -71,12 +67,12 @@ module ClickhouseRuby
|
|
|
71
67
|
# @param value [String] the string to escape
|
|
72
68
|
# @return [String] the escaped string
|
|
73
69
|
def escape_string(value)
|
|
74
|
-
value.gsub("\\", "
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
70
|
+
value.gsub("\\", "\\\\\\\\")
|
|
71
|
+
.gsub("'", "\\\\'")
|
|
72
|
+
.gsub("\n", "\\\\n")
|
|
73
|
+
.gsub("\r", "\\\\r")
|
|
74
|
+
.gsub("\t", "\\\\t")
|
|
75
|
+
.gsub("\0", "\\\\0")
|
|
80
76
|
end
|
|
81
77
|
end
|
|
82
78
|
end
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ClickhouseRuby
|
|
4
|
+
module Types
|
|
5
|
+
# Shared utilities for parsing ClickHouse string representations
|
|
6
|
+
# of complex types (arrays, tuples, maps)
|
|
7
|
+
#
|
|
8
|
+
# These methods handle the common patterns of parsing nested structures
|
|
9
|
+
# with proper handling of quotes, escape characters, and bracket depth.
|
|
10
|
+
#
|
|
11
|
+
module StringParser
|
|
12
|
+
module_function
|
|
13
|
+
|
|
14
|
+
# Parses a comma-separated list of elements, respecting nested structures and quotes
|
|
15
|
+
#
|
|
16
|
+
# @param str [String] the string to parse
|
|
17
|
+
# @param open_brackets [Array<String>] characters that increase nesting depth
|
|
18
|
+
# @param close_brackets [Array<String>] characters that decrease nesting depth
|
|
19
|
+
# @return [Array<String>] parsed elements
|
|
20
|
+
#
|
|
21
|
+
# @example
|
|
22
|
+
# StringParser.parse_delimited("a, b, c")
|
|
23
|
+
# # => ["a", "b", "c"]
|
|
24
|
+
#
|
|
25
|
+
# StringParser.parse_delimited("'hello', [1, 2], 'world'")
|
|
26
|
+
# # => ["'hello'", "[1, 2]", "'world'"]
|
|
27
|
+
#
|
|
28
|
+
def parse_delimited(str, open_brackets: ["[", "(", "{"], close_brackets: ["]", ")", "}"])
|
|
29
|
+
elements = []
|
|
30
|
+
current = +""
|
|
31
|
+
depth = 0
|
|
32
|
+
in_string = false
|
|
33
|
+
escape_next = false
|
|
34
|
+
|
|
35
|
+
str.each_char do |char|
|
|
36
|
+
if escape_next
|
|
37
|
+
current << char
|
|
38
|
+
escape_next = false
|
|
39
|
+
next
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
case char
|
|
43
|
+
when "\\"
|
|
44
|
+
escape_next = true
|
|
45
|
+
current << char
|
|
46
|
+
when "'"
|
|
47
|
+
in_string = !in_string
|
|
48
|
+
current << char
|
|
49
|
+
when *open_brackets
|
|
50
|
+
depth += 1 unless in_string
|
|
51
|
+
current << char
|
|
52
|
+
when *close_brackets
|
|
53
|
+
depth -= 1 unless in_string
|
|
54
|
+
current << char
|
|
55
|
+
when ","
|
|
56
|
+
if depth.zero? && !in_string
|
|
57
|
+
elements << current.strip
|
|
58
|
+
current = +""
|
|
59
|
+
else
|
|
60
|
+
current << char
|
|
61
|
+
end
|
|
62
|
+
else
|
|
63
|
+
current << char
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
elements << current.strip unless current.strip.empty?
|
|
68
|
+
elements
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
# Removes surrounding single quotes and unescapes content
|
|
72
|
+
#
|
|
73
|
+
# @param str [String] potentially quoted string
|
|
74
|
+
# @return [String] unquoted string with escapes processed
|
|
75
|
+
#
|
|
76
|
+
# @example
|
|
77
|
+
# StringParser.unquote("'hello'")
|
|
78
|
+
# # => "hello"
|
|
79
|
+
#
|
|
80
|
+
# StringParser.unquote("'it\\'s'")
|
|
81
|
+
# # => "it's"
|
|
82
|
+
#
|
|
83
|
+
# StringParser.unquote("123")
|
|
84
|
+
# # => "123"
|
|
85
|
+
#
|
|
86
|
+
def unquote(str)
|
|
87
|
+
str = str.strip
|
|
88
|
+
if str.start_with?("'") && str.end_with?("'") && str.length >= 2
|
|
89
|
+
str[1...-1].gsub("\\'", "'")
|
|
90
|
+
else
|
|
91
|
+
str
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
# Validates and extracts content from a bracketed string
|
|
96
|
+
#
|
|
97
|
+
# @param str [String] bracketed string like "[...]" or "(...)"
|
|
98
|
+
# @param open_bracket [String] expected opening bracket
|
|
99
|
+
# @param close_bracket [String] expected closing bracket
|
|
100
|
+
# @return [String] inner content (may be empty)
|
|
101
|
+
# @raise [ArgumentError] if format is invalid
|
|
102
|
+
#
|
|
103
|
+
# @example
|
|
104
|
+
# StringParser.extract_bracketed("[1, 2, 3]", "[", "]")
|
|
105
|
+
# # => "1, 2, 3"
|
|
106
|
+
#
|
|
107
|
+
def extract_bracketed(str, open_bracket, close_bracket)
|
|
108
|
+
str = str.strip
|
|
109
|
+
unless str.start_with?(open_bracket) && str.end_with?(close_bracket) && str.length >= 2
|
|
110
|
+
raise ArgumentError, "Expected #{open_bracket}...#{close_bracket} format, got: '#{str}'"
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
str[1...-1]
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
# Parses elements and unquotes them in one step
|
|
117
|
+
#
|
|
118
|
+
# @param str [String] the string to parse
|
|
119
|
+
# @param open_brackets [Array<String>] characters that increase nesting depth
|
|
120
|
+
# @param close_brackets [Array<String>] characters that decrease nesting depth
|
|
121
|
+
# @return [Array<String>] parsed and unquoted elements
|
|
122
|
+
#
|
|
123
|
+
def parse_and_unquote(str, open_brackets: ["[", "(", "{"], close_brackets: ["]", ")", "}"])
|
|
124
|
+
parse_delimited(str, open_brackets: open_brackets, close_brackets: close_brackets).map do |el|
|
|
125
|
+
# Only unquote simple quoted strings, preserve nested structures
|
|
126
|
+
if el.start_with?("'") && el.end_with?("'") && !el.include?("[") && !el.include?("(") && !el.include?("{")
|
|
127
|
+
unquote(el)
|
|
128
|
+
else
|
|
129
|
+
el
|
|
130
|
+
end
|
|
131
|
+
end
|
|
132
|
+
end
|
|
133
|
+
end
|
|
134
|
+
end
|
|
135
|
+
end
|
|
@@ -37,12 +37,7 @@ module ClickhouseRuby
|
|
|
37
37
|
when ::String
|
|
38
38
|
parse_tuple_string(value)
|
|
39
39
|
else
|
|
40
|
-
|
|
41
|
-
"Cannot cast #{value.class} to Tuple",
|
|
42
|
-
from_type: value.class.name,
|
|
43
|
-
to_type: to_s,
|
|
44
|
-
value: value
|
|
45
|
-
)
|
|
40
|
+
raise_cast_error(value, "Cannot cast #{value.class} to Tuple")
|
|
46
41
|
end
|
|
47
42
|
|
|
48
43
|
cast_elements(arr)
|
|
@@ -72,21 +67,21 @@ module ClickhouseRuby
|
|
|
72
67
|
# @param value [Array, nil] the value to serialize
|
|
73
68
|
# @return [String] the SQL literal
|
|
74
69
|
def serialize(value)
|
|
75
|
-
return
|
|
70
|
+
return "NULL" if value.nil?
|
|
76
71
|
|
|
77
72
|
elements = value.each_with_index.map do |v, i|
|
|
78
|
-
type = @element_types[i] || Base.new(
|
|
73
|
+
type = @element_types[i] || Base.new("String")
|
|
79
74
|
type.serialize(v)
|
|
80
75
|
end
|
|
81
76
|
|
|
82
|
-
"(#{elements.join(
|
|
77
|
+
"(#{elements.join(", ")})"
|
|
83
78
|
end
|
|
84
79
|
|
|
85
80
|
# Returns the full type string including element types
|
|
86
81
|
#
|
|
87
82
|
# @return [String] the type string
|
|
88
83
|
def to_s
|
|
89
|
-
type_strs = @element_types.map(&:to_s).join(
|
|
84
|
+
type_strs = @element_types.map(&:to_s).join(", ")
|
|
90
85
|
"Tuple(#{type_strs})"
|
|
91
86
|
end
|
|
92
87
|
|
|
@@ -98,7 +93,7 @@ module ClickhouseRuby
|
|
|
98
93
|
# @return [Array] the cast array
|
|
99
94
|
def cast_elements(arr)
|
|
100
95
|
arr.each_with_index.map do |v, i|
|
|
101
|
-
type = @element_types[i] || Base.new(
|
|
96
|
+
type = @element_types[i] || Base.new("String")
|
|
102
97
|
type.cast(v)
|
|
103
98
|
end
|
|
104
99
|
end
|
|
@@ -109,7 +104,7 @@ module ClickhouseRuby
|
|
|
109
104
|
# @return [Array] the deserialized array
|
|
110
105
|
def deserialize_elements(arr)
|
|
111
106
|
arr.each_with_index.map do |v, i|
|
|
112
|
-
type = @element_types[i] || Base.new(
|
|
107
|
+
type = @element_types[i] || Base.new("String")
|
|
113
108
|
type.deserialize(v)
|
|
114
109
|
end
|
|
115
110
|
end
|
|
@@ -122,17 +117,10 @@ module ClickhouseRuby
|
|
|
122
117
|
stripped = value.strip
|
|
123
118
|
|
|
124
119
|
# Handle empty tuple
|
|
125
|
-
return [] if stripped ==
|
|
120
|
+
return [] if stripped == "()"
|
|
126
121
|
|
|
127
122
|
# Remove outer parentheses
|
|
128
|
-
unless stripped.start_with?(
|
|
129
|
-
raise TypeCastError.new(
|
|
130
|
-
"Invalid tuple format: '#{value}'",
|
|
131
|
-
from_type: 'String',
|
|
132
|
-
to_type: to_s,
|
|
133
|
-
value: value
|
|
134
|
-
)
|
|
135
|
-
end
|
|
123
|
+
raise_format_error(value, "tuple") unless stripped.start_with?("(") && stripped.end_with?(")")
|
|
136
124
|
|
|
137
125
|
inner = stripped[1...-1]
|
|
138
126
|
return [] if inner.strip.empty?
|
|
@@ -146,48 +134,7 @@ module ClickhouseRuby
|
|
|
146
134
|
# @param str [String] the inner tuple string
|
|
147
135
|
# @return [Array] the parsed elements
|
|
148
136
|
def parse_elements(str)
|
|
149
|
-
|
|
150
|
-
current = ''
|
|
151
|
-
depth = 0
|
|
152
|
-
in_string = false
|
|
153
|
-
escape_next = false
|
|
154
|
-
|
|
155
|
-
str.each_char do |char|
|
|
156
|
-
if escape_next
|
|
157
|
-
current += char
|
|
158
|
-
escape_next = false
|
|
159
|
-
next
|
|
160
|
-
end
|
|
161
|
-
|
|
162
|
-
case char
|
|
163
|
-
when '\\'
|
|
164
|
-
escape_next = true
|
|
165
|
-
current += char
|
|
166
|
-
when "'"
|
|
167
|
-
in_string = !in_string
|
|
168
|
-
current += char
|
|
169
|
-
when '(', '[', '{'
|
|
170
|
-
depth += 1 unless in_string
|
|
171
|
-
current += char
|
|
172
|
-
when ')', ']', '}'
|
|
173
|
-
depth -= 1 unless in_string
|
|
174
|
-
current += char
|
|
175
|
-
when ','
|
|
176
|
-
if depth.zero? && !in_string
|
|
177
|
-
elements << parse_element(current.strip)
|
|
178
|
-
current = ''
|
|
179
|
-
else
|
|
180
|
-
current += char
|
|
181
|
-
end
|
|
182
|
-
else
|
|
183
|
-
current += char
|
|
184
|
-
end
|
|
185
|
-
end
|
|
186
|
-
|
|
187
|
-
# Don't forget the last element
|
|
188
|
-
elements << parse_element(current.strip) unless current.strip.empty?
|
|
189
|
-
|
|
190
|
-
elements
|
|
137
|
+
StringParser.parse_delimited(str).map { |el| parse_element(el) }
|
|
191
138
|
end
|
|
192
139
|
|
|
193
140
|
# Parses a single element, removing quotes if necessary
|
|
@@ -195,11 +142,7 @@ module ClickhouseRuby
|
|
|
195
142
|
# @param str [String] the element string
|
|
196
143
|
# @return [Object] the parsed element
|
|
197
144
|
def parse_element(str)
|
|
198
|
-
|
|
199
|
-
str[1...-1].gsub("\\'", "'")
|
|
200
|
-
else
|
|
201
|
-
str
|
|
202
|
-
end
|
|
145
|
+
StringParser.unquote(str)
|
|
203
146
|
end
|
|
204
147
|
end
|
|
205
148
|
end
|
|
@@ -8,17 +8,19 @@ module ClickhouseRuby
|
|
|
8
8
|
# in the format: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
|
|
9
9
|
#
|
|
10
10
|
class UUID < Base
|
|
11
|
+
include NullSafe
|
|
12
|
+
|
|
11
13
|
# UUID regex pattern
|
|
12
14
|
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
15
|
|
|
16
|
+
protected
|
|
17
|
+
|
|
14
18
|
# Converts a Ruby value to a UUID string
|
|
15
19
|
#
|
|
16
|
-
# @param value [Object] the value to convert
|
|
17
|
-
# @return [String
|
|
20
|
+
# @param value [Object] the value to convert (guaranteed non-nil)
|
|
21
|
+
# @return [String] the UUID string
|
|
18
22
|
# @raise [TypeCastError] if the value is not a valid UUID
|
|
19
|
-
def
|
|
20
|
-
return nil if value.nil?
|
|
21
|
-
|
|
23
|
+
def cast_value(value)
|
|
22
24
|
str = normalize_uuid(value)
|
|
23
25
|
validate_uuid!(str, value)
|
|
24
26
|
str
|
|
@@ -26,21 +28,17 @@ module ClickhouseRuby
|
|
|
26
28
|
|
|
27
29
|
# Converts a value from ClickHouse to a UUID string
|
|
28
30
|
#
|
|
29
|
-
# @param value [Object] the value from ClickHouse
|
|
30
|
-
# @return [String
|
|
31
|
-
def
|
|
32
|
-
return nil if value.nil?
|
|
33
|
-
|
|
31
|
+
# @param value [Object] the value from ClickHouse (guaranteed non-nil)
|
|
32
|
+
# @return [String] the UUID string
|
|
33
|
+
def deserialize_value(value)
|
|
34
34
|
normalize_uuid(value)
|
|
35
35
|
end
|
|
36
36
|
|
|
37
37
|
# Converts a UUID to SQL literal
|
|
38
38
|
#
|
|
39
|
-
# @param value [String
|
|
39
|
+
# @param value [String] the UUID value (guaranteed non-nil)
|
|
40
40
|
# @return [String] the SQL literal
|
|
41
|
-
def
|
|
42
|
-
return 'NULL' if value.nil?
|
|
43
|
-
|
|
41
|
+
def serialize_value(value)
|
|
44
42
|
"'#{normalize_uuid(value)}'"
|
|
45
43
|
end
|
|
46
44
|
|
|
@@ -54,10 +52,10 @@ module ClickhouseRuby
|
|
|
54
52
|
str = value.to_s.strip.downcase
|
|
55
53
|
|
|
56
54
|
# Remove braces if present
|
|
57
|
-
str = str.gsub(/[{}]/,
|
|
55
|
+
str = str.gsub(/[{}]/, "")
|
|
58
56
|
|
|
59
57
|
# If no hyphens, add them
|
|
60
|
-
if str.length == 32 && !str.include?(
|
|
58
|
+
if str.length == 32 && !str.include?("-")
|
|
61
59
|
str = "#{str[0..7]}-#{str[8..11]}-#{str[12..15]}-#{str[16..19]}-#{str[20..31]}"
|
|
62
60
|
end
|
|
63
61
|
|
|
@@ -72,12 +70,7 @@ module ClickhouseRuby
|
|
|
72
70
|
def validate_uuid!(str, original)
|
|
73
71
|
return if str.match?(UUID_PATTERN)
|
|
74
72
|
|
|
75
|
-
|
|
76
|
-
"Invalid UUID format: '#{original}'",
|
|
77
|
-
from_type: original.class.name,
|
|
78
|
-
to_type: name,
|
|
79
|
-
value: original
|
|
80
|
-
)
|
|
73
|
+
raise_format_error(original, "UUID")
|
|
81
74
|
end
|
|
82
75
|
end
|
|
83
76
|
end
|
|
@@ -1,20 +1,24 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
# Load base class first, then parser, then specific types, then registry last
|
|
4
|
-
require_relative
|
|
5
|
-
require_relative
|
|
6
|
-
require_relative
|
|
7
|
-
require_relative
|
|
8
|
-
require_relative
|
|
9
|
-
require_relative
|
|
10
|
-
require_relative
|
|
11
|
-
require_relative
|
|
12
|
-
require_relative
|
|
13
|
-
require_relative
|
|
14
|
-
require_relative
|
|
15
|
-
require_relative
|
|
16
|
-
require_relative
|
|
17
|
-
require_relative
|
|
3
|
+
# Load base class first, then modules, then parser, then specific types, then registry last
|
|
4
|
+
require_relative "types/base"
|
|
5
|
+
require_relative "types/null_safe"
|
|
6
|
+
require_relative "types/string_parser"
|
|
7
|
+
require_relative "types/parser"
|
|
8
|
+
require_relative "types/integer"
|
|
9
|
+
require_relative "types/float"
|
|
10
|
+
require_relative "types/decimal"
|
|
11
|
+
require_relative "types/string"
|
|
12
|
+
require_relative "types/date_time"
|
|
13
|
+
require_relative "types/uuid"
|
|
14
|
+
require_relative "types/boolean"
|
|
15
|
+
require_relative "types/array"
|
|
16
|
+
require_relative "types/map"
|
|
17
|
+
require_relative "types/tuple"
|
|
18
|
+
require_relative "types/nullable"
|
|
19
|
+
require_relative "types/low_cardinality"
|
|
20
|
+
require_relative "types/enum"
|
|
21
|
+
require_relative "types/registry"
|
|
18
22
|
|
|
19
23
|
module ClickhouseRuby
|
|
20
24
|
# Type system for mapping between ClickHouse and Ruby types
|
data/lib/clickhouse_ruby.rb
CHANGED
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require_relative
|
|
4
|
-
require_relative
|
|
5
|
-
require_relative
|
|
6
|
-
require_relative
|
|
7
|
-
require_relative
|
|
8
|
-
require_relative
|
|
9
|
-
require_relative
|
|
10
|
-
require_relative
|
|
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/retry_handler"
|
|
9
|
+
require_relative "clickhouse_ruby/streaming_result"
|
|
10
|
+
require_relative "clickhouse_ruby/client"
|
|
11
|
+
require_relative "clickhouse_ruby/connection"
|
|
12
|
+
require_relative "clickhouse_ruby/connection_pool"
|
|
11
13
|
|
|
12
14
|
# ClickhouseRuby - Ruby/ActiveRecord integration for ClickHouse
|
|
13
15
|
#
|
|
@@ -96,6 +98,4 @@ module ClickhouseRuby
|
|
|
96
98
|
end
|
|
97
99
|
|
|
98
100
|
# Load ActiveRecord integration if ActiveRecord is available
|
|
99
|
-
if defined?(ActiveRecord)
|
|
100
|
-
require_relative 'clickhouse_ruby/active_record'
|
|
101
|
-
end
|
|
101
|
+
require_relative "clickhouse_ruby/active_record" if defined?(ActiveRecord)
|