mirror of https://github.com/usememos/memos.git
refactor(store): remove deprecated migration_history table and backward compatibility code
Complete removal of migration_history system in favor of instance_setting based schema versioning. Changes: - Remove migration_history table creation from all LATEST.sql files - Delete all migration_history model and implementation files (~300 lines) - Remove FindMigrationHistoryList and UpsertMigrationHistory from Driver interface - Replace complex backward compatibility functions with simple version check - Update health check to use instance_setting instead of migration_history - Simplify checkMinimumUpgradeVersion to detect pre-v0.22 installations Breaking change: Users on versions < v0.22.0 (May 2024) must upgrade to v0.25.x first before upgrading to this version. Clear error message with upgrade instructions will be shown for old installations. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
fae5eac31b
commit
0610257562
|
|
@ -6,15 +6,19 @@ import (
|
||||||
"google.golang.org/grpc/codes"
|
"google.golang.org/grpc/codes"
|
||||||
"google.golang.org/grpc/health/grpc_health_v1"
|
"google.golang.org/grpc/health/grpc_health_v1"
|
||||||
"google.golang.org/grpc/status"
|
"google.golang.org/grpc/status"
|
||||||
|
|
||||||
"github.com/usememos/memos/store"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func (s *APIV1Service) Check(ctx context.Context,
|
func (s *APIV1Service) Check(ctx context.Context,
|
||||||
_ *grpc_health_v1.HealthCheckRequest) (*grpc_health_v1.HealthCheckResponse, error) {
|
_ *grpc_health_v1.HealthCheckRequest) (*grpc_health_v1.HealthCheckResponse, error) {
|
||||||
history, err := s.Store.GetDriver().FindMigrationHistoryList(ctx, &store.FindMigrationHistory{})
|
// Check if database is initialized by verifying instance basic setting exists
|
||||||
if err != nil || len(history) == 0 {
|
instanceBasicSetting, err := s.Store.GetInstanceBasicSetting(ctx)
|
||||||
return nil, status.Errorf(codes.Unavailable, "not available")
|
if err != nil {
|
||||||
|
return nil, status.Errorf(codes.Unavailable, "database not initialized: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify schema version is set (empty means database not properly initialized)
|
||||||
|
if instanceBasicSetting.SchemaVersion == "" {
|
||||||
|
return nil, status.Errorf(codes.Unavailable, "schema version not set")
|
||||||
}
|
}
|
||||||
|
|
||||||
return &grpc_health_v1.HealthCheckResponse{Status: grpc_health_v1.HealthCheckResponse_SERVING}, nil
|
return &grpc_health_v1.HealthCheckResponse{Status: grpc_health_v1.HealthCheckResponse_SERVING}, nil
|
||||||
|
|
|
||||||
|
|
@ -1,60 +0,0 @@
|
||||||
package mysql
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
|
|
||||||
"github.com/usememos/memos/store"
|
|
||||||
)
|
|
||||||
|
|
||||||
// FindMigrationHistoryList retrieves all migration history records.
|
|
||||||
// NOTE: This method is deprecated along with the migration_history table.
|
|
||||||
func (d *DB) FindMigrationHistoryList(ctx context.Context, _ *store.FindMigrationHistory) ([]*store.MigrationHistory, error) {
|
|
||||||
query := "SELECT `version`, UNIX_TIMESTAMP(`created_ts`) FROM `migration_history` ORDER BY `created_ts` DESC"
|
|
||||||
rows, err := d.db.QueryContext(ctx, query)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
defer rows.Close()
|
|
||||||
|
|
||||||
list := make([]*store.MigrationHistory, 0)
|
|
||||||
for rows.Next() {
|
|
||||||
var migrationHistory store.MigrationHistory
|
|
||||||
if err := rows.Scan(
|
|
||||||
&migrationHistory.Version,
|
|
||||||
&migrationHistory.CreatedTs,
|
|
||||||
); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
list = append(list, &migrationHistory)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := rows.Err(); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return list, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// UpsertMigrationHistory inserts or updates a migration history record.
|
|
||||||
// NOTE: This method is deprecated along with the migration_history table.
|
|
||||||
// This uses separate INSERT and SELECT queries instead of INSERT...RETURNING because
|
|
||||||
// MySQL doesn't support RETURNING clause in the same way as PostgreSQL/SQLite.
|
|
||||||
// This could have race conditions but is acceptable for deprecated transition code.
|
|
||||||
func (d *DB) UpsertMigrationHistory(ctx context.Context, upsert *store.UpsertMigrationHistory) (*store.MigrationHistory, error) {
|
|
||||||
stmt := "INSERT INTO `migration_history` (`version`) VALUES (?) ON DUPLICATE KEY UPDATE `version` = ?"
|
|
||||||
_, err := d.db.ExecContext(ctx, stmt, upsert.Version, upsert.Version)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
var migrationHistory store.MigrationHistory
|
|
||||||
stmt = "SELECT `version`, UNIX_TIMESTAMP(`created_ts`) FROM `migration_history` WHERE `version` = ?"
|
|
||||||
if err := d.db.QueryRowContext(ctx, stmt, upsert.Version).Scan(
|
|
||||||
&migrationHistory.Version,
|
|
||||||
&migrationHistory.CreatedTs,
|
|
||||||
); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return &migrationHistory, nil
|
|
||||||
}
|
|
||||||
|
|
@ -1,61 +0,0 @@
|
||||||
package postgres
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
|
|
||||||
"github.com/usememos/memos/store"
|
|
||||||
)
|
|
||||||
|
|
||||||
// FindMigrationHistoryList retrieves all migration history records.
|
|
||||||
// NOTE: This method is deprecated along with the migration_history table.
|
|
||||||
func (d *DB) FindMigrationHistoryList(ctx context.Context, _ *store.FindMigrationHistory) ([]*store.MigrationHistory, error) {
|
|
||||||
query := "SELECT version, created_ts FROM migration_history ORDER BY created_ts DESC"
|
|
||||||
rows, err := d.db.QueryContext(ctx, query)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
defer rows.Close()
|
|
||||||
|
|
||||||
list := make([]*store.MigrationHistory, 0)
|
|
||||||
for rows.Next() {
|
|
||||||
var migrationHistory store.MigrationHistory
|
|
||||||
if err := rows.Scan(
|
|
||||||
&migrationHistory.Version,
|
|
||||||
&migrationHistory.CreatedTs,
|
|
||||||
); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
list = append(list, &migrationHistory)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := rows.Err(); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return list, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// UpsertMigrationHistory inserts or updates a migration history record.
|
|
||||||
// NOTE: This method is deprecated along with the migration_history table.
|
|
||||||
func (d *DB) UpsertMigrationHistory(ctx context.Context, upsert *store.UpsertMigrationHistory) (*store.MigrationHistory, error) {
|
|
||||||
stmt := `
|
|
||||||
INSERT INTO migration_history (
|
|
||||||
version
|
|
||||||
)
|
|
||||||
VALUES ($1)
|
|
||||||
ON CONFLICT(version) DO UPDATE
|
|
||||||
SET
|
|
||||||
version=EXCLUDED.version
|
|
||||||
RETURNING version, created_ts
|
|
||||||
`
|
|
||||||
var migrationHistory store.MigrationHistory
|
|
||||||
if err := d.db.QueryRowContext(ctx, stmt, upsert.Version).Scan(
|
|
||||||
&migrationHistory.Version,
|
|
||||||
&migrationHistory.CreatedTs,
|
|
||||||
); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return &migrationHistory, nil
|
|
||||||
}
|
|
||||||
|
|
@ -1,61 +0,0 @@
|
||||||
package sqlite
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
|
|
||||||
"github.com/usememos/memos/store"
|
|
||||||
)
|
|
||||||
|
|
||||||
// FindMigrationHistoryList retrieves all migration history records.
|
|
||||||
// NOTE: This method is deprecated along with the migration_history table.
|
|
||||||
func (d *DB) FindMigrationHistoryList(ctx context.Context, _ *store.FindMigrationHistory) ([]*store.MigrationHistory, error) {
|
|
||||||
query := "SELECT `version`, `created_ts` FROM `migration_history` ORDER BY `created_ts` DESC"
|
|
||||||
rows, err := d.db.QueryContext(ctx, query)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
defer rows.Close()
|
|
||||||
|
|
||||||
list := make([]*store.MigrationHistory, 0)
|
|
||||||
for rows.Next() {
|
|
||||||
var migrationHistory store.MigrationHistory
|
|
||||||
if err := rows.Scan(
|
|
||||||
&migrationHistory.Version,
|
|
||||||
&migrationHistory.CreatedTs,
|
|
||||||
); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
list = append(list, &migrationHistory)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := rows.Err(); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return list, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// UpsertMigrationHistory inserts or updates a migration history record.
|
|
||||||
// NOTE: This method is deprecated along with the migration_history table.
|
|
||||||
func (d *DB) UpsertMigrationHistory(ctx context.Context, upsert *store.UpsertMigrationHistory) (*store.MigrationHistory, error) {
|
|
||||||
stmt := `
|
|
||||||
INSERT INTO migration_history (
|
|
||||||
version
|
|
||||||
)
|
|
||||||
VALUES (?)
|
|
||||||
ON CONFLICT(version) DO UPDATE
|
|
||||||
SET
|
|
||||||
version=EXCLUDED.version
|
|
||||||
RETURNING version, created_ts
|
|
||||||
`
|
|
||||||
var migrationHistory store.MigrationHistory
|
|
||||||
if err := d.db.QueryRowContext(ctx, stmt, upsert.Version).Scan(
|
|
||||||
&migrationHistory.Version,
|
|
||||||
&migrationHistory.CreatedTs,
|
|
||||||
); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return &migrationHistory, nil
|
|
||||||
}
|
|
||||||
|
|
@ -13,13 +13,6 @@ type Driver interface {
|
||||||
|
|
||||||
IsInitialized(ctx context.Context) (bool, error)
|
IsInitialized(ctx context.Context) (bool, error)
|
||||||
|
|
||||||
// MigrationHistory model related methods.
|
|
||||||
// NOTE: These methods are deprecated. The migration_history table is no longer used
|
|
||||||
// for tracking schema versions. Schema version is now stored in instance_setting.
|
|
||||||
// These methods are kept for backward compatibility to migrate existing installations.
|
|
||||||
FindMigrationHistoryList(ctx context.Context, find *FindMigrationHistory) ([]*MigrationHistory, error)
|
|
||||||
UpsertMigrationHistory(ctx context.Context, upsert *UpsertMigrationHistory) (*MigrationHistory, error)
|
|
||||||
|
|
||||||
// Activity model related methods.
|
// Activity model related methods.
|
||||||
CreateActivity(ctx context.Context, create *Activity) (*Activity, error)
|
CreateActivity(ctx context.Context, create *Activity) (*Activity, error)
|
||||||
ListActivities(ctx context.Context, find *FindActivity) ([]*Activity, error)
|
ListActivities(ctx context.Context, find *FindActivity) ([]*Activity, error)
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,3 @@
|
||||||
-- migration_history
|
|
||||||
CREATE TABLE `migration_history` (
|
|
||||||
`version` VARCHAR(256) NOT NULL PRIMARY KEY,
|
|
||||||
`created_ts` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
|
|
||||||
);
|
|
||||||
|
|
||||||
-- system_setting
|
-- system_setting
|
||||||
CREATE TABLE `system_setting` (
|
CREATE TABLE `system_setting` (
|
||||||
`name` VARCHAR(256) NOT NULL PRIMARY KEY,
|
`name` VARCHAR(256) NOT NULL PRIMARY KEY,
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,3 @@
|
||||||
-- migration_history
|
|
||||||
CREATE TABLE migration_history (
|
|
||||||
version TEXT NOT NULL PRIMARY KEY,
|
|
||||||
created_ts BIGINT NOT NULL DEFAULT EXTRACT(EPOCH FROM NOW())
|
|
||||||
);
|
|
||||||
|
|
||||||
-- system_setting
|
-- system_setting
|
||||||
CREATE TABLE system_setting (
|
CREATE TABLE system_setting (
|
||||||
name TEXT NOT NULL PRIMARY KEY,
|
name TEXT NOT NULL PRIMARY KEY,
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,3 @@
|
||||||
-- migration_history
|
|
||||||
CREATE TABLE migration_history (
|
|
||||||
version TEXT NOT NULL PRIMARY KEY,
|
|
||||||
created_ts BIGINT NOT NULL DEFAULT (strftime('%s', 'now'))
|
|
||||||
);
|
|
||||||
|
|
||||||
-- system_setting
|
-- system_setting
|
||||||
CREATE TABLE system_setting (
|
CREATE TABLE system_setting (
|
||||||
name TEXT NOT NULL,
|
name TEXT NOT NULL,
|
||||||
|
|
|
||||||
|
|
@ -1,21 +0,0 @@
|
||||||
package store
|
|
||||||
|
|
||||||
// MigrationHistory represents a record in the migration_history table.
|
|
||||||
// NOTE: The migration_history table is deprecated in favor of storing schema version
|
|
||||||
// in system_setting (BASIC setting). This is kept for backward compatibility only.
|
|
||||||
// Migration from migration_history to system_setting happens automatically during startup.
|
|
||||||
type MigrationHistory struct {
|
|
||||||
Version string
|
|
||||||
CreatedTs int64
|
|
||||||
}
|
|
||||||
|
|
||||||
// UpsertMigrationHistory is used to insert or update a migration history record.
|
|
||||||
// NOTE: This is deprecated along with the migration_history table.
|
|
||||||
type UpsertMigrationHistory struct {
|
|
||||||
Version string
|
|
||||||
}
|
|
||||||
|
|
||||||
// FindMigrationHistory is used to query migration history records.
|
|
||||||
// NOTE: This is deprecated along with the migration_history table.
|
|
||||||
type FindMigrationHistory struct {
|
|
||||||
}
|
|
||||||
|
|
@ -21,20 +21,18 @@ import (
|
||||||
// Migration System Overview:
|
// Migration System Overview:
|
||||||
//
|
//
|
||||||
// The migration system handles database schema versioning and upgrades.
|
// The migration system handles database schema versioning and upgrades.
|
||||||
// Schema version is stored in system_setting (the new system).
|
// Schema version is stored in instance_setting (formerly system_setting).
|
||||||
// The old migration_history table is deprecated but still supported for backward compatibility.
|
|
||||||
//
|
//
|
||||||
// Migration Flow:
|
// Migration Flow:
|
||||||
// 1. preMigrate: Check if DB is initialized. If not, apply LATEST.sql
|
// 1. preMigrate: Check if DB is initialized. If not, apply LATEST.sql
|
||||||
// 2. normalizeMigrationHistoryList: Normalize old migration_history records (for pre-0.22 installations)
|
// 2. checkMinimumUpgradeVersion: Verify installation can be upgraded (reject pre-0.22 installations)
|
||||||
// 3. migrateSchemaVersionToSetting: Migrate version from migration_history to system_setting
|
// 3. Migrate (prod mode): Apply incremental migrations from current to target version
|
||||||
// 4. Migrate (prod mode): Apply incremental migrations from current to target version
|
// 4. Migrate (demo mode): Seed database with demo data
|
||||||
// 5. Migrate (demo mode): Seed database with demo data
|
|
||||||
//
|
//
|
||||||
// Version Tracking:
|
// Version Tracking:
|
||||||
// - New installations: Schema version set in system_setting immediately
|
// - New installations: Schema version set in instance_setting immediately
|
||||||
// - Old installations: Version migrated from migration_history to system_setting automatically
|
// - Existing v0.22+ installations: Schema version tracked in instance_setting
|
||||||
// - Empty version: Treated as 0.0.0 and all migrations applied
|
// - Pre-v0.22 installations: Must upgrade to v0.25.x first (migration_history → instance_setting migration)
|
||||||
//
|
//
|
||||||
// Migration Files:
|
// Migration Files:
|
||||||
// - Location: store/migration/{driver}/{version}/NN__description.sql
|
// - Location: store/migration/{driver}/{version}/NN__description.sql
|
||||||
|
|
@ -53,17 +51,13 @@ const (
|
||||||
// For example, "1__create_table.sql".
|
// For example, "1__create_table.sql".
|
||||||
MigrateFileNameSplit = "__"
|
MigrateFileNameSplit = "__"
|
||||||
// LatestSchemaFileName is the name of the latest schema file.
|
// LatestSchemaFileName is the name of the latest schema file.
|
||||||
// This file is used to apply the latest schema when no migration history is found.
|
// This file is used to initialize fresh installations with the current schema.
|
||||||
LatestSchemaFileName = "LATEST.sql"
|
LatestSchemaFileName = "LATEST.sql"
|
||||||
|
|
||||||
// defaultSchemaVersion is used when schema version is empty or not set.
|
// defaultSchemaVersion is used when schema version is empty or not set.
|
||||||
// This handles edge cases for old installations without version tracking.
|
// This handles edge cases for old installations without version tracking.
|
||||||
defaultSchemaVersion = "0.0.0"
|
defaultSchemaVersion = "0.0.0"
|
||||||
|
|
||||||
// migrationHistoryNormalizedVersion is the version where migration_history normalization was completed.
|
|
||||||
// Before 0.22, migration history had inconsistent versioning that needed normalization.
|
|
||||||
migrationHistoryNormalizedVersion = "0.22"
|
|
||||||
|
|
||||||
// Mode constants for profile mode.
|
// Mode constants for profile mode.
|
||||||
modeProd = "prod"
|
modeProd = "prod"
|
||||||
modeDemo = "demo"
|
modeDemo = "demo"
|
||||||
|
|
@ -262,11 +256,8 @@ func (s *Store) preMigrate(ctx context.Context) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
if s.profile.Mode == modeProd {
|
if s.profile.Mode == modeProd {
|
||||||
if err := s.normalizeMigrationHistoryList(ctx); err != nil {
|
if err := s.checkMinimumUpgradeVersion(ctx); err != nil {
|
||||||
return errors.Wrap(err, "failed to normalize migration history list")
|
return err // Error message is already descriptive, don't wrap it
|
||||||
}
|
|
||||||
if err := s.migrateSchemaVersionToSetting(ctx); err != nil {
|
|
||||||
return errors.Wrap(err, "failed to migrate schema version to setting")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
|
@ -380,96 +371,44 @@ func (s *Store) updateCurrentSchemaVersion(ctx context.Context, schemaVersion st
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// normalizeMigrationHistoryList normalizes the migration history list.
|
// checkMinimumUpgradeVersion verifies the installation meets minimum version requirements for upgrade.
|
||||||
// It checks the existing migration history and updates it to the latest schema version if necessary.
|
// For very old installations (< v0.22.0), users must upgrade to v0.25.x first before upgrading to current version.
|
||||||
// NOTE: This is a transition function for backward compatibility with the deprecated migration_history table.
|
// This is necessary because schema version tracking was moved from migration_history to instance_setting in v0.22.0.
|
||||||
// This ensures that old installations (< 0.22) have their migration_history normalized before migrating to system_setting.
|
func (s *Store) checkMinimumUpgradeVersion(ctx context.Context) error {
|
||||||
func (s *Store) normalizeMigrationHistoryList(ctx context.Context) error {
|
|
||||||
migrationHistoryList, err := s.driver.FindMigrationHistoryList(ctx, &FindMigrationHistory{})
|
|
||||||
if err != nil {
|
|
||||||
return errors.Wrap(err, "failed to find migration history")
|
|
||||||
}
|
|
||||||
versions := []string{}
|
|
||||||
for _, migrationHistory := range migrationHistoryList {
|
|
||||||
versions = append(versions, migrationHistory.Version)
|
|
||||||
}
|
|
||||||
if len(versions) == 0 {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
sort.Sort(version.SortVersion(versions))
|
|
||||||
latestVersion := versions[len(versions)-1]
|
|
||||||
latestMinorVersion := version.GetMinorVersion(latestVersion)
|
|
||||||
|
|
||||||
// If the latest version is greater than migrationHistoryNormalizedVersion, return.
|
|
||||||
// As of that version, the migration history is already normalized.
|
|
||||||
if version.IsVersionGreaterThan(latestMinorVersion, migrationHistoryNormalizedVersion) {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
schemaVersionMap := map[string]string{}
|
|
||||||
filePaths, err := fs.Glob(migrationFS, fmt.Sprintf("%s*/*.sql", s.getMigrationBasePath()))
|
|
||||||
if err != nil {
|
|
||||||
return errors.Wrap(err, "failed to read migration files")
|
|
||||||
}
|
|
||||||
sort.Strings(filePaths)
|
|
||||||
for _, filePath := range filePaths {
|
|
||||||
fileSchemaVersion, err := s.getSchemaVersionOfMigrateScript(filePath)
|
|
||||||
if err != nil {
|
|
||||||
return errors.Wrap(err, "failed to get schema version of migrate script")
|
|
||||||
}
|
|
||||||
schemaVersionMap[version.GetMinorVersion(fileSchemaVersion)] = fileSchemaVersion
|
|
||||||
}
|
|
||||||
|
|
||||||
latestSchemaVersion := schemaVersionMap[latestMinorVersion]
|
|
||||||
if latestSchemaVersion == "" {
|
|
||||||
return errors.Errorf("latest schema version not found")
|
|
||||||
}
|
|
||||||
if version.IsVersionGreaterOrEqualThan(latestVersion, latestSchemaVersion) {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
if _, err := s.driver.UpsertMigrationHistory(ctx, &UpsertMigrationHistory{
|
|
||||||
Version: latestSchemaVersion,
|
|
||||||
}); err != nil {
|
|
||||||
return errors.Wrap(err, "failed to upsert latest migration history")
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// migrateSchemaVersionToSetting migrates the schema version from the migration history to the instance basic setting.
|
|
||||||
// It retrieves the migration history, sorts the versions, and updates the instance basic setting if necessary.
|
|
||||||
// NOTE: This is a transition function for backward compatibility with the deprecated migration_history table.
|
|
||||||
// The migration_history table is deprecated in favor of storing schema version in system_setting.
|
|
||||||
// This handles upgrades from old installations that only have migration_history but no system_setting.
|
|
||||||
func (s *Store) migrateSchemaVersionToSetting(ctx context.Context) error {
|
|
||||||
migrationHistoryList, err := s.driver.FindMigrationHistoryList(ctx, &FindMigrationHistory{})
|
|
||||||
if err != nil {
|
|
||||||
return errors.Wrap(err, "failed to find migration history")
|
|
||||||
}
|
|
||||||
versions := []string{}
|
|
||||||
for _, migrationHistory := range migrationHistoryList {
|
|
||||||
versions = append(versions, migrationHistory.Version)
|
|
||||||
}
|
|
||||||
if len(versions) == 0 {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
sort.Sort(version.SortVersion(versions))
|
|
||||||
latestVersion := versions[len(versions)-1]
|
|
||||||
|
|
||||||
instanceBasicSetting, err := s.GetInstanceBasicSetting(ctx)
|
instanceBasicSetting, err := s.GetInstanceBasicSetting(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Wrap(err, "failed to get instance basic setting")
|
return errors.Wrap(err, "failed to get instance basic setting")
|
||||||
}
|
}
|
||||||
|
|
||||||
// If instance_setting has no schema version (empty), or migration_history has a newer version, update instance_setting.
|
schemaVersion := instanceBasicSetting.SchemaVersion
|
||||||
// This handles upgrades from old installations where schema version was only tracked in migration_history.
|
|
||||||
if isVersionEmpty(instanceBasicSetting.SchemaVersion) || version.IsVersionGreaterThan(latestVersion, instanceBasicSetting.SchemaVersion) {
|
// If schema version is >= 0.22.0, the installation is up-to-date
|
||||||
slog.Info("migrating schema version from migration_history to instance_setting",
|
if !isVersionEmpty(schemaVersion) && version.IsVersionGreaterOrEqualThan(schemaVersion, "0.22.0") {
|
||||||
slog.String("from", instanceBasicSetting.SchemaVersion),
|
return nil
|
||||||
slog.String("to", latestVersion),
|
}
|
||||||
)
|
|
||||||
if err := s.updateCurrentSchemaVersion(ctx, latestVersion); err != nil {
|
// If schema version is set but < 0.22.0, this is an old installation
|
||||||
return errors.Wrap(err, "failed to update current schema version")
|
if !isVersionEmpty(schemaVersion) && !version.IsVersionGreaterOrEqualThan(schemaVersion, "0.22.0") {
|
||||||
}
|
currentVersion, _ := s.GetCurrentSchemaVersion()
|
||||||
}
|
|
||||||
|
return errors.Errorf(
|
||||||
|
"Your Memos installation is too old to upgrade directly.\n\n"+
|
||||||
|
"Your current version: %s\n"+
|
||||||
|
"Target version: %s\n"+
|
||||||
|
"Minimum required: v0.22.0 (May 2024)\n\n"+
|
||||||
|
"Upgrade path:\n"+
|
||||||
|
"1. First upgrade to v0.25.3: https://github.com/usememos/memos/releases/tag/v0.25.3\n"+
|
||||||
|
"2. Start the server and verify it works\n"+
|
||||||
|
"3. Then upgrade to the latest version\n\n"+
|
||||||
|
"This is required because schema version tracking was moved from migration_history\n"+
|
||||||
|
"to instance_setting in v0.22.0. The intermediate upgrade handles this migration safely.",
|
||||||
|
schemaVersion,
|
||||||
|
currentVersion,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Schema version is empty - this is either a fresh install or corrupted installation
|
||||||
|
// Fresh installs will have schema version set immediately after LATEST.sql is applied
|
||||||
|
// So this should not be an issue in normal operation
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -37,7 +37,6 @@ func NewTestingStore(ctx context.Context, t *testing.T) *store.Store {
|
||||||
func resetTestingDB(ctx context.Context, profile *profile.Profile, dbDriver store.Driver) {
|
func resetTestingDB(ctx context.Context, profile *profile.Profile, dbDriver store.Driver) {
|
||||||
if profile.Driver == "mysql" {
|
if profile.Driver == "mysql" {
|
||||||
_, err := dbDriver.GetDB().ExecContext(ctx, `
|
_, err := dbDriver.GetDB().ExecContext(ctx, `
|
||||||
DROP TABLE IF EXISTS migration_history;
|
|
||||||
DROP TABLE IF EXISTS system_setting;
|
DROP TABLE IF EXISTS system_setting;
|
||||||
DROP TABLE IF EXISTS user;
|
DROP TABLE IF EXISTS user;
|
||||||
DROP TABLE IF EXISTS user_setting;
|
DROP TABLE IF EXISTS user_setting;
|
||||||
|
|
@ -57,7 +56,6 @@ func resetTestingDB(ctx context.Context, profile *profile.Profile, dbDriver stor
|
||||||
}
|
}
|
||||||
} else if profile.Driver == "postgres" {
|
} else if profile.Driver == "postgres" {
|
||||||
_, err := dbDriver.GetDB().ExecContext(ctx, `
|
_, err := dbDriver.GetDB().ExecContext(ctx, `
|
||||||
DROP TABLE IF EXISTS migration_history CASCADE;
|
|
||||||
DROP TABLE IF EXISTS system_setting CASCADE;
|
DROP TABLE IF EXISTS system_setting CASCADE;
|
||||||
DROP TABLE IF EXISTS "user" CASCADE;
|
DROP TABLE IF EXISTS "user" CASCADE;
|
||||||
DROP TABLE IF EXISTS user_setting CASCADE;
|
DROP TABLE IF EXISTS user_setting CASCADE;
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue