Skip to content

Commit b9d10e5

Browse files
cbisnettclaude
andcommitted
fix: resolve integer/enum types with limit in add_column
add_column ignored :limit and :unsigned options for :integer and :enum types because type resolution only happened in TableDefinition (used by create_table). This caused add_column to always produce the default type (e.g. UInt32 for integers) regardless of the limit specified. Extract the type resolution logic into resolve_type_with_limit and call it from add_column before delegating to super. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent c63ccb4 commit b9d10e5

File tree

4 files changed

+96
-0
lines changed

4 files changed

+96
-0
lines changed

lib/active_record/connection_adapters/clickhouse_adapter.rb

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -413,6 +413,7 @@ def drop_function(name, options = {})
413413
end
414414

415415
def add_column(table_name, column_name, type, **options)
416+
type, options = resolve_type_with_limit(type, **options)
416417
with_settings(wait_end_of_query: 1, send_progress_in_http_headers: 1) { super }
417418
end
418419

@@ -551,6 +552,52 @@ def change_column_for_alter(table_name, column_name, type, **options)
551552

552553
private
553554

555+
# Resolves standard Rails types with :limit/:unsigned options into
556+
# ClickHouse-specific types. This mirrors the logic in TableDefinition
557+
# (integer, enum) so that add_column produces the same types as
558+
# create_table.
559+
def resolve_type_with_limit(type, **options)
560+
case type.to_sym
561+
when :integer
562+
kind = resolve_integer_kind(options)
563+
[kind, options.except(:limit, :unsigned)]
564+
when :enum
565+
kind = :enum8
566+
kind = :enum8 if options[:limit] == 1
567+
kind = :enum16 if options[:limit] == 2
568+
[kind, options.except(:limit)]
569+
else
570+
[type, options]
571+
end
572+
end
573+
574+
def resolve_integer_kind(options)
575+
unsigned = options[:unsigned]
576+
unsigned = true if unsigned.nil?
577+
578+
kind = :uint32 # default
579+
580+
if options[:limit]
581+
if unsigned
582+
kind = :uint8 if options[:limit] == 1
583+
kind = :uint16 if options[:limit] == 2
584+
kind = :uint32 if [3, 4].include?(options[:limit])
585+
kind = :uint64 if [5, 6, 7].include?(options[:limit])
586+
kind = :big_integer if options[:limit] == 8
587+
kind = :uint256 if options[:limit] > 8
588+
else
589+
kind = :int8 if options[:limit] == 1
590+
kind = :int16 if options[:limit] == 2
591+
kind = :int32 if [3, 4].include?(options[:limit])
592+
kind = :int64 if options[:limit] > 5 && options[:limit] <= 8
593+
kind = :int128 if options[:limit] > 8 && options[:limit] <= 16
594+
kind = :int256 if options[:limit] > 16
595+
end
596+
end
597+
598+
kind
599+
end
600+
554601
def connect
555602
@connection = @connection_parameters[:connection] || Net::HTTP.start(@connection_parameters[:host], @connection_parameters[:port], use_ssl: @connection_parameters[:ssl], verify_mode: OpenSSL::SSL::VERIFY_NONE)
556603

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
# frozen_string_literal: true
2+
3+
class CreateSomeTable < ActiveRecord::Migration[7.1]
4+
def up
5+
create_table :some, options: 'MergeTree PARTITION BY toYYYYMM(date) ORDER BY (date)' do |t|
6+
t.date :date, null: false
7+
end
8+
end
9+
end
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
# frozen_string_literal: true
2+
3+
class AddColumnsWithLimit < ActiveRecord::Migration[7.1]
4+
def up
5+
# Signed integers with limit
6+
add_column :some, :int16_col, :integer, limit: 2, unsigned: false, null: false, default: 0
7+
add_column :some, :int32_col, :integer, limit: 4, unsigned: false, null: false, default: 0
8+
add_column :some, :int64_col, :integer, limit: 8, unsigned: false, null: false, default: 0
9+
10+
# Unsigned integers with limit
11+
add_column :some, :uint8_col, :integer, limit: 1, null: false, default: 0
12+
add_column :some, :uint16_col, :integer, limit: 2, null: false, default: 0
13+
add_column :some, :uint64_col, :integer, limit: 8, null: false, default: 0
14+
15+
# Default unsigned integer (no limit)
16+
add_column :some, :uint32_col, :integer, null: false, default: 0
17+
end
18+
end

spec/single/migration_spec.rb

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -358,6 +358,28 @@
358358
end
359359
end
360360

361+
describe 'add column with limit' do
362+
let(:directory) { 'dsl_add_column_with_limit' }
363+
it 'adds integer columns with correct types based on limit and unsigned options' do
364+
subject
365+
366+
current_schema = schema(model)
367+
368+
# Signed integers
369+
expect(current_schema['int16_col'].sql_type).to eq('Int16')
370+
expect(current_schema['int32_col'].sql_type).to eq('Int32')
371+
expect(current_schema['int64_col'].sql_type).to eq('Int64')
372+
373+
# Unsigned integers
374+
expect(current_schema['uint8_col'].sql_type).to eq('UInt8')
375+
expect(current_schema['uint16_col'].sql_type).to eq('UInt16')
376+
expect(current_schema['uint64_col'].sql_type).to eq('UInt64')
377+
378+
# Default unsigned integer (no limit)
379+
expect(current_schema['uint32_col'].sql_type).to eq('UInt32')
380+
end
381+
end
382+
361383
describe 'drop column' do
362384
let(:directory) { 'dsl_drop_column' }
363385
it 'drops column' do

0 commit comments

Comments
 (0)