Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
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
Original file line number Diff line number Diff line change
Expand Up @@ -37,29 +37,7 @@ def initialize(
end

def integer(*args, **options)
# default to unsigned
unsigned = options[:unsigned]
unsigned = true if unsigned.nil?

kind = :uint32 # default

if options[:limit]
if unsigned
kind = :uint8 if options[:limit] == 1
kind = :uint16 if options[:limit] == 2
kind = :uint32 if [3,4].include?(options[:limit])
kind = :uint64 if [5,6,7].include?(options[:limit])
kind = :big_integer if options[:limit] == 8
kind = :uint256 if options[:limit] > 8
else
kind = :int8 if options[:limit] == 1
kind = :int16 if options[:limit] == 2
kind = :int32 if [3,4].include?(options[:limit])
kind = :int64 if options[:limit] > 5 && options[:limit] <= 8
kind = :int128 if options[:limit] > 8 && options[:limit] <= 16
kind = :int256 if options[:limit] > 16
end
end
kind = @conn.send(:resolve_integer_kind, options)
args.each { |name| column(name, kind, **options.except(:limit, :unsigned)) }
end

Expand Down
47 changes: 47 additions & 0 deletions lib/active_record/connection_adapters/clickhouse_adapter.rb
Original file line number Diff line number Diff line change
Expand Up @@ -413,6 +413,7 @@ def drop_function(name, options = {})
end

def add_column(table_name, column_name, type, **options)
type, options = resolve_type_with_limit(type, **options)
with_settings(wait_end_of_query: 1, send_progress_in_http_headers: 1) { super }
end

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

private

# Resolves standard Rails types with :limit/:unsigned options into
# ClickHouse-specific types. This mirrors the logic in TableDefinition
# (integer, enum) so that add_column produces the same types as
# create_table.
def resolve_type_with_limit(type, **options)
case type.to_sym
when :integer
kind = resolve_integer_kind(options)
[kind, options.except(:limit, :unsigned)]
when :enum
kind = :enum8
kind = :enum8 if options[:limit] == 1
kind = :enum16 if options[:limit] == 2
[kind, options.except(:limit)]
else
[type, options]
end
end

def resolve_integer_kind(options)
unsigned = options[:unsigned]
unsigned = true if unsigned.nil?

kind = :uint32 # default

if options[:limit]
if unsigned
kind = :uint8 if options[:limit] == 1
kind = :uint16 if options[:limit] == 2
kind = :uint32 if [3, 4].include?(options[:limit])
kind = :uint64 if [5, 6, 7].include?(options[:limit])
kind = :big_integer if options[:limit] == 8
kind = :uint256 if options[:limit] > 8
else
kind = :int8 if options[:limit] == 1
kind = :int16 if options[:limit] == 2
kind = :int32 if [3, 4].include?(options[:limit])
kind = :int64 if options[:limit] >= 5 && options[:limit] <= 8
kind = :int128 if options[:limit] > 8 && options[:limit] <= 16
kind = :int256 if options[:limit] > 16
end
end

kind
end

def connect
@connection = @connection_parameters[:connection] || Net::HTTP.start(@connection_parameters[:host], @connection_parameters[:port], use_ssl: @connection_parameters[:ssl], verify_mode: OpenSSL::SSL::VERIFY_NONE)

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# frozen_string_literal: true

class CreateSomeTable < ActiveRecord::Migration[7.1]
def up
create_table :some, options: 'MergeTree PARTITION BY toYYYYMM(date) ORDER BY (date)' do |t|
t.date :date, null: false
end
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# frozen_string_literal: true

class AddColumnsWithLimit < ActiveRecord::Migration[7.1]
def up
# Signed integers with limit
add_column :some, :int16_col, :integer, limit: 2, unsigned: false, null: false, default: 0
add_column :some, :int32_col, :integer, limit: 4, unsigned: false, null: false, default: 0
add_column :some, :int64_limit5_col, :integer, limit: 5, unsigned: false, null: false, default: 0
add_column :some, :int64_col, :integer, limit: 8, unsigned: false, null: false, default: 0

# Unsigned integers with limit
add_column :some, :uint8_col, :integer, limit: 1, null: false, default: 0
add_column :some, :uint16_col, :integer, limit: 2, null: false, default: 0
add_column :some, :uint64_col, :integer, limit: 8, null: false, default: 0

# Default unsigned integer (no limit)
add_column :some, :uint32_col, :integer, null: false, default: 0
end
end
23 changes: 23 additions & 0 deletions spec/single/migration_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -358,6 +358,29 @@
end
end

describe 'add column with limit' do
let(:directory) { 'dsl_add_column_with_limit' }
it 'adds integer columns with correct types based on limit and unsigned options' do
subject

current_schema = schema(model)

# Signed integers
expect(current_schema['int16_col'].sql_type).to eq('Int16')
expect(current_schema['int32_col'].sql_type).to eq('Int32')
expect(current_schema['int64_limit5_col'].sql_type).to eq('Int64')
expect(current_schema['int64_col'].sql_type).to eq('Int64')

# Unsigned integers
expect(current_schema['uint8_col'].sql_type).to eq('UInt8')
expect(current_schema['uint16_col'].sql_type).to eq('UInt16')
expect(current_schema['uint64_col'].sql_type).to eq('UInt64')

# Default unsigned integer (no limit)
expect(current_schema['uint32_col'].sql_type).to eq('UInt32')
end
end

describe 'drop column' do
let(:directory) { 'dsl_drop_column' }
it 'drops column' do
Expand Down