Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 36 additions & 0 deletions cmd/modern/e2e_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (
"strings"
"sync"
"testing"
"time"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
Expand Down Expand Up @@ -312,6 +313,41 @@ func TestE2E_PipedInput_WithBytesBuffer_NoPanic(t *testing.T) {
assert.NotContains(t, outputStr, "nil pointer", "should not have nil pointer error")
}

// TestE2E_QueryTimeout_LiveConnection tests that the -t flag properly times out queries
// and doesn't hang for 10 minutes on Linux. This is a regression test for the issue
// where sqlcmd would print "Timeout expired" but then hang for ~10 minutes before exiting.
func TestE2E_QueryTimeout_LiveConnection(t *testing.T) {
skipIfNoLiveConnection(t)
binary := buildBinary(t)

// Use a 1 second timeout with a query that would take 10 seconds
args := append([]string{"-C", "-t", "1", "-Q", "WAITFOR DELAY '00:00:10'"}, getAuthArgs(t)...)
cmd := exec.Command(binary, args...)
cmd.Env = os.Environ()

// Measure execution time - this is the key validation
// The command should complete in a few seconds, not 10 minutes
start := time.Now()
output, err := cmd.CombinedOutput()
elapsed := time.Since(start)
outputStr := string(output)

// The command should fail due to timeout
assert.Error(t, err, "query should timeout and return an error")

// Output should contain timeout message
assert.Contains(t, outputStr, "Timeout expired", "output should contain 'Timeout expired' message")

// Critical: verify the command completed quickly (within 30 seconds, not 10 minutes)
// If the bug exists, this would take ~10 minutes on Linux
maxDuration := 30 * time.Second
if elapsed > maxDuration {
t.Errorf("Command took too long to complete: %v (expected < %v). The timeout hang bug may still exist.", elapsed, maxDuration)
}

t.Logf("Query timed out correctly in %v: %s", elapsed, outputStr)
}

// cleanupBinary removes the temporary build directory containing the test binary.
// TestMain calls this to ensure deterministic cleanup instead of relying on
// eventual OS temp directory maintenance.
Expand Down
3 changes: 3 additions & 0 deletions pkg/sqlcmd/sqlcmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -431,6 +431,9 @@
}
retmsg := &sqlexp.ReturnMessage{}
rows, qe := s.db.QueryContext(ctx, query, retmsg)
if rows != nil {
defer rows.Close()

Check failure on line 435 in pkg/sqlcmd/sqlcmd.go

View workflow job for this annotation

GitHub Actions / lint-pr-changes

Error return value of `rows.Close` is not checked (errcheck)
}
if qe != nil {
s.Format.AddError(qe)
}
Expand Down
Loading