PNixx / clickhouse-activerecord

A Ruby database ActiveRecord driver for ClickHouse
MIT License
198 stars 100 forks source link

Adapter should raise more specific exceptions, esp in Rake tasks #91

Closed bjeanes closed 1 year ago

bjeanes commented 1 year ago

Currently, when a database does not exist and a query is executed, a generic ActiveRecord::ActiveRecordError is raised.

Other adapters use ActiveRecord::NoDatabaseError and the db:prepare Rake task depends on this fact to do the right thing (and has done since at least Rails 6, though I suspect earlier).

Here is what is raised with postgres adapter on main DB, when trying to migrate or query against a non-existent DB:

#<ActiveRecord::NoDatabaseError:"connection to server at \"172.31.0.5\", port 5432 failed: FATAL:  database \"db\" does not exist\n">

And clickhouse with current version of this adapter:

#<ActiveRecord::ActiveRecordError:"Response code: 404:\nCode: 81. DB::Exception: Database analytics_db doesn't exist. (UNKNOWN_DATABASE) (version 22.3.7.28 (official build))\n">

Similarly, ActiveRecord::DatabaseAlreadyExists should be raised when the database already exists and it Rails is attempting to create it. This exception is caught to make the Rake task seem idempotent.

bjeanes commented 1 year ago

My workaround in the meantime:

# lib/tasks/db.rake

namespace :db do
  # Clickhouse adapter does not raise the right exception when there is no DB, which breaks 
  # `db:prepare` task. I don't want to monkeypatch broadly in our app on this driver, so 
  # this task is a workaround that only applies in this db setup context.
  #
  # https://github.com/PNixx/clickhouse-activerecord/issues/91
  task :fix_clickhouse do
    module ClickhouseAdapterNoDbFix
      def process_response(res)
        super(res)
      rescue ActiveRecord::ActiveRecordError
        case res.body
        when /DB::Exception:.*\(UNKNOWN_DATABASE\)/
          raise ActiveRecord::NoDatabaseError
        when /DB::Exception:.*\(DATABASE_ALREADY_EXISTS\)/
          raise ActiveRecord::DatabaseAlreadyExists
        else
          raise
        end
      end
    end

    ActiveRecord::ConnectionAdapters::Clickhouse::SchemaStatements.prepend(ClickhouseAdapterNoDbFix)
  end

end

Rake::Task['db:load_config'].enhance ['db:fix_clickhouse']