@@ -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
535620end
0 commit comments