eintopf.info@v0.13.16/service/revent/store_sql_migrations.go (about) 1 // Copyright (C) 2022 The Eintopf authors 2 // 3 // This program is free software: you can redistribute it and/or modify 4 // it under the terms of the GNU Affero General Public License as 5 // published by the Free Software Foundation, either version 3 of the 6 // License, or (at your option) any later version. 7 // 8 // This program is distributed in the hope that it will be useful, 9 // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 // GNU Affero General Public License for more details. 12 // 13 // You should have received a copy of the GNU Affero General Public License 14 // along with this program. If not, see <https://www.gnu.org/licenses/>. 15 16 package revent 17 18 import ( 19 "context" 20 "database/sql" 21 "fmt" 22 23 "eintopf.info/internal/crud" 24 "eintopf.info/service/dbmigration" 25 ) 26 27 func (s *SqlStore) runMigrations(ctx context.Context, migrationService dbmigration.Service) error { 28 return migrationService.RunMigrations(ctx, []dbmigration.Migration{ 29 dbmigration.NewMigration("createRepeatingEventsTable", s.createRepeatingEventsTable, nil), 30 dbmigration.NewMigration("createRepeatingEventsOrganizerTable", s.createRepeatingEventsOrganizerTable, nil), 31 dbmigration.NewMigration("createRepeatingEventsIntervalsTable", s.createRepeatingEventsIntervalsTable, nil), 32 dbmigration.NewMigration("createRepeatingEventsTagsTable", s.createRepeatingEventsTagsTable, nil), 33 dbmigration.NewMigration("createRepeatingEventsOwnedByTable", s.createRepeatingEventsOwnedByTable, nil), 34 dbmigration.NewMigration("moveOrganizerToNewTable", s.moveOrganizerToNewTable, nil), 35 dbmigration.NewMigration("removeDateColumnFromIntervalsTable", s.removeDateColumnFromIntervalsTable, nil), 36 dbmigration.NewMigration("removeEmptyTags", s.removeEmptyTags, nil), 37 }) 38 } 39 40 func (s *SqlStore) createRepeatingEventsTable(ctx context.Context) error { 41 _, err := s.db.ExecContext(ctx, ` 42 CREATE TABLE IF NOT EXISTS repeatingEvents ( 43 id varchar(32) NOT NULL PRIMARY KEY UNIQUE, 44 deactivated boolean DEFAULT FALSE, 45 name varchar(64) NOT NULL, 46 location varchar(64), 47 location2 varchar(64), 48 description varchar(512), 49 start TIMESTAMP, 50 end TIMESTAMP, 51 image varchar(128) 52 ); 53 `) 54 return err 55 } 56 57 func (s *SqlStore) recreateRepeatingEventsTable(ctx context.Context) error { 58 repeatingEvents, total, err := s.Find(ctx, nil) 59 if err != nil { 60 return err 61 } 62 if len(repeatingEvents) != total { 63 return fmt.Errorf("len(repeatingEvents) != total") 64 } 65 if _, err := s.db.Exec(`DROP TABLE repeatingEvents`); err != nil { 66 return err 67 } 68 if err := s.createRepeatingEventsTable(ctx); err != nil { 69 return err 70 } 71 for _, repeatingEvent := range repeatingEvents { 72 err := s.insertRepeatingEvent(ctx, repeatingEvent) 73 if err != nil { 74 return err 75 } 76 } 77 return nil 78 } 79 80 func (s *SqlStore) createRepeatingEventsOrganizerTable(ctx context.Context) error { 81 _, err := s.db.ExecContext(ctx, ` 82 CREATE TABLE IF NOT EXISTS repeatingEvents_organizer ( 83 ID INTEGER PRIMARY KEY AUTOINCREMENT, 84 repeatingEvent_id varchar(32), 85 organizer varchar(64) 86 ); 87 `) 88 return err 89 } 90 91 func (s *SqlStore) createRepeatingEventsIntervalsTable(ctx context.Context) error { 92 _, err := s.db.ExecContext(ctx, ` 93 CREATE TABLE IF NOT EXISTS repeatingEvents_interval ( 94 repeatingEvent_id varchar(32), 95 type varchar(32), 96 interval integer, 97 weekDay integer 98 ); 99 `) 100 return err 101 } 102 103 func (s *SqlStore) createRepeatingEventsTagsTable(ctx context.Context) error { 104 _, err := s.db.ExecContext(ctx, ` 105 CREATE TABLE IF NOT EXISTS repeatingEvents_tags ( 106 ID INTEGER PRIMARY KEY AUTOINCREMENT, 107 repeatingEvent_id varchar(32), 108 tag varchar(64) 109 ); 110 `) 111 return err 112 } 113 114 func (s *SqlStore) createRepeatingEventsOwnedByTable(ctx context.Context) error { 115 _, err := s.db.ExecContext(ctx, ` 116 CREATE TABLE IF NOT EXISTS repeatingEvents_owned_by ( 117 ID INTEGER PRIMARY KEY AUTOINCREMENT, 118 repeatingEvent_id varchar(32), 119 user_id varchar(32) 120 ); 121 `) 122 return err 123 } 124 125 func (s *SqlStore) removeEmptyTags(ctx context.Context) error { 126 revents, _, err := s.Find(context.Background(), &crud.FindParams[FindFilters]{}) 127 if err != nil { 128 return err 129 } 130 for _, e := range revents { 131 needsUpdate := false 132 for i, tag := range e.Tags { 133 if tag == "" { 134 needsUpdate = true 135 e.Tags = append(e.Tags[:i], e.Tags[i+1:]...) 136 } 137 } 138 if needsUpdate { 139 _, err := s.Update(context.Background(), e) 140 if err != nil { 141 return err 142 } 143 } 144 } 145 return err 146 } 147 148 func (s *SqlStore) moveOrganizerToNewTable(ctx context.Context) error { 149 rows, err := s.db.QueryxContext(ctx, ` 150 SELECT id, organizer, organizer2 151 FROM repeatingEvents 152 `, nil) 153 if err != nil { 154 if err == sql.ErrNoRows { 155 return nil 156 } 157 // When the organizer or organizer2 column does not exist, just ignore the migration 158 if err.Error() == "no such column: organizer" || err.Error() == "no such column: organizer2" { 159 return nil 160 } 161 return fmt.Errorf("select organizer and organizer2: %s", err) 162 } 163 defer rows.Close() 164 165 type OldRepeatingEvent struct { 166 ID string `db:"id"` 167 Organizer string `db:"organizer"` 168 Organizer2 string `db:"organizer2"` 169 } 170 171 oldEvents := []OldRepeatingEvent{} 172 for rows.Next() { 173 oldEvent := OldRepeatingEvent{} 174 err := rows.StructScan(&oldEvent) 175 if err != nil { 176 return err 177 } 178 179 oldEvents = append(oldEvents, oldEvent) 180 } 181 rows.Close() 182 183 for _, oldEvent := range oldEvents { 184 organizers := []string{} 185 if oldEvent.Organizer != "" { 186 organizers = append(organizers, oldEvent.Organizer) 187 } 188 if oldEvent.Organizer2 != "" { 189 organizers = append(organizers, oldEvent.Organizer2) 190 } 191 err = s.insertOrganizersForRepeatingEvent(ctx, &RepeatingEvent{ 192 ID: oldEvent.ID, 193 Organizers: organizers, 194 }) 195 if err != nil { 196 return fmt.Errorf("insert organizer and organizer2: %s", err) 197 } 198 } 199 if err := s.recreateRepeatingEventsTable(ctx); err != nil { 200 return fmt.Errorf("recreateRepeatingEventsTable: %s", err) 201 } 202 return nil 203 } 204 205 func (s *SqlStore) removeDateColumnFromIntervalsTable(ctx context.Context) error { 206 s.db.Exec(` 207 ALTER TABLE repeatingEvents_interval 208 DROP COLUMN date; 209 `) 210 return nil 211 }