Skip to content

Commit 8aa29e2

Browse files
use input redirection for mysql structure load
1 parent 059a059 commit 8aa29e2

File tree

2 files changed

+90
-4
lines changed

2 files changed

+90
-4
lines changed

.github/workflows/ci.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@ jobs:
8383
mysql:
8484
- "5.7"
8585
- "8.0"
86+
- "9.4"
8687
steps:
8788
- uses: earthly/actions-setup@v1
8889
- uses: actions/checkout@v3

lib/ecto/adapters/myxql.ex

Lines changed: 89 additions & 4 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, path) do
429431
{_output, 0} -> {:ok, path}
430432
{output, _} -> {:error, output}
431433
end
@@ -497,6 +499,12 @@ defmodule Ecto.Adapters.MyXQL do
497499
"please guarantee it is available before running ecto commands"
498500
end
499501

502+
{args, cmd_opts} = args_cmd_opts(opts, opt_args, cmd_opts)
503+
504+
System.cmd(cmd, args, cmd_opts)
505+
end
506+
507+
defp args_cmd_opts(opts, opt_args, cmd_opts) do
500508
env =
501509
if password = opts[:password] do
502510
[{"MYSQL_PWD", password}]
@@ -530,6 +538,83 @@ defmodule Ecto.Adapters.MyXQL do
530538
|> Keyword.put_new(:stderr_to_stdout, true)
531539
|> Keyword.update(:env, env, &Enum.concat(env, &1))
532540

533-
System.cmd(cmd, args, cmd_opts)
541+
{args, cmd_opts}
542+
end
543+
544+
# Ported from Elixir System.cmd implementation with the
545+
# intent of using file redirection for passing dumps
546+
# into the mysql client so that users don't run into
547+
# shell limits when files are too large
548+
defp run_with_port(cmd, opts, opt_args, contents, cmd_opts \\ []) do
549+
unless System.find_executable(cmd) do
550+
raise "could not find executable `#{cmd}` in path, " <>
551+
"please guarantee it is available before running ecto commands"
552+
end
553+
554+
{args, cmd_opts} = args_cmd_opts(opts, opt_args, cmd_opts)
555+
cmd = String.to_charlist(cmd)
556+
557+
cmd =
558+
if Path.type(cmd) == :absolute do
559+
cmd
560+
else
561+
:os.find_executable(cmd) || :erlang.error(:enoent, [cmd, args, cmd_opts])
562+
end
563+
564+
port_opts = port_opts(cmd_opts, args: args)
565+
port = Port.open({:spawn_executable, cmd}, port_opts)
566+
Port.command(port, contents)
567+
Port.command(port, ";SELECT '__ECTO_EOF__';\n")
568+
569+
{initial, fun} = Collectable.into("")
570+
571+
try do
572+
do_port_byte(port, initial, fun)
573+
catch
574+
kind, reason ->
575+
fun.(initial, :halt)
576+
:erlang.raise(kind, reason, __STACKTRACE__)
577+
else
578+
{acc, status} -> {fun.(acc, :done), status}
579+
end
580+
end
581+
582+
defp port_opts([{:stderr_to_stdout, true} | t], acc),
583+
do: port_opts(t, [:stderr_to_stdout | acc])
584+
585+
defp port_opts([{:stderr_to_stdout, _} | t], acc),
586+
do: port_opts(t, acc)
587+
588+
defp port_opts([{:env, enum} | t], acc),
589+
do: port_opts(t, [{:env, validate_env(enum)} | acc])
590+
591+
defp port_opts([], acc) do
592+
[:use_stdio, :exit_status, :binary, :hide] ++ acc
593+
end
594+
595+
defp validate_env(enum) do
596+
Enum.map(enum, fn
597+
{k, nil} ->
598+
{String.to_charlist(k), false}
599+
600+
{k, v} ->
601+
{String.to_charlist(k), String.to_charlist(v)}
602+
603+
other ->
604+
raise ArgumentError, "invalid environment key-value #{inspect(other)}"
605+
end)
606+
end
607+
608+
defp do_port_byte(port, acc, fun) do
609+
receive do
610+
{^port, {:data, "__ECTO_EOF__" <> _rest}} ->
611+
{acc, 0}
612+
613+
{^port, {:data, data}} ->
614+
do_port_byte(port, fun.(acc, {:cont, data}), fun)
615+
616+
{^port, {:exit_status, status}} ->
617+
{acc, status}
618+
end
534619
end
535620
end

0 commit comments

Comments
 (0)