Fix #4790: Add synchronization to prevent StreamRow/Close race

Add mutex protection to Result wrapper to prevent race condition
between StreamRow()/StreamError() and Close(). The race occurred
when Close() closed the RowChan while StreamRow() was trying to send,
causing a "send on closed channel" panic.

Solution:
- Add RWMutex and closed flag to Result struct
- StreamRow/StreamError acquire read lock and check closed flag
- Close acquires write lock and sets closed flag before closing channel

This prevents any sends after the channel is closed without blocking
concurrent StreamRow calls.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Nathan Wallace
2025-11-11 22:37:22 +08:00
parent 780b2e65b4
commit f13351a2b0

View File

@@ -31,13 +31,21 @@ func (r *Result) Close() {
})
}
// StreamRow wraps the underlying StreamRow with synchronization
func (r *Result) StreamRow(row []interface{}) {
// StreamRow safely sends a row to the RowChan, checking if it's closed first
func (r *Result) StreamRow(rowResult []interface{}) {
r.mu.RLock()
defer r.mu.RUnlock()
if !r.closed {
r.Result.StreamRow(row)
r.Result.StreamRow(rowResult)
}
}
// StreamError safely sends an error to the RowChan, checking if it's closed first
func (r *Result) StreamError(err error) {
r.mu.RLock()
defer r.mu.RUnlock()
if !r.closed {
r.Result.StreamError(err)
}
}