diff --git a/hbase-shell/src/main/ruby/irb/hirb.rb b/hbase-shell/src/main/ruby/irb/hirb.rb index ae534f6f00b3..87a62551281b 100644 --- a/hbase-shell/src/main/ruby/irb/hirb.rb +++ b/hbase-shell/src/main/ruby/irb/hirb.rb @@ -51,6 +51,8 @@ def initialize(workspace = nil, interactive = true, input_method = nil) `stty icrnl <&2` end @interactive = interactive + # Errors must abort a script run to set the exit code, but not a live prompt + @exit_on_error = !interactive || input_method.is_a?(::IRB::FileInputMethod) super(workspace, input_method) ensure f.close @@ -161,17 +163,13 @@ def eval_input rescue Interrupt => exc rescue SystemExit, SignalException raise - rescue SyntaxError => exc - # HBASE-27726: Ignore SyntaxError to prevent exiting Shell on unexpected syntax. - raise exc unless @interactive - rescue NameError => exc - raise exc unless @interactive - # HBASE-26880: Ignore NameError to prevent exiting Shell on mistyped commands. rescue Exception => exc # HBASE-26741: Raise exception so Shell::exception_handler can catch it. # This modifies this copied method from JRuby so that the HBase shell can # manage the exception and set a proper exit code on the process. - raise exc + # Otherwise keep the session alive and report the error, as the shell did before + # HBASE-26741. Supersedes HBASE-26880 (NameError) and HBASE-27726 (SyntaxError). + raise if @exit_on_error else exc = nil next diff --git a/hbase-shell/src/test/ruby/shell/general_test_cluster.rb b/hbase-shell/src/test/ruby/shell/general_test_cluster.rb index 8c1687dea019..4c3195d00c3c 100644 --- a/hbase-shell/src/test/ruby/shell/general_test_cluster.rb +++ b/hbase-shell/src/test/ruby/shell/general_test_cluster.rb @@ -21,6 +21,7 @@ require 'hbase_shell' require 'irb/hirb' require 'stringio' +require 'tempfile' class ShellTest < Test::Unit::TestCase include Hbase::TestHelpers @@ -226,4 +227,25 @@ def readable_after_eof? assert_match(/WARN: 'scan' is a reserved HBase command/, err_output) assert_match(/WARN: 'processlist' is a reserved HBase command/, err_output) end + + def new_hirb(input_method) + IRB.setup(__FILE__) unless IRB.conf[:IRB_NAME] + IRB::HIRB.new(@shell.workspace, true, input_method) + end + + define_test 'Shell::Shell should keep an interactive session alive on any error' do + hirb = new_hirb(MockInputMethod.new(["1 + '2'\n", "my_var = 5\n"])) + capture_stdout { hirb.eval_input } + assert_equal(5, hirb.context.workspace.binding.local_variable_get(:my_var)) + end + + define_test 'Shell::Shell should abort a script on error even when interactive' do + Tempfile.create(['hirb_test', '.rb']) do |file| + file.write("1 + '2'\n") + file.close + # interactive is true, as it is for `hbase shell script.rb` without -n + hirb = new_hirb(IRB::HBaseLoader.file_for_load(file.path)) + assert_raise(TypeError) { capture_stdout { hirb.eval_input } } + end + end end