116 lines
		
	
	
		
			3.6 KiB
		
	
	
	
		
			Elixir
		
	
	
	
	
	
			
		
		
	
	
			116 lines
		
	
	
		
			3.6 KiB
		
	
	
	
		
			Elixir
		
	
	
	
	
	
defmodule Mix.Tasks.Ecto.Dump do
 | 
						|
  use Mix.Task
 | 
						|
  import Mix.Ecto
 | 
						|
  import Mix.EctoSQL
 | 
						|
 | 
						|
  @shortdoc "Dumps the repository database structure"
 | 
						|
  @default_opts [quiet: false]
 | 
						|
 | 
						|
  @aliases [
 | 
						|
    d: :dump_path,
 | 
						|
    q: :quiet,
 | 
						|
    r: :repo
 | 
						|
  ]
 | 
						|
 | 
						|
  @switches [
 | 
						|
    dump_path: :string,
 | 
						|
    quiet: :boolean,
 | 
						|
    repo: [:string, :keep],
 | 
						|
    no_compile: :boolean,
 | 
						|
    no_deps_check: :boolean,
 | 
						|
    prefix: [:string, :keep]
 | 
						|
  ]
 | 
						|
 | 
						|
  @moduledoc """
 | 
						|
  Dumps the current environment's database structure for the
 | 
						|
  given repository into a structure file.
 | 
						|
 | 
						|
  The repository must be set under `:ecto_repos` in the
 | 
						|
  current app configuration or given via the `-r` option.
 | 
						|
 | 
						|
  This task needs some shell utility to be present on the machine
 | 
						|
  running the task.
 | 
						|
 | 
						|
   Database   | Utility needed
 | 
						|
   :--------- | :-------------
 | 
						|
   PostgreSQL | pg_dump
 | 
						|
   MySQL      | mysqldump
 | 
						|
 | 
						|
  ## Example
 | 
						|
 | 
						|
      $ mix ecto.dump
 | 
						|
 | 
						|
  ## Command line options
 | 
						|
 | 
						|
    * `-r`, `--repo` - the repo to load the structure info from
 | 
						|
    * `-d`, `--dump-path` - the path of the dump file to create
 | 
						|
    * `-q`, `--quiet` - run the command quietly
 | 
						|
    * `--no-compile` - does not compile applications before dumping
 | 
						|
    * `--no-deps-check` - does not check dependencies before dumping
 | 
						|
    * `--prefix` - prefix that will be included in the structure dump.
 | 
						|
      Can include multiple prefixes (ex. `--prefix foo --prefix bar`) with
 | 
						|
      PostgreSQL but not MySQL. When specified, the prefixes will have
 | 
						|
      their definitions dumped along with the data in their migration table.
 | 
						|
      The default behavior is dependent on the adapter for backwards compatibility
 | 
						|
      reasons. For PostgreSQL, the configured database has the definitions dumped
 | 
						|
      from all of its schemas but only the data from the migration table
 | 
						|
      from the `public` schema is included. For MySQL, only the configured
 | 
						|
      database and its migration table are dumped.
 | 
						|
  """
 | 
						|
 | 
						|
  @impl true
 | 
						|
  def run(args) do
 | 
						|
    {opts, _} = OptionParser.parse!(args, strict: @switches, aliases: @aliases)
 | 
						|
 | 
						|
    dump_prefixes =
 | 
						|
      case Keyword.get_values(opts, :prefix) do
 | 
						|
        [_ | _] = prefixes -> prefixes
 | 
						|
        [] -> nil
 | 
						|
      end
 | 
						|
 | 
						|
    opts =
 | 
						|
      @default_opts
 | 
						|
      |> Keyword.merge(opts)
 | 
						|
      |> Keyword.put(:dump_prefixes, dump_prefixes)
 | 
						|
 | 
						|
    Enum.each(parse_repo(args), fn repo ->
 | 
						|
      ensure_repo(repo, args)
 | 
						|
 | 
						|
      ensure_implements(
 | 
						|
        repo.__adapter__(),
 | 
						|
        Ecto.Adapter.Structure,
 | 
						|
        "dump structure for #{inspect(repo)}"
 | 
						|
      )
 | 
						|
 | 
						|
      migration_repo = repo.config()[:migration_repo] || repo
 | 
						|
 | 
						|
      for repo <- Enum.uniq([repo, migration_repo]) do
 | 
						|
        config = Keyword.merge(repo.config(), opts)
 | 
						|
        start_time = System.system_time()
 | 
						|
 | 
						|
        case repo.__adapter__().structure_dump(source_repo_priv(repo), config) do
 | 
						|
          {:ok, location} ->
 | 
						|
            unless opts[:quiet] do
 | 
						|
              elapsed =
 | 
						|
                System.convert_time_unit(System.system_time() - start_time, :native, :microsecond)
 | 
						|
 | 
						|
              Mix.shell().info(
 | 
						|
                "The structure for #{inspect(repo)} has been dumped to #{location} in #{format_time(elapsed)}"
 | 
						|
              )
 | 
						|
            end
 | 
						|
 | 
						|
          {:error, term} when is_binary(term) ->
 | 
						|
            Mix.raise("The structure for #{inspect(repo)} couldn't be dumped: #{term}")
 | 
						|
 | 
						|
          {:error, term} ->
 | 
						|
            Mix.raise("The structure for #{inspect(repo)} couldn't be dumped: #{inspect(term)}")
 | 
						|
        end
 | 
						|
      end
 | 
						|
    end)
 | 
						|
  end
 | 
						|
 | 
						|
  defp format_time(microsec) when microsec < 1_000, do: "#{microsec} μs"
 | 
						|
  defp format_time(microsec) when microsec < 1_000_000, do: "#{div(microsec, 1_000)} ms"
 | 
						|
  defp format_time(microsec), do: "#{Float.round(microsec / 1_000_000.0)} s"
 | 
						|
end
 |