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  }