@@ -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,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,80 @@ 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+ abs_cmd = System . find_executable ( cmd )
550+
551+ unless abs_cmd do
552+ raise "could not find executable `#{ cmd } ` in path, " <>
553+ "please guarantee it is available before running ecto commands"
554+ end
555+
556+ abs_cmd = String . to_charlist ( abs_cmd )
557+ { args , cmd_opts } = args_cmd_opts ( opts , opt_args , cmd_opts )
558+
559+ port_opts = port_opts ( cmd_opts , args: args )
560+ port = Port . open ( { :spawn_executable , abs_cmd } , port_opts )
561+ Port . command ( port , contents )
562+ # Use this as a signal to close the port since we cannot
563+ # send an exit command to mysql in batch mode
564+ Port . command ( port , ";SELECT '__ECTO_EOF__';\n " )
565+
566+ { initial , fun } = Collectable . into ( "" )
567+
568+ try do
569+ collect_output ( port , initial , fun )
570+ catch
571+ kind , reason ->
572+ fun . ( initial , :halt )
573+ :erlang . raise ( kind , reason , __STACKTRACE__ )
574+ else
575+ { acc , status } -> { fun . ( acc , :done ) , status }
576+ end
577+ end
578+
579+ defp port_opts ( [ { :stderr_to_stdout , true } | t ] , acc ) ,
580+ do: port_opts ( t , [ :stderr_to_stdout | acc ] )
581+
582+ defp port_opts ( [ { :stderr_to_stdout , _ } | t ] , acc ) ,
583+ do: port_opts ( t , acc )
584+
585+ defp port_opts ( [ { :env , enum } | t ] , acc ) ,
586+ do: port_opts ( t , [ { :env , validate_env ( enum ) } | acc ] )
587+
588+ defp port_opts ( [ ] , acc ) do
589+ [ :use_stdio , :exit_status , :binary , :hide ] ++ acc
590+ end
591+
592+ defp validate_env ( enum ) do
593+ Enum . map ( enum , fn
594+ { k , nil } ->
595+ { String . to_charlist ( k ) , false }
596+
597+ { k , v } ->
598+ { String . to_charlist ( k ) , String . to_charlist ( v ) }
599+
600+ other ->
601+ raise ArgumentError , "invalid environment key-value #{ inspect ( other ) } "
602+ end )
603+ end
604+
605+ defp collect_output ( port , acc , fun ) do
606+ receive do
607+ { ^ port , { :data , "__ECTO_EOF__" <> _rest } } ->
608+ { acc , 0 }
609+
610+ { ^ port , { :data , data } } ->
611+ collect_output ( port , fun . ( acc , { :cont , data } ) , fun )
612+
613+ { ^ port , { :exit_status , status } } ->
614+ { acc , status }
615+ end
534616 end
535617end
0 commit comments