mirror of
https://github.com/unmojang/drasl.git
synced 2025-08-03 19:06:04 -04:00
Fix 3->4 DB migration, DB migration tests
This commit is contained in:
parent
b034fd5a51
commit
2dab34440b
62
db.go
62
db.go
@ -4,6 +4,7 @@ import (
|
||||
"database/sql"
|
||||
"errors"
|
||||
"fmt"
|
||||
mapset "github.com/deckarep/golang-set/v2"
|
||||
"gorm.io/driver/sqlite"
|
||||
"gorm.io/gorm"
|
||||
"gorm.io/gorm/logger"
|
||||
@ -53,7 +54,7 @@ type V1User struct {
|
||||
Username string `gorm:"unique;not null"`
|
||||
PasswordSalt []byte `gorm:"not null"`
|
||||
PasswordHash []byte `gorm:"not null"`
|
||||
Clients []V3Client `gorm:"foreignKey:UserUUID"`
|
||||
Clients []V1Client `gorm:"foreignKey:UserUUID"`
|
||||
ServerID sql.NullString
|
||||
PlayerName string `gorm:"unique;not null;type:text collate nocase"`
|
||||
FallbackPlayer string
|
||||
@ -71,26 +72,28 @@ func (V1User) TableName() string {
|
||||
return "users"
|
||||
}
|
||||
|
||||
type V2Client struct {
|
||||
type V1Client struct {
|
||||
ClientToken string `gorm:"primaryKey"`
|
||||
Version int
|
||||
UserUUID string
|
||||
User V3User
|
||||
}
|
||||
|
||||
func (V2Client) TableName() string {
|
||||
func (V1Client) TableName() string {
|
||||
return "clients"
|
||||
}
|
||||
|
||||
type V3Client struct {
|
||||
type V2User = V1User
|
||||
|
||||
type V2Client struct {
|
||||
UUID string `gorm:"primaryKey"`
|
||||
ClientToken string
|
||||
Version int
|
||||
UserUUID string
|
||||
User V3User
|
||||
User V2User
|
||||
}
|
||||
|
||||
func (V3Client) TableName() string {
|
||||
func (V2Client) TableName() string {
|
||||
return "clients"
|
||||
}
|
||||
|
||||
@ -120,6 +123,8 @@ func (V3User) TableName() string {
|
||||
return "users"
|
||||
}
|
||||
|
||||
type V3Client = V2Client
|
||||
|
||||
type V4User = User
|
||||
type V4Player = Player
|
||||
type V4Client = Client
|
||||
@ -132,7 +137,7 @@ func OpenDB(config *Config) (*gorm.DB, error) {
|
||||
db := Unwrap(gorm.Open(sqlite.Open(dbPath), &gorm.Config{
|
||||
Logger: logger.Default.LogMode(logger.Silent),
|
||||
}))
|
||||
err = migrate(db, alreadyExisted)
|
||||
err = Migrate(config, db, alreadyExisted, CURRENT_USER_VERSION)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Error migrating database: %w", err)
|
||||
}
|
||||
@ -145,7 +150,7 @@ func setUserVersion(tx *gorm.DB, userVersion uint) error {
|
||||
return tx.Exec(fmt.Sprintf("PRAGMA user_version = %d", userVersion)).Error
|
||||
}
|
||||
|
||||
func migrate(db *gorm.DB, alreadyExisted bool) error {
|
||||
func Migrate(config *Config, db *gorm.DB, alreadyExisted bool, targetUserVersion uint) error {
|
||||
var userVersion uint
|
||||
|
||||
if alreadyExisted {
|
||||
@ -153,16 +158,18 @@ func migrate(db *gorm.DB, alreadyExisted bool) error {
|
||||
return nil
|
||||
}
|
||||
} else {
|
||||
userVersion = CURRENT_USER_VERSION
|
||||
userVersion = targetUserVersion
|
||||
}
|
||||
|
||||
initialUserVersion := userVersion
|
||||
if initialUserVersion < CURRENT_USER_VERSION {
|
||||
log.Printf("Started migration of database version %d to %d", userVersion, CURRENT_USER_VERSION)
|
||||
if initialUserVersion < targetUserVersion {
|
||||
log.Printf("Started migration of database version %d to %d", userVersion, targetUserVersion)
|
||||
} else if initialUserVersion > targetUserVersion {
|
||||
return fmt.Errorf("Database is version %d, migration target version is %d, cannot continue. Are you trying to run an older version of %s with a newer database?", userVersion, targetUserVersion, config.ApplicationName)
|
||||
}
|
||||
|
||||
err := db.Transaction(func(tx *gorm.DB) error {
|
||||
if userVersion == 0 {
|
||||
if userVersion == 0 && targetUserVersion >= 1 {
|
||||
// Version 0 to 1
|
||||
// Add User.OfflineUUID
|
||||
if err := tx.AutoMigrate(&V1User{}); err != nil {
|
||||
@ -183,7 +190,7 @@ func migrate(db *gorm.DB, alreadyExisted bool) error {
|
||||
}
|
||||
userVersion += 1
|
||||
}
|
||||
if userVersion == 1 {
|
||||
if userVersion == 1 && targetUserVersion >= 2 {
|
||||
// Version 1 to 2
|
||||
// Change Client primaryKey from ClientToken to UUID
|
||||
if err := tx.Exec("ALTER TABLE clients RENAME client_token TO uuid").Error; err != nil {
|
||||
@ -197,7 +204,7 @@ func migrate(db *gorm.DB, alreadyExisted bool) error {
|
||||
}
|
||||
userVersion += 1
|
||||
}
|
||||
if userVersion == 2 {
|
||||
if userVersion == 2 && targetUserVersion >= 3 {
|
||||
// Version 2 to 3
|
||||
// Add User.APIToken
|
||||
|
||||
@ -219,7 +226,7 @@ func migrate(db *gorm.DB, alreadyExisted bool) error {
|
||||
}
|
||||
userVersion += 1
|
||||
}
|
||||
if userVersion == 3 {
|
||||
if userVersion == 3 && targetUserVersion >= 4 {
|
||||
// Version 3 to 4
|
||||
// Split Users and Players
|
||||
|
||||
@ -231,15 +238,26 @@ func migrate(db *gorm.DB, alreadyExisted bool) error {
|
||||
if err := tx.AutoMigrate(&V4User{}); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := tx.AutoMigrate(&V4Player{}); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := tx.AutoMigrate(&V4Client{}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Drop player_name, it has a non-null constraint and SQLite has no
|
||||
// mechanism to remove it
|
||||
// Drop player_name and offline_uuid, they have non-null
|
||||
// constraints and SQLite has no mechanism to remove them
|
||||
if err := tx.Migrator().DropColumn(&V4User{}, "player_name"); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := tx.Migrator().DropColumn(&V4User{}, "offline_uuid"); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
allUsernames := mapset.NewSet[string]()
|
||||
for _, v3User := range v3Users {
|
||||
allUsernames.Add(v3User.Username)
|
||||
}
|
||||
|
||||
users := make([]V4User, 0, len(v3Users))
|
||||
for _, v3User := range v3Users {
|
||||
@ -253,9 +271,15 @@ func migrate(db *gorm.DB, alreadyExisted bool) error {
|
||||
PlayerUUID: &v3Client.UserUUID,
|
||||
})
|
||||
}
|
||||
// If the player name is in use as someone else's username,
|
||||
// reset the player name to its owner's username
|
||||
playerName := v3User.PlayerName
|
||||
if playerName != v3User.Username && allUsernames.Contains(playerName) {
|
||||
playerName = v3User.Username
|
||||
}
|
||||
player := V4Player{
|
||||
UUID: v3User.UUID,
|
||||
Name: v3User.PlayerName,
|
||||
Name: playerName,
|
||||
OfflineUUID: v3User.OfflineUUID,
|
||||
CreatedAt: v3User.CreatedAt,
|
||||
NameLastChangedAt: v3User.NameLastChangedAt,
|
||||
@ -387,7 +411,7 @@ func migrate(db *gorm.DB, alreadyExisted bool) error {
|
||||
return err
|
||||
}
|
||||
|
||||
if initialUserVersion < CURRENT_USER_VERSION {
|
||||
if initialUserVersion < targetUserVersion {
|
||||
log.Printf("Finished migration from version %d to %d", initialUserVersion, userVersion)
|
||||
}
|
||||
|
||||
|
148
db_test.go
Normal file
148
db_test.go
Normal file
@ -0,0 +1,148 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
"gorm.io/driver/sqlite"
|
||||
"gorm.io/gorm"
|
||||
"gorm.io/gorm/logger"
|
||||
"io"
|
||||
"log"
|
||||
"os"
|
||||
"path"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func (ts *TestSuite) getFreshDatabase(t *testing.T) *gorm.DB {
|
||||
dbPath := path.Join(ts.Config.StateDirectory, "drasl.db")
|
||||
if err := os.Remove(dbPath); err != nil {
|
||||
assert.True(t, os.IsNotExist(err))
|
||||
}
|
||||
db, err := gorm.Open(sqlite.Open(dbPath), &gorm.Config{
|
||||
Logger: logger.Default.LogMode(logger.Silent),
|
||||
})
|
||||
assert.Nil(t, err)
|
||||
return db
|
||||
}
|
||||
|
||||
func TestDB(t *testing.T) {
|
||||
t.Parallel()
|
||||
ts := TestSuite{}
|
||||
|
||||
log.SetOutput(io.Discard)
|
||||
|
||||
tempStateDirectory := Unwrap(os.MkdirTemp("", "tmp"))
|
||||
ts.StateDirectory = tempStateDirectory
|
||||
|
||||
config := DefaultConfig()
|
||||
config.StateDirectory = tempStateDirectory
|
||||
config.DataDirectory = "."
|
||||
ts.Config = &config
|
||||
|
||||
defer ts.Teardown()
|
||||
|
||||
t.Run("Test with a fresh database", ts.testFreshDatabase)
|
||||
t.Run("Test 1->2 migration", ts.testMigrate1To2)
|
||||
t.Run("Test 2->3 migration", ts.testMigrate2To3)
|
||||
t.Run("Test 3->4 migration", ts.testMigrate3To4)
|
||||
t.Run("Test 3->4 migration, username/player name collision", ts.testMigrate3To4Collision)
|
||||
}
|
||||
|
||||
func (ts *TestSuite) testFreshDatabase(t *testing.T) {
|
||||
db := ts.getFreshDatabase(t)
|
||||
err := Migrate(ts.Config, db, false, CURRENT_USER_VERSION)
|
||||
assert.Nil(t, err)
|
||||
}
|
||||
|
||||
func (ts *TestSuite) testMigrate1To2(t *testing.T) {
|
||||
db := ts.getFreshDatabase(t)
|
||||
|
||||
query, err := os.ReadFile("sql/1.sql")
|
||||
assert.Nil(t, err)
|
||||
assert.Nil(t, db.Exec(string(query)).Error)
|
||||
|
||||
var v1Client V1Client
|
||||
assert.Nil(t, db.First(&v1Client).Error)
|
||||
|
||||
err = Migrate(ts.Config, db, true, 2)
|
||||
assert.Nil(t, err)
|
||||
|
||||
var v2Client V2Client
|
||||
assert.Nil(t, db.First(&v2Client).Error)
|
||||
assert.NotEqual(t, "", v2Client.UUID)
|
||||
assert.Equal(t, v1Client.UserUUID, v2Client.UserUUID)
|
||||
assert.Equal(t, v1Client.Version, v2Client.Version)
|
||||
}
|
||||
|
||||
func (ts *TestSuite) testMigrate2To3(t *testing.T) {
|
||||
db := ts.getFreshDatabase(t)
|
||||
|
||||
query, err := os.ReadFile("sql/2.sql")
|
||||
assert.Nil(t, err)
|
||||
assert.Nil(t, db.Exec(string(query)).Error)
|
||||
|
||||
var v2User V2User
|
||||
assert.Nil(t, db.First(&v2User).Error)
|
||||
|
||||
err = Migrate(ts.Config, db, true, 3)
|
||||
assert.Nil(t, err)
|
||||
|
||||
var v3User V3User
|
||||
assert.Nil(t, db.First(&v3User).Error)
|
||||
assert.NotEqual(t, "", v3User.APIToken)
|
||||
}
|
||||
|
||||
func (ts *TestSuite) testMigrate3To4(t *testing.T) {
|
||||
db := ts.getFreshDatabase(t)
|
||||
|
||||
query, err := os.ReadFile("sql/3.sql")
|
||||
assert.Nil(t, err)
|
||||
assert.Nil(t, db.Exec(string(query)).Error)
|
||||
|
||||
var v3User V3User
|
||||
assert.Nil(t, db.First(&v3User).Error)
|
||||
|
||||
err = Migrate(ts.Config, db, true, 4)
|
||||
assert.Nil(t, err)
|
||||
|
||||
var v4User V4User
|
||||
assert.Nil(t, db.First(&v4User).Error)
|
||||
assert.Equal(t, 1, len(v4User.Players))
|
||||
player := v4User.Players[0]
|
||||
assert.Equal(t, v3User.OfflineUUID, player.OfflineUUID)
|
||||
assert.Equal(t, *UnmakeNullString(&v3User.SkinHash), *UnmakeNullString(&player.SkinHash))
|
||||
assert.Equal(t, *UnmakeNullString(&v3User.CapeHash), *UnmakeNullString(&player.CapeHash))
|
||||
}
|
||||
|
||||
func (ts *TestSuite) testMigrate3To4Collision(t *testing.T) {
|
||||
// User foo has player qux
|
||||
// User qux has player foo
|
||||
// After migration, user foo should have player foo and user qux should
|
||||
// have player qux
|
||||
|
||||
db := ts.getFreshDatabase(t)
|
||||
|
||||
query, err := os.ReadFile("sql/3-username-player-name-collison.sql")
|
||||
assert.Nil(t, err)
|
||||
assert.Nil(t, db.Exec(string(query)).Error)
|
||||
|
||||
var v3foo V3User
|
||||
assert.Nil(t, db.First(&v3foo, "username = ?", "foo").Error)
|
||||
assert.Equal(t, "qux", v3foo.PlayerName)
|
||||
|
||||
var v3qux V3User
|
||||
assert.Nil(t, db.First(&v3qux, "username = ?", "qux").Error)
|
||||
assert.Equal(t, "foo", v3qux.PlayerName)
|
||||
|
||||
err = Migrate(ts.Config, db, true, 4)
|
||||
assert.Nil(t, err)
|
||||
|
||||
var v4foo V4User
|
||||
assert.Nil(t, db.First(&v4foo, "username = ?", "foo").Error)
|
||||
assert.Equal(t, 1, len(v4foo.Players))
|
||||
assert.Equal(t, "foo", v4foo.Players[0].Name)
|
||||
|
||||
var v4qux V4User
|
||||
assert.Nil(t, db.First(&v4qux, "username = ?", "qux").Error)
|
||||
assert.Equal(t, 1, len(v4qux.Players))
|
||||
assert.Equal(t, "qux", v4qux.Players[0].Name)
|
||||
}
|
@ -48,7 +48,7 @@
|
||||
];
|
||||
|
||||
# Update whenever Go dependencies change
|
||||
vendorHash = "sha256-XLkICl7cL6FaWArl99xUH6kYLxHAZ/VsS0sP3d4yLws=";
|
||||
vendorHash = "sha256-bkmi3yvE/JPQvTjBzOHZ07PBVXp8lQEv2l0q6GTC94k=";
|
||||
|
||||
outputs = ["out"];
|
||||
|
||||
|
1
go.mod
1
go.mod
@ -20,6 +20,7 @@ require (
|
||||
require (
|
||||
github.com/cespare/xxhash/v2 v2.2.0 // indirect
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/deckarep/golang-set/v2 v2.6.0 // indirect
|
||||
github.com/dustin/go-humanize v1.0.1 // indirect
|
||||
github.com/golang-jwt/jwt v3.2.2+incompatible // indirect
|
||||
github.com/golang/glog v1.1.2 // indirect
|
||||
|
2
go.sum
2
go.sum
@ -7,6 +7,8 @@ github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ3
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/deckarep/golang-set/v2 v2.6.0 h1:XfcQbWM1LlMB8BsJ8N9vW5ehnnPVIw0je80NsVHagjM=
|
||||
github.com/deckarep/golang-set/v2 v2.6.0/go.mod h1:VAky9rY/yGXJOLEDv3OMci+7wtDpOF4IN+y82NBOac4=
|
||||
github.com/dgraph-io/ristretto v0.1.1 h1:6CWw5tJNgpegArSHpNHJKldNeq03FQCwYvfMVWajOK8=
|
||||
github.com/dgraph-io/ristretto v0.1.1/go.mod h1:S1GPSBCYCIhmVNfcth17y2zZtQT6wzkzgwUve0VDWWA=
|
||||
github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2 h1:tdlZCpZ/P9DhczCTSixgIKmwPv6+wP5DGjqLYw5SUiA=
|
||||
|
13
sql/1.sql
Normal file
13
sql/1.sql
Normal file
@ -0,0 +1,13 @@
|
||||
PRAGMA user_version=1;
|
||||
PRAGMA foreign_keys=OFF;
|
||||
BEGIN TRANSACTION;
|
||||
CREATE TABLE `users` (`is_admin` numeric,`is_locked` numeric,`uuid` text,`username` text NOT NULL UNIQUE,`password_salt` blob NOT NULL,`password_hash` blob NOT NULL,`server_id` text,`player_name` text collate nocase NOT NULL UNIQUE,`offline_uuid` text,`fallback_player` text,`preferred_language` text,`browser_token` text,`skin_hash` text,`skin_model` text,`cape_hash` text,`created_at` datetime,`name_last_changed_at` datetime,PRIMARY KEY (`uuid`));
|
||||
INSERT INTO users VALUES(1,0,'dc500452-7745-4939-a187-a8ce37beca28','foo',X'cdae655061130b4594991676095739d6',X'76f1040e8fa5c96f6d94b3b088d8079bfbc46efcb94d270a750a5673c88e757d',NULL,'foo','ab980ae0-02d3-3064-adcf-22d6ca24b404','dc500452-7745-4939-a187-a8ce37beca28','en','23313163410b65f0fcb4bc1beea75a3a8bfbbc25af626c3e1d34b61f9f8c050b',NULL,'classic',NULL,'2024-11-27 17:21:50.994337304-05:00','2024-11-27 17:21:50.994337485-05:00');
|
||||
CREATE TABLE `clients` (`client_token` text,`version` integer,`user_uuid` text,PRIMARY KEY (`client_token`),CONSTRAINT `fk_users_clients` FOREIGN KEY (`user_uuid`) REFERENCES `users`(`uuid`));
|
||||
INSERT INTO clients VALUES('e7926dc1e9b74b598251dd16277d0bba',0,'dc500452-7745-4939-a187-a8ce37beca28');
|
||||
CREATE TABLE `invites` (`code` text,`created_at` datetime,PRIMARY KEY (`code`));
|
||||
INSERT INTO invites VALUES('cwB03PjPqSJ','2024-11-27 17:22:00.218111184-05:00');
|
||||
CREATE INDEX `idx_users_cape_hash` ON `users`(`cape_hash`);
|
||||
CREATE INDEX `idx_users_skin_hash` ON `users`(`skin_hash`);
|
||||
CREATE INDEX `idx_users_browser_token` ON `users`(`browser_token`);
|
||||
COMMIT;
|
12
sql/2.sql
Normal file
12
sql/2.sql
Normal file
@ -0,0 +1,12 @@
|
||||
PRAGMA user_version=2;
|
||||
PRAGMA foreign_keys=OFF;
|
||||
BEGIN TRANSACTION;
|
||||
CREATE TABLE `users` (`is_admin` numeric,`is_locked` numeric,`uuid` text,`username` text NOT NULL UNIQUE,`password_salt` blob NOT NULL,`password_hash` blob NOT NULL,`server_id` text,`player_name` text collate nocase NOT NULL UNIQUE,`offline_uuid` text,`fallback_player` text,`preferred_language` text,`browser_token` text,`skin_hash` text,`skin_model` text,`cape_hash` text,`created_at` datetime,`name_last_changed_at` datetime,PRIMARY KEY (`uuid`));
|
||||
INSERT INTO users VALUES(1,0,'d8783ef3-39f9-4bd9-af49-adfc778bf2b5','foo',X'24e51b055d01172da6bdea76f5708688',X'f969f3d96a28006ddfe376a5a38f5a81749d403a42f58cf2852c2c24477271ea',NULL,'foo','ab980ae0-02d3-3064-adcf-22d6ca24b404','d8783ef3-39f9-4bd9-af49-adfc778bf2b5','en','84ea8e23311b4049e0d7b9dbf1f7470d2e030e50e411096fe8f0968146ad9071',NULL,'classic',NULL,'2024-11-28 11:35:08.923943529-05:00','2024-11-28 11:35:08.92394374-05:00');
|
||||
CREATE TABLE `clients` (`uuid` text,`client_token` text,`version` integer,`user_uuid` text,PRIMARY KEY (`uuid`),CONSTRAINT `fk_users_clients` FOREIGN KEY (`user_uuid`) REFERENCES `users`(`uuid`));
|
||||
INSERT INTO clients VALUES('d76bcd64-0499-4c14-9826-2a40b84439cf','4236ebec07e34e13ab6890e3dac36fbf',0,'d8783ef3-39f9-4bd9-af49-adfc778bf2b5');
|
||||
CREATE TABLE `invites` (`code` text,`created_at` datetime,PRIMARY KEY (`code`));
|
||||
CREATE INDEX `idx_users_cape_hash` ON `users`(`cape_hash`);
|
||||
CREATE INDEX `idx_users_skin_hash` ON `users`(`skin_hash`);
|
||||
CREATE INDEX `idx_users_browser_token` ON `users`(`browser_token`);
|
||||
COMMIT;
|
13
sql/3-username-player-name-collison.sql
Normal file
13
sql/3-username-player-name-collison.sql
Normal file
@ -0,0 +1,13 @@
|
||||
PRAGMA user_version=3;
|
||||
PRAGMA foreign_keys=OFF;
|
||||
BEGIN TRANSACTION;
|
||||
CREATE TABLE `users` (`is_admin` numeric,`is_locked` numeric,`uuid` text,`username` text NOT NULL UNIQUE,`password_salt` blob NOT NULL,`password_hash` blob NOT NULL,`server_id` text,`player_name` text collate nocase NOT NULL UNIQUE,`offline_uuid` text NOT NULL,`fallback_player` text,`preferred_language` text,`browser_token` text,`api_token` text,`skin_hash` text,`skin_model` text,`cape_hash` text,`created_at` datetime,`name_last_changed_at` datetime,PRIMARY KEY (`uuid`));
|
||||
INSERT INTO users VALUES(1,0,'8a94719d-94b5-49f6-93c1-bff20aeb9d70','foo',X'a5c1419d67c0ae15e9894e1d505e215e',X'7e5b0222eb21362cea20609501dbe7c69bfcdebca05e66341fe5ad85593ea922',NULL,'qux','abc129bc-460a-324b-90cd-de4d47e63076','8a94719d-94b5-49f6-93c1-bff20aeb9d70','en','f4cf867a642912ac28a73dc7e37f94f5bbf6dba9d2175eb8f12185b179c9b1bb','qVefdhlf90THN49ceNLc1T','27818f0eadf68945ad0880c6c63c2baa0f466ac41960b3b6cc00c51e5dd23125','classic','5630e530c3853fde80d99c60eb91ac8d11061d18f0404a189f73503940473187','2024-11-28 11:41:24.273481686-05:00','2024-11-28 11:53:13.174934976-05:00');
|
||||
INSERT INTO users VALUES(0,0,'022f6807-95f8-41a4-ae39-9a4f7ebfc2cf','qux',X'b9169adc02b7d197611b6f947ed8819b',X'fa74d79864b19fd49dd619238eae1a46f70178c5ac2591aa9233f353a2ff4270',NULL,'foo','ab980ae0-02d3-3064-adcf-22d6ca24b404','022f6807-95f8-41a4-ae39-9a4f7ebfc2cf','en',NULL,'qDoveQLAQtvtwdeiD815vM',NULL,'classic',NULL,'2024-11-28 11:52:22.307406939-05:00','2024-11-28 11:52:50.699505043-05:00');
|
||||
CREATE TABLE `clients` (`uuid` text,`client_token` text,`version` integer,`user_uuid` text,PRIMARY KEY (`uuid`),CONSTRAINT `fk_users_clients` FOREIGN KEY (`user_uuid`) REFERENCES `users`(`uuid`));
|
||||
INSERT INTO clients VALUES('1e654965-89f5-4ab5-8b21-9c9087652ce4','951b701320a84d34b6d873c68db58de4',1,'8a94719d-94b5-49f6-93c1-bff20aeb9d70');
|
||||
CREATE TABLE `invites` (`code` text,`created_at` datetime,PRIMARY KEY (`code`));
|
||||
CREATE INDEX `idx_users_cape_hash` ON `users`(`cape_hash`);
|
||||
CREATE INDEX `idx_users_skin_hash` ON `users`(`skin_hash`);
|
||||
CREATE INDEX `idx_users_browser_token` ON `users`(`browser_token`);
|
||||
COMMIT;
|
12
sql/3.sql
Normal file
12
sql/3.sql
Normal file
@ -0,0 +1,12 @@
|
||||
PRAGMA user_version=3;
|
||||
PRAGMA foreign_keys=OFF;
|
||||
BEGIN TRANSACTION;
|
||||
CREATE TABLE `users` (`is_admin` numeric,`is_locked` numeric,`uuid` text,`username` text NOT NULL UNIQUE,`password_salt` blob NOT NULL,`password_hash` blob NOT NULL,`server_id` text,`player_name` text collate nocase NOT NULL UNIQUE,`offline_uuid` text NOT NULL,`fallback_player` text,`preferred_language` text,`browser_token` text,`api_token` text,`skin_hash` text,`skin_model` text,`cape_hash` text,`created_at` datetime,`name_last_changed_at` datetime,PRIMARY KEY (`uuid`));
|
||||
INSERT INTO users VALUES(1,0,'8a94719d-94b5-49f6-93c1-bff20aeb9d70','foo',X'a5c1419d67c0ae15e9894e1d505e215e',X'7e5b0222eb21362cea20609501dbe7c69bfcdebca05e66341fe5ad85593ea922',NULL,'foo','ab980ae0-02d3-3064-adcf-22d6ca24b404','8a94719d-94b5-49f6-93c1-bff20aeb9d70','en','8e1da35a9e20f1651404c3315bfebb438028fb6495b1407622d8546749c4998b','qVefdhlf90THN49ceNLc1T','27818f0eadf68945ad0880c6c63c2baa0f466ac41960b3b6cc00c51e5dd23125','classic','5630e530c3853fde80d99c60eb91ac8d11061d18f0404a189f73503940473187','2024-11-28 11:41:24.273481686-05:00','2024-11-28 11:41:24.273481896-05:00');
|
||||
CREATE TABLE `clients` (`uuid` text,`client_token` text,`version` integer,`user_uuid` text,PRIMARY KEY (`uuid`),CONSTRAINT `fk_users_clients` FOREIGN KEY (`user_uuid`) REFERENCES `users`(`uuid`));
|
||||
INSERT INTO clients VALUES('1e654965-89f5-4ab5-8b21-9c9087652ce4','951b701320a84d34b6d873c68db58de4',1,'8a94719d-94b5-49f6-93c1-bff20aeb9d70');
|
||||
CREATE TABLE `invites` (`code` text,`created_at` datetime,PRIMARY KEY (`code`));
|
||||
CREATE INDEX `idx_users_cape_hash` ON `users`(`cape_hash`);
|
||||
CREATE INDEX `idx_users_skin_hash` ON `users`(`skin_hash`);
|
||||
CREATE INDEX `idx_users_browser_token` ON `users`(`browser_token`);
|
||||
COMMIT;
|
Loading…
x
Reference in New Issue
Block a user