From 700bd698282e6dafdadaddf195bc85cbe2a95b12 Mon Sep 17 00:00:00 2001 From: kim Date: Wed, 25 Jun 2025 20:22:50 +0200 Subject: [PATCH] [bugfix] move broken stage of filters migration into new migration (#4293) Reviewed-on: https://codeberg.org/superseriousbusiness/gotosocial/pulls/4293 Co-authored-by: kim Co-committed-by: kim --- .../20250617122055_filter_improvements.go | 90 +----------- .../20250625173327_filter_migration_fix.go | 138 ++++++++++++++++++ 2 files changed, 141 insertions(+), 87 deletions(-) create mode 100644 internal/db/bundb/migrations/20250625173327_filter_migration_fix.go diff --git a/internal/db/bundb/migrations/20250617122055_filter_improvements.go b/internal/db/bundb/migrations/20250617122055_filter_improvements.go index 09fde089e..908346979 100644 --- a/internal/db/bundb/migrations/20250617122055_filter_improvements.go +++ b/internal/db/bundb/migrations/20250617122055_filter_improvements.go @@ -19,8 +19,6 @@ package migrations import ( "context" - "database/sql" - "errors" "reflect" "strings" @@ -204,91 +202,9 @@ func init() { return err } - // SQLITE: force WAL checkpoint to merge writes. - if err := doWALCheckpoint(ctx, db); err != nil { - return err - } - - // Create links from 'filters' table to 'filter_{keywords,statuses}' tables. - return db.RunInTx(ctx, nil, func(ctx context.Context, tx bun.Tx) error { - newFilterType := reflect.TypeOf((*newmodel.Filter)(nil)) - - var filterIDs string - - // Select all filter IDs. - if err := tx.NewSelect(). - Model((*newmodel.Filter)(nil)). - Column("id"). - Scan(ctx, &filterIDs); err != nil && !errors.Is(err, sql.ErrNoRows) { - return gtserror.Newf("error selecting filter ids: %w", err) - } - - for _, data := range []struct { - Field string - Model any - }{ - { - Field: "KeywordIDs", - Model: (*newmodel.FilterKeyword)(nil), - }, - { - Field: "StatusIDs", - Model: (*newmodel.FilterStatus)(nil), - }, - } { - // Generate bun definition for new filter table field column. - newColDef, err := getBunColumnDef(tx, newFilterType, data.Field) - if err != nil { - return gtserror.Newf("error getting bun column def: %w", err) - } - - // Add new column type to table. - if _, err := tx.NewAddColumn(). - Model((*oldmodel.Filter)(nil)). - ColumnExpr(newColDef). - Exec(ctx); err != nil { - return gtserror.Newf("error adding filter.%s column: %w", data.Field, err) - } - - // Get the SQL field information from bun for Filter{}.$Field. - field, _, err := getModelField(tx, newFilterType, data.Field) - if err != nil { - return gtserror.Newf("error getting bun model field: %w", err) - } - - // Extract column name. - col := field.SQLName - - var relatedIDs []string - for _, filterID := range filterIDs { - // Reset related IDs. - clear(relatedIDs) - relatedIDs = relatedIDs[:0] - - // Select $Model IDs that - // are attached to filterID. - if err := tx.NewSelect(). - Model(data.Model). - Column("id"). - Where("? = ?", bun.Ident("filter_id"), filterID). - Scan(ctx, &relatedIDs); err != nil { - return gtserror.Newf("error selecting %T ids: %w", data.Model, err) - } - - // Now update the relevant filter - // row to contain these related IDs. - if _, err := tx.NewUpdate(). - Model((*newmodel.Filter)(nil)). - Where("? = ?", bun.Ident("id"), filterID). - Set("? = ?", bun.Ident(col), relatedIDs). - Exec(ctx); err != nil { - return gtserror.Newf("error updating filters.%s ids: %w", col, err) - } - } - } - - return nil - }) + // SQLITE: force WAL checkpoint + // to merge writes before return. + return doWALCheckpoint(ctx, db) } down := func(ctx context.Context, db *bun.DB) error { diff --git a/internal/db/bundb/migrations/20250625173327_filter_migration_fix.go b/internal/db/bundb/migrations/20250625173327_filter_migration_fix.go new file mode 100644 index 000000000..c5fa965a4 --- /dev/null +++ b/internal/db/bundb/migrations/20250625173327_filter_migration_fix.go @@ -0,0 +1,138 @@ +// GoToSocial +// Copyright (C) GoToSocial Authors admin@gotosocial.org +// SPDX-License-Identifier: AGPL-3.0-or-later +// +// 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 . + +package migrations + +import ( + "context" + "database/sql" + "errors" + "reflect" + + oldmodel "code.superseriousbusiness.org/gotosocial/internal/db/bundb/migrations/20241018151036_filter_unique_fix" + newmodel "code.superseriousbusiness.org/gotosocial/internal/db/bundb/migrations/20250617122055_filter_improvements" + "code.superseriousbusiness.org/gotosocial/internal/gtserror" + "github.com/uptrace/bun" +) + +func init() { + up := func(ctx context.Context, db *bun.DB) error { + return db.RunInTx(ctx, nil, func(ctx context.Context, tx bun.Tx) error { + newFilterType := reflect.TypeOf((*newmodel.Filter)(nil)) + + // A SLICE!! SLICE !!!! + // NOT JUST A STRING!!! + // + // silly kim + var filterIDs []string + + // Select all filter IDs. + if err := tx.NewSelect(). + Model((*oldmodel.Filter)(nil)). + Column("id"). + Scan(ctx, &filterIDs); err != nil && !errors.Is(err, sql.ErrNoRows) { + return gtserror.Newf("error selecting filter ids: %w", err) + } + + for _, data := range []struct { + Field string + Model any + }{ + { + Field: "KeywordIDs", + Model: (*newmodel.FilterKeyword)(nil), + }, + { + Field: "StatusIDs", + Model: (*newmodel.FilterStatus)(nil), + }, + } { + // Get the SQL field information from bun for Filter{}.$Field. + field, table, err := getModelField(tx, newFilterType, data.Field) + if err != nil { + return gtserror.Newf("error getting bun model field: %w", err) + } + + // Check whether this part of the migration + // has already been run before, if so skip. + if exists, err := doesColumnExist(ctx, tx, + table.Name, + field.Name, + ); err != nil { + return gtserror.Newf("error checking if column exists: %w", err) + } else if !exists { + + // Generate bun definition for new filter table field column. + newColDef, err := getBunColumnDef(tx, newFilterType, data.Field) + if err != nil { + return gtserror.Newf("error getting bun column def: %w", err) + } + + // Add new column type to table. + if _, err := tx.NewAddColumn(). + Model((*oldmodel.Filter)(nil)). + ColumnExpr(newColDef). + Exec(ctx); err != nil { + return gtserror.Newf("error adding filter.%s column: %w", data.Field, err) + } + } + + // Get column name. + col := field.Name + + var relatedIDs []string + for _, filterID := range filterIDs { + // Reset related IDs. + clear(relatedIDs) + relatedIDs = relatedIDs[:0] + + // Select $Model IDs that + // are attached to filterID. + if err := tx.NewSelect(). + Model(data.Model). + Column("id"). + Where("? = ?", bun.Ident("filter_id"), filterID). + Scan(ctx, &relatedIDs); err != nil { + return gtserror.Newf("error selecting %T ids: %w", data.Model, err) + } + + // Now update the relevant filter + // row to contain these related IDs. + if _, err := tx.NewUpdate(). + Model((*newmodel.Filter)(nil)). + Where("? = ?", bun.Ident("id"), filterID). + Set("? = ?", bun.Ident(col), relatedIDs). + Exec(ctx); err != nil { + return gtserror.Newf("error updating filters.%s ids: %w", col, 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) + } +}