1
0
mirror of https://github.com/pocketbase/pocketbase.git synced 2025-03-18 21:57:50 +02:00

[#5611] removed writable_schema usage

This commit is contained in:
Gani Georgiev 2024-10-18 07:55:25 +03:00
parent ade061cc80
commit 5dbf975424
4 changed files with 66 additions and 49 deletions

View File

@ -7,6 +7,8 @@
- Fixed the JSVM types to include properly generated function declarations when the related Go functions have shortened/combined return values.
- Reorganized the record table fields<->columns syncing to remove the `PRAGMA writable_schema` usage.
## v0.23.0-rc6

View File

@ -265,7 +265,7 @@ func (app *BaseApp) registerCollectionHooks() {
// ---
onErrorReloadCachedCollections := func(ce *CollectionErrorEvent) error {
if err := ce.App.ReloadCachedCollections(); err != nil {
ce.App.Logger().Warn("Failed to reload collections cache", "error", err)
ce.App.Logger().Warn("Failed to reload collections cache after collection change error", "error", err)
}
return ce.Next()

View File

@ -158,15 +158,6 @@ func normalizeSingleVsMultipleFieldChanges(app App, newCollection *Collection, o
}
return app.RunInTransaction(func(txApp App) error {
// temporary disable the schema error checks to prevent view and trigger errors
// when "altering" (aka. deleting and recreating) the non-normalized columns
if _, err := txApp.DB().NewQuery("PRAGMA writable_schema = ON").Execute(); err != nil {
return err
}
// executed with defer to make sure that the pragma is always reverted
// in case of an error and when nested transactions are used
defer txApp.DB().NewQuery("PRAGMA writable_schema = RESET").Execute()
for _, newField := range newCollection.Fields {
// allow to continue even if there is no old field for the cases
// when a new field is added and there are already inserted data
@ -186,17 +177,42 @@ func normalizeSingleVsMultipleFieldChanges(app App, newCollection *Collection, o
continue // no change
}
// update the column definition by:
// 1. inserting a new column with the new definition
// 2. copy normalized values from the original column to the new one
// 3. drop the original column
// 4. rename the new column to the original column
// -------------------------------------------------------
// update the field column definition
// -------------------------------------------------------
originalName := newField.GetName()
tempName := "_" + newField.GetName() + security.PseudorandomString(5)
// temporary drop all views to prevent reference errors during the columns renaming
// (this is used as an "alternative" to the writable_schema PRAGMA)
views := []struct {
Name string `db:"name"`
SQL string `db:"sql"`
}{}
err := txApp.DB().Select("name", "sql").
From("sqlite_master").
AndWhere(dbx.NewExp("sql is not null")).
AndWhere(dbx.HashExp{"type": "view"}).
All(&views)
if err != nil {
return err
}
for _, view := range views {
err = txApp.DeleteView(view.Name)
if err != nil {
return err
}
}
_, err := txApp.DB().AddColumn(newCollection.Name, tempName, newField.ColumnType(txApp)).Execute()
originalName := newField.GetName()
oldTempName := "_" + newField.GetName() + security.PseudorandomString(5)
// rename temporary the original column to something else to allow inserting a new one in its place
_, err = txApp.DB().RenameColumn(newCollection.Name, originalName, oldTempName).Execute()
if err != nil {
return err
}
// reinsert the field column with the new type
_, err = txApp.DB().AddColumn(newCollection.Name, originalName, newField.ColumnType(txApp)).Execute()
if err != nil {
return err
}
@ -220,12 +236,12 @@ func normalizeSingleVsMultipleFieldChanges(app App, newCollection *Collection, o
END
)`,
newCollection.Name,
tempName,
originalName,
originalName,
originalName,
originalName,
originalName,
oldTempName,
oldTempName,
oldTempName,
oldTempName,
oldTempName,
))
} else {
// multiple -> single (keep only the last element)
@ -247,35 +263,37 @@ func normalizeSingleVsMultipleFieldChanges(app App, newCollection *Collection, o
END
)`,
newCollection.Name,
tempName,
originalName,
originalName,
originalName,
originalName,
originalName,
oldTempName,
oldTempName,
oldTempName,
oldTempName,
oldTempName,
))
}
// copy the normalized values
if _, err := copyQuery.Execute(); err != nil {
_, err = copyQuery.Execute()
if err != nil {
return err
}
// drop the original column
if _, err := txApp.DB().DropColumn(newCollection.Name, originalName).Execute(); err != nil {
_, err = txApp.DB().DropColumn(newCollection.Name, oldTempName).Execute()
if err != nil {
return err
}
// rename the new column back to the original
if _, err := txApp.DB().RenameColumn(newCollection.Name, tempName, originalName).Execute(); err != nil {
return err
// restore views
for _, view := range views {
_, err = txApp.DB().NewQuery(view.SQL).Execute()
if err != nil {
return err
}
}
}
// revert the pragma and reload the schema
_, revertErr := txApp.DB().NewQuery("PRAGMA writable_schema = RESET").Execute()
return revertErr
return nil
})
}

View File

@ -140,10 +140,9 @@ func (app *BaseApp) delete(ctx context.Context, model Model, isForAuxDB bool) er
})
})
if deleteErr != nil {
hookErr := app.OnModelAfterDeleteError().Trigger(&ModelErrorEvent{
ModelEvent: *event,
Error: deleteErr,
})
errEvent := &ModelErrorEvent{ModelEvent: *event, Error: deleteErr}
errEvent.App = app
hookErr := app.OnModelAfterDeleteError().Trigger(errEvent)
if hookErr != nil {
return errors.Join(deleteErr, hookErr)
}
@ -332,10 +331,9 @@ func (app *BaseApp) create(ctx context.Context, model Model, withValidations boo
if saveErr != nil {
event.Model.MarkAsNew() // reset "new" state
hookErr := app.OnModelAfterCreateError().Trigger(&ModelErrorEvent{
ModelEvent: *event,
Error: saveErr,
})
errEvent := &ModelErrorEvent{ModelEvent: *event, Error: saveErr}
errEvent.App = app
hookErr := app.OnModelAfterCreateError().Trigger(errEvent)
if hookErr != nil {
return errors.Join(saveErr, hookErr)
}
@ -417,10 +415,9 @@ func (app *BaseApp) update(ctx context.Context, model Model, withValidations boo
})
})
if saveErr != nil {
hookErr := app.OnModelAfterUpdateError().Trigger(&ModelErrorEvent{
ModelEvent: *event,
Error: saveErr,
})
errEvent := &ModelErrorEvent{ModelEvent: *event, Error: saveErr}
errEvent.App = app
hookErr := app.OnModelAfterUpdateError().Trigger(errEvent)
if hookErr != nil {
return errors.Join(saveErr, hookErr)
}