class Apartment::Adapters::PostgresqlSchemaFromSqlAdapter

Another Adapter for Postgresql when using schemas and SQL

def check_input_against_regexps(input, regexps)


Checks if any of regexps matches against input
def check_input_against_regexps(input, regexps)
  regexps.select { |c| input.match c }
end

def clone_pg_schema


Clone default schema into new schema named after current tenant
def clone_pg_schema
  pg_schema_sql = patch_search_path(pg_dump_schema)
  Apartment.connection.execute(pg_schema_sql)
end

def collect_table_names(models)


Collect table names from AR Models
def collect_table_names(models)
  models.map do |m|
    m.constantize.table_name
  end
end

def copy_schema_migrations


Copy data from schema_migrations into new schema
def copy_schema_migrations
  pg_migrations_data = patch_search_path(pg_dump_schema_migrations_data)
  Apartment.connection.execute(pg_migrations_data)
end

def dbname


Convenience method for current database name
def dbname
  Apartment.connection_config[:database]
end

def import_database_schema

def import_database_schema
  preserving_search_path do
    clone_pg_schema
    copy_schema_migrations
  end
end

def patch_search_path(sql)

Returns:
  • (String) - patched raw SQL dump
def patch_search_path(sql)
  search_path = "SET search_path = \"#{current}\", #{default_tenant};"
  swap_schema_qualifier(sql)
    .split("\n")
    .select { |line| check_input_against_regexps(line, PSQL_DUMP_BLACKLISTED_STATEMENTS).empty? }
    .prepend(search_path)
    .join("\n")
end

def pg_dump_schema

Returns:
  • (String) - raw SQL contaning only postgres schema dump
def pg_dump_schema
  # Skip excluded tables? :/
  # excluded_tables =
  #   collect_table_names(Apartment.excluded_models)
  #   .map! {|t| "-T #{t}"}
  #   .join(' ')
  # `pg_dump -s -x -O -n #{default_tenant} #{excluded_tables} #{dbname}`
  with_pg_env { `pg_dump -s -x -O -n #{default_tenant} #{dbname}` }
end

def pg_dump_schema_migrations_data

Returns:
  • (String) - raw SQL contaning inserts with data from schema_migrations
def pg_dump_schema_migrations_data
  with_pg_env { `pg_dump -a --inserts -t #{default_tenant}.schema_migrations -t #{default_tenant}.ar_internal_metadata #{dbname}` }
end

def preserving_search_path


and it mut be reset
Postgres now sets search path to empty before dumping the schema
Re-set search path after the schema is imported.
def preserving_search_path
  search_path = Apartment.connection.execute('show search_path').first['search_path']
  yield
  Apartment.connection.execute("set search_path = #{search_path}")
end

def swap_schema_qualifier(sql)

def swap_schema_qualifier(sql)
  sql.gsub(/#{default_tenant}\.\w*/) do |match|
    if Apartment.pg_excluded_names.any? { |name| match.include? name }
      match
    else
      match.gsub("#{default_tenant}.", %("#{current}".))
    end
  end
end

def with_pg_env


Temporary set Postgresql related environment variables if there are in @config
def with_pg_env
  pghost = ENV['PGHOST']
  pgport = ENV['PGPORT']
  pguser = ENV['PGUSER']
  pgpassword = ENV['PGPASSWORD']
  ENV['PGHOST'] = @config[:host] if @config[:host]
  ENV['PGPORT'] = @config[:port].to_s if @config[:port]
  ENV['PGUSER'] = @config[:username].to_s if @config[:username]
  ENV['PGPASSWORD'] = @config[:password].to_s if @config[:password]
  yield
ensure
  ENV['PGHOST'] = pghost
  ENV['PGPORT'] = pgport
  ENV['PGUSER'] = pguser
  ENV['PGPASSWORD'] = pgpassword
end