click_house 2.0.2 → 2.1.1
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 +38 -0
 - data/Gemfile.lock +1 -1
 - data/Gemfile_faraday1 +1 -1
 - data/Gemfile_faraday1.lock +3 -4
 - data/Gemfile_faraday2.lock +1 -1
 - data/README.md +59 -35
 - data/lib/click_house/benchmark/map_join.rb +20 -0
 - data/lib/click_house/config.rb +29 -3
 - data/lib/click_house/connection.rb +3 -2
 - data/lib/click_house/errors.rb +1 -0
 - data/lib/click_house/extend/connection_inserting.rb +62 -10
 - data/lib/click_house/extend/connection_selective.rb +12 -3
 - data/lib/click_house/extend/connection_table.rb +6 -1
 - data/lib/click_house/middleware/logging.rb +23 -39
 - data/lib/click_house/middleware/response_base.rb +4 -4
 - data/lib/click_house/middleware/summary_middleware.rb +26 -0
 - data/lib/click_house/middleware.rb +1 -0
 - data/lib/click_house/response/factory.rb +37 -9
 - data/lib/click_house/response/result_set.rb +94 -12
 - data/lib/click_house/response/summary.rb +109 -0
 - data/lib/click_house/response.rb +1 -1
 - data/lib/click_house/serializer/base.rb +32 -0
 - data/lib/click_house/serializer/json_oj_serializer.rb +17 -0
 - data/lib/click_house/serializer/json_serializer.rb +11 -0
 - data/lib/click_house/serializer.rb +9 -0
 - data/lib/click_house/type/array_type.rb +4 -0
 - data/lib/click_house/type/base_type.rb +4 -0
 - data/lib/click_house/type/boolean_type.rb +6 -1
 - data/lib/click_house/type/date_time64_type.rb +1 -1
 - data/lib/click_house/type/date_time_type.rb +1 -1
 - data/lib/click_house/type/decimal_type.rb +5 -4
 - data/lib/click_house/type/string_type.rb +1 -1
 - data/lib/click_house/util.rb +7 -0
 - data/lib/click_house/version.rb +1 -1
 - data/lib/click_house.rb +5 -0
 - metadata +9 -2
 
    
        checksums.yaml
    CHANGED
    
    | 
         @@ -1,7 +1,7 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            ---
         
     | 
| 
       2 
2 
     | 
    
         
             
            SHA256:
         
     | 
| 
       3 
     | 
    
         
            -
              metadata.gz:  
     | 
| 
       4 
     | 
    
         
            -
              data.tar.gz:  
     | 
| 
      
 3 
     | 
    
         
            +
              metadata.gz: 364117bb31fd3478fcf00d981b61205aa9fb864abbfeb9f9bdf253e53a0917fa
         
     | 
| 
      
 4 
     | 
    
         
            +
              data.tar.gz: d0b0d7a21175a9c2e044a88351ddd6d107b1875769ae737d3e547e41e01d93d1
         
     | 
| 
       5 
5 
     | 
    
         
             
            SHA512:
         
     | 
| 
       6 
     | 
    
         
            -
              metadata.gz:  
     | 
| 
       7 
     | 
    
         
            -
              data.tar.gz:  
     | 
| 
      
 6 
     | 
    
         
            +
              metadata.gz: 481ba8166f2300302c1a0836e336097e97a25015fbe5a5cf8e8fb5912b2170d50a2a1bc25178deb8b4a4b17d63e5638d0200bbca5a84c6a237de694ecf5f4d76
         
     | 
| 
      
 7 
     | 
    
         
            +
              data.tar.gz: ba051b743929517452507d54f98de6d57de724b4400d1cf9db02666a48c5b00d4dbd00a3ffab6b5f2f9c3a7db78c15f84ae36523e2ee76946526fe131722ee05
         
     | 
    
        data/CHANGELOG.md
    CHANGED
    
    | 
         @@ -1,3 +1,40 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # 2.1.1
         
     | 
| 
      
 2 
     | 
    
         
            +
            * Fix logging with symbolized keys JSON
         
     | 
| 
      
 3 
     | 
    
         
            +
            * Unknown formats return raw `Response::ResultSelt` like regular JSON query
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
            # 2.1.0
         
     | 
| 
      
 6 
     | 
    
         
            +
            * `ClickHouse.connection.insert` now returns `ClickHouse::Response::Summary` object
         
     | 
| 
      
 7 
     | 
    
         
            +
              with methods `headers`, `summary`, `written_rows`, `written_bytes`, etc... 
         
     | 
| 
      
 8 
     | 
    
         
            +
            * `ClickHouse.connection.insert(columns: ["id"], values: [1])` now uses `JSONCompactEachRow` by default
         
     | 
| 
      
 9 
     | 
    
         
            +
              (to increase JSON serialization speed)
         
     | 
| 
      
 10 
     | 
    
         
            +
            * Methods `insert_rows` and `insert_compact` added to `connection`
         
     | 
| 
      
 11 
     | 
    
         
            +
            * Added ability to pass object directly to insert like:
         
     | 
| 
      
 12 
     | 
    
         
            +
              `ClickHouse.connection.insert("table", {id: 1})` or
         
     | 
| 
      
 13 
     | 
    
         
            +
              `ClickHouse.connection.insert("table", [{id: 1})]` (for ruby < 3.0 use `ClickHouse.connection.insert("table", [{id: 1}], {})`)
         
     | 
| 
      
 14 
     | 
    
         
            +
            * 🔥 Added config option `json_serializer` (one of `ClickHouse::Serializer::JsonSerializer`, `ClickHouse::Serializer::JsonOjSerializer`)
         
     | 
| 
      
 15 
     | 
    
         
            +
            * 🔥 Added config option `symbolize_keys`
         
     | 
| 
      
 16 
     | 
    
         
            +
            * 🔥 Added type serialization for INSERT statements, example below:
         
     | 
| 
      
 17 
     | 
    
         
            +
             
     | 
| 
      
 18 
     | 
    
         
            +
            ```sql
         
     | 
| 
      
 19 
     | 
    
         
            +
            CREATE TABLE assets(visible Boolean, tags Array(Nullable(String))) ENGINE Memory
         
     | 
| 
      
 20 
     | 
    
         
            +
            ```
         
     | 
| 
      
 21 
     | 
    
         
            +
             
     | 
| 
      
 22 
     | 
    
         
            +
            ```ruby
         
     | 
| 
      
 23 
     | 
    
         
            +
            # cache table schema in a class variable
         
     | 
| 
      
 24 
     | 
    
         
            +
            @schema = ClickHouse.connection.table_schema('assets')
         
     | 
| 
      
 25 
     | 
    
         
            +
             
     | 
| 
      
 26 
     | 
    
         
            +
            # Json each row
         
     | 
| 
      
 27 
     | 
    
         
            +
            ClickHouse.connection.insert('assets', @schema.serialize({'visible' => true, 'tags' => ['ruby']}))
         
     | 
| 
      
 28 
     | 
    
         
            +
             
     | 
| 
      
 29 
     | 
    
         
            +
            # Json compact
         
     | 
| 
      
 30 
     | 
    
         
            +
            ClickHouse.connection.insert('assets', columns: %w[visible tags]) do |buffer|
         
     | 
| 
      
 31 
     | 
    
         
            +
              buffer << [
         
     | 
| 
      
 32 
     | 
    
         
            +
                @schema.serialize_column("visible", true),
         
     | 
| 
      
 33 
     | 
    
         
            +
                @schema.serialize_column("tags", ['ruby']),
         
     | 
| 
      
 34 
     | 
    
         
            +
              ]
         
     | 
| 
      
 35 
     | 
    
         
            +
            end
         
     | 
| 
      
 36 
     | 
    
         
            +
            ```
         
     | 
| 
      
 37 
     | 
    
         
            +
             
     | 
| 
       1 
38 
     | 
    
         
             
            # 2.0.0
         
     | 
| 
       2 
39 
     | 
    
         
             
            * Fixed `Bigdecimal` casting with high precision
         
     | 
| 
       3 
40 
     | 
    
         
             
            * Added nested `type casting like Array(Array(Array(Nullable(T))))`
         
     | 
| 
         @@ -5,6 +42,7 @@ 
     | 
|
| 
       5 
42 
     | 
    
         
             
            * Added `Tuple(T1, T2)` support
         
     | 
| 
       6 
43 
     | 
    
         
             
            * Added support for `Faraday` v1 and v2
         
     | 
| 
       7 
44 
     | 
    
         
             
            * Added support for `Oj` parser
         
     | 
| 
      
 45 
     | 
    
         
            +
            * Time types return `Time` class instead of `DateTime` for now
         
     | 
| 
       8 
46 
     | 
    
         | 
| 
       9 
47 
     | 
    
         
             
            # 1.6.3
         
     | 
| 
       10 
48 
     | 
    
         
             
            * [PR](https://github.com/shlima/click_house/pull/38) Add option format for insert
         
     | 
    
        data/Gemfile.lock
    CHANGED
    
    
    
        data/Gemfile_faraday1
    CHANGED
    
    
    
        data/Gemfile_faraday1.lock
    CHANGED
    
    | 
         @@ -1,7 +1,7 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            PATH
         
     | 
| 
       2 
2 
     | 
    
         
             
              remote: .
         
     | 
| 
       3 
3 
     | 
    
         
             
              specs:
         
     | 
| 
       4 
     | 
    
         
            -
                click_house (2. 
     | 
| 
      
 4 
     | 
    
         
            +
                click_house (2.1.1)
         
     | 
| 
       5 
5 
     | 
    
         
             
                  activesupport
         
     | 
| 
       6 
6 
     | 
    
         
             
                  faraday (>= 1.7, < 3)
         
     | 
| 
       7 
7 
     | 
    
         | 
| 
         @@ -40,8 +40,6 @@ GEM 
     | 
|
| 
       40 
40 
     | 
    
         
             
                faraday-patron (1.0.0)
         
     | 
| 
       41 
41 
     | 
    
         
             
                faraday-rack (1.0.0)
         
     | 
| 
       42 
42 
     | 
    
         
             
                faraday-retry (1.0.3)
         
     | 
| 
       43 
     | 
    
         
            -
                faraday_middleware (1.2.0)
         
     | 
| 
       44 
     | 
    
         
            -
                  faraday (~> 1.0)
         
     | 
| 
       45 
43 
     | 
    
         
             
                i18n (1.12.0)
         
     | 
| 
       46 
44 
     | 
    
         
             
                  concurrent-ruby (~> 1.0)
         
     | 
| 
       47 
45 
     | 
    
         
             
                json (2.6.2)
         
     | 
| 
         @@ -94,12 +92,13 @@ GEM 
     | 
|
| 
       94 
92 
     | 
    
         
             
                unicode-display_width (2.3.0)
         
     | 
| 
       95 
93 
     | 
    
         | 
| 
       96 
94 
     | 
    
         
             
            PLATFORMS
         
     | 
| 
      
 95 
     | 
    
         
            +
              ruby
         
     | 
| 
       97 
96 
     | 
    
         
             
              x86_64-darwin-21
         
     | 
| 
       98 
97 
     | 
    
         | 
| 
       99 
98 
     | 
    
         
             
            DEPENDENCIES
         
     | 
| 
       100 
99 
     | 
    
         
             
              bundler
         
     | 
| 
       101 
100 
     | 
    
         
             
              click_house!
         
     | 
| 
       102 
     | 
    
         
            -
               
     | 
| 
      
 101 
     | 
    
         
            +
              faraday (< 2)
         
     | 
| 
       103 
102 
     | 
    
         
             
              oj
         
     | 
| 
       104 
103 
     | 
    
         
             
              pry
         
     | 
| 
       105 
104 
     | 
    
         
             
              rake
         
     | 
    
        data/Gemfile_faraday2.lock
    CHANGED
    
    
    
        data/README.md
    CHANGED
    
    | 
         @@ -52,6 +52,8 @@ ClickHouse.config do |config| 
     | 
|
| 
       52 
52 
     | 
    
         
             
              config.timeout = 60
         
     | 
| 
       53 
53 
     | 
    
         
             
              config.open_timeout = 3
         
     | 
| 
       54 
54 
     | 
    
         
             
              config.ssl_verify = false
         
     | 
| 
      
 55 
     | 
    
         
            +
              # set to true to symbolize keys for SELECT and INSERT statements (type casting)
         
     | 
| 
      
 56 
     | 
    
         
            +
              config.symbolize_keys = false
         
     | 
| 
       55 
57 
     | 
    
         
             
              config.headers = {}
         
     | 
| 
       56 
58 
     | 
    
         | 
| 
       57 
59 
     | 
    
         
             
              # or provide connection options separately
         
     | 
| 
         @@ -66,10 +68,15 @@ ClickHouse.config do |config| 
     | 
|
| 
       66 
68 
     | 
    
         
             
              # if you want to add settings to all queries
         
     | 
| 
       67 
69 
     | 
    
         
             
              config.global_params = { mutations_sync: 1 }
         
     | 
| 
       68 
70 
     | 
    
         | 
| 
       69 
     | 
    
         
            -
              # choose a ruby JSON parser
         
     | 
| 
      
 71 
     | 
    
         
            +
              # choose a ruby JSON parser (default one)
         
     | 
| 
       70 
72 
     | 
    
         
             
              config.json_parser = ClickHouse::Middleware::ParseJson
         
     | 
| 
       71 
73 
     | 
    
         
             
              # or Oj parser
         
     | 
| 
       72 
74 
     | 
    
         
             
              config.json_parser = ClickHouse::Middleware::ParseJsonOj
         
     | 
| 
      
 75 
     | 
    
         
            +
             
     | 
| 
      
 76 
     | 
    
         
            +
              # JSON.dump (default one)
         
     | 
| 
      
 77 
     | 
    
         
            +
              config.json_serializer = ClickHouse::Serializer::JsonSerializer
         
     | 
| 
      
 78 
     | 
    
         
            +
              # or Oj.dump
         
     | 
| 
      
 79 
     | 
    
         
            +
              config.json_serializer = ClickHouse::Serializer::JsonOjSerializer
         
     | 
| 
       73 
80 
     | 
    
         
             
            end
         
     | 
| 
       74 
81 
     | 
    
         
             
            ```
         
     | 
| 
       75 
82 
     | 
    
         | 
| 
         @@ -131,12 +138,16 @@ Select all type-casted result set 
     | 
|
| 
       131 
138 
     | 
    
         
             
            @result = ClickHouse.connection.select_all('SELECT * FROM visits')
         
     | 
| 
       132 
139 
     | 
    
         | 
| 
       133 
140 
     | 
    
         
             
            # all enumerable methods are delegated like #each, #map, #select etc
         
     | 
| 
      
 141 
     | 
    
         
            +
            # results of #to_a is type casted
         
     | 
| 
       134 
142 
     | 
    
         
             
            @result.to_a #=> [{"date"=>#<Date: 2000-01-01>, "id"=>1}]
         
     | 
| 
       135 
143 
     | 
    
         | 
| 
       136 
144 
     | 
    
         
             
            # you can access raw data
         
     | 
| 
       137 
145 
     | 
    
         
             
            @result.meta #=> [{"name"=>"date", "type"=>"Date"}, {"name"=>"id", "type"=>"UInt32"}]
         
     | 
| 
       138 
146 
     | 
    
         
             
            @result.data #=> [{"date"=>"2000-01-01", "id"=>1}, {"date"=>"2000-01-02", "id"=>2}]
         
     | 
| 
       139 
147 
     | 
    
         
             
            @result.statistics #=> {"elapsed"=>0.0002271, "rows_read"=>2, "bytes_read"=>12}
         
     | 
| 
      
 148 
     | 
    
         
            +
            @result.summary #=> ClickHouse::Response::Summary
         
     | 
| 
      
 149 
     | 
    
         
            +
            @result.headers #=> {"x-clickhouse-query-id"=>"9bf5f604-31fc-4eff-a4b5-277f2c71d199"}
         
     | 
| 
      
 150 
     | 
    
         
            +
            @result.types #=> [Hash<String|Symbol, ClickHouse::Ast::Statement>]
         
     | 
| 
       140 
151 
     | 
    
         
             
            ```
         
     | 
| 
       141 
152 
     | 
    
         | 
| 
       142 
153 
     | 
    
         
             
            ### Select Value
         
     | 
| 
         @@ -195,7 +206,8 @@ response.body #=> "\u0002\u0000\u0000\u0000\u0000\u0000\u0000\u0000" 
     | 
|
| 
       195 
206 
     | 
    
         | 
| 
       196 
207 
     | 
    
         
             
            ## Insert
         
     | 
| 
       197 
208 
     | 
    
         | 
| 
       198 
     | 
    
         
            -
            When column names and values are transferred separately
         
     | 
| 
      
 209 
     | 
    
         
            +
            When column names and values are transferred separately, data sends to the server 
         
     | 
| 
      
 210 
     | 
    
         
            +
            using `JSONCompactEachRow` format by default.
         
     | 
| 
       199 
211 
     | 
    
         | 
| 
       200 
212 
     | 
    
         
             
            ```ruby
         
     | 
| 
       201 
213 
     | 
    
         
             
            ClickHouse.connection.insert('table', columns: %i[id name]) do |buffer|
         
     | 
| 
         @@ -203,23 +215,42 @@ ClickHouse.connection.insert('table', columns: %i[id name]) do |buffer| 
     | 
|
| 
       203 
215 
     | 
    
         
             
              buffer << [2, 'Venus']
         
     | 
| 
       204 
216 
     | 
    
         
             
            end
         
     | 
| 
       205 
217 
     | 
    
         | 
| 
      
 218 
     | 
    
         
            +
            # or
         
     | 
| 
       206 
219 
     | 
    
         
             
            ClickHouse.connection.insert('table', columns: %i[id name], values: [[1, 'Mercury'], [2, 'Venus']])
         
     | 
| 
       207 
     | 
    
         
            -
            #=> true
         
     | 
| 
       208 
220 
     | 
    
         
             
            ```
         
     | 
| 
       209 
221 
     | 
    
         | 
| 
       210 
     | 
    
         
            -
            When rows are passed as a  
     | 
| 
       211 
     | 
    
         
            -
             
     | 
| 
      
 222 
     | 
    
         
            +
            When rows are passed as an Array or a Hash, data sends to the server
         
     | 
| 
      
 223 
     | 
    
         
            +
            using `JSONEachRow` format by default.
         
     | 
| 
       212 
224 
     | 
    
         | 
| 
       213 
225 
     | 
    
         
             
            ```ruby
         
     | 
| 
       214 
     | 
    
         
            -
            ClickHouse.connection.insert('table',  
     | 
| 
      
 226 
     | 
    
         
            +
            ClickHouse.connection.insert('table', [{ name: 'Sun', id: 1 }, { name: 'Moon', id: 2 }])
         
     | 
| 
      
 227 
     | 
    
         
            +
             
     | 
| 
      
 228 
     | 
    
         
            +
            # or
         
     | 
| 
      
 229 
     | 
    
         
            +
            ClickHouse.connection.insert('table', { name: 'Sun', id: 1 })
         
     | 
| 
      
 230 
     | 
    
         
            +
             
     | 
| 
      
 231 
     | 
    
         
            +
            # for ruby < 3.0 provide an extra argument
         
     | 
| 
      
 232 
     | 
    
         
            +
            ClickHouse.connection.insert('table', { name: 'Sun', id: 1 }, {})
         
     | 
| 
       215 
233 
     | 
    
         | 
| 
      
 234 
     | 
    
         
            +
            # or
         
     | 
| 
       216 
235 
     | 
    
         
             
            ClickHouse.connection.insert('table') do |buffer|
         
     | 
| 
       217 
236 
     | 
    
         
             
              buffer << { name: 'Sun', id: 1 }
         
     | 
| 
       218 
237 
     | 
    
         
             
              buffer << { name: 'Moon', id: 2 }
         
     | 
| 
       219 
238 
     | 
    
         
             
            end
         
     | 
| 
       220 
     | 
    
         
            -
            #=> true
         
     | 
| 
       221 
239 
     | 
    
         
             
            ```
         
     | 
| 
       222 
240 
     | 
    
         | 
| 
      
 241 
     | 
    
         
            +
            Sometimes it's needed to use other format than `JSONEachRow` For example if you want to send BigDecimal's 
         
     | 
| 
      
 242 
     | 
    
         
            +
            you could use `JSONStringsEachRow` format so string representation of `BigDecimal` will be parsed:
         
     | 
| 
      
 243 
     | 
    
         
            +
             
     | 
| 
      
 244 
     | 
    
         
            +
            ```ruby
         
     | 
| 
      
 245 
     | 
    
         
            +
            ClickHouse.connection.insert('table', { name: 'Sun', id: '1' }, format: 'JSONStringsEachRow')
         
     | 
| 
      
 246 
     | 
    
         
            +
            # or
         
     | 
| 
      
 247 
     | 
    
         
            +
            ClickHouse.connection.insert_rows('table', { name: 'Sun', id: '1' }, format: 'JSONStringsEachRow')
         
     | 
| 
      
 248 
     | 
    
         
            +
            # or
         
     | 
| 
      
 249 
     | 
    
         
            +
            ClickHouse.connection.insert_compact('table', columns: %w[name id], values: %w[Sun 1], format: 'JSONCompactStringsEachRow')
         
     | 
| 
      
 250 
     | 
    
         
            +
            ```
         
     | 
| 
      
 251 
     | 
    
         
            +
             
     | 
| 
      
 252 
     | 
    
         
            +
            See the [type casting](#type-casting) section to insert the data in a proper way.
         
     | 
| 
      
 253 
     | 
    
         
            +
             
     | 
| 
       223 
254 
     | 
    
         
             
            ## Create a table
         
     | 
| 
       224 
255 
     | 
    
         
             
            ### Create table using DSL
         
     | 
| 
       225 
256 
     | 
    
         | 
| 
         @@ -326,42 +357,35 @@ end 
     | 
|
| 
       326 
357 
     | 
    
         
             
            ## Type casting
         
     | 
| 
       327 
358 
     | 
    
         | 
| 
       328 
359 
     | 
    
         
             
            By default gem provides all necessary type casting, but you may overwrite or define
         
     | 
| 
       329 
     | 
    
         
            -
            your own logic
         
     | 
| 
      
 360 
     | 
    
         
            +
            your own logic. if you need to redefine all built-in types with your implementation,
         
     | 
| 
      
 361 
     | 
    
         
            +
            just clear the default type system:
         
     | 
| 
       330 
362 
     | 
    
         | 
| 
       331 
363 
     | 
    
         
             
            ```ruby
         
     | 
| 
       332 
     | 
    
         
            -
             
     | 
| 
       333 
     | 
    
         
            -
             
     | 
| 
       334 
     | 
    
         
            -
             
     | 
| 
       335 
     | 
    
         
            -
              end
         
     | 
| 
       336 
     | 
    
         
            -
             
     | 
| 
       337 
     | 
    
         
            -
              def serialize(value)
         
     | 
| 
       338 
     | 
    
         
            -
                value.strftime('%Y-%m-%d')
         
     | 
| 
       339 
     | 
    
         
            -
              end
         
     | 
| 
       340 
     | 
    
         
            -
            end
         
     | 
| 
       341 
     | 
    
         
            -
             
     | 
| 
       342 
     | 
    
         
            -
            ClickHouse.add_type('Date', DateType.new)
         
     | 
| 
      
 364 
     | 
    
         
            +
            ClickHouse.types.clear
         
     | 
| 
      
 365 
     | 
    
         
            +
            ClickHouse.types # => {}
         
     | 
| 
      
 366 
     | 
    
         
            +
            ClickHouse.types.default #=> #<ClickHouse::Type::UndefinedType>
         
     | 
| 
       343 
367 
     | 
    
         
             
            ```
         
     | 
| 
       344 
368 
     | 
    
         | 
| 
       345 
     | 
    
         
            -
             
     | 
| 
       346 
     | 
    
         
            -
            argument and *Numeric* type with `%d` argument:
         
     | 
| 
      
 369 
     | 
    
         
            +
            Type casting works automatically when fetching data, when inserting data, you must serialize the types yourself
         
     | 
| 
       347 
370 
     | 
    
         | 
| 
       348 
     | 
    
         
            -
            ``` 
     | 
| 
       349 
     | 
    
         
            -
             
     | 
| 
       350 
     | 
    
         
            -
              def cast(value, time_zone)
         
     | 
| 
       351 
     | 
    
         
            -
                Time.parse("#{value} #{time_zone}")
         
     | 
| 
       352 
     | 
    
         
            -
              end
         
     | 
| 
       353 
     | 
    
         
            -
            end
         
     | 
| 
       354 
     | 
    
         
            -
             
     | 
| 
       355 
     | 
    
         
            -
            ClickHouse.add_type('DateTime(%s)', DateTimeType.new)
         
     | 
| 
      
 371 
     | 
    
         
            +
            ```sql
         
     | 
| 
      
 372 
     | 
    
         
            +
            CREATE TABLE assets(visible Boolean, tags Array(Nullable(String))) ENGINE Memory
         
     | 
| 
       356 
373 
     | 
    
         
             
            ```
         
     | 
| 
       357 
374 
     | 
    
         | 
| 
       358 
     | 
    
         
            -
            if you need to redefine all built-in types with your implementation,
         
     | 
| 
       359 
     | 
    
         
            -
            just clear the default type system:
         
     | 
| 
       360 
     | 
    
         
            -
             
     | 
| 
       361 
375 
     | 
    
         
             
            ```ruby
         
     | 
| 
       362 
     | 
    
         
            -
             
     | 
| 
       363 
     | 
    
         
            -
            ClickHouse. 
     | 
| 
       364 
     | 
    
         
            -
             
     | 
| 
      
 376 
     | 
    
         
            +
            # cache table schema in a class variable
         
     | 
| 
      
 377 
     | 
    
         
            +
            @schema = ClickHouse.connection.table_schema('assets')
         
     | 
| 
      
 378 
     | 
    
         
            +
             
     | 
| 
      
 379 
     | 
    
         
            +
            # Json each row
         
     | 
| 
      
 380 
     | 
    
         
            +
            ClickHouse.connection.insert('assets', @schema.serialize({'visible' => true, 'tags' => ['ruby']}))
         
     | 
| 
      
 381 
     | 
    
         
            +
             
     | 
| 
      
 382 
     | 
    
         
            +
            # Json compact
         
     | 
| 
      
 383 
     | 
    
         
            +
            ClickHouse.connection.insert('assets', columns: %w[visible tags]) do |buffer|
         
     | 
| 
      
 384 
     | 
    
         
            +
              buffer << [
         
     | 
| 
      
 385 
     | 
    
         
            +
                @schema.serialize_column("visible", true),
         
     | 
| 
      
 386 
     | 
    
         
            +
                @schema.serialize_column("tags", ['ruby']),
         
     | 
| 
      
 387 
     | 
    
         
            +
              ]
         
     | 
| 
      
 388 
     | 
    
         
            +
            end
         
     | 
| 
       365 
389 
     | 
    
         
             
            ```
         
     | 
| 
       366 
390 
     | 
    
         | 
| 
       367 
391 
     | 
    
         
             
            ## Using with a connection pool
         
     | 
| 
         @@ -0,0 +1,20 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            require 'benchmark'
         
     | 
| 
      
 4 
     | 
    
         
            +
            require 'stringio'
         
     | 
| 
      
 5 
     | 
    
         
            +
             
     | 
| 
      
 6 
     | 
    
         
            +
            INPUT = Array.new(5_000_000, 'foo bar')
         
     | 
| 
      
 7 
     | 
    
         
            +
             
     | 
| 
      
 8 
     | 
    
         
            +
            Benchmark.bm do |x|
         
     | 
| 
      
 9 
     | 
    
         
            +
              x.report('map.join') do
         
     | 
| 
      
 10 
     | 
    
         
            +
                INPUT.map(&:to_s).join("\n")
         
     | 
| 
      
 11 
     | 
    
         
            +
              end
         
     | 
| 
      
 12 
     | 
    
         
            +
             
     | 
| 
      
 13 
     | 
    
         
            +
              x.report('StringIO') do
         
     | 
| 
      
 14 
     | 
    
         
            +
                out = StringIO.new
         
     | 
| 
      
 15 
     | 
    
         
            +
                INPUT.each do |value|
         
     | 
| 
      
 16 
     | 
    
         
            +
                  out << "#{value}\n"
         
     | 
| 
      
 17 
     | 
    
         
            +
                end
         
     | 
| 
      
 18 
     | 
    
         
            +
                out.string
         
     | 
| 
      
 19 
     | 
    
         
            +
              end
         
     | 
| 
      
 20 
     | 
    
         
            +
            end
         
     | 
    
        data/lib/click_house/config.rb
    CHANGED
    
    | 
         @@ -18,18 +18,21 @@ module ClickHouse 
     | 
|
| 
       18 
18 
     | 
    
         
             
                  headers: {},
         
     | 
| 
       19 
19 
     | 
    
         
             
                  global_params: {},
         
     | 
| 
       20 
20 
     | 
    
         
             
                  json_parser: ClickHouse::Middleware::ParseJson,
         
     | 
| 
      
 21 
     | 
    
         
            +
                  json_serializer: ClickHouse::Serializer::JsonSerializer,
         
     | 
| 
      
 22 
     | 
    
         
            +
                  oj_dump_options: {
         
     | 
| 
      
 23 
     | 
    
         
            +
                    mode: :compat # to be able to dump improper JSON like {1 => 2}
         
     | 
| 
      
 24 
     | 
    
         
            +
                  },
         
     | 
| 
       21 
25 
     | 
    
         
             
                  oj_load_options: {
         
     | 
| 
       22 
26 
     | 
    
         
             
                    mode: :custom,
         
     | 
| 
       23 
27 
     | 
    
         
             
                    allow_blank: true,
         
     | 
| 
       24 
28 
     | 
    
         
             
                    bigdecimal_as_decimal: false, # dump BigDecimal as a String
         
     | 
| 
       25 
29 
     | 
    
         
             
                    bigdecimal_load: :bigdecimal, # convert all decimal numbers to BigDecimal
         
     | 
| 
       26 
     | 
    
         
            -
                    empty_string: false,
         
     | 
| 
       27 
     | 
    
         
            -
                    second_precision: 6,
         
     | 
| 
       28 
     | 
    
         
            -
                    time_format: :ruby,
         
     | 
| 
       29 
30 
     | 
    
         
             
                  },
         
     | 
| 
       30 
31 
     | 
    
         
             
                  json_load_options: {
         
     | 
| 
       31 
32 
     | 
    
         
             
                    decimal_class: BigDecimal,
         
     | 
| 
       32 
33 
     | 
    
         
             
                  },
         
     | 
| 
      
 34 
     | 
    
         
            +
                  # should be after json load options
         
     | 
| 
      
 35 
     | 
    
         
            +
                  symbolize_keys: false,
         
     | 
| 
       33 
36 
     | 
    
         
             
                }.freeze
         
     | 
| 
       34 
37 
     | 
    
         | 
| 
       35 
38 
     | 
    
         
             
                attr_accessor :adapter
         
     | 
| 
         @@ -49,6 +52,9 @@ module ClickHouse 
     | 
|
| 
       49 
52 
     | 
    
         
             
                attr_accessor :oj_load_options
         
     | 
| 
       50 
53 
     | 
    
         
             
                attr_accessor :json_load_options
         
     | 
| 
       51 
54 
     | 
    
         
             
                attr_accessor :json_parser # response middleware
         
     | 
| 
      
 55 
     | 
    
         
            +
                attr_accessor :oj_dump_options
         
     | 
| 
      
 56 
     | 
    
         
            +
                attr_accessor :json_serializer # [ClickHouse::Serializer::Base]
         
     | 
| 
      
 57 
     | 
    
         
            +
                attr_accessor :symbolize_keys # [NilClass, Boolean]
         
     | 
| 
       52 
58 
     | 
    
         | 
| 
       53 
59 
     | 
    
         
             
                def initialize(params = {})
         
     | 
| 
       54 
60 
     | 
    
         
             
                  assign(DEFAULTS.merge(params))
         
     | 
| 
         @@ -77,5 +83,25 @@ module ClickHouse 
     | 
|
| 
       77 
83 
     | 
    
         
             
                def null_logger
         
     | 
| 
       78 
84 
     | 
    
         
             
                  @null_logger ||= Logger.new(IO::NULL)
         
     | 
| 
       79 
85 
     | 
    
         
             
                end
         
     | 
| 
      
 86 
     | 
    
         
            +
             
     | 
| 
      
 87 
     | 
    
         
            +
                # @param klass [ClickHouse::Serializer::Base]
         
     | 
| 
      
 88 
     | 
    
         
            +
                def json_serializer=(klass)
         
     | 
| 
      
 89 
     | 
    
         
            +
                  @json_serializer = klass.new(self)
         
     | 
| 
      
 90 
     | 
    
         
            +
                end
         
     | 
| 
      
 91 
     | 
    
         
            +
             
     | 
| 
      
 92 
     | 
    
         
            +
                def symbolize_keys=(value)
         
     | 
| 
      
 93 
     | 
    
         
            +
                  bool = value ? true : false
         
     | 
| 
      
 94 
     | 
    
         
            +
             
     | 
| 
      
 95 
     | 
    
         
            +
                  # merge to be able to clone a config
         
     | 
| 
      
 96 
     | 
    
         
            +
                  # prevent overriding default values
         
     | 
| 
      
 97 
     | 
    
         
            +
                  self.oj_load_options = oj_load_options.merge(symbol_keys: bool)
         
     | 
| 
      
 98 
     | 
    
         
            +
                  self.json_load_options = json_load_options.merge(symbolize_names: bool)
         
     | 
| 
      
 99 
     | 
    
         
            +
                  @symbolize_keys = bool
         
     | 
| 
      
 100 
     | 
    
         
            +
                end
         
     | 
| 
      
 101 
     | 
    
         
            +
             
     | 
| 
      
 102 
     | 
    
         
            +
                # @param name [Symbol, String]
         
     | 
| 
      
 103 
     | 
    
         
            +
                def key(name)
         
     | 
| 
      
 104 
     | 
    
         
            +
                  symbolize_keys ? name.to_sym : name.to_s
         
     | 
| 
      
 105 
     | 
    
         
            +
                end
         
     | 
| 
       80 
106 
     | 
    
         
             
              end
         
     | 
| 
       81 
107 
     | 
    
         
             
            end
         
     | 
| 
         @@ -65,8 +65,9 @@ module ClickHouse 
     | 
|
| 
       65 
65 
     | 
    
         | 
| 
       66 
66 
     | 
    
         
             
                    conn.response Middleware::RaiseError
         
     | 
| 
       67 
67 
     | 
    
         
             
                    conn.response Middleware::Logging, logger: config.logger!
         
     | 
| 
       68 
     | 
    
         
            -
                    conn.response  
     | 
| 
       69 
     | 
    
         
            -
                    conn.response  
     | 
| 
      
 68 
     | 
    
         
            +
                    conn.response Middleware::SummaryMiddleware, options: { config: config } # should be after logger
         
     | 
| 
      
 69 
     | 
    
         
            +
                    conn.response config.json_parser, content_type: %r{application/json}, options: { config: config }
         
     | 
| 
      
 70 
     | 
    
         
            +
                    conn.response Middleware::ParseCsv, content_type: %r{text/csv}, options: { config: config }
         
     | 
| 
       70 
71 
     | 
    
         
             
                    conn.adapter config.adapter
         
     | 
| 
       71 
72 
     | 
    
         
             
                  end
         
     | 
| 
       72 
73 
     | 
    
         
             
                end
         
     | 
    
        data/lib/click_house/errors.rb
    CHANGED
    
    
| 
         @@ -3,7 +3,8 @@ 
     | 
|
| 
       3 
3 
     | 
    
         
             
            module ClickHouse
         
     | 
| 
       4 
4 
     | 
    
         
             
              module Extend
         
     | 
| 
       5 
5 
     | 
    
         
             
                module ConnectionInserting
         
     | 
| 
       6 
     | 
    
         
            -
                   
     | 
| 
      
 6 
     | 
    
         
            +
                  DEFAULT_JSON_EACH_ROW_FORMAT = 'JSONEachRow'
         
     | 
| 
      
 7 
     | 
    
         
            +
                  DEFAULT_JSON_COMPACT_EACH_ROW_FORMAT = 'JSONCompactEachRow'
         
     | 
| 
       7 
8 
     | 
    
         | 
| 
       8 
9 
     | 
    
         
             
                  # @return [Boolean]
         
     | 
| 
       9 
10 
     | 
    
         
             
                  #
         
     | 
| 
         @@ -13,20 +14,71 @@ module ClickHouse 
     | 
|
| 
       13 
14 
     | 
    
         
             
                  #   buffer << ['Moon', 2]
         
     | 
| 
       14 
15 
     | 
    
         
             
                  # end
         
     | 
| 
       15 
16 
     | 
    
         
             
                  #
         
     | 
| 
       16 
     | 
    
         
            -
                  #  
     | 
| 
       17 
     | 
    
         
            -
                  #  
     | 
| 
       18 
     | 
    
         
            -
                   
     | 
| 
       19 
     | 
    
         
            -
             
     | 
| 
      
 17 
     | 
    
         
            +
                  # @return [Response::Execution]
         
     | 
| 
      
 18 
     | 
    
         
            +
                  # @param body [Array, Hash]
         
     | 
| 
      
 19 
     | 
    
         
            +
                  # rubocop:disable Metrics/PerceivedComplexity, Metrics/CyclomaticComplexity
         
     | 
| 
      
 20 
     | 
    
         
            +
                  def insert(table, body = [], **opts)
         
     | 
| 
      
 21 
     | 
    
         
            +
                    # In Ruby < 3.0, if the last argument is a hash, and the method being called
         
     | 
| 
      
 22 
     | 
    
         
            +
                    # accepts keyword arguments, then it is always converted to keyword arguments.
         
     | 
| 
      
 23 
     | 
    
         
            +
                    columns = opts.fetch(:columns, [])
         
     | 
| 
      
 24 
     | 
    
         
            +
                    values =  opts.fetch(:values, [])
         
     | 
| 
      
 25 
     | 
    
         
            +
                    format = opts.fetch(:format, nil)
         
     | 
| 
      
 26 
     | 
    
         
            +
             
     | 
| 
      
 27 
     | 
    
         
            +
                    yield(body) if block_given?
         
     | 
| 
       20 
28 
     | 
    
         | 
| 
       21 
     | 
    
         
            -
                     
     | 
| 
       22 
     | 
    
         
            -
             
     | 
| 
      
 29 
     | 
    
         
            +
                    # values: [{id: 1}]
         
     | 
| 
      
 30 
     | 
    
         
            +
                    if values.any? && columns.empty?
         
     | 
| 
      
 31 
     | 
    
         
            +
                      return insert_rows(table, values, format: format)
         
     | 
| 
      
 32 
     | 
    
         
            +
                    end
         
     | 
| 
      
 33 
     | 
    
         
            +
             
     | 
| 
      
 34 
     | 
    
         
            +
                    # body: [{id: 1}]
         
     | 
| 
      
 35 
     | 
    
         
            +
                    if body.any? && columns.empty?
         
     | 
| 
      
 36 
     | 
    
         
            +
                      return insert_rows(table, body, format: format)
         
     | 
| 
      
 37 
     | 
    
         
            +
                    end
         
     | 
| 
      
 38 
     | 
    
         
            +
             
     | 
| 
      
 39 
     | 
    
         
            +
                    # body: [1], columns: ["id"]
         
     | 
| 
      
 40 
     | 
    
         
            +
                    if body.any? && columns.any?
         
     | 
| 
      
 41 
     | 
    
         
            +
                      return insert_compact(table, columns: columns, values: body, format: format)
         
     | 
| 
      
 42 
     | 
    
         
            +
                    end
         
     | 
| 
      
 43 
     | 
    
         
            +
             
     | 
| 
      
 44 
     | 
    
         
            +
                    # columns: ["id"], values: [[1]]
         
     | 
| 
      
 45 
     | 
    
         
            +
                    if columns.any? && values.any?
         
     | 
| 
      
 46 
     | 
    
         
            +
                      return insert_compact(table, columns: columns, values: values, format: format)
         
     | 
| 
      
 47 
     | 
    
         
            +
                    end
         
     | 
| 
      
 48 
     | 
    
         
            +
             
     | 
| 
      
 49 
     | 
    
         
            +
                    Response::Factory.empty_exec(config)
         
     | 
| 
      
 50 
     | 
    
         
            +
                  end
         
     | 
| 
      
 51 
     | 
    
         
            +
                  # rubocop:enable Metrics/PerceivedComplexity, Metrics/CyclomaticComplexity
         
     | 
| 
      
 52 
     | 
    
         
            +
             
     | 
| 
      
 53 
     | 
    
         
            +
                  # @param table [String]
         
     | 
| 
      
 54 
     | 
    
         
            +
                  # @param body [Array, Hash]
         
     | 
| 
      
 55 
     | 
    
         
            +
                  # @param format [String]
         
     | 
| 
      
 56 
     | 
    
         
            +
                  # @return [Response::Execution]
         
     | 
| 
      
 57 
     | 
    
         
            +
                  #
         
     | 
| 
      
 58 
     | 
    
         
            +
                  # Sometimes it's needed to use other format than JSONEachRow
         
     | 
| 
      
 59 
     | 
    
         
            +
                  # For example if you want to send BigDecimal's you could use
         
     | 
| 
      
 60 
     | 
    
         
            +
                  # JSONStringsEachRow format so string representation of BigDecimal will be parsed
         
     | 
| 
      
 61 
     | 
    
         
            +
                  def insert_rows(table, body, format: nil)
         
     | 
| 
      
 62 
     | 
    
         
            +
                    format ||= DEFAULT_JSON_EACH_ROW_FORMAT
         
     | 
| 
      
 63 
     | 
    
         
            +
             
     | 
| 
      
 64 
     | 
    
         
            +
                    case body
         
     | 
| 
      
 65 
     | 
    
         
            +
                    when Hash
         
     | 
| 
      
 66 
     | 
    
         
            +
                      Response::Factory.exec(execute("INSERT INTO #{table} FORMAT #{format}", config.json_serializer.dump(body)))
         
     | 
| 
      
 67 
     | 
    
         
            +
                    when Array
         
     | 
| 
      
 68 
     | 
    
         
            +
                      Response::Factory.exec(execute("INSERT INTO #{table} FORMAT #{format}", config.json_serializer.dump_each_row(body)))
         
     | 
| 
       23 
69 
     | 
    
         
             
                    else
         
     | 
| 
       24 
     | 
    
         
            -
                       
     | 
| 
      
 70 
     | 
    
         
            +
                      raise ArgumentError, "unknown body class <#{body.class}>"
         
     | 
| 
       25 
71 
     | 
    
         
             
                    end
         
     | 
| 
      
 72 
     | 
    
         
            +
                  end
         
     | 
| 
       26 
73 
     | 
    
         | 
| 
       27 
     | 
    
         
            -
             
     | 
| 
      
 74 
     | 
    
         
            +
                  # @return [Response::Execution]
         
     | 
| 
      
 75 
     | 
    
         
            +
                  def insert_compact(table, columns: [], values: [], format: nil)
         
     | 
| 
      
 76 
     | 
    
         
            +
                    format ||= DEFAULT_JSON_COMPACT_EACH_ROW_FORMAT
         
     | 
| 
      
 77 
     | 
    
         
            +
             
     | 
| 
      
 78 
     | 
    
         
            +
                    yield(values) if block_given?
         
     | 
| 
       28 
79 
     | 
    
         | 
| 
       29 
     | 
    
         
            -
                    execute("INSERT INTO #{table} FORMAT #{format}",  
     | 
| 
      
 80 
     | 
    
         
            +
                    response = execute("INSERT INTO #{table} (#{columns.join(',')}) FORMAT #{format}", config.json_serializer.dump_each_row(values))
         
     | 
| 
      
 81 
     | 
    
         
            +
                    Response::Factory.exec(response)
         
     | 
| 
       30 
82 
     | 
    
         
             
                  end
         
     | 
| 
       31 
83 
     | 
    
         
             
                end
         
     | 
| 
       32 
84 
     | 
    
         
             
              end
         
     | 
| 
         @@ -6,17 +6,26 @@ module ClickHouse 
     | 
|
| 
       6 
6 
     | 
    
         
             
                  # @return [ResultSet]
         
     | 
| 
       7 
7 
     | 
    
         
             
                  def select_all(sql)
         
     | 
| 
       8 
8 
     | 
    
         
             
                    response = get(body: sql, query: { default_format: 'JSON' })
         
     | 
| 
       9 
     | 
    
         
            -
                    Response::Factory 
     | 
| 
      
 9 
     | 
    
         
            +
                    Response::Factory.response(response, config)
         
     | 
| 
       10 
10 
     | 
    
         
             
                  end
         
     | 
| 
       11 
11 
     | 
    
         | 
| 
       12 
12 
     | 
    
         
             
                  def select_value(sql)
         
     | 
| 
       13 
13 
     | 
    
         
             
                    response = get(body: sql, query: { default_format: 'JSON' })
         
     | 
| 
       14 
     | 
    
         
            -
                     
     | 
| 
      
 14 
     | 
    
         
            +
                    got = Response::Factory.response(response, config).first
         
     | 
| 
      
 15 
     | 
    
         
            +
             
     | 
| 
      
 16 
     | 
    
         
            +
                    case got
         
     | 
| 
      
 17 
     | 
    
         
            +
                    when Hash
         
     | 
| 
      
 18 
     | 
    
         
            +
                      Array(got).dig(0, -1) # get a value of a first key for JSON format
         
     | 
| 
      
 19 
     | 
    
         
            +
                    when Array
         
     | 
| 
      
 20 
     | 
    
         
            +
                      got[0] # for CSV format
         
     | 
| 
      
 21 
     | 
    
         
            +
                    else
         
     | 
| 
      
 22 
     | 
    
         
            +
                      got # for RowBinary format
         
     | 
| 
      
 23 
     | 
    
         
            +
                    end
         
     | 
| 
       15 
24 
     | 
    
         
             
                  end
         
     | 
| 
       16 
25 
     | 
    
         | 
| 
       17 
26 
     | 
    
         
             
                  def select_one(sql)
         
     | 
| 
       18 
27 
     | 
    
         
             
                    response = get(body: sql, query: { default_format: 'JSON' })
         
     | 
| 
       19 
     | 
    
         
            -
                    Response::Factory 
     | 
| 
      
 28 
     | 
    
         
            +
                    Response::Factory.response(response, config).first
         
     | 
| 
       20 
29 
     | 
    
         
             
                  end
         
     | 
| 
       21 
30 
     | 
    
         
             
                end
         
     | 
| 
       22 
31 
     | 
    
         
             
              end
         
     | 
| 
         @@ -10,7 +10,12 @@ module ClickHouse 
     | 
|
| 
       10 
10 
     | 
    
         | 
| 
       11 
11 
     | 
    
         
             
                  # @return [ResultSet]
         
     | 
| 
       12 
12 
     | 
    
         
             
                  def describe_table(name)
         
     | 
| 
       13 
     | 
    
         
            -
                    Response::Factory 
     | 
| 
      
 13 
     | 
    
         
            +
                    Response::Factory.response(execute("DESCRIBE TABLE #{name} FORMAT JSON"), config)
         
     | 
| 
      
 14 
     | 
    
         
            +
                  end
         
     | 
| 
      
 15 
     | 
    
         
            +
             
     | 
| 
      
 16 
     | 
    
         
            +
                  # @return [ResultSet]
         
     | 
| 
      
 17 
     | 
    
         
            +
                  def table_schema(name)
         
     | 
| 
      
 18 
     | 
    
         
            +
                    Response::Factory.response(execute("SELECT * FROM #{name} WHERE 1=0 FORMAT JSON"), config)
         
     | 
| 
       14 
19 
     | 
    
         
             
                  end
         
     | 
| 
       15 
20 
     | 
    
         | 
| 
       16 
21 
     | 
    
         
             
                  # @return [Boolean]
         
     | 
| 
         @@ -5,36 +5,32 @@ module ClickHouse 
     | 
|
| 
       5 
5 
     | 
    
         
             
                class Logging < Faraday::Middleware
         
     | 
| 
       6 
6 
     | 
    
         
             
                  Faraday::Response.register_middleware self => self
         
     | 
| 
       7 
7 
     | 
    
         | 
| 
       8 
     | 
    
         
            -
                   
     | 
| 
      
 8 
     | 
    
         
            +
                  EMPTY = ''
         
     | 
| 
      
 9 
     | 
    
         
            +
                  GET = :get
         
     | 
| 
       9 
10 
     | 
    
         | 
| 
       10 
     | 
    
         
            -
                  attr_reader :logger, :starting 
     | 
| 
      
 11 
     | 
    
         
            +
                  attr_reader :logger, :starting
         
     | 
| 
       11 
12 
     | 
    
         | 
| 
       12 
13 
     | 
    
         
             
                  def initialize(app = nil, logger:)
         
     | 
| 
       13 
14 
     | 
    
         
             
                    @logger = logger
         
     | 
| 
       14 
15 
     | 
    
         
             
                    super(app)
         
     | 
| 
       15 
16 
     | 
    
         
             
                  end
         
     | 
| 
       16 
17 
     | 
    
         | 
| 
       17 
     | 
    
         
            -
                  def call( 
     | 
| 
      
 18 
     | 
    
         
            +
                  def call(env)
         
     | 
| 
       18 
19 
     | 
    
         
             
                    @starting = timestamp
         
     | 
| 
       19 
     | 
    
         
            -
                     
     | 
| 
       20 
     | 
    
         
            -
                    @app.call(environment).on_complete(&method(:on_complete))
         
     | 
| 
       21 
     | 
    
         
            -
                  end
         
     | 
| 
       22 
     | 
    
         
            -
             
     | 
| 
       23 
     | 
    
         
            -
                  private
         
     | 
| 
       24 
     | 
    
         
            -
             
     | 
| 
       25 
     | 
    
         
            -
                  def log_body?
         
     | 
| 
       26 
     | 
    
         
            -
                    logger.level == Logger::DEBUG
         
     | 
| 
      
 20 
     | 
    
         
            +
                    super
         
     | 
| 
       27 
21 
     | 
    
         
             
                  end
         
     | 
| 
       28 
22 
     | 
    
         | 
| 
       29 
23 
     | 
    
         
             
                  # rubocop:disable Layout/LineLength
         
     | 
| 
       30 
24 
     | 
    
         
             
                  def on_complete(env)
         
     | 
| 
       31 
     | 
    
         
            -
                    summary =  
     | 
| 
       32 
     | 
    
         
            -
                    logger.info("\e[1m[35mSQL (#{duration_stats_log( 
     | 
| 
       33 
     | 
    
         
            -
                    logger.debug( 
     | 
| 
       34 
     | 
    
         
            -
                    logger.info("\e[1m[36mRead: #{summary. 
     | 
| 
      
 25 
     | 
    
         
            +
                    summary = SummaryMiddleware.extract(env)
         
     | 
| 
      
 26 
     | 
    
         
            +
                    logger.info("\e[1m[35mSQL (#{duration_stats_log(summary)})\e[0m #{query(env)};")
         
     | 
| 
      
 27 
     | 
    
         
            +
                    logger.debug(env.request_body) if log_body?(env)
         
     | 
| 
      
 28 
     | 
    
         
            +
                    logger.info("\e[1m[36mRead: #{summary.read_rows} rows, #{summary.read_bytes_pretty}. Written: #{summary.written_rows} rows, #{summary.written_bytes_pretty}\e[0m")
         
     | 
| 
       35 
29 
     | 
    
         
             
                  end
         
     | 
| 
       36 
30 
     | 
    
         
             
                  # rubocop:enable Layout/LineLength
         
     | 
| 
       37 
31 
     | 
    
         | 
| 
      
 32 
     | 
    
         
            +
                  private
         
     | 
| 
      
 33 
     | 
    
         
            +
             
     | 
| 
       38 
34 
     | 
    
         
             
                  def duration
         
     | 
| 
       39 
35 
     | 
    
         
             
                    timestamp - starting
         
     | 
| 
       40 
36 
     | 
    
         
             
                  end
         
     | 
| 
         @@ -43,37 +39,25 @@ module ClickHouse 
     | 
|
| 
       43 
39 
     | 
    
         
             
                    Process.clock_gettime(Process::CLOCK_MONOTONIC)
         
     | 
| 
       44 
40 
     | 
    
         
             
                  end
         
     | 
| 
       45 
41 
     | 
    
         | 
| 
       46 
     | 
    
         
            -
                   
     | 
| 
       47 
     | 
    
         
            -
             
     | 
| 
      
 42 
     | 
    
         
            +
                  # @return [Boolean]
         
     | 
| 
      
 43 
     | 
    
         
            +
                  def log_body?(env)
         
     | 
| 
      
 44 
     | 
    
         
            +
                    return unless logger.debug?
         
     | 
| 
      
 45 
     | 
    
         
            +
                    return if env.method == GET # GET queries logs body as a statement
         
     | 
| 
      
 46 
     | 
    
         
            +
                    return if env.request_body.nil? || env.request_body == EMPTY
         
     | 
| 
      
 47 
     | 
    
         
            +
             
     | 
| 
      
 48 
     | 
    
         
            +
                    true
         
     | 
| 
       48 
49 
     | 
    
         
             
                  end
         
     | 
| 
       49 
50 
     | 
    
         | 
| 
       50 
51 
     | 
    
         
             
                  def query(env)
         
     | 
| 
       51 
     | 
    
         
            -
                    if  
     | 
| 
       52 
     | 
    
         
            -
                       
     | 
| 
      
 52 
     | 
    
         
            +
                    if env.method == GET
         
     | 
| 
      
 53 
     | 
    
         
            +
                      env.request_body
         
     | 
| 
       53 
54 
     | 
    
         
             
                    else
         
     | 
| 
       54 
     | 
    
         
            -
                      CGI.parse(env.url.query.to_s).dig('query', 0) || '[NO QUERY]'
         
     | 
| 
      
 55 
     | 
    
         
            +
                      String(CGI.parse(env.url.query.to_s).dig('query', 0) || '[NO QUERY]').chomp
         
     | 
| 
       55 
56 
     | 
    
         
             
                    end
         
     | 
| 
       56 
57 
     | 
    
         
             
                  end
         
     | 
| 
       57 
58 
     | 
    
         | 
| 
       58 
     | 
    
         
            -
                  def duration_stats_log( 
     | 
| 
       59 
     | 
    
         
            -
                     
     | 
| 
       60 
     | 
    
         
            -
                    clickhouse_elapsed = body['statistics'].fetch('elapsed') if body.is_a?(Hash) && body.key?('statistics')
         
     | 
| 
       61 
     | 
    
         
            -
             
     | 
| 
       62 
     | 
    
         
            -
                    [
         
     | 
| 
       63 
     | 
    
         
            -
                      "Total: #{Util::Pretty.measure(elapsed * 1000)}",
         
     | 
| 
       64 
     | 
    
         
            -
                      ("CH: #{Util::Pretty.measure(clickhouse_elapsed * 1000)}" if clickhouse_elapsed)
         
     | 
| 
       65 
     | 
    
         
            -
                    ].compact.join(', ')
         
     | 
| 
       66 
     | 
    
         
            -
                  end
         
     | 
| 
       67 
     | 
    
         
            -
             
     | 
| 
       68 
     | 
    
         
            -
                  def extract_summary(headers)
         
     | 
| 
       69 
     | 
    
         
            -
                    JSON.parse(headers.fetch('x-clickhouse-summary', '{}')).tap do |summary|
         
     | 
| 
       70 
     | 
    
         
            -
                      summary[:read_rows] = summary['read_rows']
         
     | 
| 
       71 
     | 
    
         
            -
                      summary[:read_bytes] = Util::Pretty.size(summary['read_bytes'].to_i)
         
     | 
| 
       72 
     | 
    
         
            -
                      summary[:written_rows] = summary['written_rows']
         
     | 
| 
       73 
     | 
    
         
            -
                      summary[:written_bytes] = Util::Pretty.size(summary['written_bytes'].to_i)
         
     | 
| 
       74 
     | 
    
         
            -
                    end
         
     | 
| 
       75 
     | 
    
         
            -
                  rescue JSON::ParserError
         
     | 
| 
       76 
     | 
    
         
            -
                    {}
         
     | 
| 
      
 59 
     | 
    
         
            +
                  def duration_stats_log(summary)
         
     | 
| 
      
 60 
     | 
    
         
            +
                    "Total: #{Util::Pretty.measure(duration * 1000)}, CH: #{summary.elapsed_pretty}"
         
     | 
| 
       77 
61 
     | 
    
         
             
                  end
         
     | 
| 
       78 
62 
     | 
    
         
             
                end
         
     | 
| 
       79 
63 
     | 
    
         
             
              end
         
     | 
| 
         @@ -5,12 +5,12 @@ module ClickHouse 
     | 
|
| 
       5 
5 
     | 
    
         
             
                class ResponseBase < Faraday::Middleware
         
     | 
| 
       6 
6 
     | 
    
         
             
                  CONTENT_TYPE_HEADER = 'content-type'
         
     | 
| 
       7 
7 
     | 
    
         | 
| 
       8 
     | 
    
         
            -
                  attr_reader : 
     | 
| 
      
 8 
     | 
    
         
            +
                  attr_reader :options
         
     | 
| 
       9 
9 
     | 
    
         
             
                  attr_reader :content_type
         
     | 
| 
       10 
10 
     | 
    
         | 
| 
       11 
     | 
    
         
            -
                  def initialize(app = nil,  
     | 
| 
      
 11 
     | 
    
         
            +
                  def initialize(app = nil, options: {}, content_type: nil, preserve_raw: false)
         
     | 
| 
       12 
12 
     | 
    
         
             
                    super(app)
         
     | 
| 
       13 
     | 
    
         
            -
                    @ 
     | 
| 
      
 13 
     | 
    
         
            +
                    @options = options
         
     | 
| 
       14 
14 
     | 
    
         
             
                    @content_type = content_type
         
     | 
| 
       15 
15 
     | 
    
         
             
                    @preserve_raw = preserve_raw
         
     | 
| 
       16 
16 
     | 
    
         
             
                    on_setup
         
     | 
| 
         @@ -32,7 +32,7 @@ module ClickHouse 
     | 
|
| 
       32 
32 
     | 
    
         | 
| 
       33 
33 
     | 
    
         
             
                  # @return [Config]
         
     | 
| 
       34 
34 
     | 
    
         
             
                  def config
         
     | 
| 
       35 
     | 
    
         
            -
                     
     | 
| 
      
 35 
     | 
    
         
            +
                    options.fetch(:config)
         
     | 
| 
       36 
36 
     | 
    
         
             
                  end
         
     | 
| 
       37 
37 
     | 
    
         | 
| 
       38 
38 
     | 
    
         
             
                  private
         
     | 
| 
         @@ -0,0 +1,26 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            module ClickHouse
         
     | 
| 
      
 4 
     | 
    
         
            +
              module Middleware
         
     | 
| 
      
 5 
     | 
    
         
            +
                class SummaryMiddleware < ResponseBase
         
     | 
| 
      
 6 
     | 
    
         
            +
                  Faraday::Response.register_middleware self => self
         
     | 
| 
      
 7 
     | 
    
         
            +
             
     | 
| 
      
 8 
     | 
    
         
            +
                  KEY = :summary
         
     | 
| 
      
 9 
     | 
    
         
            +
             
     | 
| 
      
 10 
     | 
    
         
            +
                  # @param env [Faraday::Env]
         
     | 
| 
      
 11 
     | 
    
         
            +
                  # @return [Response::Summary]
         
     | 
| 
      
 12 
     | 
    
         
            +
                  def self.extract(env)
         
     | 
| 
      
 13 
     | 
    
         
            +
                    env.custom_members.fetch(KEY)
         
     | 
| 
      
 14 
     | 
    
         
            +
                  end
         
     | 
| 
      
 15 
     | 
    
         
            +
             
     | 
| 
      
 16 
     | 
    
         
            +
                  # @param env [Faraday::Env]
         
     | 
| 
      
 17 
     | 
    
         
            +
                  def on_complete(env)
         
     | 
| 
      
 18 
     | 
    
         
            +
                    env.custom_members[KEY] = Response::Summary.new(
         
     | 
| 
      
 19 
     | 
    
         
            +
                      config,
         
     | 
| 
      
 20 
     | 
    
         
            +
                      headers: env.response_headers,
         
     | 
| 
      
 21 
     | 
    
         
            +
                      body: env.body.is_a?(Hash) ? env.body : {}
         
     | 
| 
      
 22 
     | 
    
         
            +
                    )
         
     | 
| 
      
 23 
     | 
    
         
            +
                  end
         
     | 
| 
      
 24 
     | 
    
         
            +
                end
         
     | 
| 
      
 25 
     | 
    
         
            +
              end
         
     | 
| 
      
 26 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -3,6 +3,7 @@ 
     | 
|
| 
       3 
3 
     | 
    
         
             
            module ClickHouse
         
     | 
| 
       4 
4 
     | 
    
         
             
              module Middleware
         
     | 
| 
       5 
5 
     | 
    
         
             
                autoload :ResponseBase, 'click_house/middleware/response_base'
         
     | 
| 
      
 6 
     | 
    
         
            +
                autoload :SummaryMiddleware, 'click_house/middleware/summary_middleware'
         
     | 
| 
       6 
7 
     | 
    
         
             
                autoload :Logging, 'click_house/middleware/logging'
         
     | 
| 
       7 
8 
     | 
    
         
             
                autoload :ParseCsv, 'click_house/middleware/parse_csv'
         
     | 
| 
       8 
9 
     | 
    
         
             
                autoload :ParseJsonOj, 'click_house/middleware/parse_json_oj'
         
     | 
| 
         @@ -3,21 +3,49 @@ 
     | 
|
| 
       3 
3 
     | 
    
         
             
            module ClickHouse
         
     | 
| 
       4 
4 
     | 
    
         
             
              module Response
         
     | 
| 
       5 
5 
     | 
    
         
             
                class Factory
         
     | 
| 
       6 
     | 
    
         
            -
                   
     | 
| 
       7 
     | 
    
         
            -
                   
     | 
| 
       8 
     | 
    
         
            -
             
     | 
| 
      
 6 
     | 
    
         
            +
                  KEY_META = 'meta'
         
     | 
| 
      
 7 
     | 
    
         
            +
                  KEY_DATA = 'data'
         
     | 
| 
      
 8 
     | 
    
         
            +
             
     | 
| 
      
 9 
     | 
    
         
            +
                  # @return [ResultSet]
         
     | 
| 
      
 10 
     | 
    
         
            +
                  # @params faraday [Faraday::Response]
         
     | 
| 
      
 11 
     | 
    
         
            +
                  # @params config [Config]
         
     | 
| 
      
 12 
     | 
    
         
            +
                  def self.response(faraday, config)
         
     | 
| 
       9 
13 
     | 
    
         
             
                    body = faraday.body
         
     | 
| 
       10 
14 
     | 
    
         | 
| 
       11 
     | 
    
         
            -
                     
     | 
| 
      
 15 
     | 
    
         
            +
                    # wrap to be able to use connection#select_one, connection#select_value
         
     | 
| 
      
 16 
     | 
    
         
            +
                    # with other formats like binary
         
     | 
| 
      
 17 
     | 
    
         
            +
                    return raw(faraday, config) unless body.is_a?(Hash)
         
     | 
| 
      
 18 
     | 
    
         
            +
                    return raw(faraday, config) unless body.key?(config.key(KEY_META)) && body.key?(config.key(KEY_DATA))
         
     | 
| 
       12 
19 
     | 
    
         | 
| 
       13 
20 
     | 
    
         
             
                    ResultSet.new(
         
     | 
| 
       14 
     | 
    
         
            -
                       
     | 
| 
       15 
     | 
    
         
            -
                       
     | 
| 
       16 
     | 
    
         
            -
                       
     | 
| 
       17 
     | 
    
         
            -
                       
     | 
| 
       18 
     | 
    
         
            -
             
     | 
| 
      
 21 
     | 
    
         
            +
                      config: config,
         
     | 
| 
      
 22 
     | 
    
         
            +
                      meta: body.fetch(config.key(KEY_META)),
         
     | 
| 
      
 23 
     | 
    
         
            +
                      data: body.fetch(config.key(KEY_DATA)),
         
     | 
| 
      
 24 
     | 
    
         
            +
                      summary: Middleware::SummaryMiddleware.extract(faraday.env)
         
     | 
| 
      
 25 
     | 
    
         
            +
                    )
         
     | 
| 
      
 26 
     | 
    
         
            +
                  end
         
     | 
| 
      
 27 
     | 
    
         
            +
             
     | 
| 
      
 28 
     | 
    
         
            +
                  # @return [ResultSet]
         
     | 
| 
      
 29 
     | 
    
         
            +
                  # Rae ResultSet (without type casting)
         
     | 
| 
      
 30 
     | 
    
         
            +
                  def self.raw(faraday, config)
         
     | 
| 
      
 31 
     | 
    
         
            +
                    ResultSet.raw(
         
     | 
| 
      
 32 
     | 
    
         
            +
                      config: config,
         
     | 
| 
      
 33 
     | 
    
         
            +
                      data: Util.array(faraday.body),
         
     | 
| 
      
 34 
     | 
    
         
            +
                      summary: Middleware::SummaryMiddleware.extract(faraday.env)
         
     | 
| 
       19 
35 
     | 
    
         
             
                    )
         
     | 
| 
       20 
36 
     | 
    
         
             
                  end
         
     | 
| 
      
 37 
     | 
    
         
            +
             
     | 
| 
      
 38 
     | 
    
         
            +
                  # Result of execution
         
     | 
| 
      
 39 
     | 
    
         
            +
                  # @return [Response::Summary]
         
     | 
| 
      
 40 
     | 
    
         
            +
                  # @params faraday [Faraday::Response]
         
     | 
| 
      
 41 
     | 
    
         
            +
                  def self.exec(faraday)
         
     | 
| 
      
 42 
     | 
    
         
            +
                    Middleware::SummaryMiddleware.extract(faraday.env)
         
     | 
| 
      
 43 
     | 
    
         
            +
                  end
         
     | 
| 
      
 44 
     | 
    
         
            +
             
     | 
| 
      
 45 
     | 
    
         
            +
                  # @return [Response::Summary]
         
     | 
| 
      
 46 
     | 
    
         
            +
                  def self.empty_exec(config)
         
     | 
| 
      
 47 
     | 
    
         
            +
                    Summary.new(config)
         
     | 
| 
      
 48 
     | 
    
         
            +
                  end
         
     | 
| 
       21 
49 
     | 
    
         
             
                end
         
     | 
| 
       22 
50 
     | 
    
         
             
              end
         
     | 
| 
       23 
51 
     | 
    
         
             
            end
         
     | 
| 
         @@ -6,26 +6,69 @@ module ClickHouse 
     | 
|
| 
       6 
6 
     | 
    
         
             
                  extend Forwardable
         
     | 
| 
       7 
7 
     | 
    
         
             
                  include Enumerable
         
     | 
| 
       8 
8 
     | 
    
         | 
| 
       9 
     | 
    
         
            -
                   
     | 
| 
       10 
     | 
    
         
            -
                   
     | 
| 
      
 9 
     | 
    
         
            +
                  KEY_META_NAME = 'name'
         
     | 
| 
      
 10 
     | 
    
         
            +
                  KEY_META_TYPE = 'type'
         
     | 
| 
       11 
11 
     | 
    
         | 
| 
       12 
12 
     | 
    
         
             
                  def_delegators :to_a,
         
     | 
| 
       13 
13 
     | 
    
         
             
                                 :inspect, :each, :fetch, :length, :count, :size,
         
     | 
| 
       14 
14 
     | 
    
         
             
                                 :first, :last, :[], :to_h
         
     | 
| 
       15 
15 
     | 
    
         | 
| 
       16 
     | 
    
         
            -
                   
     | 
| 
      
 16 
     | 
    
         
            +
                  def_delegators :summary,
         
     | 
| 
      
 17 
     | 
    
         
            +
                                 :statistics, :headers,
         
     | 
| 
      
 18 
     | 
    
         
            +
                                 :totals, :rows_before_limit_at_least
         
     | 
| 
       17 
19 
     | 
    
         | 
| 
      
 20 
     | 
    
         
            +
                  attr_reader :config, :meta, :data, :summary
         
     | 
| 
      
 21 
     | 
    
         
            +
             
     | 
| 
      
 22 
     | 
    
         
            +
                  class << self
         
     | 
| 
      
 23 
     | 
    
         
            +
                    # @param config [Config]
         
     | 
| 
      
 24 
     | 
    
         
            +
                    # @return [ResultSet]
         
     | 
| 
      
 25 
     | 
    
         
            +
                    def raw(config:, data:, summary:)
         
     | 
| 
      
 26 
     | 
    
         
            +
                      new(config: config, data: data, to_a: data, meta: [], summary: summary)
         
     | 
| 
      
 27 
     | 
    
         
            +
                    end
         
     | 
| 
      
 28 
     | 
    
         
            +
                  end
         
     | 
| 
      
 29 
     | 
    
         
            +
             
     | 
| 
      
 30 
     | 
    
         
            +
                  # @param config [Config]
         
     | 
| 
       18 
31 
     | 
    
         
             
                  # @param meta [Array]
         
     | 
| 
       19 
32 
     | 
    
         
             
                  # @param data [Array]
         
     | 
| 
       20 
     | 
    
         
            -
                  # @param  
     | 
| 
       21 
     | 
    
         
            -
                   
     | 
| 
       22 
     | 
    
         
            -
             
     | 
| 
       23 
     | 
    
         
            -
                  def initialize(meta:, data:, totals: nil, statistics: nil, rows_before_limit_at_least: nil)
         
     | 
| 
      
 33 
     | 
    
         
            +
                  # @param summary [Response::Summary]
         
     | 
| 
      
 34 
     | 
    
         
            +
                  def initialize(config:, meta:, data:, summary:, to_a: nil)
         
     | 
| 
      
 35 
     | 
    
         
            +
                    @config = config
         
     | 
| 
       24 
36 
     | 
    
         
             
                    @meta = meta
         
     | 
| 
       25 
37 
     | 
    
         
             
                    @data = data
         
     | 
| 
       26 
     | 
    
         
            -
                    @ 
     | 
| 
       27 
     | 
    
         
            -
                    @ 
     | 
| 
       28 
     | 
    
         
            -
             
     | 
| 
      
 38 
     | 
    
         
            +
                    @summary = summary
         
     | 
| 
      
 39 
     | 
    
         
            +
                    @to_a = to_a
         
     | 
| 
      
 40 
     | 
    
         
            +
                  end
         
     | 
| 
      
 41 
     | 
    
         
            +
             
     | 
| 
      
 42 
     | 
    
         
            +
                  # @return [Array, Hash]
         
     | 
| 
      
 43 
     | 
    
         
            +
                  # @param data [Array, Hash]
         
     | 
| 
      
 44 
     | 
    
         
            +
                  def serialize(data)
         
     | 
| 
      
 45 
     | 
    
         
            +
                    case data
         
     | 
| 
      
 46 
     | 
    
         
            +
                    when Hash
         
     | 
| 
      
 47 
     | 
    
         
            +
                      serialize_one(data)
         
     | 
| 
      
 48 
     | 
    
         
            +
                    when Array
         
     | 
| 
      
 49 
     | 
    
         
            +
                      data.map(&method(:serialize_one))
         
     | 
| 
      
 50 
     | 
    
         
            +
                    else
         
     | 
| 
      
 51 
     | 
    
         
            +
                      raise ArgumentError, "expect Hash or Array, got: #{data.class}"
         
     | 
| 
      
 52 
     | 
    
         
            +
                    end
         
     | 
| 
      
 53 
     | 
    
         
            +
                  end
         
     | 
| 
      
 54 
     | 
    
         
            +
             
     | 
| 
      
 55 
     | 
    
         
            +
                  # @return [Hash]
         
     | 
| 
      
 56 
     | 
    
         
            +
                  # @param row [Hash]
         
     | 
| 
      
 57 
     | 
    
         
            +
                  def serialize_one(row)
         
     | 
| 
      
 58 
     | 
    
         
            +
                    row.each_with_object({}) do |(key, value), object|
         
     | 
| 
      
 59 
     | 
    
         
            +
                      object[key] = serialize_column(key, value)
         
     | 
| 
      
 60 
     | 
    
         
            +
                    end
         
     | 
| 
      
 61 
     | 
    
         
            +
                  end
         
     | 
| 
      
 62 
     | 
    
         
            +
             
     | 
| 
      
 63 
     | 
    
         
            +
                  # @param name [String] column name
         
     | 
| 
      
 64 
     | 
    
         
            +
                  # @param value [Any]
         
     | 
| 
      
 65 
     | 
    
         
            +
                  def serialize_column(name, value)
         
     | 
| 
      
 66 
     | 
    
         
            +
                    stmt = types.fetch(name)
         
     | 
| 
      
 67 
     | 
    
         
            +
                    serialize_type(stmt, value)
         
     | 
| 
      
 68 
     | 
    
         
            +
                  rescue KeyError => e
         
     | 
| 
      
 69 
     | 
    
         
            +
                    raise SerializeError, "field <#{name}> does not exists in table schema: #{types}", e.backtrace
         
     | 
| 
      
 70 
     | 
    
         
            +
                  rescue StandardError => e
         
     | 
| 
      
 71 
     | 
    
         
            +
                    raise SerializeError, "failed to serialize <#{name}> with #{stmt}, #{e.class}, #{e.message}", e.backtrace
         
     | 
| 
       29 
72 
     | 
    
         
             
                  end
         
     | 
| 
       30 
73 
     | 
    
         | 
| 
       31 
74 
     | 
    
         
             
                  def to_a
         
     | 
| 
         @@ -39,8 +82,11 @@ module ClickHouse 
     | 
|
| 
       39 
82 
     | 
    
         
             
                  # @return [Hash<String, Ast::Statement>]
         
     | 
| 
       40 
83 
     | 
    
         
             
                  def types
         
     | 
| 
       41 
84 
     | 
    
         
             
                    @types ||= meta.each_with_object({}) do |row, object|
         
     | 
| 
       42 
     | 
    
         
            -
                       
     | 
| 
       43 
     | 
    
         
            -
             
     | 
| 
      
 85 
     | 
    
         
            +
                      column = row.fetch(config.key(KEY_META_NAME))
         
     | 
| 
      
 86 
     | 
    
         
            +
                      # make symbol keys, if config.symbolize_keys is true,
         
     | 
| 
      
 87 
     | 
    
         
            +
                      # to be able to cast and serialize properly
         
     | 
| 
      
 88 
     | 
    
         
            +
                      object[config.key(column)] = begin
         
     | 
| 
      
 89 
     | 
    
         
            +
                        current = Ast::Parser.new(row.fetch(config.key(KEY_META_TYPE))).parse
         
     | 
| 
       44 
90 
     | 
    
         
             
                        assign_type(current)
         
     | 
| 
       45 
91 
     | 
    
         
             
                        current
         
     | 
| 
       46 
92 
     | 
    
         
             
                      end
         
     | 
| 
         @@ -96,6 +142,42 @@ module ClickHouse 
     | 
|
| 
       96 
142 
     | 
    
         
             
                      cast_type(stmt.arguments.fetch(ix), item)
         
     | 
| 
       97 
143 
     | 
    
         
             
                    end
         
     | 
| 
       98 
144 
     | 
    
         
             
                  end
         
     | 
| 
      
 145 
     | 
    
         
            +
             
     | 
| 
      
 146 
     | 
    
         
            +
                  # @param stmt [Ast::Statement]
         
     | 
| 
      
 147 
     | 
    
         
            +
                  def serialize_type(stmt, value)
         
     | 
| 
      
 148 
     | 
    
         
            +
                    return serialize_container(stmt, value) if stmt.caster.container?
         
     | 
| 
      
 149 
     | 
    
         
            +
                    return serialize_map(stmt, value) if stmt.caster.map?
         
     | 
| 
      
 150 
     | 
    
         
            +
                    return serialize_tuple(stmt, Array(value)) if stmt.caster.tuple?
         
     | 
| 
      
 151 
     | 
    
         
            +
             
     | 
| 
      
 152 
     | 
    
         
            +
                    stmt.caster.serialize(value, *stmt.arguments.map(&:value))
         
     | 
| 
      
 153 
     | 
    
         
            +
                  end
         
     | 
| 
      
 154 
     | 
    
         
            +
             
     | 
| 
      
 155 
     | 
    
         
            +
                  # @param stmt [Ast::Statement]
         
     | 
| 
      
 156 
     | 
    
         
            +
                  def serialize_container(stmt, value)
         
     | 
| 
      
 157 
     | 
    
         
            +
                    stmt.caster.cast_each(value) do |item|
         
     | 
| 
      
 158 
     | 
    
         
            +
                      # TODO: raise an error if multiple arguments
         
     | 
| 
      
 159 
     | 
    
         
            +
                      serialize_type(stmt.arguments.first, item)
         
     | 
| 
      
 160 
     | 
    
         
            +
                    end
         
     | 
| 
      
 161 
     | 
    
         
            +
                  end
         
     | 
| 
      
 162 
     | 
    
         
            +
             
     | 
| 
      
 163 
     | 
    
         
            +
                  # @return [Hash]
         
     | 
| 
      
 164 
     | 
    
         
            +
                  # @param stmt [Ast::Statement]
         
     | 
| 
      
 165 
     | 
    
         
            +
                  # @param hash [Hash]
         
     | 
| 
      
 166 
     | 
    
         
            +
                  def serialize_map(stmt, hash)
         
     | 
| 
      
 167 
     | 
    
         
            +
                    raise ArgumentError, "expect hash got #{hash.class}" unless hash.is_a?(Hash)
         
     | 
| 
      
 168 
     | 
    
         
            +
             
     | 
| 
      
 169 
     | 
    
         
            +
                    key_type, value_type = stmt.arguments
         
     | 
| 
      
 170 
     | 
    
         
            +
                    hash.each_with_object({}) do |(key, value), object|
         
     | 
| 
      
 171 
     | 
    
         
            +
                      object[serialize_type(key_type, key)] = serialize_type(value_type, value)
         
     | 
| 
      
 172 
     | 
    
         
            +
                    end
         
     | 
| 
      
 173 
     | 
    
         
            +
                  end
         
     | 
| 
      
 174 
     | 
    
         
            +
             
     | 
| 
      
 175 
     | 
    
         
            +
                  # @param stmt [Ast::Statement]
         
     | 
| 
      
 176 
     | 
    
         
            +
                  def serialize_tuple(stmt, value)
         
     | 
| 
      
 177 
     | 
    
         
            +
                    value.map.with_index do |item, ix|
         
     | 
| 
      
 178 
     | 
    
         
            +
                      serialize_type(stmt.arguments.fetch(ix), item)
         
     | 
| 
      
 179 
     | 
    
         
            +
                    end
         
     | 
| 
      
 180 
     | 
    
         
            +
                  end
         
     | 
| 
       99 
181 
     | 
    
         
             
                end
         
     | 
| 
       100 
182 
     | 
    
         
             
              end
         
     | 
| 
       101 
183 
     | 
    
         
             
            end
         
     | 
| 
         @@ -0,0 +1,109 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            module ClickHouse
         
     | 
| 
      
 4 
     | 
    
         
            +
              module Response
         
     | 
| 
      
 5 
     | 
    
         
            +
                class Summary
         
     | 
| 
      
 6 
     | 
    
         
            +
                  SUMMARY_HEADER = 'x-clickhouse-summary'
         
     | 
| 
      
 7 
     | 
    
         
            +
                  KEY_TOTALS = 'totals'
         
     | 
| 
      
 8 
     | 
    
         
            +
                  KEY_STATISTICS = 'statistics'
         
     | 
| 
      
 9 
     | 
    
         
            +
                  KEY_ROWS_BEFORE_LIMIT_AT_LEAST = 'rows_before_limit_at_least'
         
     | 
| 
      
 10 
     | 
    
         
            +
                  KEY_STAT_ELAPSED = 'elapsed'
         
     | 
| 
      
 11 
     | 
    
         
            +
             
     | 
| 
      
 12 
     | 
    
         
            +
                  attr_reader :config,
         
     | 
| 
      
 13 
     | 
    
         
            +
                              :headers,
         
     | 
| 
      
 14 
     | 
    
         
            +
                              :summary,
         
     | 
| 
      
 15 
     | 
    
         
            +
                              # {:elapsed=>0.387287e-3, :rows_read=>0, :bytes_read=>0}}
         
     | 
| 
      
 16 
     | 
    
         
            +
                              :statistics,
         
     | 
| 
      
 17 
     | 
    
         
            +
                              :totals,
         
     | 
| 
      
 18 
     | 
    
         
            +
                              :rows_before_limit_at_least
         
     | 
| 
      
 19 
     | 
    
         
            +
             
     | 
| 
      
 20 
     | 
    
         
            +
                  # @param config [Config]
         
     | 
| 
      
 21 
     | 
    
         
            +
                  # @param headers [Faraday::Utils::Headers]
         
     | 
| 
      
 22 
     | 
    
         
            +
                  # @param body [Hash]
         
     | 
| 
      
 23 
     | 
    
         
            +
                  # TOTALS [Array|Hash|NilClass] Support for 'GROUP BY WITH TOTALS' modifier
         
     | 
| 
      
 24 
     | 
    
         
            +
                  #   https://clickhouse.tech/docs/en/sql-reference/statements/select/group-by/#with-totals-modifier
         
     | 
| 
      
 25 
     | 
    
         
            +
                  #   Hash in JSON format and Array in JSONCompact
         
     | 
| 
      
 26 
     | 
    
         
            +
                  def initialize(config, headers: Faraday::Utils::Headers.new, body: {})
         
     | 
| 
      
 27 
     | 
    
         
            +
                    @headers = headers
         
     | 
| 
      
 28 
     | 
    
         
            +
                    @config = config
         
     | 
| 
      
 29 
     | 
    
         
            +
                    @statistics = body.fetch(config.key(KEY_STATISTICS), {})
         
     | 
| 
      
 30 
     | 
    
         
            +
                    @totals = body[config.key(KEY_TOTALS)]
         
     | 
| 
      
 31 
     | 
    
         
            +
                    @rows_before_limit_at_least = body[config.key(KEY_ROWS_BEFORE_LIMIT_AT_LEAST)]
         
     | 
| 
      
 32 
     | 
    
         
            +
                    @summary = parse_summary(headers[SUMMARY_HEADER])
         
     | 
| 
      
 33 
     | 
    
         
            +
                  end
         
     | 
| 
      
 34 
     | 
    
         
            +
             
     | 
| 
      
 35 
     | 
    
         
            +
                  # @return [Integer]
         
     | 
| 
      
 36 
     | 
    
         
            +
                  def read_rows
         
     | 
| 
      
 37 
     | 
    
         
            +
                    summary[config.key('read_rows')].to_i
         
     | 
| 
      
 38 
     | 
    
         
            +
                  end
         
     | 
| 
      
 39 
     | 
    
         
            +
             
     | 
| 
      
 40 
     | 
    
         
            +
                  # @return [Integer]
         
     | 
| 
      
 41 
     | 
    
         
            +
                  def read_bytes
         
     | 
| 
      
 42 
     | 
    
         
            +
                    summary[config.key('read_bytes')].to_i
         
     | 
| 
      
 43 
     | 
    
         
            +
                  end
         
     | 
| 
      
 44 
     | 
    
         
            +
             
     | 
| 
      
 45 
     | 
    
         
            +
                  # @return [String]
         
     | 
| 
      
 46 
     | 
    
         
            +
                  def read_bytes_pretty
         
     | 
| 
      
 47 
     | 
    
         
            +
                    Util::Pretty.size(read_bytes)
         
     | 
| 
      
 48 
     | 
    
         
            +
                  end
         
     | 
| 
      
 49 
     | 
    
         
            +
             
     | 
| 
      
 50 
     | 
    
         
            +
                  # @return [Integer]
         
     | 
| 
      
 51 
     | 
    
         
            +
                  def written_rows
         
     | 
| 
      
 52 
     | 
    
         
            +
                    summary[config.key('written_rows')].to_i
         
     | 
| 
      
 53 
     | 
    
         
            +
                  end
         
     | 
| 
      
 54 
     | 
    
         
            +
             
     | 
| 
      
 55 
     | 
    
         
            +
                  # @return [Integer]
         
     | 
| 
      
 56 
     | 
    
         
            +
                  def written_bytes
         
     | 
| 
      
 57 
     | 
    
         
            +
                    summary[config.key('written_bytes')].to_i
         
     | 
| 
      
 58 
     | 
    
         
            +
                  end
         
     | 
| 
      
 59 
     | 
    
         
            +
             
     | 
| 
      
 60 
     | 
    
         
            +
                  # @return [String]
         
     | 
| 
      
 61 
     | 
    
         
            +
                  def written_bytes_pretty
         
     | 
| 
      
 62 
     | 
    
         
            +
                    Util::Pretty.size(written_bytes)
         
     | 
| 
      
 63 
     | 
    
         
            +
                  end
         
     | 
| 
      
 64 
     | 
    
         
            +
             
     | 
| 
      
 65 
     | 
    
         
            +
                  # @return [Integer]
         
     | 
| 
      
 66 
     | 
    
         
            +
                  def total_rows_to_read
         
     | 
| 
      
 67 
     | 
    
         
            +
                    summary[config.key('total_rows_to_read')].to_i
         
     | 
| 
      
 68 
     | 
    
         
            +
                  end
         
     | 
| 
      
 69 
     | 
    
         
            +
             
     | 
| 
      
 70 
     | 
    
         
            +
                  # @return [Integer]
         
     | 
| 
      
 71 
     | 
    
         
            +
                  def result_rows
         
     | 
| 
      
 72 
     | 
    
         
            +
                    summary[config.key('result_rows')].to_i
         
     | 
| 
      
 73 
     | 
    
         
            +
                  end
         
     | 
| 
      
 74 
     | 
    
         
            +
             
     | 
| 
      
 75 
     | 
    
         
            +
                  # @return [Integer]
         
     | 
| 
      
 76 
     | 
    
         
            +
                  def result_bytes
         
     | 
| 
      
 77 
     | 
    
         
            +
                    summary[config.key('result_bytes')].to_i
         
     | 
| 
      
 78 
     | 
    
         
            +
                  end
         
     | 
| 
      
 79 
     | 
    
         
            +
             
     | 
| 
      
 80 
     | 
    
         
            +
                  # @return [Float]
         
     | 
| 
      
 81 
     | 
    
         
            +
                  def elapsed
         
     | 
| 
      
 82 
     | 
    
         
            +
                    statistics[config.key(KEY_STAT_ELAPSED)].to_f
         
     | 
| 
      
 83 
     | 
    
         
            +
                  end
         
     | 
| 
      
 84 
     | 
    
         
            +
             
     | 
| 
      
 85 
     | 
    
         
            +
                  # @return [String]
         
     | 
| 
      
 86 
     | 
    
         
            +
                  def elapsed_pretty
         
     | 
| 
      
 87 
     | 
    
         
            +
                    Util::Pretty.measure(elapsed * 1000)
         
     | 
| 
      
 88 
     | 
    
         
            +
                  end
         
     | 
| 
      
 89 
     | 
    
         
            +
             
     | 
| 
      
 90 
     | 
    
         
            +
                  private
         
     | 
| 
      
 91 
     | 
    
         
            +
             
     | 
| 
      
 92 
     | 
    
         
            +
                  # @return [Hash]
         
     | 
| 
      
 93 
     | 
    
         
            +
                  # {
         
     | 
| 
      
 94 
     | 
    
         
            +
                  #   "read_rows" => "1",
         
     | 
| 
      
 95 
     | 
    
         
            +
                  #   "read_bytes" => "23",
         
     | 
| 
      
 96 
     | 
    
         
            +
                  #   "written_rows" => "1",
         
     | 
| 
      
 97 
     | 
    
         
            +
                  #   "written_bytes" => "23",
         
     | 
| 
      
 98 
     | 
    
         
            +
                  #   "total_rows_to_read" => "0",
         
     | 
| 
      
 99 
     | 
    
         
            +
                  #   "result_rows" => "1",
         
     | 
| 
      
 100 
     | 
    
         
            +
                  #   "result_bytes" => "23",
         
     | 
| 
      
 101 
     | 
    
         
            +
                  # }
         
     | 
| 
      
 102 
     | 
    
         
            +
                  def parse_summary(value)
         
     | 
| 
      
 103 
     | 
    
         
            +
                    return {} if value.nil? || value.empty?
         
     | 
| 
      
 104 
     | 
    
         
            +
             
     | 
| 
      
 105 
     | 
    
         
            +
                    JSON.parse(value)
         
     | 
| 
      
 106 
     | 
    
         
            +
                  end
         
     | 
| 
      
 107 
     | 
    
         
            +
                end
         
     | 
| 
      
 108 
     | 
    
         
            +
              end
         
     | 
| 
      
 109 
     | 
    
         
            +
            end
         
     | 
    
        data/lib/click_house/response.rb
    CHANGED
    
    
| 
         @@ -0,0 +1,32 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            module ClickHouse
         
     | 
| 
      
 4 
     | 
    
         
            +
              module Serializer
         
     | 
| 
      
 5 
     | 
    
         
            +
                class Base
         
     | 
| 
      
 6 
     | 
    
         
            +
                  attr_reader :config
         
     | 
| 
      
 7 
     | 
    
         
            +
             
     | 
| 
      
 8 
     | 
    
         
            +
                  # @param config [Config]
         
     | 
| 
      
 9 
     | 
    
         
            +
                  def initialize(config)
         
     | 
| 
      
 10 
     | 
    
         
            +
                    @config = config
         
     | 
| 
      
 11 
     | 
    
         
            +
                    on_setup
         
     | 
| 
      
 12 
     | 
    
         
            +
                  end
         
     | 
| 
      
 13 
     | 
    
         
            +
             
     | 
| 
      
 14 
     | 
    
         
            +
                  def dump(data)
         
     | 
| 
      
 15 
     | 
    
         
            +
                    raise NotImplementedError, __method__
         
     | 
| 
      
 16 
     | 
    
         
            +
                  end
         
     | 
| 
      
 17 
     | 
    
         
            +
             
     | 
| 
      
 18 
     | 
    
         
            +
                  # @return [String]
         
     | 
| 
      
 19 
     | 
    
         
            +
                  # @param data [Array]
         
     | 
| 
      
 20 
     | 
    
         
            +
                  def dump_each_row(data, sep = "\n")
         
     | 
| 
      
 21 
     | 
    
         
            +
                    data.map(&method(:dump)).join(sep)
         
     | 
| 
      
 22 
     | 
    
         
            +
                  end
         
     | 
| 
      
 23 
     | 
    
         
            +
             
     | 
| 
      
 24 
     | 
    
         
            +
                  private
         
     | 
| 
      
 25 
     | 
    
         
            +
             
     | 
| 
      
 26 
     | 
    
         
            +
                  # require external dependencies here
         
     | 
| 
      
 27 
     | 
    
         
            +
                  def on_setup
         
     | 
| 
      
 28 
     | 
    
         
            +
                    nil
         
     | 
| 
      
 29 
     | 
    
         
            +
                  end
         
     | 
| 
      
 30 
     | 
    
         
            +
                end
         
     | 
| 
      
 31 
     | 
    
         
            +
              end
         
     | 
| 
      
 32 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,17 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            module ClickHouse
         
     | 
| 
      
 4 
     | 
    
         
            +
              module Serializer
         
     | 
| 
      
 5 
     | 
    
         
            +
                class JsonOjSerializer < Base
         
     | 
| 
      
 6 
     | 
    
         
            +
                  def dump(data)
         
     | 
| 
      
 7 
     | 
    
         
            +
                    Oj.dump(data, config.oj_dump_options)
         
     | 
| 
      
 8 
     | 
    
         
            +
                  end
         
     | 
| 
      
 9 
     | 
    
         
            +
             
     | 
| 
      
 10 
     | 
    
         
            +
                  private
         
     | 
| 
      
 11 
     | 
    
         
            +
             
     | 
| 
      
 12 
     | 
    
         
            +
                  def on_setup
         
     | 
| 
      
 13 
     | 
    
         
            +
                    require 'oj' unless defined?(Oj)
         
     | 
| 
      
 14 
     | 
    
         
            +
                  end
         
     | 
| 
      
 15 
     | 
    
         
            +
                end
         
     | 
| 
      
 16 
     | 
    
         
            +
              end
         
     | 
| 
      
 17 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,9 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            module ClickHouse
         
     | 
| 
      
 4 
     | 
    
         
            +
              module Serializer
         
     | 
| 
      
 5 
     | 
    
         
            +
                autoload :Base, 'click_house/serializer/base'
         
     | 
| 
      
 6 
     | 
    
         
            +
                autoload :JsonSerializer, 'click_house/serializer/json_serializer'
         
     | 
| 
      
 7 
     | 
    
         
            +
                autoload :JsonOjSerializer, 'click_house/serializer/json_oj_serializer'
         
     | 
| 
      
 8 
     | 
    
         
            +
              end
         
     | 
| 
      
 9 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -11,6 +11,10 @@ module ClickHouse 
     | 
|
| 
       11 
11 
     | 
    
         
             
                    raise NotImplementedError, __method__
         
     | 
| 
       12 
12 
     | 
    
         
             
                  end
         
     | 
| 
       13 
13 
     | 
    
         | 
| 
      
 14 
     | 
    
         
            +
                  def serialize_each(_value, *)
         
     | 
| 
      
 15 
     | 
    
         
            +
                    raise NotImplementedError, __method__
         
     | 
| 
      
 16 
     | 
    
         
            +
                  end
         
     | 
| 
      
 17 
     | 
    
         
            +
             
     | 
| 
       14 
18 
     | 
    
         
             
                  # @return [Boolean]
         
     | 
| 
       15 
19 
     | 
    
         
             
                  # true if type contains another type like Nullable(T) or Array(T)
         
     | 
| 
       16 
20 
     | 
    
         
             
                  def container?
         
     | 
| 
         @@ -3,14 +3,14 @@ 
     | 
|
| 
       3 
3 
     | 
    
         
             
            module ClickHouse
         
     | 
| 
       4 
4 
     | 
    
         
             
              module Type
         
     | 
| 
       5 
5 
     | 
    
         
             
                class DecimalType < BaseType
         
     | 
| 
       6 
     | 
    
         
            -
                  MAXIMUM = Float::DIG
         
     | 
| 
      
 6 
     | 
    
         
            +
                  MAXIMUM = Float::DIG.next
         
     | 
| 
       7 
7 
     | 
    
         | 
| 
       8 
8 
     | 
    
         
             
                  # clickhouse:
         
     | 
| 
       9 
9 
     | 
    
         
             
                  # P - precision. Valid range: [ 1 : 76 ]. Determines how many decimal digits number can have (including fraction).
         
     | 
| 
       10 
10 
     | 
    
         
             
                  # S - scale. Valid range: [ 0 : P ]. Determines how many decimal digits fraction can have.
         
     | 
| 
       11 
11 
     | 
    
         
             
                  #
         
     | 
| 
       12 
12 
     | 
    
         
             
                  # when Oj parser @refs https://stackoverflow.com/questions/47885304/deserialise-json-numbers-as-bigdecimal
         
     | 
| 
       13 
     | 
    
         
            -
                  def cast(value, precision =  
     | 
| 
      
 13 
     | 
    
         
            +
                  def cast(value, precision = MAXIMUM, _scale = nil)
         
     | 
| 
       14 
14 
     | 
    
         
             
                    case value
         
     | 
| 
       15 
15 
     | 
    
         
             
                    when BigDecimal
         
     | 
| 
       16 
16 
     | 
    
         
             
                      value
         
     | 
| 
         @@ -21,8 +21,9 @@ module ClickHouse 
     | 
|
| 
       21 
21 
     | 
    
         
             
                    end
         
     | 
| 
       22 
22 
     | 
    
         
             
                  end
         
     | 
| 
       23 
23 
     | 
    
         | 
| 
       24 
     | 
    
         
            -
                   
     | 
| 
       25 
     | 
    
         
            -
             
     | 
| 
      
 24 
     | 
    
         
            +
                  # @return [BigDecimal]
         
     | 
| 
      
 25 
     | 
    
         
            +
                  def serialize(value, precision = MAXIMUM, _scale = nil)
         
     | 
| 
      
 26 
     | 
    
         
            +
                    cast(value, precision)
         
     | 
| 
       26 
27 
     | 
    
         
             
                  end
         
     | 
| 
       27 
28 
     | 
    
         
             
                end
         
     | 
| 
       28 
29 
     | 
    
         
             
              end
         
     | 
    
        data/lib/click_house/util.rb
    CHANGED
    
    
    
        data/lib/click_house/version.rb
    CHANGED
    
    
    
        data/lib/click_house.rb
    CHANGED
    
    | 
         @@ -12,6 +12,7 @@ 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 
     | 
    
         
            +
            require 'click_house/serializer'
         
     | 
| 
       15 
16 
     | 
    
         
             
            require 'click_house/type'
         
     | 
| 
       16 
17 
     | 
    
         
             
            require 'click_house/middleware'
         
     | 
| 
       17 
18 
     | 
    
         
             
            require 'click_house/extend'
         
     | 
| 
         @@ -33,6 +34,10 @@ module ClickHouse 
     | 
|
| 
       33 
34 
     | 
    
         
             
              add_type 'LowCardinality', Type::LowCardinalityType.new
         
     | 
| 
       34 
35 
     | 
    
         
             
              add_type 'Tuple', Type::TupleType.new
         
     | 
| 
       35 
36 
     | 
    
         | 
| 
      
 37 
     | 
    
         
            +
              %w[Bool].each do |column|
         
     | 
| 
      
 38 
     | 
    
         
            +
                add_type column, Type::BooleanType.new
         
     | 
| 
      
 39 
     | 
    
         
            +
              end
         
     | 
| 
      
 40 
     | 
    
         
            +
             
     | 
| 
       36 
41 
     | 
    
         
             
              %w[Date].each do |column|
         
     | 
| 
       37 
42 
     | 
    
         
             
                add_type column, Type::DateType.new
         
     | 
| 
       38 
43 
     | 
    
         
             
              end
         
     | 
    
        metadata
    CHANGED
    
    | 
         @@ -1,14 +1,14 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            --- !ruby/object:Gem::Specification
         
     | 
| 
       2 
2 
     | 
    
         
             
            name: click_house
         
     | 
| 
       3 
3 
     | 
    
         
             
            version: !ruby/object:Gem::Version
         
     | 
| 
       4 
     | 
    
         
            -
              version: 2. 
     | 
| 
      
 4 
     | 
    
         
            +
              version: 2.1.1
         
     | 
| 
       5 
5 
     | 
    
         
             
            platform: ruby
         
     | 
| 
       6 
6 
     | 
    
         
             
            authors:
         
     | 
| 
       7 
7 
     | 
    
         
             
            - Aliaksandr Shylau
         
     | 
| 
       8 
8 
     | 
    
         
             
            autorequire:
         
     | 
| 
       9 
9 
     | 
    
         
             
            bindir: bin
         
     | 
| 
       10 
10 
     | 
    
         
             
            cert_chain: []
         
     | 
| 
       11 
     | 
    
         
            -
            date: 2022-11- 
     | 
| 
      
 11 
     | 
    
         
            +
            date: 2022-11-20 00:00:00.000000000 Z
         
     | 
| 
       12 
12 
     | 
    
         
             
            dependencies:
         
     | 
| 
       13 
13 
     | 
    
         
             
            - !ruby/object:Gem::Dependency
         
     | 
| 
       14 
14 
     | 
    
         
             
              name: faraday
         
     | 
| 
         @@ -175,6 +175,7 @@ files: 
     | 
|
| 
       175 
175 
     | 
    
         
             
            - lib/click_house/ast/parser.rb
         
     | 
| 
       176 
176 
     | 
    
         
             
            - lib/click_house/ast/statement.rb
         
     | 
| 
       177 
177 
     | 
    
         
             
            - lib/click_house/ast/ticker.rb
         
     | 
| 
      
 178 
     | 
    
         
            +
            - lib/click_house/benchmark/map_join.rb
         
     | 
| 
       178 
179 
     | 
    
         
             
            - lib/click_house/config.rb
         
     | 
| 
       179 
180 
     | 
    
         
             
            - lib/click_house/connection.rb
         
     | 
| 
       180 
181 
     | 
    
         
             
            - lib/click_house/definition.rb
         
     | 
| 
         @@ -199,9 +200,15 @@ files: 
     | 
|
| 
       199 
200 
     | 
    
         
             
            - lib/click_house/middleware/parse_json_oj.rb
         
     | 
| 
       200 
201 
     | 
    
         
             
            - lib/click_house/middleware/raise_error.rb
         
     | 
| 
       201 
202 
     | 
    
         
             
            - lib/click_house/middleware/response_base.rb
         
     | 
| 
      
 203 
     | 
    
         
            +
            - lib/click_house/middleware/summary_middleware.rb
         
     | 
| 
       202 
204 
     | 
    
         
             
            - lib/click_house/response.rb
         
     | 
| 
       203 
205 
     | 
    
         
             
            - lib/click_house/response/factory.rb
         
     | 
| 
       204 
206 
     | 
    
         
             
            - lib/click_house/response/result_set.rb
         
     | 
| 
      
 207 
     | 
    
         
            +
            - lib/click_house/response/summary.rb
         
     | 
| 
      
 208 
     | 
    
         
            +
            - lib/click_house/serializer.rb
         
     | 
| 
      
 209 
     | 
    
         
            +
            - lib/click_house/serializer/base.rb
         
     | 
| 
      
 210 
     | 
    
         
            +
            - lib/click_house/serializer/json_oj_serializer.rb
         
     | 
| 
      
 211 
     | 
    
         
            +
            - lib/click_house/serializer/json_serializer.rb
         
     | 
| 
       205 
212 
     | 
    
         
             
            - lib/click_house/type.rb
         
     | 
| 
       206 
213 
     | 
    
         
             
            - lib/click_house/type/array_type.rb
         
     | 
| 
       207 
214 
     | 
    
         
             
            - lib/click_house/type/base_type.rb
         
     |