# frozen_string_literal: true require "hanami/utils/blank" module Hanami module Model class Migrator # PostgreSQL adapter # # @since 0.4.0 # @api private class PostgresAdapter < Adapter # @since 0.4.0 # @api private HOST = "PGHOST" # @since 0.4.0 # @api private PORT = "PGPORT" # @since 0.4.0 # @api private USER = "PGUSER" # @since 0.4.0 # @api private PASSWORD = "PGPASSWORD" # @since 1.0.0 # @api private DB_CREATION_ERROR = "createdb: database creation failed. If the database exists, " \ "then its console may be open. See this issue for more details: " \ "https://github.com/hanami/model/issues/250" # @since 0.4.0 # @api private def create call_db_command("createdb") end # @since 0.4.0 # @api private def drop call_db_command("dropdb") end # @since 0.4.0 # @api private def dump dump_structure dump_migrations_data end # @since 0.4.0 # @api private def load load_structure end private # @since 1.3.3 # @api private def environment_variables {}.tap do |env| env[HOST] = host unless host.nil? env[PORT] = port.to_s unless port.nil? env[PASSWORD] = password unless password.nil? env[USER] = username unless username.nil? end end # @since 0.4.0 # @api private def dump_structure execute "pg_dump -s -x -O -T #{migrations_table} -f #{escape(schema)} #{database}", env: environment_variables end # @since 0.4.0 # @api private def load_structure return unless schema.exist? execute "psql -X -q -f #{escape(schema)} #{database}", env: environment_variables end # @since 0.4.0 # @api private def dump_migrations_data error = ->(err) { raise MigrationError.new(err) unless err =~ /no matching tables/i } execute "pg_dump -t #{migrations_table} #{database} >> #{escape(schema)}", error: error, env: environment_variables end # @since 0.5.1 # @api private def call_db_command(command) require "open3" begin Open3.popen3(environment_variables, command, database) do |_stdin, _stdout, stderr, wait_thr| raise MigrationError.new(modified_message(stderr.read)) unless wait_thr.value.success? # wait_thr.value is the exit status end rescue SystemCallError => exception raise MigrationError.new(modified_message(exception.message)) end end # @since 1.1.0 # @api private def modified_message(original_message) case original_message when /already exists/ DB_CREATION_ERROR when /does not exist/ "Cannot find database: #{database}" when /No such file or directory/ "Could not find executable in your PATH: `#{original_message.split.last}`" else original_message end end end end end end