mirror of
1
Fork 0

[bugfix] add ON CONFLICT statements to status updates (#1580)

This commit is contained in:
tobi 2023-03-02 16:58:23 +01:00 committed by GitHub
parent e6cde25466
commit bfccf4e450
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 43 additions and 6 deletions

View File

@ -200,7 +200,9 @@ func (s *statusDB) PutStatus(ctx context.Context, status *gtsmodel.Status) db.Er
Model(&gtsmodel.StatusToEmoji{ Model(&gtsmodel.StatusToEmoji{
StatusID: status.ID, StatusID: status.ID,
EmojiID: i, EmojiID: i,
}).Exec(ctx); err != nil { }).
On("CONFLICT (?, ?) DO NOTHING", bun.Ident("status_id"), bun.Ident("emoji_id")).
Exec(ctx); err != nil {
err = s.conn.ProcessError(err) err = s.conn.ProcessError(err)
if !errors.Is(err, db.ErrAlreadyExists) { if !errors.Is(err, db.ErrAlreadyExists) {
return err return err
@ -215,7 +217,9 @@ func (s *statusDB) PutStatus(ctx context.Context, status *gtsmodel.Status) db.Er
Model(&gtsmodel.StatusToTag{ Model(&gtsmodel.StatusToTag{
StatusID: status.ID, StatusID: status.ID,
TagID: i, TagID: i,
}).Exec(ctx); err != nil { }).
On("CONFLICT (?, ?) DO NOTHING", bun.Ident("status_id"), bun.Ident("tag_id")).
Exec(ctx); err != nil {
err = s.conn.ProcessError(err) err = s.conn.ProcessError(err)
if !errors.Is(err, db.ErrAlreadyExists) { if !errors.Is(err, db.ErrAlreadyExists) {
return err return err
@ -261,7 +265,9 @@ func (s *statusDB) UpdateStatus(ctx context.Context, status *gtsmodel.Status, co
Model(&gtsmodel.StatusToEmoji{ Model(&gtsmodel.StatusToEmoji{
StatusID: status.ID, StatusID: status.ID,
EmojiID: i, EmojiID: i,
}).Exec(ctx); err != nil { }).
On("CONFLICT (?, ?) DO NOTHING", bun.Ident("status_id"), bun.Ident("emoji_id")).
Exec(ctx); err != nil {
err = s.conn.ProcessError(err) err = s.conn.ProcessError(err)
if !errors.Is(err, db.ErrAlreadyExists) { if !errors.Is(err, db.ErrAlreadyExists) {
return err return err
@ -276,7 +282,9 @@ func (s *statusDB) UpdateStatus(ctx context.Context, status *gtsmodel.Status, co
Model(&gtsmodel.StatusToTag{ Model(&gtsmodel.StatusToTag{
StatusID: status.ID, StatusID: status.ID,
TagID: i, TagID: i,
}).Exec(ctx); err != nil { }).
On("CONFLICT (?, ?) DO NOTHING", bun.Ident("status_id"), bun.Ident("tag_id")).
Exec(ctx); err != nil {
err = s.conn.ProcessError(err) err = s.conn.ProcessError(err)
if !errors.Is(err, db.ErrAlreadyExists) { if !errors.Is(err, db.ErrAlreadyExists) {
return err return err
@ -300,7 +308,7 @@ func (s *statusDB) UpdateStatus(ctx context.Context, status *gtsmodel.Status, co
} }
} }
// Finally, insert the status // Finally, update the status
_, err := tx. _, err := tx.
NewUpdate(). NewUpdate().
Model(status). Model(status).

View File

@ -26,6 +26,7 @@ import (
"github.com/stretchr/testify/suite" "github.com/stretchr/testify/suite"
"github.com/superseriousbusiness/gotosocial/internal/db" "github.com/superseriousbusiness/gotosocial/internal/db"
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
) )
type StatusTestSuite struct { type StatusTestSuite struct {
@ -176,7 +177,10 @@ func (suite *StatusTestSuite) TestGetStatusChildren() {
} }
func (suite *StatusTestSuite) TestDeleteStatus() { func (suite *StatusTestSuite) TestDeleteStatus() {
targetStatus := suite.testStatuses["admin_account_status_1"] // Take a copy of the status.
targetStatus := &gtsmodel.Status{}
*targetStatus = *suite.testStatuses["admin_account_status_1"]
err := suite.db.DeleteStatusByID(context.Background(), targetStatus.ID) err := suite.db.DeleteStatusByID(context.Background(), targetStatus.ID)
suite.NoError(err) suite.NoError(err)
@ -184,6 +188,31 @@ func (suite *StatusTestSuite) TestDeleteStatus() {
suite.ErrorIs(err, db.ErrNoEntries) suite.ErrorIs(err, db.ErrNoEntries)
} }
// This test was added specifically to ensure that Postgres wasn't getting upset
// about trying to use a transaction in which an error has already occurred, which
// was previously leading to errors like 'current transaction is aborted, commands
// ignored until end of transaction block' when updating a status that already had
// emojis or tags set on it.
//
// To run this test for postgres specifically, start a postgres container on localhost
// and then run:
//
// GTS_DB_TYPE=postgres GTS_DB_ADDRESS=localhost go test ./internal/db/bundb -run '^TestStatusTestSuite$' -testify.m '^(TestUpdateStatus)$' github.com/superseriousbusiness/gotosocial/internal/db/bundb
func (suite *StatusTestSuite) TestUpdateStatus() {
// Take a copy of the status.
targetStatus := &gtsmodel.Status{}
*targetStatus = *suite.testStatuses["admin_account_status_1"]
targetStatus.PinnedAt = time.Time{}
err := suite.db.UpdateStatus(context.Background(), targetStatus, "pinned_at")
suite.NoError(err)
updated, err := suite.db.GetStatusByID(context.Background(), targetStatus.ID)
suite.NoError(err)
suite.True(updated.PinnedAt.IsZero())
}
func TestStatusTestSuite(t *testing.T) { func TestStatusTestSuite(t *testing.T) {
suite.Run(t, new(StatusTestSuite)) suite.Run(t, new(StatusTestSuite))
} }