Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

misc: Sync with upstream #7

Merged
merged 22 commits into from
Jun 13, 2024
Merged
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Add support for binary string (PNixx#116)
* Add support for binary string
PauloMiranda98 authored Feb 14, 2024

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature.
commit ce5789255822979a317ceb0e174fb3c8b270be2e
Original file line number Diff line number Diff line change
@@ -6,6 +6,8 @@ module ActiveRecord
module ConnectionAdapters
module Clickhouse
module SchemaStatements
DEFAULT_RESPONSE_FORMAT = 'JSONCompactEachRowWithNamesAndTypes'.freeze

def execute(sql, name = nil, settings: {})
do_execute(sql, name, settings: settings)
end
@@ -64,19 +66,19 @@ def data_sources

def do_system_execute(sql, name = nil)
log_with_debug(sql, "#{adapter_name} #{name}") do
res = @connection.post("/?#{@connection_config.to_param}", "#{sql} FORMAT JSONCompact", 'User-Agent' => "Clickhouse ActiveRecord #{ClickhouseActiverecord::VERSION}")
res = @connection.post("/?#{@connection_config.to_param}", "#{sql} FORMAT #{DEFAULT_RESPONSE_FORMAT}", 'User-Agent' => "Clickhouse ActiveRecord #{ClickhouseActiverecord::VERSION}")

process_response(res)
process_response(res, DEFAULT_RESPONSE_FORMAT)
end
end

def do_execute(sql, name = nil, format: 'JSONCompact', settings: {})
def do_execute(sql, name = nil, format: DEFAULT_RESPONSE_FORMAT, settings: {})
log(sql, "#{adapter_name} #{name}") do
formatted_sql = apply_format(sql, format)
request_params = @connection_config || {}
res = @connection.post("/?#{request_params.merge(settings).to_param}", formatted_sql, 'User-Agent' => "Clickhouse ActiveRecord #{ClickhouseActiverecord::VERSION}")

process_response(res)
process_response(res, format)
end
end

@@ -116,13 +118,13 @@ def apply_format(sql, format)
format ? "#{sql} FORMAT #{format}" : sql
end

def process_response(res)
def process_response(res, format)
case res.code.to_i
when 200
if res.body.to_s.include?("DB::Exception")
raise ActiveRecord::ActiveRecordError, "Response code: #{res.code}:\n#{res.body}"
else
res.body.presence && JSON.parse(res.body)
format_body_response(res.body, format)
end
else
case res.body
@@ -203,6 +205,40 @@ def extract_default_function(default_value, default) # :nodoc:
def has_default_function?(default_value, default) # :nodoc:
!default_value && (%r{\w+\(.*\)} === default)
end

def format_body_response(body, format)
return body if body.blank?

case format
when 'JSONCompact'
format_from_json_compact(body)
when 'JSONCompactEachRowWithNamesAndTypes'
format_from_json_compact_each_row_with_names_and_types(body)
else
body
end
end

def format_from_json_compact(body)
JSON.parse(body)
end

def format_from_json_compact_each_row_with_names_and_types(body)
rows = body.split("\n").map { |row| JSON.parse(row) }
names, types, *data = rows

meta = names.zip(types).map do |name, type|
{
'name' => name,
'type' => type
}
end

{
'meta' => meta,
'data' => data
}
end
end
end
end
38 changes: 33 additions & 5 deletions spec/cases/model_spec.rb
Original file line number Diff line number Diff line change
@@ -22,12 +22,29 @@ class Model < ActiveRecord::Base
quietly { ActiveRecord::MigrationContext.new(migrations_dir, model.connection.schema_migration).up }
end

it '#do_execute' do
result = model.connection.do_execute('SELECT 1 AS t', format: 'JSONCompact')
expect(result['data']).to eq([[1]])
expect(result['meta']).to eq([{'name' => 't', 'type' => 'UInt8'}])
end
describe '#do_execute' do
it 'returns formatted result' do
result = model.connection.do_execute('SELECT 1 AS t')
expect(result['data']).to eq([[1]])
expect(result['meta']).to eq([{ 'name' => 't', 'type' => 'UInt8' }])
end

context 'with JSONCompact format' do
it 'returns formatted result' do
result = model.connection.do_execute('SELECT 1 AS t', format: 'JSONCompact')
expect(result['data']).to eq([[1]])
expect(result['meta']).to eq([{ 'name' => 't', 'type' => 'UInt8' }])
end
end

context 'with JSONCompactEachRowWithNamesAndTypes format' do
it 'returns formatted result' do
result = model.connection.do_execute('SELECT 1 AS t', format: 'JSONCompactEachRowWithNamesAndTypes')
expect(result['data']).to eq([[1]])
expect(result['meta']).to eq([{ 'name' => 't', 'type' => 'UInt8' }])
end
end
end

describe '#create' do
it 'creates a new record' do
@@ -104,6 +121,17 @@ class Model < ActiveRecord::Base
end
end

describe 'string column type as byte array' do
let(:bytes) { (0..255).to_a }
let!(:record1) { model.create!(event_name: 'some event', byte_array: bytes.pack('C*')) }

it 'keeps all bytes' do
returned_byte_array = model.first.byte_array

expect(returned_byte_array.unpack('C*')).to eq(bytes)
end
end

describe 'UUID column type' do
let(:random_uuid) { SecureRandom.uuid }
let!(:record1) do
Original file line number Diff line number Diff line change
@@ -9,6 +9,7 @@ def up
t.date :date, null: false
t.datetime :datetime, null: false
t.datetime :datetime64, precision: 3, null: true
t.string :byte_array, null: true
t.uuid :relation_uuid
end
end