click_house 1.7.0 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (42) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/main.yml +15 -4
  3. data/CHANGELOG.md +8 -0
  4. data/Gemfile.lock +22 -31
  5. data/Gemfile_faraday1 +11 -0
  6. data/Gemfile_faraday1.lock +111 -0
  7. data/Gemfile_faraday2 +1 -0
  8. data/Gemfile_faraday2.lock +88 -0
  9. data/Makefile +25 -0
  10. data/README.md +5 -19
  11. data/click_house.gemspec +4 -3
  12. data/lib/click_house/ast/parser.rb +53 -0
  13. data/lib/click_house/ast/statement.rb +99 -0
  14. data/lib/click_house/ast/ticker.rb +42 -0
  15. data/lib/click_house/ast.rb +9 -0
  16. data/lib/click_house/config.rb +17 -1
  17. data/lib/click_house/connection.rb +3 -2
  18. data/lib/click_house/definition/column_set.rb +3 -2
  19. data/lib/click_house/extend/configurable.rb +2 -0
  20. data/lib/click_house/extend/type_definition.rb +1 -11
  21. data/lib/click_house/middleware/parse_csv.rb +5 -6
  22. data/lib/click_house/middleware/parse_json.rb +16 -0
  23. data/lib/click_house/middleware/parse_json_oj.rb +22 -0
  24. data/lib/click_house/middleware/raise_error.rb +6 -12
  25. data/lib/click_house/middleware/response_base.rb +45 -0
  26. data/lib/click_house/middleware.rb +3 -0
  27. data/lib/click_house/response/result_set.rb +56 -43
  28. data/lib/click_house/response.rb +1 -0
  29. data/lib/click_house/type/array_type.rb +6 -12
  30. data/lib/click_house/type/base_type.rb +25 -1
  31. data/lib/click_house/type/date_time64_type.rb +5 -1
  32. data/lib/click_house/type/date_time_type.rb +5 -1
  33. data/lib/click_house/type/decimal_type.rb +15 -1
  34. data/lib/click_house/type/low_cardinality_type.rb +4 -10
  35. data/lib/click_house/type/map_type.rb +15 -0
  36. data/lib/click_house/type/nullable_type.rb +6 -8
  37. data/lib/click_house/type/string_type.rb +1 -1
  38. data/lib/click_house/type/tuple_type.rb +15 -0
  39. data/lib/click_house/type.rb +2 -0
  40. data/lib/click_house/version.rb +1 -1
  41. data/lib/click_house.rb +9 -2
  42. metadata +37 -4
@@ -16,7 +16,20 @@ module ClickHouse
16
16
  open_timeout: nil,
17
17
  ssl_verify: false,
18
18
  headers: {},
19
- global_params: {}
19
+ global_params: {},
20
+ json_parser: ClickHouse::Middleware::ParseJson,
21
+ oj_load_options: {
22
+ mode: :custom,
23
+ allow_blank: true,
24
+ bigdecimal_as_decimal: false, # dump BigDecimal as a String
25
+ bigdecimal_load: :bigdecimal, # convert all decimal numbers to BigDecimal
26
+ empty_string: false,
27
+ second_precision: 6,
28
+ time_format: :ruby,
29
+ },
30
+ json_load_options: {
31
+ decimal_class: BigDecimal,
32
+ },
20
33
  }.freeze
21
34
 
22
35
  attr_accessor :adapter
@@ -33,6 +46,9 @@ module ClickHouse
33
46
  attr_accessor :ssl_verify
34
47
  attr_accessor :headers
35
48
  attr_accessor :global_params
49
+ attr_accessor :oj_load_options
50
+ attr_accessor :json_load_options
51
+ attr_accessor :json_parser # response middleware
36
52
 
37
53
  def initialize(params = {})
38
54
  assign(DEFAULTS.merge(params))
@@ -46,6 +46,7 @@ module ClickHouse
46
46
  transport.post(compose('/', query.merge(database: database, **params)), body)
47
47
  end
48
48
 
49
+ # transport should work the same both with Faraday v1 and Faraday v2
49
50
  def transport
50
51
  @transport ||= Faraday.new(config.url!) do |conn|
51
52
  conn.options.timeout = config.timeout
@@ -53,10 +54,10 @@ module ClickHouse
53
54
  conn.headers = config.headers
54
55
  conn.ssl.verify = config.ssl_verify
55
56
  conn.request(:basic_auth, config.username, config.password) if config.auth?
56
- conn.response :json, content_type: %r{application/json}
57
57
  conn.response Middleware::RaiseError
58
58
  conn.response Middleware::Logging, logger: config.logger!
59
- conn.response Middleware::ParseCsv, content_type: %r{text/csv}
59
+ conn.response config.json_parser, content_type: %r{application/json}, parser_options: { config: config }
60
+ conn.response Middleware::ParseCsv, content_type: %r{text/csv}, parser_options: { config: config }
60
61
  conn.adapter config.adapter
61
62
  end
62
63
  end
@@ -3,7 +3,9 @@
3
3
  module ClickHouse
4
4
  module Definition
5
5
  class ColumnSet
6
- TYPES = ClickHouse.type_names(nullable: false).map { |s| s.sub('%s', "'%s'") }.freeze
6
+ TYPES = ClickHouse.types.each_with_object([]) do |(name, type), object|
7
+ object << name.sub('%s', "'%s'") if type.ddl?
8
+ end
7
9
 
8
10
  class << self
9
11
  # @input "DateTime('%s')"
@@ -15,7 +17,6 @@ module ClickHouse
15
17
 
16
18
  TYPES.each do |type|
17
19
  method_name = method_name_for_type(type)
18
-
19
20
  # t.Decimal :customer_id, nullable: true, default: ''
20
21
  # t.Decimal :money, 1, 2, nullable: true, default: ''
21
22
  class_eval <<-METHODS, __FILE__, __LINE__ + 1
@@ -4,6 +4,8 @@ module ClickHouse
4
4
  module Extend
5
5
  module Configurable
6
6
  def config(&block)
7
+ yield(@config) if defined?(@config) && block
8
+
7
9
  @config ||= Config.new(&block)
8
10
  end
9
11
  end
@@ -3,22 +3,12 @@
3
3
  module ClickHouse
4
4
  module Extend
5
5
  module TypeDefinition
6
- NULLABLE = 'Nullable'
7
- NULLABLE_RE = /#{NULLABLE}/i.freeze
8
- LOW_CARDINALITY = 'LowCardinality'
9
-
10
6
  def types
11
7
  @types ||= Hash.new(Type::UndefinedType.new)
12
8
  end
13
9
 
14
- def add_type(type, klass, nullable: true)
10
+ def add_type(type, klass)
15
11
  types[type] = klass
16
- types["#{NULLABLE}(#{type})"] = Type::NullableType.new(klass) if nullable
17
- end
18
-
19
- # @return [Enum<String>]
20
- def type_names(nullable:)
21
- nullable ? types.keys : types.keys.grep_v(NULLABLE_RE)
22
12
  end
23
13
  end
24
14
  end
@@ -2,15 +2,14 @@
2
2
 
3
3
  module ClickHouse
4
4
  module Middleware
5
- class ParseCsv < FaradayMiddleware::ResponseMiddleware
5
+ class ParseCsv < ResponseBase
6
6
  Faraday::Response.register_middleware self => self
7
7
 
8
- dependency do
9
- require 'csv' unless defined?(CSV)
10
- end
8
+ # @param env [Faraday::Env]
9
+ def on_complete(env)
10
+ return unless content_type?(env, content_type)
11
11
 
12
- define_parser do |body, parser_options|
13
- CSV.parse(body, **Hash.new(parser_options)) unless body.strip.empty?
12
+ env.body = env.body.strip.empty? ? nil : CSV.parse(env.body)
14
13
  end
15
14
  end
16
15
  end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ClickHouse
4
+ module Middleware
5
+ class ParseJson < ResponseBase
6
+ Faraday::Response.register_middleware self => self
7
+
8
+ # @param env [Faraday::Env]
9
+ def on_complete(env)
10
+ return unless content_type?(env, content_type)
11
+
12
+ env.body = JSON.parse(env.body, config.json_load_options) unless env.body.strip.empty?
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ClickHouse
4
+ module Middleware
5
+ class ParseJsonOj < ResponseBase
6
+ Faraday::Response.register_middleware self => self
7
+
8
+ # @param env [Faraday::Env]
9
+ def on_complete(env)
10
+ return unless content_type?(env, content_type)
11
+
12
+ env.body = Oj.load(env.body, config.oj_load_options) unless env.body.strip.empty?
13
+ end
14
+
15
+ private
16
+
17
+ def on_setup
18
+ require 'oj' unless defined?(Oj)
19
+ end
20
+ end
21
+ end
22
+ end
@@ -3,28 +3,22 @@
3
3
  module ClickHouse
4
4
  module Middleware
5
5
  class RaiseError < Faraday::Middleware
6
- SUCCEED_STATUSES = (200..299).freeze
7
6
  EXCEPTION_CODE_HEADER = 'x-clickhouse-exception-code'
8
7
 
9
8
  Faraday::Response.register_middleware self => self
10
9
 
11
- def call(environment)
12
- @app.call(environment).on_complete(&method(:on_complete))
10
+ # @param env [Faraday::Env]
11
+ def call(env)
12
+ super
13
13
  rescue Faraday::ConnectionFailed => e
14
14
  raise NetworkException, e.message, e.backtrace
15
15
  end
16
16
 
17
- private
18
-
17
+ # @param env [Faraday::Env]
19
18
  def on_complete(env)
20
- # Valid since Clickhouse 22.6
21
- if env.response_headers.key?(EXCEPTION_CODE_HEADER)
22
- raise DbException, env.body
19
+ if env.response_headers.include?(EXCEPTION_CODE_HEADER) || !env.success?
20
+ raise DbException, "[#{env.status}] #{env.body}"
23
21
  end
24
-
25
- return if SUCCEED_STATUSES.include?(env.status)
26
-
27
- raise DbException, "[#{env.status}] #{env.body}"
28
22
  end
29
23
  end
30
24
  end
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ClickHouse
4
+ module Middleware
5
+ class ResponseBase < Faraday::Middleware
6
+ CONTENT_TYPE_HEADER = 'content-type'
7
+
8
+ attr_reader :parser_options
9
+ attr_reader :content_type
10
+
11
+ def initialize(app = nil, parser_options: {}, content_type: nil, preserve_raw: false)
12
+ super(app)
13
+ @parser_options = parser_options
14
+ @content_type = content_type
15
+ @preserve_raw = preserve_raw
16
+ on_setup
17
+ end
18
+
19
+ # @return [Boolean]
20
+ # @param env [Faraday::Env]
21
+ # @param regexp [NilClass, Regexp]
22
+ def content_type?(env, regexp)
23
+ case regexp
24
+ when NilClass
25
+ false
26
+ when Regexp
27
+ regexp.match?(String(env[:response_headers][CONTENT_TYPE_HEADER]))
28
+ else
29
+ raise ArgumentError, "expected regexp got #{regexp.class}"
30
+ end
31
+ end
32
+
33
+ # @return [Config]
34
+ def config
35
+ parser_options.fetch(:config)
36
+ end
37
+
38
+ private
39
+
40
+ def on_setup
41
+ # require external dependencies here
42
+ end
43
+ end
44
+ end
45
+ end
@@ -2,8 +2,11 @@
2
2
 
3
3
  module ClickHouse
4
4
  module Middleware
5
+ autoload :ResponseBase, 'click_house/middleware/response_base'
5
6
  autoload :Logging, 'click_house/middleware/logging'
6
7
  autoload :ParseCsv, 'click_house/middleware/parse_csv'
8
+ autoload :ParseJsonOj, 'click_house/middleware/parse_json_oj'
9
+ autoload :ParseJson, 'click_house/middleware/parse_json'
7
10
  autoload :RaiseError, 'click_house/middleware/raise_error'
8
11
  end
9
12
  end
@@ -6,10 +6,6 @@ module ClickHouse
6
6
  extend Forwardable
7
7
  include Enumerable
8
8
 
9
- TYPE_ARGV_DELIM = ','
10
- NULLABLE = 'Nullable'
11
- NULLABLE_TYPE_RE = /#{NULLABLE}\((.+)\)/i.freeze
12
- ARG_D_RE = /\A-?\d+\Z/.freeze
13
9
  PLACEHOLDER_D = '%d'
14
10
  PLACEHOLDER_S = '%s'
15
11
 
@@ -19,38 +15,6 @@ module ClickHouse
19
15
 
20
16
  attr_reader :meta, :data, :totals, :statistics, :rows_before_limit_at_least
21
17
 
22
- class << self
23
- # @return [Array<String, Array>]
24
- # * first element is name of "ClickHouse.types.keys"
25
- # * second element is extra arguments that should to be passed to <cast> function
26
- #
27
- # @input "DateTime('Europe/Moscow')"
28
- # @output "DateTime(%s)"
29
- #
30
- # @input "Nullable(Decimal(10, 5))"
31
- # @output "Nullable(Decimal(%d, %d))"
32
- #
33
- # @input "Decimal(10, 5)"
34
- # @output "Decimal(%d, %d)"
35
- def extract_type_info(type)
36
- type = type.gsub(NULLABLE_TYPE_RE, '\1')
37
- nullable = Regexp.last_match(1)
38
- argv = []
39
-
40
- type = type.gsub(/\((.+)\)/, '')
41
-
42
- if (match = Regexp.last_match(1))
43
- argv = match.split("#{TYPE_ARGV_DELIM} ")
44
- placeholder = argv.map do |value|
45
- value.match?(ARG_D_RE) ? PLACEHOLDER_D : PLACEHOLDER_S
46
- end
47
- type = "#{type}(#{placeholder.join("#{TYPE_ARGV_DELIM} ")})"
48
- end
49
-
50
- [nullable ? "#{NULLABLE}(#{type})" : type, argv]
51
- end
52
- end
53
-
54
18
  # @param meta [Array]
55
19
  # @param data [Array]
56
20
  # @param totals [Array|Hash|NilClass] Support for 'GROUP BY WITH TOTALS' modifier
@@ -67,20 +31,69 @@ module ClickHouse
67
31
  def to_a
68
32
  @to_a ||= data.each do |row|
69
33
  row.each do |name, value|
70
- casting = types.fetch(name)
71
- row[name] = casting.fetch(:caster).cast(value, *casting.fetch(:arguments))
34
+ row[name] = cast_type(types.fetch(name), value)
72
35
  end
73
36
  end
74
37
  end
75
38
 
39
+ # @return [Hash<String, Ast::Statement>]
76
40
  def types
77
41
  @types ||= meta.each_with_object({}) do |row, object|
78
- type_name, argv = self.class.extract_type_info(row.fetch('type'))
42
+ object[row.fetch('name')] = begin
43
+ current = Ast::Parser.new(row.fetch('type')).parse
44
+ assign_type(current)
45
+ current
46
+ end
47
+ end
48
+ end
49
+
50
+ private
51
+
52
+ # @param stmt [Ast::Statement]
53
+ def assign_type(stmt)
54
+ stmt.caster = ClickHouse.types[stmt.name]
55
+
56
+ if stmt.caster.is_a?(Type::UndefinedType)
57
+ placeholders = stmt.arguments.map(&:placeholder)
58
+ stmt.caster = ClickHouse.types["#{stmt.name}(#{placeholders.join(', ')})"]
59
+ end
60
+
61
+ stmt.arguments.each(&method(:assign_type))
62
+ end
63
+
64
+ # @param stmt [Ast::Statement]
65
+ def cast_type(stmt, value)
66
+ return cast_container(stmt, value) if stmt.caster.container?
67
+ return cast_map(stmt, Hash(value)) if stmt.caster.map?
68
+ return cast_tuple(stmt, Array(value)) if stmt.caster.tuple?
69
+
70
+ stmt.caster.cast(value, *stmt.arguments.map(&:value))
71
+ end
72
+
73
+ # @return [Hash]
74
+ # @param stmt [Ast::Statement]
75
+ # @param hash [Hash]
76
+ def cast_map(stmt, hash)
77
+ raise ArgumentError, "expect hash got #{hash.class}" unless hash.is_a?(Hash)
78
+
79
+ key_type, value_type = stmt.arguments
80
+ hash.each_with_object({}) do |(key, value), object|
81
+ object[cast_type(key_type, key)] = cast_type(value_type, value)
82
+ end
83
+ end
84
+
85
+ # @param stmt [Ast::Statement]
86
+ def cast_container(stmt, value)
87
+ stmt.caster.cast_each(value) do |item|
88
+ # TODO: raise an error if multiple arguments
89
+ cast_type(stmt.arguments.first, item)
90
+ end
91
+ end
79
92
 
80
- object[row.fetch('name')] = {
81
- caster: ClickHouse.types[type_name],
82
- arguments: argv
83
- }
93
+ # @param stmt [Ast::Statement]
94
+ def cast_tuple(stmt, value)
95
+ value.map.with_index do |item, ix|
96
+ cast_type(stmt.arguments.fetch(ix), item)
84
97
  end
85
98
  end
86
99
  end
@@ -4,5 +4,6 @@ module ClickHouse
4
4
  module Response
5
5
  autoload :Factory, 'click_house/response/factory'
6
6
  autoload :ResultSet, 'click_house/response/result_set'
7
+ autoload :Tokenize, 'click_house/response/tokenize'
7
8
  end
8
9
  end
@@ -3,22 +3,16 @@
3
3
  module ClickHouse
4
4
  module Type
5
5
  class ArrayType < BaseType
6
- attr_reader :subtype
7
-
8
- def initialize(subtype)
9
- @subtype = subtype
6
+ def cast_each(value, *_argv, &block)
7
+ value.map(&block)
10
8
  end
11
9
 
12
- def cast(value, *)
13
- value
10
+ def container?
11
+ true
14
12
  end
15
13
 
16
- def serialize(array, *argv)
17
- return unless array.is_a?(Array)
18
-
19
- array.map do |value|
20
- subtype.serialize(value, *argv)
21
- end
14
+ def ddl?
15
+ false
22
16
  end
23
17
  end
24
18
  end
@@ -7,9 +7,33 @@ module ClickHouse
7
7
  raise NotImplementedError, __method__
8
8
  end
9
9
 
10
- def serialize(_value, *)
10
+ def cast_each(_value, *)
11
11
  raise NotImplementedError, __method__
12
12
  end
13
+
14
+ # @return [Boolean]
15
+ # true if type contains another type like Nullable(T) or Array(T)
16
+ def container?
17
+ false
18
+ end
19
+
20
+ # @return [Boolean]
21
+ # true if type is a Map
22
+ def map?
23
+ false
24
+ end
25
+
26
+ # @return [Boolean]
27
+ # true if type is a Tuple
28
+ def tuple?
29
+ false
30
+ end
31
+
32
+ # @return [Boolean]
33
+ # skip type from DDL statements
34
+ def ddl?
35
+ true
36
+ end
13
37
  end
14
38
  end
15
39
  end
@@ -4,7 +4,11 @@ module ClickHouse
4
4
  module Type
5
5
  class DateTime64Type < BaseType
6
6
  def cast(value, _precision = nil, tz = nil)
7
- DateTime.parse("#{value} #{tz}")
7
+ if tz
8
+ Time.find_zone(tz).parse(value)
9
+ else
10
+ Time.parse(value)
11
+ end
8
12
  end
9
13
 
10
14
  def serialize(value, precision = 3)
@@ -4,7 +4,11 @@ module ClickHouse
4
4
  module Type
5
5
  class DateTimeType < BaseType
6
6
  def cast(value, tz = nil)
7
- DateTime.parse("#{value} #{tz}")
7
+ if tz
8
+ Time.find_zone(tz).parse(value)
9
+ else
10
+ Time.parse(value)
11
+ end
8
12
  end
9
13
 
10
14
  def serialize(value)
@@ -3,8 +3,22 @@
3
3
  module ClickHouse
4
4
  module Type
5
5
  class DecimalType < BaseType
6
+ MAXIMUM = Float::DIG
7
+
8
+ # clickhouse:
9
+ # P - precision. Valid range: [ 1 : 76 ]. Determines how many decimal digits number can have (including fraction).
10
+ # S - scale. Valid range: [ 0 : P ]. Determines how many decimal digits fraction can have.
11
+ #
12
+ # when Oj parser @refs https://stackoverflow.com/questions/47885304/deserialise-json-numbers-as-bigdecimal
6
13
  def cast(value, precision = Float::DIG, _scale = nil)
7
- BigDecimal(value, precision.to_i)
14
+ case value
15
+ when BigDecimal
16
+ value
17
+ when String
18
+ BigDecimal(value)
19
+ else
20
+ BigDecimal(value, precision > MAXIMUM ? MAXIMUM : precision)
21
+ end
8
22
  end
9
23
 
10
24
  def serialize(value, precision = Float::DIG, _scale = nil)
@@ -3,18 +3,12 @@
3
3
  module ClickHouse
4
4
  module Type
5
5
  class LowCardinalityType < BaseType
6
- attr_reader :subtype
7
-
8
- def initialize(subtype)
9
- @subtype = subtype
10
- end
11
-
12
- def cast(*argv)
13
- subtype.cast(*argv)
6
+ def cast_each(value, *_argv)
7
+ yield(value) unless value.nil?
14
8
  end
15
9
 
16
- def serialize(*argv)
17
- subtype.serialize(*argv)
10
+ def container?
11
+ true
18
12
  end
19
13
  end
20
14
  end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ClickHouse
4
+ module Type
5
+ class MapType < BaseType
6
+ def map?
7
+ true
8
+ end
9
+
10
+ def ddl?
11
+ false
12
+ end
13
+ end
14
+ end
15
+ end
@@ -3,18 +3,16 @@
3
3
  module ClickHouse
4
4
  module Type
5
5
  class NullableType < BaseType
6
- attr_reader :subtype
7
-
8
- def initialize(subtype)
9
- @subtype = subtype
6
+ def cast_each(value, *_argv)
7
+ yield(value) unless value.nil?
10
8
  end
11
9
 
12
- def cast(*argv)
13
- subtype.cast(*argv) unless argv.first.nil?
10
+ def container?
11
+ true
14
12
  end
15
13
 
16
- def serialize(*argv)
17
- subtype.serialize(*argv) unless argv.first.nil?
14
+ def ddl?
15
+ false
18
16
  end
19
17
  end
20
18
  end
@@ -3,7 +3,7 @@
3
3
  module ClickHouse
4
4
  module Type
5
5
  class StringType < BaseType
6
- def cast(value)
6
+ def cast(value, *)
7
7
  value.to_s unless value.nil?
8
8
  end
9
9
 
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ClickHouse
4
+ module Type
5
+ class TupleType < BaseType
6
+ def tuple?
7
+ true
8
+ end
9
+
10
+ def ddl?
11
+ false
12
+ end
13
+ end
14
+ end
15
+ end
@@ -14,6 +14,8 @@ module ClickHouse
14
14
  autoload :DecimalType, 'click_house/type/decimal_type'
15
15
  autoload :FixedStringType, 'click_house/type/fixed_string_type'
16
16
  autoload :ArrayType, 'click_house/type/array_type'
17
+ autoload :TupleType, 'click_house/type/tuple_type'
18
+ autoload :MapType, 'click_house/type/map_type'
17
19
  autoload :StringType, 'click_house/type/string_type'
18
20
  autoload :IPType, 'click_house/type/ip_type'
19
21
  autoload :LowCardinalityType, 'click_house/type/low_cardinality_type'
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ClickHouse
4
- VERSION = '1.7.0'
4
+ VERSION = '2.0.0'
5
5
  end
data/lib/click_house.rb CHANGED
@@ -8,13 +8,14 @@ require 'logger'
8
8
  require 'faraday'
9
9
  require 'forwardable'
10
10
  require 'bigdecimal'
11
- require 'faraday_middleware'
11
+ require 'active_support/core_ext/time/calculations'
12
12
  require 'click_house/version'
13
13
  require 'click_house/errors'
14
14
  require 'click_house/response'
15
15
  require 'click_house/type'
16
16
  require 'click_house/middleware'
17
17
  require 'click_house/extend'
18
+ require 'click_house/ast'
18
19
  require 'click_house/util'
19
20
  require 'click_house/definition'
20
21
 
@@ -26,6 +27,12 @@ module ClickHouse
26
27
  autoload :Config, 'click_house/config'
27
28
  autoload :Connection, 'click_house/connection'
28
29
 
30
+ add_type 'Array', Type::ArrayType.new
31
+ add_type 'Nullable', Type::NullableType.new
32
+ add_type 'Map', Type::MapType.new
33
+ add_type 'LowCardinality', Type::LowCardinalityType.new
34
+ add_type 'Tuple', Type::TupleType.new
35
+
29
36
  %w[Date].each do |column|
30
37
  add_type column, Type::DateType.new
31
38
  end
@@ -42,7 +49,7 @@ module ClickHouse
42
49
  add_type column, Type::DateTime64Type.new
43
50
  end
44
51
 
45
- ['Decimal(%d, %d)', 'Decimal32(%d)', 'Decimal64(%d)', 'Decimal128(%d)'].each do |column|
52
+ ['Decimal(%d, %d)', 'Decimal32(%d)', 'Decimal64(%d)', 'Decimal128(%d)', 'Decimal256(%d)'].each do |column|
46
53
  add_type column, Type::DecimalType.new
47
54
  end
48
55