From 0e43bd7b56f043d08ed3154ce31bb7f07effaebe Mon Sep 17 00:00:00 2001 From: jenskdsgn Date: Wed, 3 Apr 2024 08:08:44 +0200 Subject: [PATCH] Fix precision loss due to JSON float parsing (#129) --- .../clickhouse/schema_statements.rb | 8 ++++++-- .../add_sample_data/1_create_sample_table.rb | 1 + spec/single/model_spec.rb | 13 +++++++++++++ 3 files changed, 20 insertions(+), 2 deletions(-) diff --git a/lib/active_record/connection_adapters/clickhouse/schema_statements.rb b/lib/active_record/connection_adapters/clickhouse/schema_statements.rb index d7a3386d..a3be9e7c 100644 --- a/lib/active_record/connection_adapters/clickhouse/schema_statements.rb +++ b/lib/active_record/connection_adapters/clickhouse/schema_statements.rb @@ -233,11 +233,11 @@ def format_body_response(body, format) end def format_from_json_compact(body) - JSON.parse(body) + parse_json_payload(body) end def format_from_json_compact_each_row_with_names_and_types(body) - rows = body.split("\n").map { |row| JSON.parse(row) } + rows = body.split("\n").map { |row| parse_json_payload(row) } names, types, *data = rows meta = names.zip(types).map do |name, type| @@ -252,6 +252,10 @@ def format_from_json_compact_each_row_with_names_and_types(body) 'data' => data } end + + def parse_json_payload(payload) + JSON.parse(payload, decimal_class: BigDecimal) + end end end end diff --git a/spec/fixtures/migrations/add_sample_data/1_create_sample_table.rb b/spec/fixtures/migrations/add_sample_data/1_create_sample_table.rb index 699c4da7..9619bfe5 100644 --- a/spec/fixtures/migrations/add_sample_data/1_create_sample_table.rb +++ b/spec/fixtures/migrations/add_sample_data/1_create_sample_table.rb @@ -11,6 +11,7 @@ def up t.datetime :datetime64, precision: 3, null: true t.string :byte_array, null: true t.uuid :relation_uuid + t.decimal :decimal_value, precision: 38, scale: 16, null: true end end end diff --git a/spec/single/model_spec.rb b/spec/single/model_spec.rb index 88c9d54a..e9e46b54 100644 --- a/spec/single/model_spec.rb +++ b/spec/single/model_spec.rb @@ -180,6 +180,19 @@ class ModelPk < ActiveRecord::Base end end + describe 'decimal column type' do + let!(:record1) do + Model.create!(event_name: 'some event', decimal_value: BigDecimal('95891.74')) + end + + # If converted to float, the value would be 9589174.000000001. This happened previously + # due to JSON parsing of numeric values to floats. + it 'keeps precision' do + decimal_value = Model.first.decimal_value + expect(decimal_value).to eq(BigDecimal('95891.74')) + end + end + describe '#settings' do it 'works' do sql = Model.settings(optimize_read_in_order: 1, cast_keep_nullable: 1).to_sql