diff --git a/cmd/sqlcmd/pipe_detection_test.go b/cmd/sqlcmd/pipe_detection_test.go new file mode 100644 index 00000000..b2bc8be3 --- /dev/null +++ b/cmd/sqlcmd/pipe_detection_test.go @@ -0,0 +1,28 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +package sqlcmd + +import ( + "os" + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestStdinPipeDetection(t *testing.T) { + // Get stdin info + fi, err := os.Stdin.Stat() + assert.NoError(t, err, "os.Stdin.Stat()") + + // On most CI systems, stdin will be a pipe or file (not a terminal) + // We're testing the logic, not expecting a specific result + isPipe := false + if fi != nil && (fi.Mode()&os.ModeCharDevice) == 0 { + isPipe = true + } + + // Just making sure the detection code doesn't crash + // The actual value will depend on the environment + t.Logf("Stdin detected as pipe: %v", isPipe) +} \ No newline at end of file diff --git a/cmd/sqlcmd/sqlcmd.go b/cmd/sqlcmd/sqlcmd.go index 5c1c16a2..4710bfa9 100644 --- a/cmd/sqlcmd/sqlcmd.go +++ b/cmd/sqlcmd/sqlcmd.go @@ -714,25 +714,31 @@ func setConnect(connect *sqlcmd.ConnectSettings, args *SQLCmdArguments, vars *sq } } -func isConsoleInitializationRequired(connect *sqlcmd.ConnectSettings, args *SQLCmdArguments) bool { - // Password input always requires console initialization - if connect.RequiresPassword() { - return true - } +func isConsoleInitializationRequired(connect *sqlcmd.ConnectSettings, args *SQLCmdArguments) (bool, bool) { + needsConsole := false // Check if stdin is from a terminal or a redirection + isStdinRedirected := false file, err := os.Stdin.Stat() if err == nil { // If stdin is not a character device, it's coming from a pipe or redirect if (file.Mode() & os.ModeCharDevice) == 0 { - // Non-interactive: stdin is redirected - return false + isStdinRedirected = true } } - // If we get here, stdin is from a terminal or we couldn't determine - iactive := args.InputFile == nil && args.Query == "" && len(args.ChangePasswordAndExit) == 0 - return iactive + // Determine if we're in interactive mode + iactive := args.InputFile == nil && args.Query == "" && len(args.ChangePasswordAndExit) == 0 && !isStdinRedirected + + // Password input always requires console initialization + if connect.RequiresPassword() { + needsConsole = true + } else if iactive { + // Interactive mode also requires console + needsConsole = true + } + + return needsConsole, iactive } func run(vars *sqlcmd.Variables, args *SQLCmdArguments) (int, error) { @@ -744,7 +750,8 @@ func run(vars *sqlcmd.Variables, args *SQLCmdArguments) (int, error) { var connectConfig sqlcmd.ConnectSettings setConnect(&connectConfig, args, vars) var line sqlcmd.Console = nil - if isConsoleInitializationRequired(&connectConfig, args) { + needsConsole, isInteractive := isConsoleInitializationRequired(&connectConfig, args) + if needsConsole { line = console.NewConsole("") defer line.Close() } @@ -835,7 +842,10 @@ func run(vars *sqlcmd.Variables, args *SQLCmdArguments) (int, error) { } iactive := args.InputFile == nil && args.Query == "" if iactive || s.Query != "" { - err = s.Run(once, false) + // If we're not in interactive mode and stdin is redirected, + // we want to process all input without requiring GO statements + processAll := !isInteractive + err = s.Run(once, processAll) } else { for f := range args.InputFile { if err = s.IncludeFile(args.InputFile[f], true); err != nil { diff --git a/cmd/sqlcmd/sqlcmd_test.go b/cmd/sqlcmd/sqlcmd_test.go index e3c8a1e9..ce657420 100644 --- a/cmd/sqlcmd/sqlcmd_test.go +++ b/cmd/sqlcmd/sqlcmd_test.go @@ -506,7 +506,8 @@ func TestConditionsForPasswordPrompt(t *testing.T) { setConnect(&connectConfig, &args, vars) connectConfig.AuthenticationMethod = testcase.authenticationMethod connectConfig.Password = testcase.pwd - assert.Equal(t, testcase.expectedResult, isConsoleInitializationRequired(&connectConfig, &args), "Unexpected test result encountered for console initialization") + needsConsole, _ := isConsoleInitializationRequired(&connectConfig, &args) + assert.Equal(t, testcase.expectedResult, needsConsole, "Unexpected test result encountered for console initialization") assert.Equal(t, testcase.expectedResult, connectConfig.RequiresPassword() && connectConfig.Password == "", "Unexpected test result encountered for password prompt conditions") } } diff --git a/cmd/sqlcmd/stdin_console_test.go b/cmd/sqlcmd/stdin_console_test.go index 2c9f7ba5..10ba6cd7 100644 --- a/cmd/sqlcmd/stdin_console_test.go +++ b/cmd/sqlcmd/stdin_console_test.go @@ -56,13 +56,25 @@ func TestIsConsoleInitializationRequiredWithRedirectedStdin(t *testing.T) { t.Logf("RequiresPassword() returns: %v", connectConfig.RequiresPassword()) // Test with SQL authentication that requires a password - res := isConsoleInitializationRequired(&connectConfig, args) - // Should be true since password is required, even with redirected stdin - assert.True(t, res, "Console initialization should be required when SQL authentication is used") + needsConsole, isInteractive := isConsoleInitializationRequired(&connectConfig, args) + // Should need console since password is required, but not be interactive + assert.True(t, needsConsole, "Console should be needed when SQL authentication is used") + assert.False(t, isInteractive, "Should not be interactive mode with redirected stdin") // Now test with no authentication (no password required) connectConfig = sqlcmd.ConnectSettings{} - res = isConsoleInitializationRequired(&connectConfig, args) - // Should be false since stdin is redirected and no password is required - assert.False(t, res, "Console initialization should not be required with redirected stdin and no password") + needsConsole, isInteractive = isConsoleInitializationRequired(&connectConfig, args) + // Should not need console and not be interactive + assert.False(t, needsConsole, "Console should not be needed with redirected stdin and no password") + assert.False(t, isInteractive, "Should not be interactive mode with redirected stdin") + + // Test with direct terminal input (simulated by restoring original stdin) + os.Stdin = originalStdin + connectConfig = sqlcmd.ConnectSettings{} // No password needed + needsConsole, isInteractive = isConsoleInitializationRequired(&connectConfig, args) + // If no input file or query is specified, it should be interactive mode + assert.Equal(t, args.InputFile == nil && args.Query == "" && len(args.ChangePasswordAndExit) == 0, needsConsole, + "Console needs should match interactive mode requirements with terminal stdin") + assert.Equal(t, args.InputFile == nil && args.Query == "" && len(args.ChangePasswordAndExit) == 0, isInteractive, + "Interactive mode should be true with terminal stdin and no input files or queries") }