Skip to content

Commit b75665a

Browse files
Use input redirection for mysql structure_load (#706)
1 parent 059a059 commit b75665a

File tree

1 file changed

+75
-8
lines changed

1 file changed

+75
-8
lines changed

lib/ecto/adapters/myxql.ex

Lines changed: 75 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -419,13 +419,15 @@ defmodule Ecto.Adapters.MyXQL do
419419
case File.read(path) do
420420
{:ok, contents} ->
421421
args = [
422-
"--execute",
423-
"SET FOREIGN_KEY_CHECKS = 0; " <> contents <> "; SET FOREIGN_KEY_CHECKS = 1",
422+
"--silent",
423+
"--batch",
424+
"--unbuffered",
425+
"--init-command=SET FOREIGN_KEY_CHECKS = 0;",
424426
"--database",
425427
config[:database]
426428
]
427429

428-
case run_with_cmd("mysql", config, args) do
430+
case run_with_port("mysql", config, args, contents) do
429431
{_output, 0} -> {:ok, path}
430432
{output, _} -> {:error, output}
431433
end
@@ -497,6 +499,46 @@ defmodule Ecto.Adapters.MyXQL do
497499
"please guarantee it is available before running ecto commands"
498500
end
499501

502+
{args, env} = args_env(opts, opt_args)
503+
504+
cmd_opts =
505+
cmd_opts
506+
|> Keyword.put_new(:stderr_to_stdout, true)
507+
|> Keyword.update(:env, env, &Enum.concat(env, &1))
508+
509+
System.cmd(cmd, args, cmd_opts)
510+
end
511+
512+
defp run_with_port(cmd, opts, opt_args, contents) do
513+
abs_cmd = System.find_executable(cmd)
514+
515+
unless abs_cmd do
516+
raise "could not find executable `#{cmd}` in path, " <>
517+
"please guarantee it is available before running ecto commands"
518+
end
519+
520+
abs_cmd = String.to_charlist(abs_cmd)
521+
{args, env} = args_env(opts, opt_args)
522+
523+
port_opts = [
524+
:use_stdio,
525+
:exit_status,
526+
:binary,
527+
:hide,
528+
:stderr_to_stdout,
529+
env: validate_env(env),
530+
args: args
531+
]
532+
533+
port = Port.open({:spawn_executable, abs_cmd}, port_opts)
534+
Port.command(port, contents)
535+
# Use this as a signal to close the port since we cannot
536+
# send an exit command to mysql in batch mode
537+
Port.command(port, ";SELECT '__ECTO_EOF__';\n")
538+
collect_output(port, "")
539+
end
540+
541+
defp args_env(opts, opt_args) do
500542
env =
501543
if password = opts[:password] do
502544
[{"MYSQL_PWD", password}]
@@ -525,11 +567,36 @@ defmodule Ecto.Adapters.MyXQL do
525567
protocol
526568
] ++ user_args ++ opt_args
527569

528-
cmd_opts =
529-
cmd_opts
530-
|> Keyword.put_new(:stderr_to_stdout, true)
531-
|> Keyword.update(:env, env, &Enum.concat(env, &1))
570+
{args, env}
571+
end
532572

533-
System.cmd(cmd, args, cmd_opts)
573+
defp validate_env(enum) do
574+
Enum.map(enum, fn
575+
{k, nil} ->
576+
{String.to_charlist(k), false}
577+
578+
{k, v} ->
579+
{String.to_charlist(k), String.to_charlist(v)}
580+
581+
other ->
582+
raise ArgumentError, "invalid environment key-value #{inspect(other)}"
583+
end)
584+
end
585+
586+
defp collect_output(port, acc) do
587+
receive do
588+
{^port, {:data, data}} ->
589+
acc = acc <> data
590+
591+
if acc =~ "__ECTO_EOF__" do
592+
Port.close(port)
593+
{acc, 0}
594+
else
595+
collect_output(port, acc)
596+
end
597+
598+
{^port, {:exit_status, status}} ->
599+
{acc, status}
600+
end
534601
end
535602
end

0 commit comments

Comments
 (0)