mirror of
https://github.com/pocketbase/pocketbase.git
synced 2024-11-29 02:12:14 +02:00
148 lines
4.3 KiB
Go
148 lines
4.3 KiB
Go
package daos
|
|
|
|
import (
|
|
"fmt"
|
|
"strings"
|
|
|
|
"github.com/pocketbase/pocketbase/models"
|
|
"github.com/pocketbase/pocketbase/models/schema"
|
|
"github.com/pocketbase/pocketbase/tools/security"
|
|
)
|
|
|
|
// SyncRecordTableSchema compares the two provided collections
|
|
// and applies the necessary related record table changes.
|
|
//
|
|
// If `oldCollection` is null, then only `newCollection` is used to create the record table.
|
|
func (dao *Dao) SyncRecordTableSchema(newCollection *models.Collection, oldCollection *models.Collection) error {
|
|
// create
|
|
if oldCollection == nil {
|
|
cols := map[string]string{
|
|
schema.FieldNameId: "TEXT PRIMARY KEY NOT NULL",
|
|
schema.FieldNameCreated: "TEXT DEFAULT '' NOT NULL",
|
|
schema.FieldNameUpdated: "TEXT DEFAULT '' NOT NULL",
|
|
}
|
|
|
|
if newCollection.IsAuth() {
|
|
cols[schema.FieldNameUsername] = "TEXT NOT NULL"
|
|
cols[schema.FieldNameEmail] = "TEXT DEFAULT '' NOT NULL"
|
|
cols[schema.FieldNameEmailVisibility] = "BOOLEAN DEFAULT FALSE NOT NULL"
|
|
cols[schema.FieldNameVerified] = "BOOLEAN DEFAULT FALSE NOT NULL"
|
|
cols[schema.FieldNameTokenKey] = "TEXT NOT NULL"
|
|
cols[schema.FieldNamePasswordHash] = "TEXT NOT NULL"
|
|
cols[schema.FieldNameLastResetSentAt] = "TEXT DEFAULT '' NOT NULL"
|
|
cols[schema.FieldNameLastVerificationSentAt] = "TEXT DEFAULT '' NOT NULL"
|
|
}
|
|
|
|
// ensure that the new collection has an id
|
|
if !newCollection.HasId() {
|
|
newCollection.RefreshId()
|
|
newCollection.MarkAsNew()
|
|
}
|
|
|
|
tableName := newCollection.Name
|
|
|
|
// add schema field definitions
|
|
for _, field := range newCollection.Schema.Fields() {
|
|
cols[field.Name] = field.ColDefinition()
|
|
}
|
|
|
|
// create table
|
|
if _, err := dao.DB().CreateTable(tableName, cols).Execute(); err != nil {
|
|
return err
|
|
}
|
|
|
|
// add named index on the base `created` column
|
|
if _, err := dao.DB().CreateIndex(tableName, "_"+newCollection.Id+"_created_idx", "created").Execute(); err != nil {
|
|
return err
|
|
}
|
|
|
|
// add named unique index on the email and tokenKey columns
|
|
if newCollection.IsAuth() {
|
|
_, err := dao.DB().NewQuery(fmt.Sprintf(
|
|
`
|
|
CREATE UNIQUE INDEX _%s_username_idx ON {{%s}} ([[username]]);
|
|
CREATE UNIQUE INDEX _%s_email_idx ON {{%s}} ([[email]]) WHERE [[email]] != '';
|
|
CREATE UNIQUE INDEX _%s_tokenKey_idx ON {{%s}} ([[tokenKey]]);
|
|
`,
|
|
newCollection.Id, tableName,
|
|
newCollection.Id, tableName,
|
|
newCollection.Id, tableName,
|
|
)).Execute()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// update
|
|
return dao.RunInTransaction(func(txDao *Dao) error {
|
|
oldTableName := oldCollection.Name
|
|
newTableName := newCollection.Name
|
|
oldSchema := oldCollection.Schema
|
|
newSchema := newCollection.Schema
|
|
|
|
// check for renamed table
|
|
if !strings.EqualFold(oldTableName, newTableName) {
|
|
_, err := txDao.DB().RenameTable(oldTableName, newTableName).Execute()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
// check for deleted columns
|
|
for _, oldField := range oldSchema.Fields() {
|
|
if f := newSchema.GetFieldById(oldField.Id); f != nil {
|
|
continue // exist
|
|
}
|
|
|
|
_, err := txDao.DB().DropColumn(newTableName, oldField.Name).Execute()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
// check for new or renamed columns
|
|
toRename := map[string]string{}
|
|
for _, field := range newSchema.Fields() {
|
|
oldField := oldSchema.GetFieldById(field.Id)
|
|
// Note:
|
|
// We are using a temporary column name when adding or renaming columns
|
|
// to ensure that there are no name collisions in case there is
|
|
// names switch/reuse of existing columns (eg. name, title -> title, name).
|
|
// This way we are always doing 1 more rename operation but it provides better dev experience.
|
|
|
|
if oldField == nil {
|
|
tempName := field.Name + security.PseudorandomString(5)
|
|
toRename[tempName] = field.Name
|
|
|
|
// add
|
|
_, err := txDao.DB().AddColumn(newTableName, tempName, field.ColDefinition()).Execute()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
} else if oldField.Name != field.Name {
|
|
tempName := field.Name + security.PseudorandomString(5)
|
|
toRename[tempName] = field.Name
|
|
|
|
// rename
|
|
_, err := txDao.DB().RenameColumn(newTableName, oldField.Name, tempName).Execute()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
}
|
|
|
|
// set the actual columns name
|
|
for tempName, actualName := range toRename {
|
|
_, err := txDao.DB().RenameColumn(newTableName, tempName, actualName).Execute()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
return nil
|
|
})
|
|
}
|