diff --git a/README.md b/README.md index 5f34376e..52663ebd 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,7 @@ # Clickhouse::Activerecord A Ruby database ActiveRecord driver for ClickHouse. Support Rails >= 5.2. +Tested on ClickHouse version 18.14. ## Installation @@ -17,13 +18,10 @@ And then execute: Or install it yourself as: $ gem install clickhouse-activerecord - -## Usage - -Add your `database.yml` connection information with postfix `_clickhouse` for you environment: - + +## Available database connection parameters ```yml -development_clickhouse: +default: &default adapter: clickhouse database: database host: localhost @@ -32,6 +30,17 @@ development_clickhouse: password: password ssl: true # optional for using ssl connection debug: true # use for showing in to log technical information + migrations_paths: db/clickhouse # optional, default: db/migrate_clickhouse +``` + +## Usage in Rails 5 + +Add your `database.yml` connection information with postfix `_clickhouse` for you environment: + +```yml +development_clickhouse: + adapter: clickhouse + database: database ``` Add to your model: @@ -56,13 +65,34 @@ Or global connection: development: adapter: clickhouse database: database - host: localhost - username: username - password: password +``` + +## Usage in Rails 6 with second database + +Add your `database.yml` connection information for you environment: + +```yml +development: + primary: + ... + + clickhouse: + adapter: clickhouse + database: database +``` + +Connection [Multiple Databases with Active Record](https://guides.rubyonrails.org/active_record_multiple_databases.html) or short example: + +```ruby +class Action < ActiveRecord::Base + connects_to database: { writing: :clickhouse, reading: :clickhouse } +end ``` ### Rake tasks +**Note!** For Rails 6 you can use default rake tasks if you configure `migrations_paths` in your `database.yml`, for example: `rake db:migrate` + Create / drop / purge / reset database: $ rake clickhouse:create diff --git a/lib/active_record/connection_adapters/clickhouse/schema_statements.rb b/lib/active_record/connection_adapters/clickhouse/schema_statements.rb index a7a3e450..ca63f19f 100644 --- a/lib/active_record/connection_adapters/clickhouse/schema_statements.rb +++ b/lib/active_record/connection_adapters/clickhouse/schema_statements.rb @@ -38,37 +38,6 @@ def table_options(table) { options: sql.gsub(/^(?:.*?)ENGINE = (.*?)$/, '\\1') } end - # @todo copied from ActiveRecord::ConnectionAdapters::SchemaStatements v5.2.2 - # Why version column type of String, but insert to Integer? - def assume_migrated_upto_version(version, migrations_paths) - migrations_paths = Array(migrations_paths) - version = version.to_i - sm_table = quote_table_name(ActiveRecord::SchemaMigration.table_name) - - migrated = ActiveRecord::SchemaMigration.all_versions.map(&:to_i) - versions = migration_context.migration_files.map do |file| - migration_context.parse_migration_filename(file).first.to_i - end - - unless migrated.include?(version) - do_execute( "INSERT INTO #{sm_table} (version) VALUES (#{quote(version.to_s)})", 'SchemaMigration', format: nil) - end - - inserting = (versions - migrated).select { |v| v < version } - if inserting.any? - if (duplicate = inserting.detect { |v| inserting.count(v) > 1 }) - raise "Duplicate migration #{duplicate}. Please renumber your migrations to resolve the conflict." - end - if supports_multi_insert? - do_system_execute insert_versions_sql(inserting.map(&:to_s)) - else - inserting.each do |v| - do_system_execute insert_versions_sql(v) - end - end - end - end - # Not indexes on clickhouse def indexes(table_name, name = nil) [] @@ -122,7 +91,11 @@ def schema_creation end def create_table_definition(*args) - Clickhouse::TableDefinition.new(*args) + if ActiveRecord::version >= Gem::Version.new('6') + Clickhouse::TableDefinition.new(self, *args) + else + Clickhouse::TableDefinition.new(*args) + end end def new_column_from_field(table_name, field) diff --git a/lib/active_record/connection_adapters/clickhouse_adapter.rb b/lib/active_record/connection_adapters/clickhouse_adapter.rb index b7f31d4b..8dfb3253 100644 --- a/lib/active_record/connection_adapters/clickhouse_adapter.rb +++ b/lib/active_record/connection_adapters/clickhouse_adapter.rb @@ -26,7 +26,7 @@ def clickhouse_connection(config) raise ArgumentError, 'No database specified. Missing argument: database.' end - ConnectionAdapters::ClickhouseAdapter.new(logger, [host, port, ssl], { user: config[:username], password: config[:password], database: database }.compact, config[:debug]) + ConnectionAdapters::ClickhouseAdapter.new(logger, [host, port, ssl], { user: config[:username], password: config[:password], database: database }.compact, config) end end end @@ -93,21 +93,34 @@ class ClickhouseAdapter < AbstractAdapter include Clickhouse::SchemaStatements # Initializes and connects a Clickhouse adapter. - def initialize(logger, connection_parameters, config, debug = false) + def initialize(logger, connection_parameters, config, full_config) super(nil, logger) @connection_parameters = connection_parameters @config = config - @debug = debug + @debug = full_config[:debug] || false + @full_config = full_config - if ActiveRecord::version >= Gem::Version.new('6') + @prepared_statements = false + if ActiveRecord::version == Gem::Version.new('6.0.0') @prepared_statement_status = Concurrent::ThreadLocalVar.new(false) - else - @prepared_statements = false end connect end + # Support SchemaMigration from v5.2.2 to v6+ + def schema_migration # :nodoc: + if ActiveRecord::version >= Gem::Version.new('6') + super + else + ActiveRecord::SchemaMigration + end + end + + def migrations_paths + @full_config[:migrations_paths] || 'db/migrate_clickhouse' + end + def arel_visitor # :nodoc: ClickhouseActiverecord::Arel::Visitors::ToSql.new(self) end diff --git a/lib/clickhouse-activerecord/version.rb b/lib/clickhouse-activerecord/version.rb index dd7f9e9e..bdfa7999 100644 --- a/lib/clickhouse-activerecord/version.rb +++ b/lib/clickhouse-activerecord/version.rb @@ -1,3 +1,3 @@ module ClickhouseActiverecord - VERSION = '0.3.8' + VERSION = '0.3.9' end diff --git a/lib/generators/clickhouse_migration_generator.rb b/lib/generators/clickhouse_migration_generator.rb index 0b1c6147..61e6efc7 100644 --- a/lib/generators/clickhouse_migration_generator.rb +++ b/lib/generators/clickhouse_migration_generator.rb @@ -6,6 +6,20 @@ class ClickhouseMigrationGenerator < ActiveRecord::Generators::MigrationGenerato def create_migration_file set_local_assigns! validate_file_name! - migration_template @migration_template, "db/migrate_clickhouse/#{file_name}.rb" + migration_template @migration_template, File.join(db_migrate_path, "#{file_name}.rb") + end + + private + + def db_migrate_path + if defined?(Rails.application) && Rails.application + configured_migrate_path || default_migrate_path + else + default_migrate_path + end + end + + def default_migrate_path + "db/migrate_clickhouse" end end diff --git a/spec/cases/migration_spec.rb b/spec/cases/migration_spec.rb index c21fd8e1..ed087a29 100644 --- a/spec/cases/migration_spec.rb +++ b/spec/cases/migration_spec.rb @@ -12,7 +12,7 @@ context 'plain' do it 'creates a table' do migrations_dir = File.join(FIXTURES_PATH, 'migrations', 'plain_table_creation') - quietly { ActiveRecord::MigrationContext.new(migrations_dir).up } + quietly { ActiveRecord::MigrationContext.new(migrations_dir, model.connection.schema_migration).up } current_schema = schema(model) @@ -28,7 +28,7 @@ context 'empty' do it 'creates a table' do migrations_dir = File.join(FIXTURES_PATH, 'migrations', 'dsl_table_creation') - quietly { ActiveRecord::MigrationContext.new(migrations_dir).up } + quietly { ActiveRecord::MigrationContext.new(migrations_dir, model.connection.schema_migration).up } current_schema = schema(model) @@ -41,7 +41,7 @@ context 'with engine' do it 'creates a table' do migrations_dir = File.join(FIXTURES_PATH, 'migrations', 'dsl_table_with_engine_creation') - quietly { ActiveRecord::MigrationContext.new(migrations_dir).up } + quietly { ActiveRecord::MigrationContext.new(migrations_dir, model.connection.schema_migration).up } current_schema = schema(model) @@ -57,7 +57,7 @@ context 'decimal' do it 'creates a table with valid scale and precision' do migrations_dir = File.join(FIXTURES_PATH, 'migrations', 'dsl_table_with_decimal_creation') - quietly { ActiveRecord::MigrationContext.new(migrations_dir).up } + quietly { ActiveRecord::MigrationContext.new(migrations_dir, model.connection.schema_migration).up } current_schema = schema(model) @@ -77,11 +77,11 @@ describe 'drop table' do it 'drops table' do migrations_dir = File.join(FIXTURES_PATH, 'migrations', 'dsl_drop_table') - quietly { ActiveRecord::MigrationContext.new(migrations_dir).up(1) } + quietly { ActiveRecord::MigrationContext.new(migrations_dir, model.connection.schema_migration).up(1) } expect(ActiveRecord::Base.connection.tables).to include('some') - quietly { ActiveRecord::MigrationContext.new(migrations_dir).up(2) } + quietly { ActiveRecord::MigrationContext.new(migrations_dir, model.connection.schema_migration).up(2) } expect(ActiveRecord::Base.connection.tables).not_to include('some') end @@ -90,7 +90,7 @@ describe 'add column' do it 'adds a new column' do migrations_dir = File.join(FIXTURES_PATH, 'migrations', 'dsl_add_column') - quietly { ActiveRecord::MigrationContext.new(migrations_dir).up } + quietly { ActiveRecord::MigrationContext.new(migrations_dir, model.connection.schema_migration).up } current_schema = schema(model) @@ -107,7 +107,7 @@ describe 'drop column' do it 'drops column' do migrations_dir = File.join(FIXTURES_PATH, 'migrations', 'dsl_drop_column') - quietly { ActiveRecord::MigrationContext.new(migrations_dir).up } + quietly { ActiveRecord::MigrationContext.new(migrations_dir, model.connection.schema_migration).up } current_schema = schema(model) diff --git a/spec/cases/model_spec.rb b/spec/cases/model_spec.rb index 81484487..7f6f62c9 100644 --- a/spec/cases/model_spec.rb +++ b/spec/cases/model_spec.rb @@ -9,7 +9,7 @@ before do migrations_dir = File.join(FIXTURES_PATH, 'migrations', 'add_sample_data') - quietly { ActiveRecord::MigrationContext.new(migrations_dir).up } + quietly { ActiveRecord::MigrationContext.new(migrations_dir, model.connection.schema_migration).up } end let(:date) { Date.today }