mirror of
https://github.com/usememos/memos.git
synced 2024-11-23 13:56:22 +03:00
refactor: store migrator
This commit is contained in:
parent
1ae3afc0ba
commit
6e901fc940
@ -58,16 +58,11 @@ var (
|
|||||||
slog.Error("failed to create db driver", "error", err)
|
slog.Error("failed to create db driver", "error", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if err := dbDriver.Migrate(ctx); err != nil {
|
|
||||||
cancel()
|
|
||||||
slog.Error("failed to migrate database", "error", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
storeInstance := store.New(dbDriver, instanceProfile)
|
storeInstance := store.New(dbDriver, instanceProfile)
|
||||||
if err := storeInstance.MigrateManually(ctx); err != nil {
|
if err := storeInstance.Migrate(ctx); err != nil {
|
||||||
cancel()
|
cancel()
|
||||||
slog.Error("failed to migrate manually", "error", err)
|
slog.Error("failed to migrate", "error", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,7 +2,7 @@ root = "."
|
|||||||
tmp_dir = ".air"
|
tmp_dir = ".air"
|
||||||
|
|
||||||
[build]
|
[build]
|
||||||
bin = "./.air/memos --mode dev --public true"
|
bin = "./.air/memos --mode demo --public true"
|
||||||
cmd = "go build -o ./.air/memos ./bin/memos/main.go"
|
cmd = "go build -o ./.air/memos ./bin/memos/main.go"
|
||||||
delay = 1000
|
delay = 1000
|
||||||
exclude_dir = [".air", "web", "build"]
|
exclude_dir = [".air", "web", "build"]
|
||||||
|
@ -1,167 +0,0 @@
|
|||||||
package mysql
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"embed"
|
|
||||||
"fmt"
|
|
||||||
"io/fs"
|
|
||||||
"regexp"
|
|
||||||
"sort"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/pkg/errors"
|
|
||||||
|
|
||||||
"github.com/usememos/memos/server/version"
|
|
||||||
"github.com/usememos/memos/store"
|
|
||||||
)
|
|
||||||
|
|
||||||
//go:embed migration
|
|
||||||
var migrationFS embed.FS
|
|
||||||
|
|
||||||
const (
|
|
||||||
latestSchemaFileName = "LATEST__SCHEMA.sql"
|
|
||||||
)
|
|
||||||
|
|
||||||
func (d *DB) Migrate(ctx context.Context) error {
|
|
||||||
if d.profile.IsDev() {
|
|
||||||
return d.nonProdMigrate(ctx)
|
|
||||||
}
|
|
||||||
|
|
||||||
return d.prodMigrate(ctx)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *DB) nonProdMigrate(ctx context.Context) error {
|
|
||||||
rows, err := d.db.QueryContext(ctx, "SHOW TABLES")
|
|
||||||
if err != nil {
|
|
||||||
return errors.Errorf("failed to query database tables: %s", err)
|
|
||||||
}
|
|
||||||
if rows.Err() != nil {
|
|
||||||
return errors.Errorf("failed to query database tables: %s", err)
|
|
||||||
}
|
|
||||||
defer rows.Close()
|
|
||||||
|
|
||||||
var tables []string
|
|
||||||
for rows.Next() {
|
|
||||||
var table string
|
|
||||||
err := rows.Scan(&table)
|
|
||||||
if err != nil {
|
|
||||||
return errors.Errorf("failed to scan table name: %s", err)
|
|
||||||
}
|
|
||||||
tables = append(tables, table)
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(tables) != 0 {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
buf, err := migrationFS.ReadFile("migration/dev/" + 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)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *DB) prodMigrate(ctx context.Context) error {
|
|
||||||
currentVersion := version.GetCurrentVersion(d.profile.Mode)
|
|
||||||
migrationHistoryList, err := d.FindMigrationHistoryList(ctx, &store.FindMigrationHistory{})
|
|
||||||
// If there is no migration history, we should apply the latest schema.
|
|
||||||
if err != nil || len(migrationHistoryList) == 0 {
|
|
||||||
buf, err := migrationFS.ReadFile("migration/prod/" + latestSchemaFileName)
|
|
||||||
if err != nil {
|
|
||||||
return errors.Errorf("failed to read latest schema file: %s", err)
|
|
||||||
}
|
|
||||||
if _, err := d.db.ExecContext(ctx, string(buf)); err != nil {
|
|
||||||
return errors.Errorf("failed to exec latest schema: %s", err)
|
|
||||||
}
|
|
||||||
if _, err := d.UpsertMigrationHistory(ctx, &store.UpsertMigrationHistory{
|
|
||||||
Version: currentVersion,
|
|
||||||
}); err != nil {
|
|
||||||
return errors.Wrap(err, "failed to upsert migration history")
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
migrationHistoryVersionList := []string{}
|
|
||||||
for _, migrationHistory := range migrationHistoryList {
|
|
||||||
migrationHistoryVersionList = append(migrationHistoryVersionList, migrationHistory.Version)
|
|
||||||
}
|
|
||||||
sort.Sort(version.SortVersion(migrationHistoryVersionList))
|
|
||||||
latestMigrationHistoryVersion := migrationHistoryVersionList[len(migrationHistoryVersionList)-1]
|
|
||||||
if !version.IsVersionGreaterThan(version.GetSchemaVersion(currentVersion), latestMigrationHistoryVersion) {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Println("start to migrate database schema")
|
|
||||||
for _, minorVersion := range getMinorVersionList() {
|
|
||||||
normalizedVersion := minorVersion + ".0"
|
|
||||||
if version.IsVersionGreaterThan(normalizedVersion, latestMigrationHistoryVersion) && version.IsVersionGreaterOrEqualThan(currentVersion, normalizedVersion) {
|
|
||||||
fmt.Println("applying migration of", normalizedVersion)
|
|
||||||
if err := d.applyMigrationForMinorVersion(ctx, minorVersion); err != nil {
|
|
||||||
return errors.Wrap(err, "failed to apply minor version migration")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fmt.Println("end migrate")
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *DB) applyMigrationForMinorVersion(ctx context.Context, minorVersion string) error {
|
|
||||||
filenames, err := fs.Glob(migrationFS, fmt.Sprintf("migration/prod/%s/*.sql", minorVersion))
|
|
||||||
if err != nil {
|
|
||||||
return errors.Wrap(err, "failed to read ddl files")
|
|
||||||
}
|
|
||||||
|
|
||||||
sort.Strings(filenames)
|
|
||||||
// Loop over all migration files and execute them in order.
|
|
||||||
for _, filename := range filenames {
|
|
||||||
buf, err := migrationFS.ReadFile(filename)
|
|
||||||
if err != nil {
|
|
||||||
return errors.Wrapf(err, "failed to read minor version migration file, filename=%s", filename)
|
|
||||||
}
|
|
||||||
for _, stmt := range strings.Split(string(buf), ";") {
|
|
||||||
if strings.TrimSpace(stmt) == "" {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if _, err := d.db.ExecContext(ctx, stmt); err != nil {
|
|
||||||
return errors.Wrapf(err, "migrate error: %s", stmt)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Upsert the newest version to migration_history.
|
|
||||||
version := minorVersion + ".0"
|
|
||||||
if _, err = d.UpsertMigrationHistory(ctx, &store.UpsertMigrationHistory{Version: version}); err != nil {
|
|
||||||
return errors.Wrapf(err, "failed to upsert migration history with version: %s", version)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// minorDirRegexp is a regular expression for minor version directory.
|
|
||||||
var minorDirRegexp = regexp.MustCompile(`^migration/prod/[0-9]+\.[0-9]+$`)
|
|
||||||
|
|
||||||
func getMinorVersionList() []string {
|
|
||||||
minorVersionList := []string{}
|
|
||||||
|
|
||||||
if err := fs.WalkDir(migrationFS, "migration", func(path string, file fs.DirEntry, err error) error {
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if file.IsDir() && minorDirRegexp.MatchString(path) {
|
|
||||||
minorVersionList = append(minorVersionList, file.Name())
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}); err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
sort.Sort(version.SortVersion(minorVersionList))
|
|
||||||
|
|
||||||
return minorVersionList
|
|
||||||
}
|
|
@ -1,9 +1,7 @@
|
|||||||
package mysql
|
package mysql
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
"database/sql"
|
"database/sql"
|
||||||
"log/slog"
|
|
||||||
|
|
||||||
"github.com/go-sql-driver/mysql"
|
"github.com/go-sql-driver/mysql"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
@ -45,30 +43,8 @@ func (d *DB) GetDB() *sql.DB {
|
|||||||
return d.db
|
return d.db
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *DB) GetCurrentDBSize(ctx context.Context) (int64, error) {
|
func (d *DB) Type() string {
|
||||||
query := "SELECT SUM(`data_length` + `index_length`) AS `size` " +
|
return "mysql"
|
||||||
" FROM information_schema.TABLES" +
|
|
||||||
" WHERE `table_schema` = ?" +
|
|
||||||
" GROUP BY `table_schema`"
|
|
||||||
rows, err := d.db.QueryContext(ctx, query, d.config.DBName)
|
|
||||||
if err != nil {
|
|
||||||
slog.Error("Query db size error, make sure you have enough privilege", "error", err)
|
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
defer rows.Close()
|
|
||||||
|
|
||||||
var size int64
|
|
||||||
for rows.Next() {
|
|
||||||
if err := rows.Scan(&size); err != nil {
|
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if rows.Err() != nil {
|
|
||||||
return 0, rows.Err()
|
|
||||||
}
|
|
||||||
|
|
||||||
return size, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *DB) Close() error {
|
func (d *DB) Close() error {
|
||||||
|
@ -1,170 +0,0 @@
|
|||||||
package postgres
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"embed"
|
|
||||||
"fmt"
|
|
||||||
"io/fs"
|
|
||||||
"regexp"
|
|
||||||
"sort"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/pkg/errors"
|
|
||||||
|
|
||||||
"github.com/usememos/memos/server/version"
|
|
||||||
"github.com/usememos/memos/store"
|
|
||||||
)
|
|
||||||
|
|
||||||
//go:embed migration
|
|
||||||
var migrationFS embed.FS
|
|
||||||
|
|
||||||
const (
|
|
||||||
latestSchemaFileName = "LATEST__SCHEMA.sql"
|
|
||||||
)
|
|
||||||
|
|
||||||
func (d *DB) Migrate(ctx context.Context) error {
|
|
||||||
if d.profile.IsDev() {
|
|
||||||
return d.nonProdMigrate(ctx)
|
|
||||||
}
|
|
||||||
|
|
||||||
return d.prodMigrate(ctx)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *DB) nonProdMigrate(ctx context.Context) error {
|
|
||||||
rows, err := d.db.QueryContext(ctx, "SELECT tablename FROM pg_catalog.pg_tables WHERE schemaname != 'pg_catalog' AND schemaname != 'information_schema';")
|
|
||||||
if err != nil {
|
|
||||||
return errors.Errorf("failed to query database tables: %s", err)
|
|
||||||
}
|
|
||||||
if rows.Err() != nil {
|
|
||||||
return errors.Errorf("failed to query database tables: %s", err)
|
|
||||||
}
|
|
||||||
defer rows.Close()
|
|
||||||
|
|
||||||
var tables []string
|
|
||||||
for rows.Next() {
|
|
||||||
var table string
|
|
||||||
err := rows.Scan(&table)
|
|
||||||
if err != nil {
|
|
||||||
return errors.Errorf("failed to scan table name: %s", err)
|
|
||||||
}
|
|
||||||
tables = append(tables, table)
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(tables) != 0 {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
buf, err := migrationFS.ReadFile("migration/dev/" + 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)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *DB) prodMigrate(ctx context.Context) error {
|
|
||||||
currentVersion := version.GetCurrentVersion(d.profile.Mode)
|
|
||||||
migrationHistoryList, err := d.FindMigrationHistoryList(ctx, &store.FindMigrationHistory{})
|
|
||||||
// If there is no migration history, we should apply the latest schema.
|
|
||||||
if err != nil || len(migrationHistoryList) == 0 {
|
|
||||||
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, &store.UpsertMigrationHistory{
|
|
||||||
Version: currentVersion,
|
|
||||||
}); err != nil {
|
|
||||||
return errors.Wrap(err, "failed to upsert migration history")
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
migrationHistoryVersionList := []string{}
|
|
||||||
for _, migrationHistory := range migrationHistoryList {
|
|
||||||
migrationHistoryVersionList = append(migrationHistoryVersionList, migrationHistory.Version)
|
|
||||||
}
|
|
||||||
sort.Sort(version.SortVersion(migrationHistoryVersionList))
|
|
||||||
latestMigrationHistoryVersion := migrationHistoryVersionList[len(migrationHistoryVersionList)-1]
|
|
||||||
if !version.IsVersionGreaterThan(version.GetSchemaVersion(currentVersion), latestMigrationHistoryVersion) {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Println("start migrate")
|
|
||||||
for _, minorVersion := range getMinorVersionList() {
|
|
||||||
normalizedVersion := minorVersion + ".0"
|
|
||||||
if version.IsVersionGreaterThan(normalizedVersion, latestMigrationHistoryVersion) && version.IsVersionGreaterOrEqualThan(currentVersion, normalizedVersion) {
|
|
||||||
fmt.Println("applying migration for", normalizedVersion)
|
|
||||||
if err := d.applyMigrationForMinorVersion(ctx, minorVersion); err != nil {
|
|
||||||
return errors.Wrap(err, "failed to apply minor version migration")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fmt.Println("end migrate")
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *DB) applyMigrationForMinorVersion(ctx context.Context, minorVersion string) error {
|
|
||||||
filenames, err := fs.Glob(migrationFS, fmt.Sprintf("migration/prod/%s/*.sql", minorVersion))
|
|
||||||
if err != nil {
|
|
||||||
return errors.Wrap(err, "failed to read ddl files")
|
|
||||||
}
|
|
||||||
|
|
||||||
sort.Strings(filenames)
|
|
||||||
// Loop over all migration files and execute them in order.
|
|
||||||
for _, filename := range filenames {
|
|
||||||
buf, err := migrationFS.ReadFile(filename)
|
|
||||||
if err != nil {
|
|
||||||
return errors.Wrapf(err, "failed to read minor version migration file, filename=%s", filename)
|
|
||||||
}
|
|
||||||
for _, stmt := range strings.Split(string(buf), ";") {
|
|
||||||
if strings.TrimSpace(stmt) == "" {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if _, err := d.db.ExecContext(ctx, stmt); err != nil {
|
|
||||||
return errors.Wrapf(err, "migrate error: %s", stmt)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Upsert the newest version to migration_history.
|
|
||||||
version := minorVersion + ".0"
|
|
||||||
if _, err = d.UpsertMigrationHistory(ctx, &store.UpsertMigrationHistory{Version: version}); err != nil {
|
|
||||||
return errors.Wrapf(err, "failed to upsert migration history with version: %s", version)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// minorDirRegexp is a regular expression for minor version directory.
|
|
||||||
var minorDirRegexp = regexp.MustCompile(`^migration/prod/[0-9]+\.[0-9]+$`)
|
|
||||||
|
|
||||||
func getMinorVersionList() []string {
|
|
||||||
minorVersionList := []string{}
|
|
||||||
|
|
||||||
if err := fs.WalkDir(migrationFS, "migration", func(path string, file fs.DirEntry, err error) error {
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if file.IsDir() && minorDirRegexp.MatchString(path) {
|
|
||||||
minorVersionList = append(minorVersionList, file.Name())
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}); err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
sort.Sort(version.SortVersion(minorVersionList))
|
|
||||||
|
|
||||||
return minorVersionList
|
|
||||||
}
|
|
@ -1,7 +1,6 @@
|
|||||||
package postgres
|
package postgres
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
"database/sql"
|
"database/sql"
|
||||||
"log"
|
"log"
|
||||||
|
|
||||||
@ -44,8 +43,8 @@ func (d *DB) GetDB() *sql.DB {
|
|||||||
return d.db
|
return d.db
|
||||||
}
|
}
|
||||||
|
|
||||||
func (*DB) GetCurrentDBSize(context.Context) (int64, error) {
|
func (d *DB) Type() string {
|
||||||
return 0, errors.New("unimplemented")
|
return "postgres"
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *DB) Close() error {
|
func (d *DB) Close() error {
|
||||||
|
@ -1,13 +1,9 @@
|
|||||||
package sqlite
|
package sqlite
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
"database/sql"
|
"database/sql"
|
||||||
"os"
|
|
||||||
|
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"google.golang.org/grpc/codes"
|
|
||||||
"google.golang.org/grpc/status"
|
|
||||||
|
|
||||||
// Import the SQLite driver.
|
// Import the SQLite driver.
|
||||||
_ "modernc.org/sqlite"
|
_ "modernc.org/sqlite"
|
||||||
@ -58,13 +54,8 @@ func (d *DB) GetDB() *sql.DB {
|
|||||||
return d.db
|
return d.db
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *DB) GetCurrentDBSize(context.Context) (int64, error) {
|
func (d *DB) Type() string {
|
||||||
fi, err := os.Stat(d.profile.DSN)
|
return "sqlite"
|
||||||
if err != nil {
|
|
||||||
return 0, status.Errorf(codes.Internal, "failed to get file info: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return fi.Size(), nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *DB) Close() error {
|
func (d *DB) Close() error {
|
||||||
|
@ -11,10 +11,9 @@ type Driver interface {
|
|||||||
GetDB() *sql.DB
|
GetDB() *sql.DB
|
||||||
Close() error
|
Close() error
|
||||||
|
|
||||||
Migrate(ctx context.Context) error
|
// Type returns the type of the driver.
|
||||||
|
// Supported types are: sqlite, mysql, postgres.
|
||||||
// current file is driver
|
Type() string
|
||||||
GetCurrentDBSize(ctx context.Context) (int64, error)
|
|
||||||
|
|
||||||
// MigrationHistory model related methods.
|
// MigrationHistory model related methods.
|
||||||
FindMigrationHistoryList(ctx context.Context, find *FindMigrationHistory) ([]*MigrationHistory, error)
|
FindMigrationHistoryList(ctx context.Context, find *FindMigrationHistory) ([]*MigrationHistory, error)
|
||||||
|
146
store/migration/sqlite/dev/LATEST__SCHEMA.sql
Normal file
146
store/migration/sqlite/dev/LATEST__SCHEMA.sql
Normal file
@ -0,0 +1,146 @@
|
|||||||
|
-- migration_history
|
||||||
|
CREATE TABLE migration_history (
|
||||||
|
version TEXT NOT NULL PRIMARY KEY,
|
||||||
|
created_ts BIGINT NOT NULL DEFAULT (strftime('%s', 'now'))
|
||||||
|
);
|
||||||
|
|
||||||
|
-- system_setting
|
||||||
|
CREATE TABLE system_setting (
|
||||||
|
name TEXT NOT NULL,
|
||||||
|
value TEXT NOT NULL,
|
||||||
|
description TEXT NOT NULL DEFAULT '',
|
||||||
|
UNIQUE(name)
|
||||||
|
);
|
||||||
|
|
||||||
|
-- user
|
||||||
|
CREATE TABLE user (
|
||||||
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
created_ts BIGINT NOT NULL DEFAULT (strftime('%s', 'now')),
|
||||||
|
updated_ts BIGINT NOT NULL DEFAULT (strftime('%s', 'now')),
|
||||||
|
row_status TEXT NOT NULL CHECK (row_status IN ('NORMAL', 'ARCHIVED')) DEFAULT 'NORMAL',
|
||||||
|
username TEXT NOT NULL UNIQUE,
|
||||||
|
role TEXT NOT NULL CHECK (role IN ('HOST', 'ADMIN', 'USER')) DEFAULT 'USER',
|
||||||
|
email TEXT NOT NULL DEFAULT '',
|
||||||
|
nickname TEXT NOT NULL DEFAULT '',
|
||||||
|
password_hash TEXT NOT NULL,
|
||||||
|
avatar_url TEXT NOT NULL DEFAULT '',
|
||||||
|
description TEXT NOT NULL DEFAULT ''
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE INDEX idx_user_username ON user (username);
|
||||||
|
|
||||||
|
-- user_setting
|
||||||
|
CREATE TABLE user_setting (
|
||||||
|
user_id INTEGER NOT NULL,
|
||||||
|
key TEXT NOT NULL,
|
||||||
|
value TEXT NOT NULL,
|
||||||
|
UNIQUE(user_id, key)
|
||||||
|
);
|
||||||
|
|
||||||
|
-- memo
|
||||||
|
CREATE TABLE memo (
|
||||||
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
uid TEXT NOT NULL UNIQUE,
|
||||||
|
creator_id INTEGER NOT NULL,
|
||||||
|
created_ts BIGINT NOT NULL DEFAULT (strftime('%s', 'now')),
|
||||||
|
updated_ts BIGINT NOT NULL DEFAULT (strftime('%s', 'now')),
|
||||||
|
row_status TEXT NOT NULL CHECK (row_status IN ('NORMAL', 'ARCHIVED')) DEFAULT 'NORMAL',
|
||||||
|
content TEXT NOT NULL DEFAULT '',
|
||||||
|
visibility TEXT NOT NULL CHECK (visibility IN ('PUBLIC', 'PROTECTED', 'PRIVATE')) DEFAULT 'PRIVATE',
|
||||||
|
tags TEXT NOT NULL DEFAULT '[]',
|
||||||
|
payload TEXT NOT NULL DEFAULT '{}'
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE INDEX idx_memo_creator_id ON memo (creator_id);
|
||||||
|
CREATE INDEX idx_memo_content ON memo (content);
|
||||||
|
CREATE INDEX idx_memo_visibility ON memo (visibility);
|
||||||
|
CREATE INDEX idx_memo_tags ON memo (tags);
|
||||||
|
|
||||||
|
-- memo_organizer
|
||||||
|
CREATE TABLE memo_organizer (
|
||||||
|
memo_id INTEGER NOT NULL,
|
||||||
|
user_id INTEGER NOT NULL,
|
||||||
|
pinned INTEGER NOT NULL CHECK (pinned IN (0, 1)) DEFAULT 0,
|
||||||
|
UNIQUE(memo_id, user_id)
|
||||||
|
);
|
||||||
|
|
||||||
|
-- memo_relation
|
||||||
|
CREATE TABLE memo_relation (
|
||||||
|
memo_id INTEGER NOT NULL,
|
||||||
|
related_memo_id INTEGER NOT NULL,
|
||||||
|
type TEXT NOT NULL,
|
||||||
|
UNIQUE(memo_id, related_memo_id, type)
|
||||||
|
);
|
||||||
|
|
||||||
|
-- resource
|
||||||
|
CREATE TABLE resource (
|
||||||
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
uid TEXT NOT NULL UNIQUE,
|
||||||
|
creator_id INTEGER NOT NULL,
|
||||||
|
created_ts BIGINT NOT NULL DEFAULT (strftime('%s', 'now')),
|
||||||
|
updated_ts BIGINT NOT NULL DEFAULT (strftime('%s', 'now')),
|
||||||
|
filename TEXT NOT NULL DEFAULT '',
|
||||||
|
blob BLOB DEFAULT NULL,
|
||||||
|
type TEXT NOT NULL DEFAULT '',
|
||||||
|
size INTEGER NOT NULL DEFAULT 0,
|
||||||
|
memo_id INTEGER,
|
||||||
|
storage_type TEXT NOT NULL DEFAULT '',
|
||||||
|
reference TEXT NOT NULL DEFAULT '',
|
||||||
|
payload TEXT NOT NULL DEFAULT '{}'
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE INDEX idx_resource_creator_id ON resource (creator_id);
|
||||||
|
|
||||||
|
CREATE INDEX idx_resource_memo_id ON resource (memo_id);
|
||||||
|
|
||||||
|
-- activity
|
||||||
|
CREATE TABLE activity (
|
||||||
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
creator_id INTEGER NOT NULL,
|
||||||
|
created_ts BIGINT NOT NULL DEFAULT (strftime('%s', 'now')),
|
||||||
|
type TEXT NOT NULL DEFAULT '',
|
||||||
|
level TEXT NOT NULL CHECK (level IN ('INFO', 'WARN', 'ERROR')) DEFAULT 'INFO',
|
||||||
|
payload TEXT NOT NULL DEFAULT '{}'
|
||||||
|
);
|
||||||
|
|
||||||
|
-- idp
|
||||||
|
CREATE TABLE idp (
|
||||||
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
name TEXT NOT NULL,
|
||||||
|
type TEXT NOT NULL,
|
||||||
|
identifier_filter TEXT NOT NULL DEFAULT '',
|
||||||
|
config TEXT NOT NULL DEFAULT '{}'
|
||||||
|
);
|
||||||
|
|
||||||
|
-- inbox
|
||||||
|
CREATE TABLE inbox (
|
||||||
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
created_ts BIGINT NOT NULL DEFAULT (strftime('%s', 'now')),
|
||||||
|
sender_id INTEGER NOT NULL,
|
||||||
|
receiver_id INTEGER NOT NULL,
|
||||||
|
status TEXT NOT NULL,
|
||||||
|
message TEXT NOT NULL DEFAULT '{}'
|
||||||
|
);
|
||||||
|
|
||||||
|
-- webhook
|
||||||
|
CREATE TABLE webhook (
|
||||||
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
created_ts BIGINT NOT NULL DEFAULT (strftime('%s', 'now')),
|
||||||
|
updated_ts BIGINT NOT NULL DEFAULT (strftime('%s', 'now')),
|
||||||
|
row_status TEXT NOT NULL CHECK (row_status IN ('NORMAL', 'ARCHIVED')) DEFAULT 'NORMAL',
|
||||||
|
creator_id INTEGER NOT NULL,
|
||||||
|
name TEXT NOT NULL,
|
||||||
|
url TEXT NOT NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE INDEX idx_webhook_creator_id ON webhook (creator_id);
|
||||||
|
|
||||||
|
-- reaction
|
||||||
|
CREATE TABLE reaction (
|
||||||
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
created_ts BIGINT NOT NULL DEFAULT (strftime('%s', 'now')),
|
||||||
|
creator_id INTEGER NOT NULL,
|
||||||
|
content_id TEXT NOT NULL,
|
||||||
|
reaction_type TEXT NOT NULL,
|
||||||
|
UNIQUE(creator_id, content_id, reaction_type)
|
||||||
|
);
|
9
store/migration/sqlite/prod/0.10/00__activity.sql
Normal file
9
store/migration/sqlite/prod/0.10/00__activity.sql
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
-- activity
|
||||||
|
CREATE TABLE activity (
|
||||||
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
creator_id INTEGER NOT NULL,
|
||||||
|
created_ts BIGINT NOT NULL DEFAULT (strftime('%s', 'now')),
|
||||||
|
type TEXT NOT NULL DEFAULT '',
|
||||||
|
level TEXT NOT NULL CHECK (level IN ('INFO', 'WARN', 'ERROR')) DEFAULT 'INFO',
|
||||||
|
payload TEXT NOT NULL DEFAULT '{}'
|
||||||
|
);
|
4
store/migration/sqlite/prod/0.11/00__user_avatar.sql
Normal file
4
store/migration/sqlite/prod/0.11/00__user_avatar.sql
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
ALTER TABLE
|
||||||
|
user
|
||||||
|
ADD
|
||||||
|
COLUMN avatar_url TEXT NOT NULL DEFAULT '';
|
8
store/migration/sqlite/prod/0.11/01__idp.sql
Normal file
8
store/migration/sqlite/prod/0.11/01__idp.sql
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
-- idp
|
||||||
|
CREATE TABLE idp (
|
||||||
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
name TEXT NOT NULL,
|
||||||
|
type TEXT NOT NULL,
|
||||||
|
identifier_filter TEXT NOT NULL DEFAULT '',
|
||||||
|
config TEXT NOT NULL DEFAULT '{}'
|
||||||
|
);
|
7
store/migration/sqlite/prod/0.11/02__storage.sql
Normal file
7
store/migration/sqlite/prod/0.11/02__storage.sql
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
-- storage
|
||||||
|
CREATE TABLE storage (
|
||||||
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
name TEXT NOT NULL,
|
||||||
|
type TEXT NOT NULL,
|
||||||
|
config TEXT NOT NULL DEFAULT '{}'
|
||||||
|
);
|
6
store/migration/sqlite/prod/0.12/00__user_setting.sql
Normal file
6
store/migration/sqlite/prod/0.12/00__user_setting.sql
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
UPDATE
|
||||||
|
user_setting
|
||||||
|
SET
|
||||||
|
key = 'memo-visibility'
|
||||||
|
WHERE
|
||||||
|
key = 'memoVisibility';
|
69
store/migration/sqlite/prod/0.12/01__system_setting.sql
Normal file
69
store/migration/sqlite/prod/0.12/01__system_setting.sql
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
UPDATE
|
||||||
|
system_setting
|
||||||
|
SET
|
||||||
|
name = 'server-id'
|
||||||
|
WHERE
|
||||||
|
name = 'serverId';
|
||||||
|
|
||||||
|
UPDATE
|
||||||
|
system_setting
|
||||||
|
SET
|
||||||
|
name = 'secret-session'
|
||||||
|
WHERE
|
||||||
|
name = 'secretSessionName';
|
||||||
|
|
||||||
|
UPDATE
|
||||||
|
system_setting
|
||||||
|
SET
|
||||||
|
name = 'allow-signup'
|
||||||
|
WHERE
|
||||||
|
name = 'allowSignUp';
|
||||||
|
|
||||||
|
UPDATE
|
||||||
|
system_setting
|
||||||
|
SET
|
||||||
|
name = 'disable-public-memos'
|
||||||
|
WHERE
|
||||||
|
name = 'disablePublicMemos';
|
||||||
|
|
||||||
|
UPDATE
|
||||||
|
system_setting
|
||||||
|
SET
|
||||||
|
name = 'additional-style'
|
||||||
|
WHERE
|
||||||
|
name = 'additionalStyle';
|
||||||
|
|
||||||
|
UPDATE
|
||||||
|
system_setting
|
||||||
|
SET
|
||||||
|
name = 'additional-script'
|
||||||
|
WHERE
|
||||||
|
name = 'additionalScript';
|
||||||
|
|
||||||
|
UPDATE
|
||||||
|
system_setting
|
||||||
|
SET
|
||||||
|
name = 'customized-profile'
|
||||||
|
WHERE
|
||||||
|
name = 'customizedProfile';
|
||||||
|
|
||||||
|
UPDATE
|
||||||
|
system_setting
|
||||||
|
SET
|
||||||
|
name = 'storage-service-id'
|
||||||
|
WHERE
|
||||||
|
name = 'storageServiceId';
|
||||||
|
|
||||||
|
UPDATE
|
||||||
|
system_setting
|
||||||
|
SET
|
||||||
|
name = 'local-storage-path'
|
||||||
|
WHERE
|
||||||
|
name = 'localStoragePath';
|
||||||
|
|
||||||
|
UPDATE
|
||||||
|
system_setting
|
||||||
|
SET
|
||||||
|
name = 'openai-config'
|
||||||
|
WHERE
|
||||||
|
name = 'openAIConfig';
|
@ -0,0 +1,4 @@
|
|||||||
|
ALTER TABLE
|
||||||
|
resource
|
||||||
|
ADD
|
||||||
|
COLUMN internal_path TEXT NOT NULL DEFAULT '';
|
18
store/migration/sqlite/prod/0.12/04__resource_public_id.sql
Normal file
18
store/migration/sqlite/prod/0.12/04__resource_public_id.sql
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
ALTER TABLE
|
||||||
|
resource
|
||||||
|
ADD
|
||||||
|
COLUMN public_id TEXT NOT NULL DEFAULT '';
|
||||||
|
|
||||||
|
CREATE UNIQUE INDEX resource_id_public_id_unique_index ON resource (id, public_id);
|
||||||
|
|
||||||
|
UPDATE
|
||||||
|
resource
|
||||||
|
SET
|
||||||
|
public_id = printf (
|
||||||
|
'%s-%s-%s-%s-%s',
|
||||||
|
lower(hex(randomblob(4))),
|
||||||
|
lower(hex(randomblob(2))),
|
||||||
|
lower(hex(randomblob(2))),
|
||||||
|
lower(hex(randomblob(2))),
|
||||||
|
lower(hex(randomblob(6)))
|
||||||
|
);
|
7
store/migration/sqlite/prod/0.13/00__memo_relation.sql
Normal file
7
store/migration/sqlite/prod/0.13/00__memo_relation.sql
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
-- memo_relation
|
||||||
|
CREATE TABLE memo_relation (
|
||||||
|
memo_id INTEGER NOT NULL,
|
||||||
|
related_memo_id INTEGER NOT NULL,
|
||||||
|
type TEXT NOT NULL,
|
||||||
|
UNIQUE(memo_id, related_memo_id, type)
|
||||||
|
);
|
@ -0,0 +1,22 @@
|
|||||||
|
DROP TABLE IF EXISTS memo_organizer_temp;
|
||||||
|
|
||||||
|
CREATE TABLE memo_organizer_temp (
|
||||||
|
memo_id INTEGER NOT NULL,
|
||||||
|
user_id INTEGER NOT NULL,
|
||||||
|
pinned INTEGER NOT NULL CHECK (pinned IN (0, 1)) DEFAULT 0,
|
||||||
|
UNIQUE(memo_id, user_id)
|
||||||
|
);
|
||||||
|
|
||||||
|
INSERT INTO
|
||||||
|
memo_organizer_temp (memo_id, user_id, pinned)
|
||||||
|
SELECT
|
||||||
|
memo_id,
|
||||||
|
user_id,
|
||||||
|
pinned
|
||||||
|
FROM
|
||||||
|
memo_organizer;
|
||||||
|
|
||||||
|
DROP TABLE memo_organizer;
|
||||||
|
|
||||||
|
ALTER TABLE
|
||||||
|
memo_organizer_temp RENAME TO memo_organizer;
|
@ -0,0 +1,25 @@
|
|||||||
|
DROP TABLE IF EXISTS resource_temp;
|
||||||
|
|
||||||
|
CREATE TABLE resource_temp (
|
||||||
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
creator_id INTEGER NOT NULL,
|
||||||
|
created_ts BIGINT NOT NULL DEFAULT (strftime('%s', 'now')),
|
||||||
|
updated_ts BIGINT NOT NULL DEFAULT (strftime('%s', 'now')),
|
||||||
|
filename TEXT NOT NULL DEFAULT '',
|
||||||
|
blob BLOB DEFAULT NULL,
|
||||||
|
external_link TEXT NOT NULL DEFAULT '',
|
||||||
|
type TEXT NOT NULL DEFAULT '',
|
||||||
|
size INTEGER NOT NULL DEFAULT 0,
|
||||||
|
internal_path TEXT NOT NULL DEFAULT ''
|
||||||
|
);
|
||||||
|
|
||||||
|
INSERT INTO
|
||||||
|
resource_temp (id, creator_id, created_ts, updated_ts, filename, blob, external_link, type, size, internal_path)
|
||||||
|
SELECT
|
||||||
|
id, creator_id, created_ts, updated_ts, filename, blob, external_link, type, size, internal_path
|
||||||
|
FROM
|
||||||
|
resource;
|
||||||
|
|
||||||
|
DROP TABLE resource;
|
||||||
|
|
||||||
|
ALTER TABLE resource_temp RENAME TO resource;
|
5
store/migration/sqlite/prod/0.14/01__create_indexes.sql
Normal file
5
store/migration/sqlite/prod/0.14/01__create_indexes.sql
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
CREATE INDEX IF NOT EXISTS idx_user_username ON user (username);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_memo_creator_id ON memo (creator_id);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_memo_content ON memo (content);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_memo_visibility ON memo (visibility);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_resource_creator_id ON resource (creator_id);
|
25
store/migration/sqlite/prod/0.15/00__drop_user_open_id.sql
Normal file
25
store/migration/sqlite/prod/0.15/00__drop_user_open_id.sql
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
DROP TABLE IF EXISTS user_temp;
|
||||||
|
|
||||||
|
CREATE TABLE user_temp (
|
||||||
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
created_ts BIGINT NOT NULL DEFAULT (strftime('%s', 'now')),
|
||||||
|
updated_ts BIGINT NOT NULL DEFAULT (strftime('%s', 'now')),
|
||||||
|
row_status TEXT NOT NULL CHECK (row_status IN ('NORMAL', 'ARCHIVED')) DEFAULT 'NORMAL',
|
||||||
|
username TEXT NOT NULL UNIQUE,
|
||||||
|
role TEXT NOT NULL CHECK (role IN ('HOST', 'ADMIN', 'USER')) DEFAULT 'USER',
|
||||||
|
email TEXT NOT NULL DEFAULT '',
|
||||||
|
nickname TEXT NOT NULL DEFAULT '',
|
||||||
|
password_hash TEXT NOT NULL,
|
||||||
|
avatar_url TEXT NOT NULL DEFAULT ''
|
||||||
|
);
|
||||||
|
|
||||||
|
INSERT INTO
|
||||||
|
user_temp (id, created_ts, updated_ts, row_status, username, role, email, nickname, password_hash, avatar_url)
|
||||||
|
SELECT
|
||||||
|
id, created_ts, updated_ts, row_status, username, role, email, nickname, password_hash, avatar_url
|
||||||
|
FROM
|
||||||
|
user;
|
||||||
|
|
||||||
|
DROP TABLE user;
|
||||||
|
|
||||||
|
ALTER TABLE user_temp RENAME TO user;
|
@ -0,0 +1,13 @@
|
|||||||
|
ALTER TABLE resource ADD COLUMN memo_id INTEGER;
|
||||||
|
|
||||||
|
UPDATE resource
|
||||||
|
SET memo_id = (
|
||||||
|
SELECT memo_id
|
||||||
|
FROM memo_resource
|
||||||
|
WHERE resource.id = memo_resource.resource_id
|
||||||
|
LIMIT 1
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE INDEX idx_resource_memo_id ON resource (memo_id);
|
||||||
|
|
||||||
|
DROP TABLE IF EXISTS memo_resource;
|
@ -0,0 +1 @@
|
|||||||
|
DROP TABLE IF EXISTS shortcut;
|
9
store/migration/sqlite/prod/0.17/00__inbox.sql
Normal file
9
store/migration/sqlite/prod/0.17/00__inbox.sql
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
-- inbox
|
||||||
|
CREATE TABLE inbox (
|
||||||
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
created_ts BIGINT NOT NULL DEFAULT (strftime('%s', 'now')),
|
||||||
|
sender_id INTEGER NOT NULL,
|
||||||
|
receiver_id INTEGER NOT NULL,
|
||||||
|
status TEXT NOT NULL,
|
||||||
|
message TEXT NOT NULL DEFAULT '{}'
|
||||||
|
);
|
@ -0,0 +1 @@
|
|||||||
|
DELETE FROM activity;
|
12
store/migration/sqlite/prod/0.18/00__webhook.sql
Normal file
12
store/migration/sqlite/prod/0.18/00__webhook.sql
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
-- webhook
|
||||||
|
CREATE TABLE webhook (
|
||||||
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
created_ts BIGINT NOT NULL DEFAULT (strftime('%s', 'now')),
|
||||||
|
updated_ts BIGINT NOT NULL DEFAULT (strftime('%s', 'now')),
|
||||||
|
row_status TEXT NOT NULL CHECK (row_status IN ('NORMAL', 'ARCHIVED')) DEFAULT 'NORMAL',
|
||||||
|
creator_id INTEGER NOT NULL,
|
||||||
|
name TEXT NOT NULL,
|
||||||
|
url TEXT NOT NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE INDEX idx_webhook_creator_id ON webhook (creator_id);
|
4
store/migration/sqlite/prod/0.18/01__user_setting.sql
Normal file
4
store/migration/sqlite/prod/0.18/01__user_setting.sql
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
UPDATE user_setting SET key = 'USER_SETTING_LOCALE', value = REPLACE(value, '"', '') WHERE key = 'locale';
|
||||||
|
UPDATE user_setting SET key = 'USER_SETTING_APPEARANCE', value = REPLACE(value, '"', '') WHERE key = 'appearance';
|
||||||
|
UPDATE user_setting SET key = 'USER_SETTING_MEMO_VISIBILITY', value = REPLACE(value, '"', '') WHERE key = 'memo-visibility';
|
||||||
|
UPDATE user_setting SET key = 'USER_SETTING_TELEGRAM_USER_ID', value = REPLACE(value, '"', '') WHERE key = 'telegram-user-id';
|
11
store/migration/sqlite/prod/0.19/00__add_resource_name.sql
Normal file
11
store/migration/sqlite/prod/0.19/00__add_resource_name.sql
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
ALTER TABLE memo ADD COLUMN resource_name TEXT NOT NULL DEFAULT "";
|
||||||
|
|
||||||
|
UPDATE memo SET resource_name = lower(hex(randomblob(8)));
|
||||||
|
|
||||||
|
CREATE UNIQUE INDEX idx_memo_resource_name ON memo (resource_name);
|
||||||
|
|
||||||
|
ALTER TABLE resource ADD COLUMN resource_name TEXT NOT NULL DEFAULT "";
|
||||||
|
|
||||||
|
UPDATE resource SET resource_name = lower(hex(randomblob(8)));
|
||||||
|
|
||||||
|
CREATE UNIQUE INDEX idx_resource_resource_name ON resource (resource_name);
|
60
store/migration/sqlite/prod/0.2/00__user_role.sql
Normal file
60
store/migration/sqlite/prod/0.2/00__user_role.sql
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
-- change user role field from "OWNER"/"USER" to "HOST"/"USER".
|
||||||
|
PRAGMA foreign_keys = off;
|
||||||
|
|
||||||
|
DROP TABLE IF EXISTS _user_old;
|
||||||
|
|
||||||
|
ALTER TABLE
|
||||||
|
user RENAME TO _user_old;
|
||||||
|
|
||||||
|
CREATE TABLE user (
|
||||||
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
created_ts BIGINT NOT NULL DEFAULT (strftime('%s', 'now')),
|
||||||
|
updated_ts BIGINT NOT NULL DEFAULT (strftime('%s', 'now')),
|
||||||
|
row_status TEXT NOT NULL CHECK (row_status IN ('NORMAL', 'ARCHIVED')) DEFAULT 'NORMAL',
|
||||||
|
email TEXT NOT NULL UNIQUE,
|
||||||
|
role TEXT NOT NULL CHECK (role IN ('HOST', 'USER')) DEFAULT 'USER',
|
||||||
|
name TEXT NOT NULL,
|
||||||
|
password_hash TEXT NOT NULL,
|
||||||
|
open_id TEXT NOT NULL UNIQUE
|
||||||
|
);
|
||||||
|
|
||||||
|
INSERT INTO
|
||||||
|
user (
|
||||||
|
id,
|
||||||
|
created_ts,
|
||||||
|
updated_ts,
|
||||||
|
row_status,
|
||||||
|
email,
|
||||||
|
name,
|
||||||
|
password_hash,
|
||||||
|
open_id
|
||||||
|
)
|
||||||
|
SELECT
|
||||||
|
id,
|
||||||
|
created_ts,
|
||||||
|
updated_ts,
|
||||||
|
row_status,
|
||||||
|
email,
|
||||||
|
name,
|
||||||
|
password_hash,
|
||||||
|
open_id
|
||||||
|
FROM
|
||||||
|
_user_old;
|
||||||
|
|
||||||
|
UPDATE
|
||||||
|
user
|
||||||
|
SET
|
||||||
|
role = 'HOST'
|
||||||
|
WHERE
|
||||||
|
id IN (
|
||||||
|
SELECT
|
||||||
|
id
|
||||||
|
FROM
|
||||||
|
_user_old
|
||||||
|
WHERE
|
||||||
|
role = 'OWNER'
|
||||||
|
);
|
||||||
|
|
||||||
|
DROP TABLE IF EXISTS _user_old;
|
||||||
|
|
||||||
|
PRAGMA foreign_keys = on;
|
4
store/migration/sqlite/prod/0.2/01__memo_visibility.sql
Normal file
4
store/migration/sqlite/prod/0.2/01__memo_visibility.sql
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
ALTER TABLE
|
||||||
|
memo
|
||||||
|
ADD
|
||||||
|
COLUMN visibility TEXT NOT NULL CHECK (visibility IN ('PUBLIC', 'PRIVATE')) DEFAULT 'PRIVATE';
|
9
store/migration/sqlite/prod/0.20/00__reaction.sql
Normal file
9
store/migration/sqlite/prod/0.20/00__reaction.sql
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
-- reaction
|
||||||
|
CREATE TABLE reaction (
|
||||||
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
created_ts BIGINT NOT NULL DEFAULT (strftime('%s', 'now')),
|
||||||
|
creator_id INTEGER NOT NULL,
|
||||||
|
content_id TEXT NOT NULL,
|
||||||
|
reaction_type TEXT NOT NULL,
|
||||||
|
UNIQUE(creator_id, content_id, reaction_type)
|
||||||
|
);
|
@ -0,0 +1 @@
|
|||||||
|
ALTER TABLE user ADD COLUMN description TEXT NOT NULL DEFAULT "";
|
3
store/migration/sqlite/prod/0.21/01__rename_uid.sql
Normal file
3
store/migration/sqlite/prod/0.21/01__rename_uid.sql
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
ALTER TABLE memo RENAME COLUMN resource_name TO uid;
|
||||||
|
|
||||||
|
ALTER TABLE resource RENAME COLUMN resource_name TO uid;
|
@ -0,0 +1,17 @@
|
|||||||
|
ALTER TABLE resource ADD COLUMN storage_type TEXT NOT NULL DEFAULT '';
|
||||||
|
|
||||||
|
ALTER TABLE resource ADD COLUMN reference TEXT NOT NULL DEFAULT '';
|
||||||
|
|
||||||
|
ALTER TABLE resource ADD COLUMN payload TEXT NOT NULL DEFAULT '{}';
|
||||||
|
|
||||||
|
UPDATE resource
|
||||||
|
SET storage_type = 'LOCAL', reference = internal_path
|
||||||
|
WHERE internal_path IS NOT NULL AND internal_path != '';
|
||||||
|
|
||||||
|
UPDATE resource
|
||||||
|
SET storage_type = 'EXTERNAL', reference = external_link
|
||||||
|
WHERE external_link IS NOT NULL AND external_link != '';
|
||||||
|
|
||||||
|
ALTER TABLE resource DROP COLUMN internal_path;
|
||||||
|
|
||||||
|
ALTER TABLE resource DROP COLUMN external_link;
|
3
store/migration/sqlite/prod/0.22/01__memo_tags.sql
Normal file
3
store/migration/sqlite/prod/0.22/01__memo_tags.sql
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
ALTER TABLE memo ADD COLUMN tags TEXT NOT NULL DEFAULT '[]';
|
||||||
|
|
||||||
|
CREATE INDEX idx_memo_tags ON memo (tags);
|
1
store/migration/sqlite/prod/0.22/02__memo_payload.sql
Normal file
1
store/migration/sqlite/prod/0.22/02__memo_payload.sql
Normal file
@ -0,0 +1 @@
|
|||||||
|
ALTER TABLE memo ADD COLUMN payload TEXT NOT NULL DEFAULT '{}';
|
1
store/migration/sqlite/prod/0.22/03__drop_tag.sql
Normal file
1
store/migration/sqlite/prod/0.22/03__drop_tag.sql
Normal file
@ -0,0 +1 @@
|
|||||||
|
DROP TABLE tag;
|
@ -0,0 +1,43 @@
|
|||||||
|
-- change memo visibility field from "PRIVATE"/"PUBLIC" to "PRIVATE"/"PROTECTED"/"PUBLIC".
|
||||||
|
PRAGMA foreign_keys = off;
|
||||||
|
|
||||||
|
DROP TABLE IF EXISTS _memo_old;
|
||||||
|
|
||||||
|
ALTER TABLE
|
||||||
|
memo RENAME TO _memo_old;
|
||||||
|
|
||||||
|
CREATE TABLE memo (
|
||||||
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
creator_id INTEGER NOT NULL,
|
||||||
|
created_ts BIGINT NOT NULL DEFAULT (strftime('%s', 'now')),
|
||||||
|
updated_ts BIGINT NOT NULL DEFAULT (strftime('%s', 'now')),
|
||||||
|
row_status TEXT NOT NULL CHECK (row_status IN ('NORMAL', 'ARCHIVED')) DEFAULT 'NORMAL',
|
||||||
|
content TEXT NOT NULL DEFAULT '',
|
||||||
|
visibility TEXT NOT NULL CHECK (visibility IN ('PUBLIC', 'PROTECTED', 'PRIVATE')) DEFAULT 'PRIVATE',
|
||||||
|
FOREIGN KEY(creator_id) REFERENCES user(id) ON DELETE CASCADE
|
||||||
|
);
|
||||||
|
|
||||||
|
INSERT INTO
|
||||||
|
memo (
|
||||||
|
id,
|
||||||
|
creator_id,
|
||||||
|
created_ts,
|
||||||
|
updated_ts,
|
||||||
|
row_status,
|
||||||
|
content,
|
||||||
|
visibility
|
||||||
|
)
|
||||||
|
SELECT
|
||||||
|
id,
|
||||||
|
creator_id,
|
||||||
|
created_ts,
|
||||||
|
updated_ts,
|
||||||
|
row_status,
|
||||||
|
content,
|
||||||
|
visibility
|
||||||
|
FROM
|
||||||
|
_memo_old;
|
||||||
|
|
||||||
|
DROP TABLE IF EXISTS _memo_old;
|
||||||
|
|
||||||
|
PRAGMA foreign_keys = on;
|
9
store/migration/sqlite/prod/0.4/00__user_setting.sql
Normal file
9
store/migration/sqlite/prod/0.4/00__user_setting.sql
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
-- user_setting
|
||||||
|
CREATE TABLE user_setting (
|
||||||
|
user_id INTEGER NOT NULL,
|
||||||
|
key TEXT NOT NULL,
|
||||||
|
value TEXT NOT NULL,
|
||||||
|
FOREIGN KEY(user_id) REFERENCES user(id) ON DELETE CASCADE
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE UNIQUE INDEX user_setting_key_user_id_index ON user_setting(key, user_id);
|
217
store/migration/sqlite/prod/0.5/00__regenerate_foreign_keys.sql
Normal file
217
store/migration/sqlite/prod/0.5/00__regenerate_foreign_keys.sql
Normal file
@ -0,0 +1,217 @@
|
|||||||
|
PRAGMA foreign_keys = off;
|
||||||
|
|
||||||
|
DROP TABLE IF EXISTS _user_old;
|
||||||
|
|
||||||
|
ALTER TABLE
|
||||||
|
user RENAME TO _user_old;
|
||||||
|
|
||||||
|
-- user
|
||||||
|
CREATE TABLE user (
|
||||||
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
created_ts BIGINT NOT NULL DEFAULT (strftime('%s', 'now')),
|
||||||
|
updated_ts BIGINT NOT NULL DEFAULT (strftime('%s', 'now')),
|
||||||
|
row_status TEXT NOT NULL CHECK (row_status IN ('NORMAL', 'ARCHIVED')) DEFAULT 'NORMAL',
|
||||||
|
email TEXT NOT NULL UNIQUE,
|
||||||
|
role TEXT NOT NULL CHECK (role IN ('HOST', 'USER')) DEFAULT 'USER',
|
||||||
|
name TEXT NOT NULL,
|
||||||
|
password_hash TEXT NOT NULL,
|
||||||
|
open_id TEXT NOT NULL UNIQUE
|
||||||
|
);
|
||||||
|
|
||||||
|
INSERT INTO
|
||||||
|
user
|
||||||
|
SELECT
|
||||||
|
*
|
||||||
|
FROM
|
||||||
|
_user_old;
|
||||||
|
|
||||||
|
DROP TABLE IF EXISTS _user_old;
|
||||||
|
|
||||||
|
DROP TRIGGER IF EXISTS `trigger_update_user_modification_time`;
|
||||||
|
|
||||||
|
CREATE TRIGGER IF NOT EXISTS `trigger_update_user_modification_time`
|
||||||
|
AFTER
|
||||||
|
UPDATE
|
||||||
|
ON `user` FOR EACH ROW BEGIN
|
||||||
|
UPDATE
|
||||||
|
`user`
|
||||||
|
SET
|
||||||
|
updated_ts = (strftime('%s', 'now'))
|
||||||
|
WHERE
|
||||||
|
rowid = old.rowid;
|
||||||
|
|
||||||
|
END;
|
||||||
|
|
||||||
|
DROP TABLE IF EXISTS _memo_old;
|
||||||
|
|
||||||
|
ALTER TABLE
|
||||||
|
memo RENAME TO _memo_old;
|
||||||
|
|
||||||
|
-- memo
|
||||||
|
CREATE TABLE memo (
|
||||||
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
creator_id INTEGER NOT NULL,
|
||||||
|
created_ts BIGINT NOT NULL DEFAULT (strftime('%s', 'now')),
|
||||||
|
updated_ts BIGINT NOT NULL DEFAULT (strftime('%s', 'now')),
|
||||||
|
row_status TEXT NOT NULL CHECK (row_status IN ('NORMAL', 'ARCHIVED')) DEFAULT 'NORMAL',
|
||||||
|
content TEXT NOT NULL DEFAULT '',
|
||||||
|
visibility TEXT NOT NULL CHECK (visibility IN ('PUBLIC', 'PROTECTED', 'PRIVATE')) DEFAULT 'PRIVATE',
|
||||||
|
FOREIGN KEY(creator_id) REFERENCES user(id) ON DELETE CASCADE
|
||||||
|
);
|
||||||
|
|
||||||
|
INSERT INTO
|
||||||
|
memo
|
||||||
|
SELECT
|
||||||
|
*
|
||||||
|
FROM
|
||||||
|
_memo_old;
|
||||||
|
|
||||||
|
DROP TABLE IF EXISTS _memo_old;
|
||||||
|
|
||||||
|
DROP TRIGGER IF EXISTS `trigger_update_memo_modification_time`;
|
||||||
|
|
||||||
|
CREATE TRIGGER IF NOT EXISTS `trigger_update_memo_modification_time`
|
||||||
|
AFTER
|
||||||
|
UPDATE
|
||||||
|
ON `memo` FOR EACH ROW BEGIN
|
||||||
|
UPDATE
|
||||||
|
`memo`
|
||||||
|
SET
|
||||||
|
updated_ts = (strftime('%s', 'now'))
|
||||||
|
WHERE
|
||||||
|
rowid = old.rowid;
|
||||||
|
|
||||||
|
END;
|
||||||
|
|
||||||
|
DROP TABLE IF EXISTS _memo_organizer_old;
|
||||||
|
|
||||||
|
ALTER TABLE
|
||||||
|
memo_organizer RENAME TO _memo_organizer_old;
|
||||||
|
|
||||||
|
-- memo_organizer
|
||||||
|
CREATE TABLE memo_organizer (
|
||||||
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
memo_id INTEGER NOT NULL,
|
||||||
|
user_id INTEGER NOT NULL,
|
||||||
|
pinned INTEGER NOT NULL CHECK (pinned IN (0, 1)) DEFAULT 0,
|
||||||
|
FOREIGN KEY(memo_id) REFERENCES memo(id) ON DELETE CASCADE,
|
||||||
|
FOREIGN KEY(user_id) REFERENCES user(id) ON DELETE CASCADE,
|
||||||
|
UNIQUE(memo_id, user_id)
|
||||||
|
);
|
||||||
|
|
||||||
|
INSERT INTO
|
||||||
|
memo_organizer
|
||||||
|
SELECT
|
||||||
|
*
|
||||||
|
FROM
|
||||||
|
_memo_organizer_old;
|
||||||
|
|
||||||
|
DROP TABLE IF EXISTS _memo_organizer_old;
|
||||||
|
|
||||||
|
DROP TABLE IF EXISTS _shortcut_old;
|
||||||
|
|
||||||
|
ALTER TABLE
|
||||||
|
shortcut RENAME TO _shortcut_old;
|
||||||
|
|
||||||
|
-- shortcut
|
||||||
|
CREATE TABLE shortcut (
|
||||||
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
creator_id INTEGER NOT NULL,
|
||||||
|
created_ts BIGINT NOT NULL DEFAULT (strftime('%s', 'now')),
|
||||||
|
updated_ts BIGINT NOT NULL DEFAULT (strftime('%s', 'now')),
|
||||||
|
row_status TEXT NOT NULL CHECK (row_status IN ('NORMAL', 'ARCHIVED')) DEFAULT 'NORMAL',
|
||||||
|
title TEXT NOT NULL DEFAULT '',
|
||||||
|
payload TEXT NOT NULL DEFAULT '{}',
|
||||||
|
FOREIGN KEY(creator_id) REFERENCES user(id) ON DELETE CASCADE
|
||||||
|
);
|
||||||
|
|
||||||
|
INSERT INTO
|
||||||
|
shortcut
|
||||||
|
SELECT
|
||||||
|
*
|
||||||
|
FROM
|
||||||
|
_shortcut_old;
|
||||||
|
|
||||||
|
DROP TABLE IF EXISTS _shortcut_old;
|
||||||
|
|
||||||
|
DROP TRIGGER IF EXISTS `trigger_update_shortcut_modification_time`;
|
||||||
|
|
||||||
|
CREATE TRIGGER IF NOT EXISTS `trigger_update_shortcut_modification_time`
|
||||||
|
AFTER
|
||||||
|
UPDATE
|
||||||
|
ON `shortcut` FOR EACH ROW BEGIN
|
||||||
|
UPDATE
|
||||||
|
`shortcut`
|
||||||
|
SET
|
||||||
|
updated_ts = (strftime('%s', 'now'))
|
||||||
|
WHERE
|
||||||
|
rowid = old.rowid;
|
||||||
|
|
||||||
|
END;
|
||||||
|
|
||||||
|
DROP TABLE IF EXISTS _resource_old;
|
||||||
|
|
||||||
|
ALTER TABLE
|
||||||
|
resource RENAME TO _resource_old;
|
||||||
|
|
||||||
|
-- resource
|
||||||
|
CREATE TABLE resource (
|
||||||
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
creator_id INTEGER NOT NULL,
|
||||||
|
created_ts BIGINT NOT NULL DEFAULT (strftime('%s', 'now')),
|
||||||
|
updated_ts BIGINT NOT NULL DEFAULT (strftime('%s', 'now')),
|
||||||
|
filename TEXT NOT NULL DEFAULT '',
|
||||||
|
blob BLOB DEFAULT NULL,
|
||||||
|
type TEXT NOT NULL DEFAULT '',
|
||||||
|
size INTEGER NOT NULL DEFAULT 0,
|
||||||
|
FOREIGN KEY(creator_id) REFERENCES user(id) ON DELETE CASCADE
|
||||||
|
);
|
||||||
|
|
||||||
|
INSERT INTO
|
||||||
|
resource
|
||||||
|
SELECT
|
||||||
|
*
|
||||||
|
FROM
|
||||||
|
_resource_old;
|
||||||
|
|
||||||
|
DROP TABLE IF EXISTS _resource_old;
|
||||||
|
|
||||||
|
DROP TRIGGER IF EXISTS `trigger_update_resource_modification_time`;
|
||||||
|
|
||||||
|
CREATE TRIGGER IF NOT EXISTS `trigger_update_resource_modification_time`
|
||||||
|
AFTER
|
||||||
|
UPDATE
|
||||||
|
ON `resource` FOR EACH ROW BEGIN
|
||||||
|
UPDATE
|
||||||
|
`resource`
|
||||||
|
SET
|
||||||
|
updated_ts = (strftime('%s', 'now'))
|
||||||
|
WHERE
|
||||||
|
rowid = old.rowid;
|
||||||
|
|
||||||
|
END;
|
||||||
|
|
||||||
|
DROP TABLE IF EXISTS _user_setting_old;
|
||||||
|
|
||||||
|
ALTER TABLE
|
||||||
|
user_setting RENAME TO _user_setting_old;
|
||||||
|
|
||||||
|
-- user_setting
|
||||||
|
CREATE TABLE user_setting (
|
||||||
|
user_id INTEGER NOT NULL,
|
||||||
|
key TEXT NOT NULL,
|
||||||
|
value TEXT NOT NULL,
|
||||||
|
FOREIGN KEY(user_id) REFERENCES user(id) ON DELETE CASCADE,
|
||||||
|
UNIQUE(user_id, key)
|
||||||
|
);
|
||||||
|
|
||||||
|
INSERT INTO
|
||||||
|
user_setting
|
||||||
|
SELECT
|
||||||
|
*
|
||||||
|
FROM
|
||||||
|
_user_setting_old;
|
||||||
|
|
||||||
|
DROP TABLE IF EXISTS _user_setting_old;
|
||||||
|
|
||||||
|
PRAGMA foreign_keys = on;
|
10
store/migration/sqlite/prod/0.5/01__memo_resource.sql
Normal file
10
store/migration/sqlite/prod/0.5/01__memo_resource.sql
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
-- memo_resource
|
||||||
|
CREATE TABLE memo_resource (
|
||||||
|
memo_id INTEGER NOT NULL,
|
||||||
|
resource_id INTEGER NOT NULL,
|
||||||
|
created_ts BIGINT NOT NULL DEFAULT (strftime('%s', 'now')),
|
||||||
|
updated_ts BIGINT NOT NULL DEFAULT (strftime('%s', 'now')),
|
||||||
|
FOREIGN KEY(memo_id) REFERENCES memo(id) ON DELETE CASCADE,
|
||||||
|
FOREIGN KEY(resource_id) REFERENCES resource(id) ON DELETE CASCADE,
|
||||||
|
UNIQUE(memo_id, resource_id)
|
||||||
|
);
|
7
store/migration/sqlite/prod/0.5/02__system_setting.sql
Normal file
7
store/migration/sqlite/prod/0.5/02__system_setting.sql
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
-- system_setting
|
||||||
|
CREATE TABLE system_setting (
|
||||||
|
name TEXT NOT NULL,
|
||||||
|
value TEXT NOT NULL,
|
||||||
|
description TEXT NOT NULL DEFAULT '',
|
||||||
|
UNIQUE(name)
|
||||||
|
);
|
@ -0,0 +1,4 @@
|
|||||||
|
ALTER TABLE
|
||||||
|
resource
|
||||||
|
ADD
|
||||||
|
COLUMN external_link TEXT NOT NULL DEFAULT '';
|
59
store/migration/sqlite/prod/0.6/00__recreate_triggers.sql
Normal file
59
store/migration/sqlite/prod/0.6/00__recreate_triggers.sql
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
DROP TRIGGER IF EXISTS `trigger_update_user_modification_time`;
|
||||||
|
|
||||||
|
CREATE TRIGGER IF NOT EXISTS `trigger_update_user_modification_time`
|
||||||
|
AFTER
|
||||||
|
UPDATE
|
||||||
|
ON `user` FOR EACH ROW BEGIN
|
||||||
|
UPDATE
|
||||||
|
`user`
|
||||||
|
SET
|
||||||
|
updated_ts = (strftime('%s', 'now'))
|
||||||
|
WHERE
|
||||||
|
rowid = old.rowid;
|
||||||
|
|
||||||
|
END;
|
||||||
|
|
||||||
|
DROP TRIGGER IF EXISTS `trigger_update_memo_modification_time`;
|
||||||
|
|
||||||
|
CREATE TRIGGER IF NOT EXISTS `trigger_update_memo_modification_time`
|
||||||
|
AFTER
|
||||||
|
UPDATE
|
||||||
|
ON `memo` FOR EACH ROW BEGIN
|
||||||
|
UPDATE
|
||||||
|
`memo`
|
||||||
|
SET
|
||||||
|
updated_ts = (strftime('%s', 'now'))
|
||||||
|
WHERE
|
||||||
|
rowid = old.rowid;
|
||||||
|
|
||||||
|
END;
|
||||||
|
|
||||||
|
DROP TRIGGER IF EXISTS `trigger_update_shortcut_modification_time`;
|
||||||
|
|
||||||
|
CREATE TRIGGER IF NOT EXISTS `trigger_update_shortcut_modification_time`
|
||||||
|
AFTER
|
||||||
|
UPDATE
|
||||||
|
ON `shortcut` FOR EACH ROW BEGIN
|
||||||
|
UPDATE
|
||||||
|
`shortcut`
|
||||||
|
SET
|
||||||
|
updated_ts = (strftime('%s', 'now'))
|
||||||
|
WHERE
|
||||||
|
rowid = old.rowid;
|
||||||
|
|
||||||
|
END;
|
||||||
|
|
||||||
|
DROP TRIGGER IF EXISTS `trigger_update_resource_modification_time`;
|
||||||
|
|
||||||
|
CREATE TRIGGER IF NOT EXISTS `trigger_update_resource_modification_time`
|
||||||
|
AFTER
|
||||||
|
UPDATE
|
||||||
|
ON `resource` FOR EACH ROW BEGIN
|
||||||
|
UPDATE
|
||||||
|
`resource`
|
||||||
|
SET
|
||||||
|
updated_ts = (strftime('%s', 'now'))
|
||||||
|
WHERE
|
||||||
|
rowid = old.rowid;
|
||||||
|
|
||||||
|
END;
|
191
store/migration/sqlite/prod/0.7/00__remove_fk.sql
Normal file
191
store/migration/sqlite/prod/0.7/00__remove_fk.sql
Normal file
@ -0,0 +1,191 @@
|
|||||||
|
PRAGMA foreign_keys = off;
|
||||||
|
|
||||||
|
DROP TABLE IF EXISTS _user_old;
|
||||||
|
|
||||||
|
ALTER TABLE
|
||||||
|
user RENAME TO _user_old;
|
||||||
|
|
||||||
|
-- user
|
||||||
|
CREATE TABLE user (
|
||||||
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
created_ts BIGINT NOT NULL DEFAULT (strftime('%s', 'now')),
|
||||||
|
updated_ts BIGINT NOT NULL DEFAULT (strftime('%s', 'now')),
|
||||||
|
row_status TEXT NOT NULL CHECK (row_status IN ('NORMAL', 'ARCHIVED')) DEFAULT 'NORMAL',
|
||||||
|
email TEXT NOT NULL UNIQUE,
|
||||||
|
role TEXT NOT NULL CHECK (role IN ('HOST', 'USER')) DEFAULT 'USER',
|
||||||
|
name TEXT NOT NULL,
|
||||||
|
password_hash TEXT NOT NULL,
|
||||||
|
open_id TEXT NOT NULL UNIQUE
|
||||||
|
);
|
||||||
|
|
||||||
|
INSERT INTO
|
||||||
|
user
|
||||||
|
SELECT
|
||||||
|
*
|
||||||
|
FROM
|
||||||
|
_user_old;
|
||||||
|
|
||||||
|
DROP TABLE IF EXISTS _user_old;
|
||||||
|
|
||||||
|
DROP TABLE IF EXISTS _memo_old;
|
||||||
|
|
||||||
|
ALTER TABLE
|
||||||
|
memo RENAME TO _memo_old;
|
||||||
|
|
||||||
|
-- memo
|
||||||
|
CREATE TABLE memo (
|
||||||
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
creator_id INTEGER NOT NULL,
|
||||||
|
created_ts BIGINT NOT NULL DEFAULT (strftime('%s', 'now')),
|
||||||
|
updated_ts BIGINT NOT NULL DEFAULT (strftime('%s', 'now')),
|
||||||
|
row_status TEXT NOT NULL CHECK (row_status IN ('NORMAL', 'ARCHIVED')) DEFAULT 'NORMAL',
|
||||||
|
content TEXT NOT NULL DEFAULT '',
|
||||||
|
visibility TEXT NOT NULL CHECK (visibility IN ('PUBLIC', 'PROTECTED', 'PRIVATE')) DEFAULT 'PRIVATE'
|
||||||
|
);
|
||||||
|
|
||||||
|
INSERT INTO
|
||||||
|
memo
|
||||||
|
SELECT
|
||||||
|
*
|
||||||
|
FROM
|
||||||
|
_memo_old;
|
||||||
|
|
||||||
|
DROP TABLE IF EXISTS _memo_old;
|
||||||
|
|
||||||
|
DROP TABLE IF EXISTS _memo_organizer_old;
|
||||||
|
|
||||||
|
ALTER TABLE
|
||||||
|
memo_organizer RENAME TO _memo_organizer_old;
|
||||||
|
|
||||||
|
-- memo_organizer
|
||||||
|
CREATE TABLE memo_organizer (
|
||||||
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
memo_id INTEGER NOT NULL,
|
||||||
|
user_id INTEGER NOT NULL,
|
||||||
|
pinned INTEGER NOT NULL CHECK (pinned IN (0, 1)) DEFAULT 0,
|
||||||
|
UNIQUE(memo_id, user_id)
|
||||||
|
);
|
||||||
|
|
||||||
|
INSERT INTO
|
||||||
|
memo_organizer
|
||||||
|
SELECT
|
||||||
|
*
|
||||||
|
FROM
|
||||||
|
_memo_organizer_old;
|
||||||
|
|
||||||
|
DROP TABLE IF EXISTS _memo_organizer_old;
|
||||||
|
|
||||||
|
DROP TABLE IF EXISTS _shortcut_old;
|
||||||
|
|
||||||
|
ALTER TABLE
|
||||||
|
shortcut RENAME TO _shortcut_old;
|
||||||
|
|
||||||
|
-- shortcut
|
||||||
|
CREATE TABLE shortcut (
|
||||||
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
creator_id INTEGER NOT NULL,
|
||||||
|
created_ts BIGINT NOT NULL DEFAULT (strftime('%s', 'now')),
|
||||||
|
updated_ts BIGINT NOT NULL DEFAULT (strftime('%s', 'now')),
|
||||||
|
row_status TEXT NOT NULL CHECK (row_status IN ('NORMAL', 'ARCHIVED')) DEFAULT 'NORMAL',
|
||||||
|
title TEXT NOT NULL DEFAULT '',
|
||||||
|
payload TEXT NOT NULL DEFAULT '{}'
|
||||||
|
);
|
||||||
|
|
||||||
|
INSERT INTO
|
||||||
|
shortcut
|
||||||
|
SELECT
|
||||||
|
*
|
||||||
|
FROM
|
||||||
|
_shortcut_old;
|
||||||
|
|
||||||
|
DROP TABLE IF EXISTS _shortcut_old;
|
||||||
|
|
||||||
|
DROP TABLE IF EXISTS _resource_old;
|
||||||
|
|
||||||
|
ALTER TABLE
|
||||||
|
resource RENAME TO _resource_old;
|
||||||
|
|
||||||
|
-- resource
|
||||||
|
CREATE TABLE resource (
|
||||||
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
creator_id INTEGER NOT NULL,
|
||||||
|
created_ts BIGINT NOT NULL DEFAULT (strftime('%s', 'now')),
|
||||||
|
updated_ts BIGINT NOT NULL DEFAULT (strftime('%s', 'now')),
|
||||||
|
filename TEXT NOT NULL DEFAULT '',
|
||||||
|
blob BLOB DEFAULT NULL,
|
||||||
|
external_link TEXT NOT NULL DEFAULT '',
|
||||||
|
type TEXT NOT NULL DEFAULT '',
|
||||||
|
size INTEGER NOT NULL DEFAULT 0
|
||||||
|
);
|
||||||
|
|
||||||
|
INSERT INTO
|
||||||
|
resource (
|
||||||
|
id,
|
||||||
|
creator_id,
|
||||||
|
created_ts,
|
||||||
|
updated_ts,
|
||||||
|
filename,
|
||||||
|
blob,
|
||||||
|
external_link,
|
||||||
|
type,
|
||||||
|
size
|
||||||
|
)
|
||||||
|
SELECT
|
||||||
|
id,
|
||||||
|
creator_id,
|
||||||
|
created_ts,
|
||||||
|
updated_ts,
|
||||||
|
filename,
|
||||||
|
blob,
|
||||||
|
external_link,
|
||||||
|
type,
|
||||||
|
size
|
||||||
|
FROM
|
||||||
|
_resource_old;
|
||||||
|
|
||||||
|
DROP TABLE IF EXISTS _resource_old;
|
||||||
|
|
||||||
|
DROP TABLE IF EXISTS _user_setting_old;
|
||||||
|
|
||||||
|
ALTER TABLE
|
||||||
|
user_setting RENAME TO _user_setting_old;
|
||||||
|
|
||||||
|
-- user_setting
|
||||||
|
CREATE TABLE user_setting (
|
||||||
|
user_id INTEGER NOT NULL,
|
||||||
|
key TEXT NOT NULL,
|
||||||
|
value TEXT NOT NULL,
|
||||||
|
UNIQUE(user_id, key)
|
||||||
|
);
|
||||||
|
|
||||||
|
INSERT INTO
|
||||||
|
user_setting
|
||||||
|
SELECT
|
||||||
|
*
|
||||||
|
FROM
|
||||||
|
_user_setting_old;
|
||||||
|
|
||||||
|
DROP TABLE IF EXISTS _user_setting_old;
|
||||||
|
|
||||||
|
DROP TABLE IF EXISTS _memo_resource_old;
|
||||||
|
|
||||||
|
ALTER TABLE
|
||||||
|
memo_resource RENAME TO _memo_resource_old;
|
||||||
|
|
||||||
|
-- memo_resource
|
||||||
|
CREATE TABLE memo_resource (
|
||||||
|
memo_id INTEGER NOT NULL,
|
||||||
|
resource_id INTEGER NOT NULL,
|
||||||
|
created_ts BIGINT NOT NULL DEFAULT (strftime('%s', 'now')),
|
||||||
|
updated_ts BIGINT NOT NULL DEFAULT (strftime('%s', 'now')),
|
||||||
|
UNIQUE(memo_id, resource_id)
|
||||||
|
);
|
||||||
|
|
||||||
|
INSERT INTO
|
||||||
|
memo_resource
|
||||||
|
SELECT
|
||||||
|
*
|
||||||
|
FROM
|
||||||
|
_memo_resource_old;
|
||||||
|
|
||||||
|
DROP TABLE IF EXISTS _memo_resource_old;
|
7
store/migration/sqlite/prod/0.7/01__remove_triggers.sql
Normal file
7
store/migration/sqlite/prod/0.7/01__remove_triggers.sql
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
DROP TRIGGER IF EXISTS `trigger_update_user_modification_time`;
|
||||||
|
|
||||||
|
DROP TRIGGER IF EXISTS `trigger_update_memo_modification_time`;
|
||||||
|
|
||||||
|
DROP TRIGGER IF EXISTS `trigger_update_shortcut_modification_time`;
|
||||||
|
|
||||||
|
DROP TRIGGER IF EXISTS `trigger_update_resource_modification_time`;
|
@ -0,0 +1,5 @@
|
|||||||
|
-- migration_history
|
||||||
|
CREATE TABLE IF NOT EXISTS migration_history (
|
||||||
|
version TEXT NOT NULL PRIMARY KEY,
|
||||||
|
created_ts BIGINT NOT NULL DEFAULT (strftime('%s', 'now'))
|
||||||
|
);
|
50
store/migration/sqlite/prod/0.8/01__user_username.sql
Normal file
50
store/migration/sqlite/prod/0.8/01__user_username.sql
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
-- add column username TEXT NOT NULL UNIQUE
|
||||||
|
-- rename column name to nickname
|
||||||
|
-- add role `ADMIN`
|
||||||
|
DROP TABLE IF EXISTS _user_old;
|
||||||
|
|
||||||
|
ALTER TABLE
|
||||||
|
user RENAME TO _user_old;
|
||||||
|
|
||||||
|
-- user
|
||||||
|
CREATE TABLE user (
|
||||||
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
created_ts BIGINT NOT NULL DEFAULT (strftime('%s', 'now')),
|
||||||
|
updated_ts BIGINT NOT NULL DEFAULT (strftime('%s', 'now')),
|
||||||
|
row_status TEXT NOT NULL CHECK (row_status IN ('NORMAL', 'ARCHIVED')) DEFAULT 'NORMAL',
|
||||||
|
username TEXT NOT NULL UNIQUE,
|
||||||
|
role TEXT NOT NULL CHECK (role IN ('HOST', 'ADMIN', 'USER')) DEFAULT 'USER',
|
||||||
|
email TEXT NOT NULL DEFAULT '',
|
||||||
|
nickname TEXT NOT NULL DEFAULT '',
|
||||||
|
password_hash TEXT NOT NULL,
|
||||||
|
open_id TEXT NOT NULL UNIQUE
|
||||||
|
);
|
||||||
|
|
||||||
|
INSERT INTO
|
||||||
|
user (
|
||||||
|
id,
|
||||||
|
created_ts,
|
||||||
|
updated_ts,
|
||||||
|
row_status,
|
||||||
|
username,
|
||||||
|
role,
|
||||||
|
email,
|
||||||
|
nickname,
|
||||||
|
password_hash,
|
||||||
|
open_id
|
||||||
|
)
|
||||||
|
SELECT
|
||||||
|
id,
|
||||||
|
created_ts,
|
||||||
|
updated_ts,
|
||||||
|
row_status,
|
||||||
|
email,
|
||||||
|
role,
|
||||||
|
email,
|
||||||
|
name,
|
||||||
|
password_hash,
|
||||||
|
open_id
|
||||||
|
FROM
|
||||||
|
_user_old;
|
||||||
|
|
||||||
|
DROP TABLE IF EXISTS _user_old;
|
6
store/migration/sqlite/prod/0.9/00__tag.sql
Normal file
6
store/migration/sqlite/prod/0.9/00__tag.sql
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
-- tag
|
||||||
|
CREATE TABLE tag (
|
||||||
|
name TEXT NOT NULL,
|
||||||
|
creator_id INTEGER NOT NULL,
|
||||||
|
UNIQUE(name, creator_id)
|
||||||
|
);
|
146
store/migration/sqlite/prod/LATEST__SCHEMA.sql
Normal file
146
store/migration/sqlite/prod/LATEST__SCHEMA.sql
Normal file
@ -0,0 +1,146 @@
|
|||||||
|
-- migration_history
|
||||||
|
CREATE TABLE migration_history (
|
||||||
|
version TEXT NOT NULL PRIMARY KEY,
|
||||||
|
created_ts BIGINT NOT NULL DEFAULT (strftime('%s', 'now'))
|
||||||
|
);
|
||||||
|
|
||||||
|
-- system_setting
|
||||||
|
CREATE TABLE system_setting (
|
||||||
|
name TEXT NOT NULL,
|
||||||
|
value TEXT NOT NULL,
|
||||||
|
description TEXT NOT NULL DEFAULT '',
|
||||||
|
UNIQUE(name)
|
||||||
|
);
|
||||||
|
|
||||||
|
-- user
|
||||||
|
CREATE TABLE user (
|
||||||
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
created_ts BIGINT NOT NULL DEFAULT (strftime('%s', 'now')),
|
||||||
|
updated_ts BIGINT NOT NULL DEFAULT (strftime('%s', 'now')),
|
||||||
|
row_status TEXT NOT NULL CHECK (row_status IN ('NORMAL', 'ARCHIVED')) DEFAULT 'NORMAL',
|
||||||
|
username TEXT NOT NULL UNIQUE,
|
||||||
|
role TEXT NOT NULL CHECK (role IN ('HOST', 'ADMIN', 'USER')) DEFAULT 'USER',
|
||||||
|
email TEXT NOT NULL DEFAULT '',
|
||||||
|
nickname TEXT NOT NULL DEFAULT '',
|
||||||
|
password_hash TEXT NOT NULL,
|
||||||
|
avatar_url TEXT NOT NULL DEFAULT '',
|
||||||
|
description TEXT NOT NULL DEFAULT ''
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE INDEX idx_user_username ON user (username);
|
||||||
|
|
||||||
|
-- user_setting
|
||||||
|
CREATE TABLE user_setting (
|
||||||
|
user_id INTEGER NOT NULL,
|
||||||
|
key TEXT NOT NULL,
|
||||||
|
value TEXT NOT NULL,
|
||||||
|
UNIQUE(user_id, key)
|
||||||
|
);
|
||||||
|
|
||||||
|
-- memo
|
||||||
|
CREATE TABLE memo (
|
||||||
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
uid TEXT NOT NULL UNIQUE,
|
||||||
|
creator_id INTEGER NOT NULL,
|
||||||
|
created_ts BIGINT NOT NULL DEFAULT (strftime('%s', 'now')),
|
||||||
|
updated_ts BIGINT NOT NULL DEFAULT (strftime('%s', 'now')),
|
||||||
|
row_status TEXT NOT NULL CHECK (row_status IN ('NORMAL', 'ARCHIVED')) DEFAULT 'NORMAL',
|
||||||
|
content TEXT NOT NULL DEFAULT '',
|
||||||
|
visibility TEXT NOT NULL CHECK (visibility IN ('PUBLIC', 'PROTECTED', 'PRIVATE')) DEFAULT 'PRIVATE',
|
||||||
|
tags TEXT NOT NULL DEFAULT '[]',
|
||||||
|
payload TEXT NOT NULL DEFAULT '{}'
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE INDEX idx_memo_creator_id ON memo (creator_id);
|
||||||
|
CREATE INDEX idx_memo_content ON memo (content);
|
||||||
|
CREATE INDEX idx_memo_visibility ON memo (visibility);
|
||||||
|
CREATE INDEX idx_memo_tags ON memo (tags);
|
||||||
|
|
||||||
|
-- memo_organizer
|
||||||
|
CREATE TABLE memo_organizer (
|
||||||
|
memo_id INTEGER NOT NULL,
|
||||||
|
user_id INTEGER NOT NULL,
|
||||||
|
pinned INTEGER NOT NULL CHECK (pinned IN (0, 1)) DEFAULT 0,
|
||||||
|
UNIQUE(memo_id, user_id)
|
||||||
|
);
|
||||||
|
|
||||||
|
-- memo_relation
|
||||||
|
CREATE TABLE memo_relation (
|
||||||
|
memo_id INTEGER NOT NULL,
|
||||||
|
related_memo_id INTEGER NOT NULL,
|
||||||
|
type TEXT NOT NULL,
|
||||||
|
UNIQUE(memo_id, related_memo_id, type)
|
||||||
|
);
|
||||||
|
|
||||||
|
-- resource
|
||||||
|
CREATE TABLE resource (
|
||||||
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
uid TEXT NOT NULL UNIQUE,
|
||||||
|
creator_id INTEGER NOT NULL,
|
||||||
|
created_ts BIGINT NOT NULL DEFAULT (strftime('%s', 'now')),
|
||||||
|
updated_ts BIGINT NOT NULL DEFAULT (strftime('%s', 'now')),
|
||||||
|
filename TEXT NOT NULL DEFAULT '',
|
||||||
|
blob BLOB DEFAULT NULL,
|
||||||
|
type TEXT NOT NULL DEFAULT '',
|
||||||
|
size INTEGER NOT NULL DEFAULT 0,
|
||||||
|
memo_id INTEGER,
|
||||||
|
storage_type TEXT NOT NULL DEFAULT '',
|
||||||
|
reference TEXT NOT NULL DEFAULT '',
|
||||||
|
payload TEXT NOT NULL DEFAULT '{}'
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE INDEX idx_resource_creator_id ON resource (creator_id);
|
||||||
|
|
||||||
|
CREATE INDEX idx_resource_memo_id ON resource (memo_id);
|
||||||
|
|
||||||
|
-- activity
|
||||||
|
CREATE TABLE activity (
|
||||||
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
creator_id INTEGER NOT NULL,
|
||||||
|
created_ts BIGINT NOT NULL DEFAULT (strftime('%s', 'now')),
|
||||||
|
type TEXT NOT NULL DEFAULT '',
|
||||||
|
level TEXT NOT NULL CHECK (level IN ('INFO', 'WARN', 'ERROR')) DEFAULT 'INFO',
|
||||||
|
payload TEXT NOT NULL DEFAULT '{}'
|
||||||
|
);
|
||||||
|
|
||||||
|
-- idp
|
||||||
|
CREATE TABLE idp (
|
||||||
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
name TEXT NOT NULL,
|
||||||
|
type TEXT NOT NULL,
|
||||||
|
identifier_filter TEXT NOT NULL DEFAULT '',
|
||||||
|
config TEXT NOT NULL DEFAULT '{}'
|
||||||
|
);
|
||||||
|
|
||||||
|
-- inbox
|
||||||
|
CREATE TABLE inbox (
|
||||||
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
created_ts BIGINT NOT NULL DEFAULT (strftime('%s', 'now')),
|
||||||
|
sender_id INTEGER NOT NULL,
|
||||||
|
receiver_id INTEGER NOT NULL,
|
||||||
|
status TEXT NOT NULL,
|
||||||
|
message TEXT NOT NULL DEFAULT '{}'
|
||||||
|
);
|
||||||
|
|
||||||
|
-- webhook
|
||||||
|
CREATE TABLE webhook (
|
||||||
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
created_ts BIGINT NOT NULL DEFAULT (strftime('%s', 'now')),
|
||||||
|
updated_ts BIGINT NOT NULL DEFAULT (strftime('%s', 'now')),
|
||||||
|
row_status TEXT NOT NULL CHECK (row_status IN ('NORMAL', 'ARCHIVED')) DEFAULT 'NORMAL',
|
||||||
|
creator_id INTEGER NOT NULL,
|
||||||
|
name TEXT NOT NULL,
|
||||||
|
url TEXT NOT NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE INDEX idx_webhook_creator_id ON webhook (creator_id);
|
||||||
|
|
||||||
|
-- reaction
|
||||||
|
CREATE TABLE reaction (
|
||||||
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
created_ts BIGINT NOT NULL DEFAULT (strftime('%s', 'now')),
|
||||||
|
creator_id INTEGER NOT NULL,
|
||||||
|
content_id TEXT NOT NULL,
|
||||||
|
reaction_type TEXT NOT NULL,
|
||||||
|
UNIQUE(creator_id, content_id, reaction_type)
|
||||||
|
);
|
@ -1 +1,198 @@
|
|||||||
package store
|
package store
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"embed"
|
||||||
|
"fmt"
|
||||||
|
"io/fs"
|
||||||
|
"log/slog"
|
||||||
|
"regexp"
|
||||||
|
"sort"
|
||||||
|
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
"github.com/usememos/memos/server/version"
|
||||||
|
)
|
||||||
|
|
||||||
|
//go:embed migration
|
||||||
|
var migrationFS embed.FS
|
||||||
|
|
||||||
|
//go:embed seed
|
||||||
|
var seedFS embed.FS
|
||||||
|
|
||||||
|
// Migrate applies the latest schema to the database.
|
||||||
|
func (s *Store) Migrate(ctx context.Context) error {
|
||||||
|
if err := s.preMigrate(ctx); err != nil {
|
||||||
|
return errors.Wrap(err, "failed to pre-migrate")
|
||||||
|
}
|
||||||
|
|
||||||
|
if s.Profile.Mode == "prod" {
|
||||||
|
migrationHistoryList, err := s.driver.FindMigrationHistoryList(ctx, &FindMigrationHistory{})
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err, "failed to find migration history")
|
||||||
|
}
|
||||||
|
if len(migrationHistoryList) == 0 {
|
||||||
|
return errors.Errorf("no migration history found")
|
||||||
|
}
|
||||||
|
|
||||||
|
migrationHistoryVersions := []string{}
|
||||||
|
for _, migrationHistory := range migrationHistoryList {
|
||||||
|
migrationHistoryVersions = append(migrationHistoryVersions, migrationHistory.Version)
|
||||||
|
}
|
||||||
|
sort.Sort(version.SortVersion(migrationHistoryVersions))
|
||||||
|
latestMigrationHistoryVersion := migrationHistoryVersions[len(migrationHistoryVersions)-1]
|
||||||
|
currentVersion := version.GetCurrentVersion(s.Profile.Mode)
|
||||||
|
|
||||||
|
if version.IsVersionGreaterThan(version.GetSchemaVersion(currentVersion), latestMigrationHistoryVersion) {
|
||||||
|
minorVersionList := s.getMinorVersionList()
|
||||||
|
fmt.Println("start migration")
|
||||||
|
for _, minorVersion := range minorVersionList {
|
||||||
|
normalizedVersion := minorVersion + ".0"
|
||||||
|
if version.IsVersionGreaterThan(normalizedVersion, latestMigrationHistoryVersion) && version.IsVersionGreaterOrEqualThan(currentVersion, normalizedVersion) {
|
||||||
|
fmt.Println("applying migration for", normalizedVersion)
|
||||||
|
if err := s.applyMigrationForMinorVersion(ctx, minorVersion); err != nil {
|
||||||
|
return errors.Wrap(err, "failed to apply minor version migration")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fmt.Println("end migrate")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// In demo mode, we should seed the database.
|
||||||
|
if s.Profile.Mode == "demo" {
|
||||||
|
if err := s.seed(ctx); err != nil {
|
||||||
|
return errors.Wrap(err, "failed to seed")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Store) preMigrate(ctx context.Context) error {
|
||||||
|
migrationHistoryList, err := s.driver.FindMigrationHistoryList(ctx, &FindMigrationHistory{})
|
||||||
|
// If there is no migration history, we should apply the latest schema.
|
||||||
|
if err != nil || len(migrationHistoryList) == 0 {
|
||||||
|
if err != nil {
|
||||||
|
slog.Error("failed to find migration history", "error", err)
|
||||||
|
}
|
||||||
|
fileName := s.getMigrationBasePath() + latestSchemaFileName
|
||||||
|
bytes, err := migrationFS.ReadFile(fileName)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Errorf("failed to read latest schema file: %s", err)
|
||||||
|
}
|
||||||
|
if err := s.execute(ctx, string(bytes)); err != nil {
|
||||||
|
return errors.Errorf("failed to exec SQL file %s, err %s", fileName, err)
|
||||||
|
}
|
||||||
|
if _, err := s.driver.UpsertMigrationHistory(ctx, &UpsertMigrationHistory{
|
||||||
|
Version: version.GetCurrentVersion(s.Profile.Mode),
|
||||||
|
}); err != nil {
|
||||||
|
return errors.Wrap(err, "failed to upsert migration history")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Store) getMigrationBasePath() string {
|
||||||
|
mode := "dev"
|
||||||
|
if s.Profile.Mode == "prod" {
|
||||||
|
mode = "prod"
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("migration/%s/%s/", s.driver.Type(), mode)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Store) getSeedBasePath() string {
|
||||||
|
return fmt.Sprintf("seed/%s/", s.driver.Type())
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
latestSchemaFileName = "LATEST__SCHEMA.sql"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (s *Store) applyMigrationForMinorVersion(ctx context.Context, minorVersion string) error {
|
||||||
|
filenames, err := fs.Glob(migrationFS, fmt.Sprintf("%s%s/*.sql", s.getMigrationBasePath(), minorVersion))
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err, "failed to read migration files")
|
||||||
|
}
|
||||||
|
|
||||||
|
sort.Strings(filenames)
|
||||||
|
migrationStmt := ""
|
||||||
|
// Loop over all migration files and execute them in order.
|
||||||
|
for _, filename := range filenames {
|
||||||
|
buf, err := migrationFS.ReadFile(filename)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrapf(err, "failed to read minor version migration file, filename=%s", filename)
|
||||||
|
}
|
||||||
|
stmt := string(buf)
|
||||||
|
migrationStmt += stmt
|
||||||
|
if err := s.execute(ctx, stmt); err != nil {
|
||||||
|
return errors.Wrapf(err, "migrate error: %s", stmt)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Upsert the newest version to migration_history.
|
||||||
|
version := minorVersion + ".0"
|
||||||
|
if _, err = s.driver.UpsertMigrationHistory(ctx, &UpsertMigrationHistory{
|
||||||
|
Version: version,
|
||||||
|
}); err != nil {
|
||||||
|
return errors.Wrapf(err, "failed to upsert migration history with version: %s", version)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Store) seed(ctx context.Context) error {
|
||||||
|
// Only seed for SQLite.
|
||||||
|
if s.driver.Type() != "sqlite" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
filenames, err := fs.Glob(seedFS, fmt.Sprintf("%s*.sql", s.getSeedBasePath()))
|
||||||
|
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 {
|
||||||
|
bytes, err := seedFS.ReadFile(filename)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrapf(err, "failed to read seed file, filename=%s", filename)
|
||||||
|
}
|
||||||
|
if err := s.execute(ctx, string(bytes)); err != nil {
|
||||||
|
return errors.Wrapf(err, "seed error: %s", filename)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// execute runs a single SQL statement within a transaction.
|
||||||
|
func (s *Store) execute(ctx context.Context, stmt string) error {
|
||||||
|
tx, err := s.driver.GetDB().Begin()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer tx.Rollback()
|
||||||
|
if _, err := tx.ExecContext(ctx, stmt); err != nil {
|
||||||
|
return errors.Wrap(err, "failed to execute statement")
|
||||||
|
}
|
||||||
|
return tx.Commit()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Store) getMinorVersionList() []string {
|
||||||
|
var minorDirRegexp = regexp.MustCompile(fmt.Sprintf(`^%s[0-9]+\.[0-9]+$`, s.getMigrationBasePath()))
|
||||||
|
minorVersionList := []string{}
|
||||||
|
if err := fs.WalkDir(migrationFS, "migration", func(path string, file fs.DirEntry, err error) error {
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if file.IsDir() && minorDirRegexp.MatchString(path) {
|
||||||
|
minorVersionList = append(minorVersionList, file.Name())
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
sort.Sort(version.SortVersion(minorVersionList))
|
||||||
|
return minorVersionList
|
||||||
|
}
|
||||||
|
@ -147,7 +147,7 @@ func (s *Store) DeleteResource(ctx context.Context, delete *DeleteResource) erro
|
|||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}(); err != nil {
|
}(); err != nil {
|
||||||
slog.Warn("Failed to delete s3 object", err)
|
slog.Warn("Failed to delete s3 object", slog.Any("err", err))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
12
store/seed/sqlite/00__reset.sql
Normal file
12
store/seed/sqlite/00__reset.sql
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
DELETE FROM system_setting;
|
||||||
|
DELETE FROM user;
|
||||||
|
DELETE FROM user_setting;
|
||||||
|
DELETE FROM memo;
|
||||||
|
DELETE FROM memo_organizer;
|
||||||
|
DELETE FROM memo_relation;
|
||||||
|
DELETE FROM resource;
|
||||||
|
DELETE FROM activity;
|
||||||
|
DELETE FROM idp;
|
||||||
|
DELETE FROM inbox;
|
||||||
|
DELETE FROM webhook;
|
||||||
|
DELETE FROM reaction;
|
10
store/seed/sqlite/01__dump.sql
Normal file
10
store/seed/sqlite/01__dump.sql
Normal file
File diff suppressed because one or more lines are too long
@ -1,7 +1,6 @@
|
|||||||
package store
|
package store
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"github.com/usememos/memos/server/profile"
|
"github.com/usememos/memos/server/profile"
|
||||||
@ -25,14 +24,6 @@ func New(driver Driver, profile *profile.Profile) *Store {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (*Store) MigrateManually(context.Context) error {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Store) Close() error {
|
func (s *Store) Close() error {
|
||||||
return s.driver.Close()
|
return s.driver.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Store) GetCurrentDBSize(ctx context.Context) (int64, error) {
|
|
||||||
return s.driver.GetCurrentDBSize(ctx)
|
|
||||||
}
|
|
||||||
|
@ -21,11 +21,11 @@ func NewTestingStore(ctx context.Context, t *testing.T) *store.Store {
|
|||||||
fmt.Printf("failed to create db driver, error: %+v\n", err)
|
fmt.Printf("failed to create db driver, error: %+v\n", err)
|
||||||
}
|
}
|
||||||
resetTestingDB(ctx, profile, dbDriver)
|
resetTestingDB(ctx, profile, dbDriver)
|
||||||
if err := dbDriver.Migrate(ctx); err != nil {
|
|
||||||
fmt.Printf("failed to migrate db, error: %+v\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
store := store.New(dbDriver, profile)
|
store := store.New(dbDriver, profile)
|
||||||
|
if err := store.Migrate(ctx); err != nil {
|
||||||
|
fmt.Printf("failed to migrate db, error: %+v\n", err)
|
||||||
|
}
|
||||||
return store
|
return store
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user