Skip to content

Commit adeff17

Browse files
committed
add settings to table dsl
1 parent bcd6e92 commit adeff17

5 files changed

Lines changed: 107 additions & 2 deletions

File tree

lib/active_record/connection_adapters/clickhouse/schema_creation.rb

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,13 @@ def add_ttl_clause!(create_sql, options)
7878
create_sql << " TTL #{ttl}"
7979
end
8080

81+
def add_settings_clause!(create_sql, options)
82+
settings = options.respond_to?(:settings) ? options.settings : options[:settings]
83+
return unless settings.present?
84+
85+
create_sql << " SETTINGS #{settings}"
86+
end
87+
8188
def add_as_clause!(create_sql, options)
8289
return unless options.as
8390

@@ -132,6 +139,7 @@ def visit_TableDefinition(o)
132139
add_table_options!(create_sql, o) if !o.view || o.view && o.materialized && !o.to
133140
add_as_clause!(create_sql, o) if o.as && o.view
134141
add_ttl_clause!(create_sql, o) if o.ttl
142+
add_settings_clause!(create_sql, o) if o.settings
135143
create_sql
136144
end
137145

lib/active_record/connection_adapters/clickhouse/schema_statements.rb

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -157,7 +157,21 @@ def show_create_function(function)
157157

158158
def table_options(table)
159159
sql = show_create_table(table)
160-
{ options: sql.gsub(/^(?:.*?)(?:ENGINE = (.*?))?( AS SELECT .*?)?$/, '\\1').presence, as: sql.match(/^CREATE (?:.*?) AS (SELECT .*?)$/).try(:[], 1) }.compact
160+
161+
as = sql[/^CREATE .*? AS (SELECT .*?)$/m, 1]
162+
engine = sql[/ENGINE = (.*?)(?: AS SELECT .*?)?$/m, 1]
163+
164+
return { as: }.compact unless engine.present?
165+
166+
engine, settings = extract_clause(engine, :settings)
167+
engine, ttl = extract_clause(engine, :ttl)
168+
169+
{
170+
options: engine.presence,
171+
ttl:,
172+
settings:,
173+
as:,
174+
}.compact
161175
end
162176

163177
# Not indexes on clickhouse
@@ -290,6 +304,18 @@ def has_default_function?(default) # :nodoc:
290304
(%r{\w+\(.*\)} === default)
291305
end
292306

307+
def extract_clause(source, clause)
308+
pattern = /\s+#{clause.to_s.upcase}\s+(.+)$/im
309+
match = source.match(pattern)
310+
311+
return [source, nil] unless match.present?
312+
313+
[
314+
source[0...match.begin(0)],
315+
match[1].strip,
316+
]
317+
end
318+
293319
def raw_execute(sql, settings: {}, except_params: [])
294320
statement = Statement.new(sql, format: @response_format)
295321
response = request(statement, settings: settings, except_params: except_params)

lib/active_record/connection_adapters/clickhouse/table_definition.rb

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ module ConnectionAdapters
55
module Clickhouse
66
class TableDefinition < ActiveRecord::ConnectionAdapters::TableDefinition
77

8-
attr_reader :view, :materialized, :if_not_exists, :to, :ttl
8+
attr_reader :view, :materialized, :if_not_exists, :to, :ttl, :settings
99

1010
def initialize(
1111
conn,
@@ -19,6 +19,7 @@ def initialize(
1919
materialized: false,
2020
to: nil,
2121
ttl: nil,
22+
settings: nil,
2223
**
2324
)
2425
@conn = conn
@@ -36,6 +37,7 @@ def initialize(
3637
@materialized = materialized
3738
@to = to
3839
@ttl = ttl
40+
@settings = settings
3941
end
4042

4143
def integer(*args, **options)
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
# frozen_string_literal: true
2+
3+
class CreateSomeTable < ActiveRecord::Migration[7.1]
4+
def up
5+
create_table :some, id: false, options: 'MergeTree ORDER BY an_id', ttl: 'date + INTERVAL 30 DAY', settings: 'allow_nullable_key = 1, index_granularity = 8192' do |t|
6+
t.uuid :an_id, null: true
7+
t.date :date, null: false
8+
t.integer :data, null: false
9+
end
10+
end
11+
end

spec/single/migration_spec.rb

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,7 @@
146146

147147
context 'ttl' do
148148
let(:directory) { 'dsl_table_with_ttl' }
149+
149150
it 'creates a table with table-level and column-level TTL' do
150151
subject
151152

@@ -162,6 +163,63 @@
162163
create_sql = ActiveRecord::Base.connection.show_create_table('some')
163164
expect(create_sql).to include('TTL date + toIntervalDay(30)')
164165
end
166+
167+
it 'creates a table with TTL using correct statement order' do
168+
subject
169+
170+
current_schema = schema(model)
171+
172+
# Verify TTL comes after ENGINE
173+
create_sql = ActiveRecord::Base.connection.show_create_table('some')
174+
engine_idx = create_sql.index('ENGINE = MergeTree')
175+
ttl_idx = create_sql.index('TTL date + toIntervalDay(30)')
176+
expect(engine_idx).to be < ttl_idx
177+
end
178+
end
179+
180+
context 'settings' do
181+
let(:directory) { 'dsl_table_with_settings' }
182+
183+
it 'creates a table with table-level SETTINGS' do
184+
subject
185+
186+
current_schema = schema(model)
187+
188+
expect(current_schema.keys.count).to eq(3)
189+
expect(current_schema).to have_key('an_id')
190+
expect(current_schema).to have_key('date')
191+
expect(current_schema).to have_key('data')
192+
193+
# Verify table-level SETTINGS in SHOW CREATE TABLE
194+
create_sql = ActiveRecord::Base.connection.show_create_table('some')
195+
expect(create_sql).to include('SETTINGS allow_nullable_key = 1, index_granularity = 8192')
196+
end
197+
198+
it 'creates a table with SETTINGS using correct statement order' do
199+
subject
200+
201+
current_schema = schema(model)
202+
203+
# Verify TTL comes before SETTINGS
204+
create_sql = ActiveRecord::Base.connection.show_create_table('some')
205+
ttl_idx = create_sql.index('TTL date')
206+
settings_idx = create_sql.index('SETTINGS')
207+
expect(ttl_idx).to be < settings_idx
208+
end
209+
210+
it 'dumps SETTINGS as separate option' do
211+
require 'clickhouse-activerecord/schema_dumper'
212+
213+
subject
214+
215+
schema = StringIO.new
216+
ClickhouseActiverecord::SchemaDumper.dump(ActiveRecord::Base.connection, schema)
217+
schema_string = schema.string
218+
219+
expect(schema_string).to include('options: "MergeTree ORDER BY an_id"')
220+
expect(schema_string).to include('ttl: "date + toIntervalDay(30)"')
221+
expect(schema_string).to include('settings: "allow_nullable_key = 1, index_granularity = 8192"')
222+
end
165223
end
166224

167225
context 'datetime' do

0 commit comments

Comments
 (0)