fix(storage): close rows before nested queries to prevent PostgreSQL driver error (#1503)

Fixes #1435
This commit is contained in:
osalloum 2026-04-19 02:19:13 +02:00 committed by GitHub
parent 42b51f5da5
commit bc433161d6
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

View File

@ -1176,23 +1176,26 @@ func (s *Store) GetAllSuiteStatuses(params *paging.SuiteStatusParams) ([]*suite.
if err != nil { if err != nil {
return nil, err return nil, err
} }
defer rows.Close()
// Collect all suite statuses first before making nested queries.
// The lib/pq driver requires the previous query to be fully closed before starting
// new queries in the same transaction, otherwise it returns "pq: unexpected Parse
// response 'C'" errors. See: https://github.com/TwiN/gatus/issues/1435
var suiteStatuses []*suite.Status var suiteStatuses []*suite.Status
suiteIDs := make(map[*suite.Status]int64) // Track suite_id for each status
for rows.Next() { for rows.Next() {
status := &suite.Status{Results: []*suite.Result{}}
var suiteID int64 var suiteID int64
var key, name, group string if err = rows.Scan(&suiteID, &status.Key, &status.Name, &status.Group); err != nil {
if err = rows.Scan(&suiteID, &key, &name, &group); err != nil { rows.Close()
return nil, err return nil, err
} }
suiteStatuses = append(suiteStatuses, status)
status := &suite.Status{ suiteIDs[status] = suiteID
Name: name,
Group: group,
Key: key,
Results: []*suite.Result{},
} }
rows.Close()
for _, status := range suiteStatuses {
// Get suite results with pagination // Get suite results with pagination
pageSize := 20 pageSize := 20
page := 1 page := 1
@ -1205,17 +1208,16 @@ func (s *Store) GetAllSuiteStatuses(params *paging.SuiteStatusParams) ([]*suite.
} }
} }
suiteID := suiteIDs[status]
status.Results, err = s.getSuiteResults(tx, suiteID, page, pageSize) status.Results, err = s.getSuiteResults(tx, suiteID, page, pageSize)
if err != nil { if err != nil {
logr.Errorf("[sql.GetAllSuiteStatuses] Failed to retrieve results for suite_id=%d: %s", suiteID, err.Error()) logr.Errorf("[sql.GetAllSuiteStatuses] Failed to retrieve results for suite_id=%d: %s", suiteID, err.Error())
} }
// Populate Name and Group fields on each result // Populate Name and Group fields on each result
for _, result := range status.Results { for _, result := range status.Results {
result.Name = name result.Name = status.Name
result.Group = group result.Group = status.Group
} }
suiteStatuses = append(suiteStatuses, status)
} }
if err = tx.Commit(); err != nil { if err = tx.Commit(); err != nil {
@ -1466,7 +1468,6 @@ func (s *Store) getSuiteResults(tx *sql.Tx, suiteID int64, page, pageSize int) (
logr.Errorf("[sql.getSuiteResults] Query failed: %v", err) logr.Errorf("[sql.getSuiteResults] Query failed: %v", err)
return nil, err return nil, err
} }
defer rows.Close()
type suiteResultData struct { type suiteResultData struct {
result *suite.Result result *suite.Result
id int64 id int64
@ -1494,6 +1495,11 @@ func (s *Store) getSuiteResults(tx *sql.Tx, suiteID int64, page, pageSize int) (
id: suiteResultID, id: suiteResultID,
}) })
} }
// Close the rows before making nested queries to avoid PostgreSQL driver issues.
// The lib/pq driver requires the previous query to be fully closed before starting
// new queries in the same transaction, otherwise it returns "pq: unexpected Parse
// response 'C'" errors. See: https://github.com/TwiN/gatus/issues/1435
rows.Close()
// Reverse the results to get chronological order (oldest to newest) // Reverse the results to get chronological order (oldest to newest)
for i := len(resultsData)/2 - 1; i >= 0; i-- { for i := len(resultsData)/2 - 1; i >= 0; i-- {