diff --git a/store/mysql/migration/dev/LATEST__SCHEMA.sql b/store/mysql/migration/dev/LATEST__SCHEMA.sql index 90a6b691..c5bcd208 100644 --- a/store/mysql/migration/dev/LATEST__SCHEMA.sql +++ b/store/mysql/migration/dev/LATEST__SCHEMA.sql @@ -14,130 +14,111 @@ DROP TABLE IF EXISTS `idp`; -- migration_history CREATE TABLE `migration_history` ( - `version` varchar(255) NOT NULL, - `created_ts` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, - PRIMARY KEY (`version`) + `version` VARCHAR(255) NOT NULL PRIMARY KEY, + `created_ts` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ); -- system_setting CREATE TABLE `system_setting` ( - `name` varchar(255) NOT NULL, - `value` text NOT NULL, - `description` text NOT NULL, - PRIMARY KEY (`name`) + `name` VARCHAR(255) NOT NULL PRIMARY KEY, + `value` TEXT NOT NULL, + `description` TEXT NOT NULL ); -- user CREATE TABLE `user` ( - `id` int NOT NULL AUTO_INCREMENT, + `id` INT NOT NULL AUTO_INCREMENT PRIMARY KEY, `created_ts` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, `updated_ts` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, - `row_status` varchar(255) NOT NULL DEFAULT 'NORMAL', - `username` varchar(255) NOT NULL, - `role` varchar(255) NOT NULL DEFAULT 'USER', - `email` varchar(255) NOT NULL DEFAULT '', - `nickname` varchar(255) NOT NULL DEFAULT '', - `password_hash` varchar(255) NOT NULL, - `avatar_url` text NOT NULL, - PRIMARY KEY (`id`), - UNIQUE KEY `username` (`username`), - CONSTRAINT `user_chk_1` CHECK ((`row_status` in (_utf8mb4'NORMAL',_utf8mb4'ARCHIVED'))), - CONSTRAINT `user_chk_2` CHECK ((`role` in (_utf8mb4'HOST',_utf8mb4'ADMIN',_utf8mb4'USER'))) + `row_status` VARCHAR(255) NOT NULL DEFAULT 'NORMAL', + `username` VARCHAR(255) NOT NULL UNIQUE, + `role` VARCHAR(255) NOT NULL DEFAULT 'USER', + `email` VARCHAR(255) NOT NULL DEFAULT '', + `nickname` VARCHAR(255) NOT NULL DEFAULT '', + `password_hash` VARCHAR(255) NOT NULL, + `avatar_url` TEXT NOT NULL ); -- user_setting CREATE TABLE `user_setting` ( - `user_id` int NOT NULL, - `key` varchar(255) NOT NULL, - `value` text NOT NULL, - UNIQUE KEY `user_id` (`user_id`,`key`) + `user_id` INT NOT NULL, + `key` VARCHAR(255) NOT NULL, + `value` TEXT NOT NULL, + UNIQUE(`user_id`,`key`) ); -- memo CREATE TABLE `memo` ( - `id` int NOT NULL AUTO_INCREMENT, - `creator_id` int NOT NULL, + `id` INT NOT NULL AUTO_INCREMENT PRIMARY KEY, + `creator_id` INT NOT NULL, `created_ts` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, `updated_ts` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, - `row_status` varchar(255) NOT NULL DEFAULT 'NORMAL', - `content` text NOT NULL, - `visibility` varchar(255) NOT NULL DEFAULT 'PRIVATE', - PRIMARY KEY (`id`), - KEY `creator_id` (`creator_id`), - KEY `visibility` (`visibility`), - CONSTRAINT `memo_chk_1` CHECK ((`row_status` in (_utf8mb4'NORMAL',_utf8mb4'ARCHIVED'))), - CONSTRAINT `memo_chk_2` CHECK ((`visibility` in (_utf8mb4'PUBLIC',_utf8mb4'PROTECTED',_utf8mb4'PRIVATE'))) + `row_status` VARCHAR(255) NOT NULL DEFAULT 'NORMAL', + `content` TEXT NOT NULL, + `visibility` VARCHAR(255) NOT NULL DEFAULT 'PRIVATE' ); -- memo_organizer CREATE TABLE `memo_organizer` ( - `memo_id` int NOT NULL, - `user_id` int NOT NULL, - `pinned` int NOT NULL DEFAULT '0', - UNIQUE KEY `memo_id` (`memo_id`,`user_id`), - CONSTRAINT `memo_organizer_chk_1` CHECK ((`pinned` in (0,1))) + `memo_id` INT NOT NULL, + `user_id` INT NOT NULL, + `pinned` INT NOT NULL DEFAULT '0', + UNIQUE(`memo_id`,`user_id`) ); -- memo_relation CREATE TABLE `memo_relation` ( - `memo_id` int NOT NULL, - `related_memo_id` int NOT NULL, - `type` varchar(256) NOT NULL, - UNIQUE KEY `memo_id` (`memo_id`,`related_memo_id`,`type`) + `memo_id` INT NOT NULL, + `related_memo_id` INT NOT NULL, + `type` VARCHAR(256) NOT NULL, + UNIQUE(`memo_id`,`related_memo_id`,`type`) ); -- resource CREATE TABLE `resource` ( - `id` int NOT NULL AUTO_INCREMENT, - `creator_id` int NOT NULL, + `id` INT NOT NULL AUTO_INCREMENT PRIMARY KEY, + `creator_id` INT NOT NULL, `created_ts` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, `updated_ts` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, - `filename` text NOT NULL, - `blob` blob, - `external_link` text NOT NULL, - `type` varchar(255) NOT NULL DEFAULT '', - `size` int NOT NULL DEFAULT '0', - `internal_path` varchar(255) NOT NULL DEFAULT '', - `memo_id` int DEFAULT NULL, - PRIMARY KEY (`id`), - KEY `creator_id` (`creator_id`), - KEY `memo_id` (`memo_id`) + `filename` TEXT NOT NULL, + `blob` BLOB, + `external_link` TEXT NOT NULL, + `type` VARCHAR(255) NOT NULL DEFAULT '', + `size` INT NOT NULL DEFAULT '0', + `INTernal_path` VARCHAR(255) NOT NULL DEFAULT '', + `memo_id` INT DEFAULT NULL ); -- tag CREATE TABLE `tag` ( - `name` varchar(255) NOT NULL, - `creator_id` int NOT NULL, - UNIQUE KEY `name` (`name`,`creator_id`) + `name` VARCHAR(255) NOT NULL, + `creator_id` INT NOT NULL, + UNIQUE(`name`,`creator_id`) ); -- activity CREATE TABLE `activity` ( - `id` int NOT NULL AUTO_INCREMENT, - `creator_id` int NOT NULL, + `id` INT NOT NULL AUTO_INCREMENT PRIMARY KEY, + `creator_id` INT NOT NULL, `created_ts` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, - `type` varchar(255) NOT NULL DEFAULT '', - `level` varchar(255) NOT NULL DEFAULT 'INFO', - `payload` text NOT NULL, - PRIMARY KEY (`id`), - CONSTRAINT `activity_chk_1` CHECK ((`level` in (_utf8mb4'INFO',_utf8mb4'WARN',_utf8mb4'ERROR'))) + `type` VARCHAR(255) NOT NULL DEFAULT '', + `level` VARCHAR(255) NOT NULL DEFAULT 'INFO', + `payload` TEXT NOT NULL ); -- storage CREATE TABLE `storage` ( - `id` int NOT NULL AUTO_INCREMENT, - `name` varchar(256) NOT NULL, - `type` varchar(256) NOT NULL, - `config` text NOT NULL, - PRIMARY KEY (`id`) + `id` INT NOT NULL AUTO_INCREMENT PRIMARY KEY, + `name` VARCHAR(256) NOT NULL, + `type` VARCHAR(256) NOT NULL, + `config` TEXT NOT NULL ); -- idp CREATE TABLE `idp` ( - `id` int NOT NULL AUTO_INCREMENT, - `name` text NOT NULL, - `type` text NOT NULL, - `identifier_filter` varchar(256) NOT NULL DEFAULT '', - `config` text NOT NULL, - PRIMARY KEY (`id`) + `id` INT NOT NULL AUTO_INCREMENT PRIMARY KEY, + `name` TEXT NOT NULL, + `type` TEXT NOT NULL, + `identifier_filter` VARCHAR(256) NOT NULL DEFAULT '', + `config` TEXT NOT NULL ); diff --git a/store/mysql/migration/prod/LATEST__SCHEMA.sql b/store/mysql/migration/prod/LATEST__SCHEMA.sql index 90a6b691..c5bcd208 100644 --- a/store/mysql/migration/prod/LATEST__SCHEMA.sql +++ b/store/mysql/migration/prod/LATEST__SCHEMA.sql @@ -14,130 +14,111 @@ DROP TABLE IF EXISTS `idp`; -- migration_history CREATE TABLE `migration_history` ( - `version` varchar(255) NOT NULL, - `created_ts` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, - PRIMARY KEY (`version`) + `version` VARCHAR(255) NOT NULL PRIMARY KEY, + `created_ts` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ); -- system_setting CREATE TABLE `system_setting` ( - `name` varchar(255) NOT NULL, - `value` text NOT NULL, - `description` text NOT NULL, - PRIMARY KEY (`name`) + `name` VARCHAR(255) NOT NULL PRIMARY KEY, + `value` TEXT NOT NULL, + `description` TEXT NOT NULL ); -- user CREATE TABLE `user` ( - `id` int NOT NULL AUTO_INCREMENT, + `id` INT NOT NULL AUTO_INCREMENT PRIMARY KEY, `created_ts` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, `updated_ts` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, - `row_status` varchar(255) NOT NULL DEFAULT 'NORMAL', - `username` varchar(255) NOT NULL, - `role` varchar(255) NOT NULL DEFAULT 'USER', - `email` varchar(255) NOT NULL DEFAULT '', - `nickname` varchar(255) NOT NULL DEFAULT '', - `password_hash` varchar(255) NOT NULL, - `avatar_url` text NOT NULL, - PRIMARY KEY (`id`), - UNIQUE KEY `username` (`username`), - CONSTRAINT `user_chk_1` CHECK ((`row_status` in (_utf8mb4'NORMAL',_utf8mb4'ARCHIVED'))), - CONSTRAINT `user_chk_2` CHECK ((`role` in (_utf8mb4'HOST',_utf8mb4'ADMIN',_utf8mb4'USER'))) + `row_status` VARCHAR(255) NOT NULL DEFAULT 'NORMAL', + `username` VARCHAR(255) NOT NULL UNIQUE, + `role` VARCHAR(255) NOT NULL DEFAULT 'USER', + `email` VARCHAR(255) NOT NULL DEFAULT '', + `nickname` VARCHAR(255) NOT NULL DEFAULT '', + `password_hash` VARCHAR(255) NOT NULL, + `avatar_url` TEXT NOT NULL ); -- user_setting CREATE TABLE `user_setting` ( - `user_id` int NOT NULL, - `key` varchar(255) NOT NULL, - `value` text NOT NULL, - UNIQUE KEY `user_id` (`user_id`,`key`) + `user_id` INT NOT NULL, + `key` VARCHAR(255) NOT NULL, + `value` TEXT NOT NULL, + UNIQUE(`user_id`,`key`) ); -- memo CREATE TABLE `memo` ( - `id` int NOT NULL AUTO_INCREMENT, - `creator_id` int NOT NULL, + `id` INT NOT NULL AUTO_INCREMENT PRIMARY KEY, + `creator_id` INT NOT NULL, `created_ts` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, `updated_ts` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, - `row_status` varchar(255) NOT NULL DEFAULT 'NORMAL', - `content` text NOT NULL, - `visibility` varchar(255) NOT NULL DEFAULT 'PRIVATE', - PRIMARY KEY (`id`), - KEY `creator_id` (`creator_id`), - KEY `visibility` (`visibility`), - CONSTRAINT `memo_chk_1` CHECK ((`row_status` in (_utf8mb4'NORMAL',_utf8mb4'ARCHIVED'))), - CONSTRAINT `memo_chk_2` CHECK ((`visibility` in (_utf8mb4'PUBLIC',_utf8mb4'PROTECTED',_utf8mb4'PRIVATE'))) + `row_status` VARCHAR(255) NOT NULL DEFAULT 'NORMAL', + `content` TEXT NOT NULL, + `visibility` VARCHAR(255) NOT NULL DEFAULT 'PRIVATE' ); -- memo_organizer CREATE TABLE `memo_organizer` ( - `memo_id` int NOT NULL, - `user_id` int NOT NULL, - `pinned` int NOT NULL DEFAULT '0', - UNIQUE KEY `memo_id` (`memo_id`,`user_id`), - CONSTRAINT `memo_organizer_chk_1` CHECK ((`pinned` in (0,1))) + `memo_id` INT NOT NULL, + `user_id` INT NOT NULL, + `pinned` INT NOT NULL DEFAULT '0', + UNIQUE(`memo_id`,`user_id`) ); -- memo_relation CREATE TABLE `memo_relation` ( - `memo_id` int NOT NULL, - `related_memo_id` int NOT NULL, - `type` varchar(256) NOT NULL, - UNIQUE KEY `memo_id` (`memo_id`,`related_memo_id`,`type`) + `memo_id` INT NOT NULL, + `related_memo_id` INT NOT NULL, + `type` VARCHAR(256) NOT NULL, + UNIQUE(`memo_id`,`related_memo_id`,`type`) ); -- resource CREATE TABLE `resource` ( - `id` int NOT NULL AUTO_INCREMENT, - `creator_id` int NOT NULL, + `id` INT NOT NULL AUTO_INCREMENT PRIMARY KEY, + `creator_id` INT NOT NULL, `created_ts` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, `updated_ts` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, - `filename` text NOT NULL, - `blob` blob, - `external_link` text NOT NULL, - `type` varchar(255) NOT NULL DEFAULT '', - `size` int NOT NULL DEFAULT '0', - `internal_path` varchar(255) NOT NULL DEFAULT '', - `memo_id` int DEFAULT NULL, - PRIMARY KEY (`id`), - KEY `creator_id` (`creator_id`), - KEY `memo_id` (`memo_id`) + `filename` TEXT NOT NULL, + `blob` BLOB, + `external_link` TEXT NOT NULL, + `type` VARCHAR(255) NOT NULL DEFAULT '', + `size` INT NOT NULL DEFAULT '0', + `INTernal_path` VARCHAR(255) NOT NULL DEFAULT '', + `memo_id` INT DEFAULT NULL ); -- tag CREATE TABLE `tag` ( - `name` varchar(255) NOT NULL, - `creator_id` int NOT NULL, - UNIQUE KEY `name` (`name`,`creator_id`) + `name` VARCHAR(255) NOT NULL, + `creator_id` INT NOT NULL, + UNIQUE(`name`,`creator_id`) ); -- activity CREATE TABLE `activity` ( - `id` int NOT NULL AUTO_INCREMENT, - `creator_id` int NOT NULL, + `id` INT NOT NULL AUTO_INCREMENT PRIMARY KEY, + `creator_id` INT NOT NULL, `created_ts` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, - `type` varchar(255) NOT NULL DEFAULT '', - `level` varchar(255) NOT NULL DEFAULT 'INFO', - `payload` text NOT NULL, - PRIMARY KEY (`id`), - CONSTRAINT `activity_chk_1` CHECK ((`level` in (_utf8mb4'INFO',_utf8mb4'WARN',_utf8mb4'ERROR'))) + `type` VARCHAR(255) NOT NULL DEFAULT '', + `level` VARCHAR(255) NOT NULL DEFAULT 'INFO', + `payload` TEXT NOT NULL ); -- storage CREATE TABLE `storage` ( - `id` int NOT NULL AUTO_INCREMENT, - `name` varchar(256) NOT NULL, - `type` varchar(256) NOT NULL, - `config` text NOT NULL, - PRIMARY KEY (`id`) + `id` INT NOT NULL AUTO_INCREMENT PRIMARY KEY, + `name` VARCHAR(256) NOT NULL, + `type` VARCHAR(256) NOT NULL, + `config` TEXT NOT NULL ); -- idp CREATE TABLE `idp` ( - `id` int NOT NULL AUTO_INCREMENT, - `name` text NOT NULL, - `type` text NOT NULL, - `identifier_filter` varchar(256) NOT NULL DEFAULT '', - `config` text NOT NULL, - PRIMARY KEY (`id`) + `id` INT NOT NULL AUTO_INCREMENT PRIMARY KEY, + `name` TEXT NOT NULL, + `type` TEXT NOT NULL, + `identifier_filter` VARCHAR(256) NOT NULL DEFAULT '', + `config` TEXT NOT NULL ); diff --git a/store/mysql/migrate.go b/store/mysql/migrator.go similarity index 87% rename from store/mysql/migrate.go rename to store/mysql/migrator.go index 861ab2ba..8d44a766 100644 --- a/store/mysql/migrate.go +++ b/store/mysql/migrator.go @@ -35,15 +35,9 @@ func (d *Driver) nonProdMigrate(ctx context.Context) error { return errors.Errorf("failed to read latest schema file: %s", err) } - for _, stmt := range strings.Split(string(buf), ";") { - stmt = strings.TrimSpace(stmt) - if stmt == "" { - continue - } - _, err := d.db.ExecContext(ctx, stmt) - if err != nil { - return errors.Errorf("failed to exec SQL %s: %s", stmt, err) - } + stmt := string(buf) + if _, err := d.db.ExecContext(ctx, stmt); err != nil { + return errors.Errorf("failed to exec SQL %s: %s", stmt, err) } // In demo mode, we should seed the database. @@ -54,17 +48,27 @@ func (d *Driver) nonProdMigrate(ctx context.Context) error { } return nil } + func (d *Driver) prodMigrate(ctx context.Context) error { currentVersion := version.GetCurrentVersion(d.profile.Mode) migrationHistoryList, err := d.FindMigrationHistoryList(ctx, &MigrationHistoryFind{}) if err != nil { return errors.Wrap(err, "failed to find migration history") } + // If there is no migration history, we should apply the latest schema. if len(migrationHistoryList) == 0 { - _, err := d.UpsertMigrationHistory(ctx, &MigrationHistoryUpsert{ - Version: currentVersion, - }) + buf, err := migrationFS.ReadFile("migration/prod/" + latestSchemaFileName) if err != nil { + return errors.Errorf("failed to read latest schema file: %s", err) + } + + stmt := string(buf) + if _, err := d.db.ExecContext(ctx, stmt); err != nil { + return errors.Errorf("failed to exec SQL %s: %s", stmt, err) + } + if _, err := d.UpsertMigrationHistory(ctx, &MigrationHistoryUpsert{ + Version: currentVersion, + }); err != nil { return errors.Wrap(err, "failed to upsert migration history") } return nil @@ -76,7 +80,6 @@ func (d *Driver) prodMigrate(ctx context.Context) error { } sort.Sort(version.SortVersion(migrationHistoryVersionList)) latestMigrationHistoryVersion := migrationHistoryVersionList[len(migrationHistoryVersionList)-1] - if !version.IsVersionGreaterThan(version.GetSchemaVersion(currentVersion), latestMigrationHistoryVersion) { return nil } @@ -96,7 +99,7 @@ func (d *Driver) prodMigrate(ctx context.Context) error { } func (d *Driver) applyMigrationForMinorVersion(ctx context.Context, minorVersion string) error { - filenames, err := fs.Glob(migrationFS, fmt.Sprintf("%s/%s/*.sql", "migration/prod", minorVersion)) + filenames, err := fs.Glob(migrationFS, fmt.Sprintf("migration/prod/%s/*.sql", minorVersion)) if err != nil { return errors.Wrap(err, "failed to read ddl files") } @@ -131,13 +134,12 @@ func (d *Driver) applyMigrationForMinorVersion(ctx context.Context, minorVersion var seedFS embed.FS func (d *Driver) seed(ctx context.Context) error { - filenames, err := fs.Glob(seedFS, fmt.Sprintf("%s/*.sql", "seed")) + filenames, err := fs.Glob(seedFS, "seed/*.sql") if err != nil { return errors.Wrap(err, "failed to read seed files") } sort.Strings(filenames) - // Loop over all seed files and execute them in order. for _, filename := range filenames { buf, err := seedFS.ReadFile(filename) diff --git a/store/mysql/mysql.go b/store/mysql/mysql.go index 55aaf089..e9791bd0 100644 --- a/store/mysql/mysql.go +++ b/store/mysql/mysql.go @@ -3,6 +3,7 @@ package mysql import ( "context" "database/sql" + "fmt" "github.com/pkg/errors" @@ -16,7 +17,10 @@ type Driver struct { } func NewDriver(profile *profile.Profile) (store.Driver, error) { - db, err := sql.Open("mysql", profile.DSN) + // Open MySQL connection with parameter. + // multiStatements=true is required for migration. + // See more in: https://github.com/go-sql-driver/mysql#multistatements + db, err := sql.Open("mysql", fmt.Sprintf("%s?multiStatements=true", profile.DSN)) if err != nil { return nil, errors.Wrapf(err, "failed to open db: %s", profile.DSN) } diff --git a/store/sqlite/migrate.go b/store/sqlite/migrator.go similarity index 98% rename from store/sqlite/migrate.go rename to store/sqlite/migrator.go index 3abf5b91..cd15ec57 100644 --- a/store/sqlite/migrate.go +++ b/store/sqlite/migrator.go @@ -117,7 +117,7 @@ func (d *Driver) applyLatestSchema(ctx context.Context) error { if d.profile.Mode == "prod" { schemaMode = "prod" } - latestSchemaPath := fmt.Sprintf("%s/%s/%s", "migration", schemaMode, latestSchemaFileName) + latestSchemaPath := fmt.Sprintf("migration/%s/%s", schemaMode, latestSchemaFileName) buf, err := migrationFS.ReadFile(latestSchemaPath) if err != nil { return errors.Wrapf(err, "failed to read latest schema %q", latestSchemaPath)