From d93fbd4a0c1236bc4846858a45fde3ebbdfa66a6 Mon Sep 17 00:00:00 2001 From: Greg Rychlewski Date: Sun, 29 Mar 2026 01:02:58 -0400 Subject: [PATCH 1/7] handle process dying gracefully --- integration_test/myxql/storage_test.exs | 11 +++++++++++ lib/ecto/adapters/myxql.ex | 6 +++++- 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/integration_test/myxql/storage_test.exs b/integration_test/myxql/storage_test.exs index 8df18235..bb813ddc 100644 --- a/integration_test/myxql/storage_test.exs +++ b/integration_test/myxql/storage_test.exs @@ -121,6 +121,17 @@ defmodule Ecto.Integration.StorageTest do drop_database() end + test "structure load will fail on SQL errors" do + File.mkdir_p!(tmp_path()) + error_path = Path.join(tmp_path(), "error.sql") + File.write!(error_path, "SELECT 1; SELECKT 1; SELECT 2;") + + {:error, message} = + Ecto.Adapters.MyXQL.structure_load(tmp_path(), [dump_path: error_path] ++ params()) + + assert message =~ ~r/ERROR.*SELECKT/ + end + test "storage status is up when database is created" do create_database() assert :up == Ecto.Adapters.MyXQL.storage_status(params()) diff --git a/lib/ecto/adapters/myxql.ex b/lib/ecto/adapters/myxql.ex index c6337f1b..c1cbab18 100644 --- a/lib/ecto/adapters/myxql.ex +++ b/lib/ecto/adapters/myxql.ex @@ -587,7 +587,8 @@ defmodule Ecto.Adapters.MyXQL do env: validate_env(env), args: args ] - + # Trap exits in case mysql dies in the middle of execution so that we can surface the error + Process.flag(:trap_exit, true) port = Port.open({:spawn_executable, abs_cmd}, port_opts) Port.command(port, contents) # Use this as a signal to close the port since we cannot @@ -653,6 +654,9 @@ defmodule Ecto.Adapters.MyXQL do collect_output(port, acc) end + {:EXIT, ^port, _reason} -> + {acc, 1} + {^port, {:exit_status, status}} -> {acc, status} end From c14f47cf283038b222d14fa4a59bf71bd0b2b104 Mon Sep 17 00:00:00 2001 From: Greg Rychlewski Date: Sun, 29 Mar 2026 01:07:32 -0400 Subject: [PATCH 2/7] fix test --- integration_test/myxql/storage_test.exs | 3 +++ lib/ecto/adapters/myxql.ex | 1 + 2 files changed, 4 insertions(+) diff --git a/integration_test/myxql/storage_test.exs b/integration_test/myxql/storage_test.exs index bb813ddc..8ba35487 100644 --- a/integration_test/myxql/storage_test.exs +++ b/integration_test/myxql/storage_test.exs @@ -122,6 +122,7 @@ defmodule Ecto.Integration.StorageTest do end test "structure load will fail on SQL errors" do + create_database() File.mkdir_p!(tmp_path()) error_path = Path.join(tmp_path(), "error.sql") File.write!(error_path, "SELECT 1; SELECKT 1; SELECT 2;") @@ -130,6 +131,8 @@ defmodule Ecto.Integration.StorageTest do Ecto.Adapters.MyXQL.structure_load(tmp_path(), [dump_path: error_path] ++ params()) assert message =~ ~r/ERROR.*SELECKT/ + after + drop_database() end test "storage status is up when database is created" do diff --git a/lib/ecto/adapters/myxql.ex b/lib/ecto/adapters/myxql.ex index c1cbab18..d62e37cf 100644 --- a/lib/ecto/adapters/myxql.ex +++ b/lib/ecto/adapters/myxql.ex @@ -587,6 +587,7 @@ defmodule Ecto.Adapters.MyXQL do env: validate_env(env), args: args ] + # Trap exits in case mysql dies in the middle of execution so that we can surface the error Process.flag(:trap_exit, true) port = Port.open({:spawn_executable, abs_cmd}, port_opts) From 21debd9ec9999efdd5a5d6869d034731a1d615ce Mon Sep 17 00:00:00 2001 From: Greg Rychlewski Date: Sun, 29 Mar 2026 08:45:50 -0400 Subject: [PATCH 3/7] test --- lib/ecto/adapters/myxql.ex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/ecto/adapters/myxql.ex b/lib/ecto/adapters/myxql.ex index d62e37cf..8936a7bd 100644 --- a/lib/ecto/adapters/myxql.ex +++ b/lib/ecto/adapters/myxql.ex @@ -589,7 +589,7 @@ defmodule Ecto.Adapters.MyXQL do ] # Trap exits in case mysql dies in the middle of execution so that we can surface the error - Process.flag(:trap_exit, true) + # Process.flag(:trap_exit, true) port = Port.open({:spawn_executable, abs_cmd}, port_opts) Port.command(port, contents) # Use this as a signal to close the port since we cannot From 2d76c3b362f5c8ae6d2440839baec71d15efef21 Mon Sep 17 00:00:00 2001 From: Greg Rychlewski Date: Sun, 29 Mar 2026 08:50:11 -0400 Subject: [PATCH 4/7] test --- lib/ecto/adapters/myxql.ex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/ecto/adapters/myxql.ex b/lib/ecto/adapters/myxql.ex index 8936a7bd..22e27324 100644 --- a/lib/ecto/adapters/myxql.ex +++ b/lib/ecto/adapters/myxql.ex @@ -589,7 +589,7 @@ defmodule Ecto.Adapters.MyXQL do ] # Trap exits in case mysql dies in the middle of execution so that we can surface the error - # Process.flag(:trap_exit, true) + # Process.flag(:trap_exit, true) port = Port.open({:spawn_executable, abs_cmd}, port_opts) Port.command(port, contents) # Use this as a signal to close the port since we cannot From ecf52ddfd710e79b0b74f248b96e53efca54c07e Mon Sep 17 00:00:00 2001 From: Greg Rychlewski Date: Sun, 29 Mar 2026 08:51:37 -0400 Subject: [PATCH 5/7] test --- lib/ecto/adapters/myxql.ex | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/ecto/adapters/myxql.ex b/lib/ecto/adapters/myxql.ex index 22e27324..faf0bd8a 100644 --- a/lib/ecto/adapters/myxql.ex +++ b/lib/ecto/adapters/myxql.ex @@ -594,6 +594,7 @@ defmodule Ecto.Adapters.MyXQL do Port.command(port, contents) # Use this as a signal to close the port since we cannot # send an exit command to mysql in batch mode + Process.sleep(10000) Port.command(port, ";SELECT '__ECTO_EOF__';\n") collect_output(port, "") end From b541c12a85859fa500004694211b999028e41636 Mon Sep 17 00:00:00 2001 From: Greg Rychlewski Date: Sun, 29 Mar 2026 08:56:14 -0400 Subject: [PATCH 6/7] test --- lib/ecto/adapters/myxql.ex | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/ecto/adapters/myxql.ex b/lib/ecto/adapters/myxql.ex index faf0bd8a..67c6cedc 100644 --- a/lib/ecto/adapters/myxql.ex +++ b/lib/ecto/adapters/myxql.ex @@ -594,7 +594,6 @@ defmodule Ecto.Adapters.MyXQL do Port.command(port, contents) # Use this as a signal to close the port since we cannot # send an exit command to mysql in batch mode - Process.sleep(10000) Port.command(port, ";SELECT '__ECTO_EOF__';\n") collect_output(port, "") end @@ -660,7 +659,7 @@ defmodule Ecto.Adapters.MyXQL do {acc, 1} {^port, {:exit_status, status}} -> - {acc, status} + collect_output(port, acc) end end end From ba48be7f62798ea95f7263ef4b16f5b0abf6a015 Mon Sep 17 00:00:00 2001 From: Greg Rychlewski Date: Sun, 29 Mar 2026 09:00:59 -0400 Subject: [PATCH 7/7] restore old trap exit --- lib/ecto/adapters/myxql.ex | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/lib/ecto/adapters/myxql.ex b/lib/ecto/adapters/myxql.ex index 67c6cedc..6c706dfe 100644 --- a/lib/ecto/adapters/myxql.ex +++ b/lib/ecto/adapters/myxql.ex @@ -589,13 +589,15 @@ defmodule Ecto.Adapters.MyXQL do ] # Trap exits in case mysql dies in the middle of execution so that we can surface the error - # Process.flag(:trap_exit, true) + old_trap_exit = Process.flag(:trap_exit, true) port = Port.open({:spawn_executable, abs_cmd}, port_opts) Port.command(port, contents) # Use this as a signal to close the port since we cannot # send an exit command to mysql in batch mode Port.command(port, ";SELECT '__ECTO_EOF__';\n") - collect_output(port, "") + result = collect_output(port, "") + Process.flag(:trap_exit, old_trap_exit) + result end defp args_env(opts, opt_args) do @@ -659,7 +661,7 @@ defmodule Ecto.Adapters.MyXQL do {acc, 1} {^port, {:exit_status, status}} -> - collect_output(port, acc) + {acc, status} end end end