summaryrefslogtreecommitdiff
path: root/internal/db/bundb/migrations/20220214175650_media_cleanup.go
diff options
context:
space:
mode:
Diffstat (limited to 'internal/db/bundb/migrations/20220214175650_media_cleanup.go')
-rw-r--r--internal/db/bundb/migrations/20220214175650_media_cleanup.go172
1 files changed, 172 insertions, 0 deletions
diff --git a/internal/db/bundb/migrations/20220214175650_media_cleanup.go b/internal/db/bundb/migrations/20220214175650_media_cleanup.go
new file mode 100644
index 000000000..427f3bb9e
--- /dev/null
+++ b/internal/db/bundb/migrations/20220214175650_media_cleanup.go
@@ -0,0 +1,172 @@
+/*
+ GoToSocial
+ Copyright (C) 2021-2022 GoToSocial Authors admin@gotosocial.org
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+package migrations
+
+import (
+ "context"
+ "database/sql"
+ "time"
+
+ previousgtsmodel "github.com/superseriousbusiness/gotosocial/internal/db/bundb/migrations/20211113114307_init"
+ newgtsmodel "github.com/superseriousbusiness/gotosocial/internal/db/bundb/migrations/20220214175650_media_cleanup"
+ "github.com/uptrace/bun"
+)
+
+func init() {
+ const batchSize = 100
+ up := func(ctx context.Context, db *bun.DB) error {
+ // we need to migrate media attachments into a new table
+ // see section 6 here: https://www.sqlite.org/lang_altertable.html
+
+ return db.RunInTx(ctx, nil, func(ctx context.Context, tx bun.Tx) error {
+ // create the new media attachments table
+ if _, err := tx.
+ NewCreateTable().
+ ModelTableExpr("new_media_attachments").
+ Model(&newgtsmodel.MediaAttachment{}).
+ IfNotExists().
+ Exec(ctx); err != nil {
+ return err
+ }
+
+ offset := time.Now()
+ // migrate existing media attachments into new table
+ migrateLoop:
+ for {
+ oldAttachments := []*previousgtsmodel.MediaAttachment{}
+ err := tx.
+ NewSelect().
+ Model(&oldAttachments).
+ // subtract a millisecond from the offset just to make sure we're not getting double entries (this happens sometimes)
+ Where("media_attachment.created_at < ?", offset.Add(-1*time.Millisecond)).
+ Order("media_attachment.created_at DESC").
+ Limit(batchSize).
+ Scan(ctx)
+ if err != nil && err != sql.ErrNoRows {
+ // there's been a real error
+ return err
+ }
+
+ if err == sql.ErrNoRows || len(oldAttachments) == 0 {
+ // we're finished migrating
+ break migrateLoop
+ }
+
+ // update the offset to the createdAt time of the oldest media attachment in the slice
+ offset = oldAttachments[len(oldAttachments)-1].CreatedAt
+
+ // for every old attachment, we need to make a new attachment out of it by taking the same values
+ newAttachments := []*newgtsmodel.MediaAttachment{}
+ for _, old := range oldAttachments {
+ new := &newgtsmodel.MediaAttachment{
+ ID: old.ID,
+ CreatedAt: old.CreatedAt,
+ UpdatedAt: old.UpdatedAt,
+ StatusID: old.StatusID,
+ URL: old.URL,
+ RemoteURL: old.RemoteURL,
+ Type: newgtsmodel.FileType(old.Type),
+ FileMeta: newgtsmodel.FileMeta{
+ Original: newgtsmodel.Original{
+ Width: old.FileMeta.Original.Width,
+ Height: old.FileMeta.Original.Height,
+ Size: old.FileMeta.Original.Size,
+ Aspect: old.FileMeta.Original.Aspect,
+ },
+ Small: newgtsmodel.Small{
+ Width: old.FileMeta.Small.Width,
+ Height: old.FileMeta.Small.Height,
+ Size: old.FileMeta.Small.Size,
+ Aspect: old.FileMeta.Small.Aspect,
+ },
+ Focus: newgtsmodel.Focus{
+ X: old.FileMeta.Focus.X,
+ Y: old.FileMeta.Focus.Y,
+ },
+ },
+ AccountID: old.AccountID,
+ Description: old.Description,
+ ScheduledStatusID: old.ScheduledStatusID,
+ Blurhash: old.Blurhash,
+ Processing: newgtsmodel.ProcessingStatus(old.Processing),
+ File: newgtsmodel.File{
+ Path: old.File.Path,
+ ContentType: old.File.ContentType,
+ FileSize: old.File.FileSize,
+ UpdatedAt: old.File.UpdatedAt,
+ },
+ Thumbnail: newgtsmodel.Thumbnail{
+ Path: old.Thumbnail.Path,
+ ContentType: old.Thumbnail.ContentType,
+ FileSize: old.Thumbnail.FileSize,
+ UpdatedAt: old.Thumbnail.UpdatedAt,
+ URL: old.Thumbnail.URL,
+ RemoteURL: old.Thumbnail.RemoteURL,
+ },
+ Avatar: old.Avatar,
+ Header: old.Header,
+ Cached: true,
+ }
+ newAttachments = append(newAttachments, new)
+ }
+
+ // insert this batch of new attachments, and then continue the loop
+ if _, err := tx.
+ NewInsert().
+ Model(&newAttachments).
+ ModelTableExpr("new_media_attachments").
+ Exec(ctx); err != nil {
+ return err
+ }
+ }
+
+ // we have all the data we need from the old table, so we can safely drop it now
+ if _, err := tx.NewDropTable().Model(&previousgtsmodel.MediaAttachment{}).Exec(ctx); err != nil {
+ return err
+ }
+
+ // rename the new table to the same name as the old table was
+ if _, err := tx.QueryContext(ctx, "ALTER TABLE new_media_attachments RENAME TO media_attachments;"); err != nil {
+ return err
+ }
+
+ // add an index to the new table
+ if _, err := tx.
+ NewCreateIndex().
+ Model(&newgtsmodel.MediaAttachment{}).
+ Index("media_attachments_id_idx").
+ Column("id").
+ Exec(ctx); err != nil {
+ return err
+ }
+
+ return nil
+ })
+ }
+
+ down := func(ctx context.Context, db *bun.DB) error {
+ return db.RunInTx(ctx, nil, func(ctx context.Context, tx bun.Tx) error {
+ return nil
+ })
+ }
+
+ if err := Migrations.Register(up, down); err != nil {
+ panic(err)
+ }
+}