Skip to content

Commit 792a4ce

Browse files
authored
Expose Structure.dump_cmd/3 (#423)
1 parent 53ce4fa commit 792a4ce

File tree

6 files changed

+88
-16
lines changed

6 files changed

+88
-16
lines changed

integration_test/myxql/storage_test.exs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,4 +159,18 @@ defmodule Ecto.Integration.StorageTest do
159159
|> Enum.reject(&String.contains?(&1, "completed on"))
160160
|> Enum.join("\n")
161161
end
162+
163+
test "structure dump_cmd" do
164+
num = @base_migration + System.unique_integer([:positive])
165+
:ok = Ecto.Migrator.up(PoolRepo, num, Migration, log: false)
166+
167+
assert {output, 0} =
168+
Ecto.Adapters.MyXQL.dump_cmd(
169+
["--no-create-info", "--tables", "schema_migrations"],
170+
[],
171+
PoolRepo.config()
172+
)
173+
174+
assert output =~ "INSERT INTO `schema_migrations` VALUES ("
175+
end
162176
end

integration_test/pg/storage_test.exs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,4 +165,16 @@ defmodule Ecto.Integration.StorageTest do
165165
assert {:error, _} = Postgres.storage_status(wrong_params())
166166
end) =~ ~r"FATAL (28000|28P01)"
167167
end
168+
169+
test "structure dump_cmd" do
170+
num = @base_migration + System.unique_integer([:positive])
171+
:ok = Ecto.Migrator.up(PoolRepo, num, Migration, log: false)
172+
173+
assert {"--\n-- PostgreSQL database dump\n--\n\n--" <> _rest, 0} =
174+
Postgres.dump_cmd(
175+
["--data-only", "--table", "schema_migrations"],
176+
[],
177+
PoolRepo.config()
178+
)
179+
end
168180
end

lib/ecto/adapter/structure.ex

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,4 +38,25 @@ defmodule Ecto.Adapter.Structure do
3838
"""
3939
@callback structure_load(default :: String.t, config :: Keyword.t) ::
4040
{:ok, String.t} | {:error, term}
41+
42+
@doc """
43+
Runs the dump command for the given repo / config.
44+
45+
Calling this function will setup authentication and run the dump cli
46+
command with your provided `args`.
47+
48+
The options in `opts` are passed directly to `System.cmd/3`.
49+
50+
Returns `{output, exit_status}` where `output` is a string of the stdout
51+
(as long as no option `into` is provided, see `System.cmd/3`) and `exit_status`
52+
is the exit status of the invoation. (`0` for success)
53+
54+
## Examples
55+
56+
iex> dump_cmd(["--data-only", "--table", "table_name"], [stdout_to_stderr: true], Acme.Repo.config())
57+
"--\n-- PostgreSQL database dump\n--\n" <> _rest
58+
59+
"""
60+
@callback dump_cmd(args :: [String.t()], opts :: Keyword.t(), config :: Keyword.t()) ::
61+
{output :: Collectable.t(), exit_status :: non_neg_integer()}
4162
end

lib/ecto/adapters/myxql.ex

Lines changed: 21 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -344,17 +344,18 @@ defmodule Ecto.Adapters.MyXQL do
344344
def structure_load(default, config) do
345345
path = config[:dump_path] || Path.join(default, "structure.sql")
346346

347-
args = [
348-
"--execute", "SET FOREIGN_KEY_CHECKS = 0; SOURCE #{path}; SET FOREIGN_KEY_CHECKS = 1",
349-
"--database", config[:database]
350-
]
347+
args = ["--execute", "SET FOREIGN_KEY_CHECKS = 0; SOURCE #{path}; SET FOREIGN_KEY_CHECKS = 1"]
351348

352349
case run_with_cmd("mysql", config, args) do
353350
{_output, 0} -> {:ok, path}
354351
{output, _} -> {:error, output}
355352
end
356353
end
357354

355+
@impl true
356+
def dump_cmd(args, opts \\ [], config) when is_list(config) and is_list(args),
357+
do: run_with_cmd("mysqldump", config, args, opts)
358+
358359
## Helpers
359360

360361
defp run_query(sql, opts) do
@@ -395,7 +396,7 @@ defmodule Ecto.Adapters.MyXQL do
395396

396397
defp exit_to_exception(reason), do: RuntimeError.exception(Exception.format_exit(reason))
397398

398-
defp run_with_cmd(cmd, opts, opt_args) do
399+
defp run_with_cmd(cmd, opts, opt_args, cmd_opts \\ []) do
399400
unless System.find_executable(cmd) do
400401
raise "could not find executable `#{cmd}` in path, " <>
401402
"please guarantee it is available before running ecto commands"
@@ -419,13 +420,26 @@ defmodule Ecto.Adapters.MyXQL do
419420
[]
420421
end
421422

423+
database_args =
424+
if database = opts[:database] do
425+
["--database", database]
426+
else
427+
[]
428+
end
429+
422430
args =
423431
[
424432
"--host", host,
425433
"--port", to_string(port),
426434
"--protocol", protocol
427-
] ++ user_args ++ opt_args
435+
] ++ user_args ++ database_args ++ opt_args
436+
437+
cmd_opts =
438+
cmd_opts
439+
|> Keyword.put_new(:stderr_to_stdout, true)
440+
|> Keyword.update(:env, env, &Enum.concat(env, &1))
441+
428442

429-
System.cmd(cmd, args, env: env, stderr_to_stdout: true)
443+
System.cmd(cmd, args, cmd_opts)
430444
end
431445
end

lib/ecto/adapters/postgres.ex

Lines changed: 19 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -267,8 +267,7 @@ defmodule Ecto.Adapters.Postgres do
267267
path = config[:dump_path] || Path.join(default, "structure.sql")
268268
File.mkdir_p!(Path.dirname(path))
269269

270-
case run_with_cmd("pg_dump", config, ["--file", path, "--schema-only", "--no-acl",
271-
"--no-owner", config[:database]]) do
270+
case run_with_cmd("pg_dump", config, ["--file", path, "--schema-only", "--no-acl", "--no-owner"]) do
272271
{_output, 0} ->
273272
{:ok, path}
274273
{output, _} ->
@@ -293,14 +292,17 @@ defmodule Ecto.Adapters.Postgres do
293292
@impl true
294293
def structure_load(default, config) do
295294
path = config[:dump_path] || Path.join(default, "structure.sql")
296-
args = ["--quiet", "--file", path, "-vON_ERROR_STOP=1",
297-
"--single-transaction", config[:database]]
295+
args = ["--quiet", "--file", path, "-vON_ERROR_STOP=1", "--single-transaction"]
298296
case run_with_cmd("psql", config, args) do
299297
{_output, 0} -> {:ok, path}
300298
{output, _} -> {:error, output}
301299
end
302300
end
303301

302+
@impl true
303+
def dump_cmd(args, opts \\ [], config) when is_list(config) and is_list(args),
304+
do: run_with_cmd("pg_dump", config, args, opts)
305+
304306
## Helpers
305307

306308
defp run_query(sql, opts) do
@@ -338,7 +340,7 @@ defmodule Ecto.Adapters.Postgres do
338340
end
339341
end
340342

341-
defp run_with_cmd(cmd, opts, opt_args) do
343+
defp run_with_cmd(cmd, opts, opt_args, cmd_opts \\ []) do
342344
unless System.find_executable(cmd) do
343345
raise "could not find executable `#{cmd}` in path, " <>
344346
"please guarantee it is available before running ecto commands"
@@ -356,9 +358,11 @@ defmodule Ecto.Adapters.Postgres do
356358
args =
357359
[]
358360
args =
359-
if username = opts[:username], do: ["-U", username|args], else: args
361+
if username = opts[:username], do: ["--username", username | args], else: args
362+
args =
363+
if port = opts[:port], do: ["--port", to_string(port) | args], else: args
360364
args =
361-
if port = opts[:port], do: ["-p", to_string(port)|args], else: args
365+
if database = opts[:database], do: ["--dbname", database | args], else: args
362366

363367
host = opts[:socket_dir] || opts[:hostname] || System.get_env("PGHOST") || "localhost"
364368

@@ -369,8 +373,14 @@ defmodule Ecto.Adapters.Postgres do
369373
)
370374
end
371375

372-
args = ["--host", host|args]
376+
args = ["--host", host | args]
373377
args = args ++ opt_args
374-
System.cmd(cmd, args, env: env, stderr_to_stdout: true)
378+
379+
cmd_opts =
380+
cmd_opts
381+
|> Keyword.put_new(:stderr_to_stdout, true)
382+
|> Keyword.update(:env, env, &Enum.concat(env, &1))
383+
384+
System.cmd(cmd, args, cmd_opts)
375385
end
376386
end

test/mix/tasks/ecto.dump_load_test.exs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ defmodule Mix.Tasks.Ecto.DumpLoadTest do
2323

2424
def structure_dump(_, _), do: Process.get(:structure_dump) || raise "no structure_dump"
2525
def structure_load(_, _), do: Process.get(:structure_load) || raise "no structure_load"
26+
def dump_cmd(_, _, _), do: Process.get(:dump_cmd) || raise "no dump_cmd"
2627
end
2728

2829
defmodule NoStructureAdapter do

0 commit comments

Comments
 (0)