click_house 1.7.0 → 2.0.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.
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