lib/apartment/adapters/postgresql_adapter.rb
module Apartment
module Database
def self.postgresql_adapter(config)
Apartment.use_postgres_schemas ?
Adapters::PostgresqlSchemaAdapter.new(config) :
Adapters::PostgresqlAdapter.new(config)
end
end
module Adapters
# Default adapter when not using Postgresql Schemas
class PostgresqlAdapter < AbstractAdapter
protected
# Connect to new database
# Abstract adapter will catch generic ActiveRecord error
# Catch specific adapter errors here
#
# @param {String} database Database name
#
def connect_to_new(database)
super
rescue PGError
raise DatabaseNotFound, "Cannot find database #{environmentify(database)}"
end
end
# Separate Adapter for Postgresql when using schemas
class PostgresqlSchemaAdapter < AbstractAdapter
attr_reader :current_database
# Drop the database schema
#
# @param {String} database Database (schema) to drop
#
def drop(database)
ActiveRecord::Base.connection.execute(%{DROP SCHEMA "#{database}" CASCADE})
rescue ActiveRecord::StatementInvalid
raise SchemaNotFound, "The schema #{database.inspect} cannot be found."
end
# Reset search path to default search_path
# Set the table_name to always use the default namespace for excluded models
#
def process_excluded_models
Apartment.excluded_models.each do |excluded_model|
# Note that due to rails reloading, we now take string references to classes rather than
# actual object references. This way when we contantize, we always get the proper class reference
if excluded_model.is_a? Class
warn "[Deprecation Warning] Passing class references to excluded models is now deprecated, please use a string instead"
excluded_model = excluded_model.name
end
excluded_model.constantize.tap do |klass|
# some models (such as delayed_job) seem to load and cache their column names before this,
# so would never get the default prefix, so reset first
klass.reset_column_information
# Ensure that if a schema *was* set, we override
table_name = klass.table_name.split('.', 2).last
# Not sure why, but Delayed::Job somehow ignores table_name_prefix... so we'll just manually set table name instead
klass.table_name = "#{Apartment.default_schema}.#{table_name}"
end
end
end
# Reset schema search path to the default schema_search_path
#
# @return {String} default schema search path
#
def reset
@current_database = Apartment.default_schema
ActiveRecord::Base.connection.schema_search_path = full_search_path
end
protected
# Set schema search path to new schema
#
def connect_to_new(database = nil)
return reset if database.nil?
@current_database = database.to_s
ActiveRecord::Base.connection.schema_search_path = full_search_path
rescue ActiveRecord::StatementInvalid
raise SchemaNotFound, "The schema #{database.inspect} cannot be found."
end
# Create the new schema
#
def create_database(database)
ActiveRecord::Base.connection.execute(%{CREATE SCHEMA "#{database}"})
rescue ActiveRecord::StatementInvalid
raise SchemaExists, "The schema #{database} already exists."
end
private
# Generate the final search path to set including persistent_schemas
#
def full_search_path
persistent_schemas = Apartment.persistent_schemas.join(', ')
@current_database.to_s + (persistent_schemas.empty? ? "" : ", #{persistent_schemas}")
end
end
end
end