diff --git a/pkg/storage/driver/sql.go b/pkg/storage/driver/sql.go index 6602f2ee3..c4ea6a901 100644 --- a/pkg/storage/driver/sql.go +++ b/pkg/storage/driver/sql.go @@ -593,6 +593,12 @@ func (s *SQL) Update(key string, rel release.Releaser) error { return err } + transaction, err := s.db.Beginx() + if err != nil { + s.Logger().Debug("failed to start SQL transaction", slog.Any("error", err)) + return fmt.Errorf("error beginning transaction: %w", err) + } + query, args, err := s.statementBuilder. Update(sqlReleaseTableName). Set(sqlReleaseTableBodyColumn, body). @@ -605,15 +611,68 @@ func (s *SQL) Update(key string, rel release.Releaser) error { Where(sq.Eq{sqlReleaseTableNamespaceColumn: namespace}). ToSql() if err != nil { + transaction.Rollback() s.Logger().Debug("failed to build update query", slog.Any("error", err)) return err } - if _, err := s.db.Exec(query, args...); err != nil { + if _, err := transaction.Exec(query, args...); err != nil { + transaction.Rollback() s.Logger().Debug("failed to update release in SQL database", slog.String("key", key), slog.Any("error", err)) return err } + deleteQuery, args, err := s.statementBuilder. + Delete(sqlCustomLabelsTableName). + Where(sq.Eq{sqlCustomLabelsTableReleaseKeyColumn: key}). + Where(sq.Eq{sqlCustomLabelsTableReleaseNamespaceColumn: namespace}). + ToSql() + if err != nil { + transaction.Rollback() + s.Logger().Debug("failed to build delete Labels query", slog.Any("error", err)) + return err + } + + if _, err := transaction.Exec(deleteQuery, args...); err != nil { + transaction.Rollback() + s.Logger().Debug("failed to delete Labels", slog.Any("error", err)) + return err + } + + for k, v := range filterSystemLabels(rls.Labels) { + insertLabelsQuery, args, err := s.statementBuilder. + Insert(sqlCustomLabelsTableName). + Columns( + sqlCustomLabelsTableReleaseKeyColumn, + sqlCustomLabelsTableReleaseNamespaceColumn, + sqlCustomLabelsTableKeyColumn, + sqlCustomLabelsTableValueColumn, + ). + Values( + key, + namespace, + k, + v, + ).ToSql() + if err != nil { + transaction.Rollback() + s.Logger().Debug("failed to build insert query", slog.Any("error", err)) + return err + } + + if _, err := transaction.Exec(insertLabelsQuery, args...); err != nil { + transaction.Rollback() + s.Logger().Debug("failed to write Labels", slog.Any("error", err)) + return err + } + } + + if err := transaction.Commit(); err != nil { + transaction.Rollback() + s.Logger().Debug("failed to commit transaction", slog.Any("error", err)) + return err + } + return nil } diff --git a/pkg/storage/driver/sql_test.go b/pkg/storage/driver/sql_test.go index e5fde405b..52fa7cb3f 100644 --- a/pkg/storage/driver/sql_test.go +++ b/pkg/storage/driver/sql_test.go @@ -309,11 +309,40 @@ func TestSqlUpdate(t *testing.T) { sqlReleaseTableNamespaceColumn, ) + mock.ExpectBegin() mock. ExpectExec(regexp.QuoteMeta(query)). WithArgs(body, rel.Name, int(rel.Version), rel.Info.Status.String(), sqlReleaseDefaultOwner, recentUnixTimestamp(), key, namespace). WillReturnResult(sqlmock.NewResult(0, 1)) + deleteLabelsQuery := fmt.Sprintf( + "DELETE FROM %s WHERE %s = $1 AND %s = $2", + sqlCustomLabelsTableName, + sqlCustomLabelsTableReleaseKeyColumn, + sqlCustomLabelsTableReleaseNamespaceColumn, + ) + mock. + ExpectExec(regexp.QuoteMeta(deleteLabelsQuery)). + WithArgs(key, namespace). + WillReturnResult(sqlmock.NewResult(0, 1)) + + insertLabelsQuery := fmt.Sprintf( + "INSERT INTO %s (%s,%s,%s,%s) VALUES ($1,$2,$3,$4)", + sqlCustomLabelsTableName, + sqlCustomLabelsTableReleaseKeyColumn, + sqlCustomLabelsTableReleaseNamespaceColumn, + sqlCustomLabelsTableKeyColumn, + sqlCustomLabelsTableValueColumn, + ) + mock.MatchExpectationsInOrder(false) + for k, v := range filterSystemLabels(rel.Labels) { + mock. + ExpectExec(regexp.QuoteMeta(insertLabelsQuery)). + WithArgs(key, namespace, k, v). + WillReturnResult(sqlmock.NewResult(0, 1)) + } + mock.ExpectCommit() + require.NoErrorf(t, sqlDriver.Update(key, rel), "failed to update release with key %s", key) assert.NoErrorf(t, mock.ExpectationsWereMet(), "sql expectations weren't met") }