Skip to content

Commit e8f9c26

Browse files
Fix piping query without go command into sqlcmd-go (#583)
Co-authored-by: copilot-swe-agent[bot] <[email protected]> Co-authored-by: shueybubbles <[email protected]> Co-authored-by: David Shiflet <[email protected]>
1 parent 34ab426 commit e8f9c26

File tree

4 files changed

+70
-19
lines changed

4 files changed

+70
-19
lines changed

cmd/sqlcmd/pipe_detection_test.go

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
// Copyright (c) Microsoft Corporation.
2+
// Licensed under the MIT license.
3+
4+
package sqlcmd
5+
6+
import (
7+
"os"
8+
"testing"
9+
10+
"github.com/stretchr/testify/assert"
11+
)
12+
13+
func TestStdinPipeDetection(t *testing.T) {
14+
// Get stdin info
15+
fi, err := os.Stdin.Stat()
16+
assert.NoError(t, err, "os.Stdin.Stat()")
17+
18+
// On most CI systems, stdin will be a pipe or file (not a terminal)
19+
// We're testing the logic, not expecting a specific result
20+
isPipe := false
21+
if fi != nil && (fi.Mode()&os.ModeCharDevice) == 0 {
22+
isPipe = true
23+
}
24+
25+
// Just making sure the detection code doesn't crash
26+
// The actual value will depend on the environment
27+
t.Logf("Stdin detected as pipe: %v", isPipe)
28+
}

cmd/sqlcmd/sqlcmd.go

Lines changed: 22 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -714,25 +714,31 @@ func setConnect(connect *sqlcmd.ConnectSettings, args *SQLCmdArguments, vars *sq
714714
}
715715
}
716716

717-
func isConsoleInitializationRequired(connect *sqlcmd.ConnectSettings, args *SQLCmdArguments) bool {
718-
// Password input always requires console initialization
719-
if connect.RequiresPassword() {
720-
return true
721-
}
717+
func isConsoleInitializationRequired(connect *sqlcmd.ConnectSettings, args *SQLCmdArguments) (bool, bool) {
718+
needsConsole := false
722719

723720
// Check if stdin is from a terminal or a redirection
721+
isStdinRedirected := false
724722
file, err := os.Stdin.Stat()
725723
if err == nil {
726724
// If stdin is not a character device, it's coming from a pipe or redirect
727725
if (file.Mode() & os.ModeCharDevice) == 0 {
728-
// Non-interactive: stdin is redirected
729-
return false
726+
isStdinRedirected = true
730727
}
731728
}
732729

733-
// If we get here, stdin is from a terminal or we couldn't determine
734-
iactive := args.InputFile == nil && args.Query == "" && len(args.ChangePasswordAndExit) == 0
735-
return iactive
730+
// Determine if we're in interactive mode
731+
iactive := args.InputFile == nil && args.Query == "" && len(args.ChangePasswordAndExit) == 0 && !isStdinRedirected
732+
733+
// Password input always requires console initialization
734+
if connect.RequiresPassword() {
735+
needsConsole = true
736+
} else if iactive {
737+
// Interactive mode also requires console
738+
needsConsole = true
739+
}
740+
741+
return needsConsole, iactive
736742
}
737743

738744
func run(vars *sqlcmd.Variables, args *SQLCmdArguments) (int, error) {
@@ -744,7 +750,8 @@ func run(vars *sqlcmd.Variables, args *SQLCmdArguments) (int, error) {
744750
var connectConfig sqlcmd.ConnectSettings
745751
setConnect(&connectConfig, args, vars)
746752
var line sqlcmd.Console = nil
747-
if isConsoleInitializationRequired(&connectConfig, args) {
753+
needsConsole, isInteractive := isConsoleInitializationRequired(&connectConfig, args)
754+
if needsConsole {
748755
line = console.NewConsole("")
749756
defer line.Close()
750757
}
@@ -835,7 +842,10 @@ func run(vars *sqlcmd.Variables, args *SQLCmdArguments) (int, error) {
835842
}
836843
iactive := args.InputFile == nil && args.Query == ""
837844
if iactive || s.Query != "" {
838-
err = s.Run(once, false)
845+
// If we're not in interactive mode and stdin is redirected,
846+
// we want to process all input without requiring GO statements
847+
processAll := !isInteractive
848+
err = s.Run(once, processAll)
839849
} else {
840850
for f := range args.InputFile {
841851
if err = s.IncludeFile(args.InputFile[f], true); err != nil {

cmd/sqlcmd/sqlcmd_test.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -506,7 +506,8 @@ func TestConditionsForPasswordPrompt(t *testing.T) {
506506
setConnect(&connectConfig, &args, vars)
507507
connectConfig.AuthenticationMethod = testcase.authenticationMethod
508508
connectConfig.Password = testcase.pwd
509-
assert.Equal(t, testcase.expectedResult, isConsoleInitializationRequired(&connectConfig, &args), "Unexpected test result encountered for console initialization")
509+
needsConsole, _ := isConsoleInitializationRequired(&connectConfig, &args)
510+
assert.Equal(t, testcase.expectedResult, needsConsole, "Unexpected test result encountered for console initialization")
510511
assert.Equal(t, testcase.expectedResult, connectConfig.RequiresPassword() && connectConfig.Password == "", "Unexpected test result encountered for password prompt conditions")
511512
}
512513
}

cmd/sqlcmd/stdin_console_test.go

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -56,13 +56,25 @@ func TestIsConsoleInitializationRequiredWithRedirectedStdin(t *testing.T) {
5656
t.Logf("RequiresPassword() returns: %v", connectConfig.RequiresPassword())
5757

5858
// Test with SQL authentication that requires a password
59-
res := isConsoleInitializationRequired(&connectConfig, args)
60-
// Should be true since password is required, even with redirected stdin
61-
assert.True(t, res, "Console initialization should be required when SQL authentication is used")
59+
needsConsole, isInteractive := isConsoleInitializationRequired(&connectConfig, args)
60+
// Should need console since password is required, but not be interactive
61+
assert.True(t, needsConsole, "Console should be needed when SQL authentication is used")
62+
assert.False(t, isInteractive, "Should not be interactive mode with redirected stdin")
6263

6364
// Now test with no authentication (no password required)
6465
connectConfig = sqlcmd.ConnectSettings{}
65-
res = isConsoleInitializationRequired(&connectConfig, args)
66-
// Should be false since stdin is redirected and no password is required
67-
assert.False(t, res, "Console initialization should not be required with redirected stdin and no password")
66+
needsConsole, isInteractive = isConsoleInitializationRequired(&connectConfig, args)
67+
// Should not need console and not be interactive
68+
assert.False(t, needsConsole, "Console should not be needed with redirected stdin and no password")
69+
assert.False(t, isInteractive, "Should not be interactive mode with redirected stdin")
70+
71+
// Test with direct terminal input (simulated by restoring original stdin)
72+
os.Stdin = originalStdin
73+
connectConfig = sqlcmd.ConnectSettings{} // No password needed
74+
needsConsole, isInteractive = isConsoleInitializationRequired(&connectConfig, args)
75+
// If no input file or query is specified, it should be interactive mode
76+
assert.Equal(t, args.InputFile == nil && args.Query == "" && len(args.ChangePasswordAndExit) == 0, needsConsole,
77+
"Console needs should match interactive mode requirements with terminal stdin")
78+
assert.Equal(t, args.InputFile == nil && args.Query == "" && len(args.ChangePasswordAndExit) == 0, isInteractive,
79+
"Interactive mode should be true with terminal stdin and no input files or queries")
6880
}

0 commit comments

Comments
 (0)