[#5611] removed writable_schema usage
This commit is contained in:
parent
ade061cc80
commit
5dbf975424
|
@ -7,6 +7,8 @@
|
||||||
|
|
||||||
- Fixed the JSVM types to include properly generated function declarations when the related Go functions have shortened/combined return values.
|
- 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
|
## v0.23.0-rc6
|
||||||
|
|
||||||
|
|
|
@ -265,7 +265,7 @@ func (app *BaseApp) registerCollectionHooks() {
|
||||||
// ---
|
// ---
|
||||||
onErrorReloadCachedCollections := func(ce *CollectionErrorEvent) error {
|
onErrorReloadCachedCollections := func(ce *CollectionErrorEvent) error {
|
||||||
if err := ce.App.ReloadCachedCollections(); err != nil {
|
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()
|
return ce.Next()
|
||||||
|
|
|
@ -158,15 +158,6 @@ func normalizeSingleVsMultipleFieldChanges(app App, newCollection *Collection, o
|
||||||
}
|
}
|
||||||
|
|
||||||
return app.RunInTransaction(func(txApp App) error {
|
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 {
|
for _, newField := range newCollection.Fields {
|
||||||
// allow to continue even if there is no old field for the cases
|
// 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
|
// 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
|
continue // no change
|
||||||
}
|
}
|
||||||
|
|
||||||
// update the column definition by:
|
// -------------------------------------------------------
|
||||||
// 1. inserting a new column with the new definition
|
// update the field column 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
|
|
||||||
// -------------------------------------------------------
|
// -------------------------------------------------------
|
||||||
|
|
||||||
originalName := newField.GetName()
|
// temporary drop all views to prevent reference errors during the columns renaming
|
||||||
tempName := "_" + newField.GetName() + security.PseudorandomString(5)
|
// (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 {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -220,12 +236,12 @@ func normalizeSingleVsMultipleFieldChanges(app App, newCollection *Collection, o
|
||||||
END
|
END
|
||||||
)`,
|
)`,
|
||||||
newCollection.Name,
|
newCollection.Name,
|
||||||
tempName,
|
|
||||||
originalName,
|
|
||||||
originalName,
|
|
||||||
originalName,
|
|
||||||
originalName,
|
|
||||||
originalName,
|
originalName,
|
||||||
|
oldTempName,
|
||||||
|
oldTempName,
|
||||||
|
oldTempName,
|
||||||
|
oldTempName,
|
||||||
|
oldTempName,
|
||||||
))
|
))
|
||||||
} else {
|
} else {
|
||||||
// multiple -> single (keep only the last element)
|
// multiple -> single (keep only the last element)
|
||||||
|
@ -247,35 +263,37 @@ func normalizeSingleVsMultipleFieldChanges(app App, newCollection *Collection, o
|
||||||
END
|
END
|
||||||
)`,
|
)`,
|
||||||
newCollection.Name,
|
newCollection.Name,
|
||||||
tempName,
|
|
||||||
originalName,
|
|
||||||
originalName,
|
|
||||||
originalName,
|
|
||||||
originalName,
|
|
||||||
originalName,
|
originalName,
|
||||||
|
oldTempName,
|
||||||
|
oldTempName,
|
||||||
|
oldTempName,
|
||||||
|
oldTempName,
|
||||||
|
oldTempName,
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
// copy the normalized values
|
// copy the normalized values
|
||||||
if _, err := copyQuery.Execute(); err != nil {
|
_, err = copyQuery.Execute()
|
||||||
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// drop the original column
|
// 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
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// rename the new column back to the original
|
// restore views
|
||||||
if _, err := txApp.DB().RenameColumn(newCollection.Name, tempName, originalName).Execute(); err != nil {
|
for _, view := range views {
|
||||||
return err
|
_, err = txApp.DB().NewQuery(view.SQL).Execute()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// revert the pragma and reload the schema
|
return nil
|
||||||
_, revertErr := txApp.DB().NewQuery("PRAGMA writable_schema = RESET").Execute()
|
|
||||||
|
|
||||||
return revertErr
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
21
core/db.go
21
core/db.go
|
@ -140,10 +140,9 @@ func (app *BaseApp) delete(ctx context.Context, model Model, isForAuxDB bool) er
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
if deleteErr != nil {
|
if deleteErr != nil {
|
||||||
hookErr := app.OnModelAfterDeleteError().Trigger(&ModelErrorEvent{
|
errEvent := &ModelErrorEvent{ModelEvent: *event, Error: deleteErr}
|
||||||
ModelEvent: *event,
|
errEvent.App = app
|
||||||
Error: deleteErr,
|
hookErr := app.OnModelAfterDeleteError().Trigger(errEvent)
|
||||||
})
|
|
||||||
if hookErr != nil {
|
if hookErr != nil {
|
||||||
return errors.Join(deleteErr, hookErr)
|
return errors.Join(deleteErr, hookErr)
|
||||||
}
|
}
|
||||||
|
@ -332,10 +331,9 @@ func (app *BaseApp) create(ctx context.Context, model Model, withValidations boo
|
||||||
if saveErr != nil {
|
if saveErr != nil {
|
||||||
event.Model.MarkAsNew() // reset "new" state
|
event.Model.MarkAsNew() // reset "new" state
|
||||||
|
|
||||||
hookErr := app.OnModelAfterCreateError().Trigger(&ModelErrorEvent{
|
errEvent := &ModelErrorEvent{ModelEvent: *event, Error: saveErr}
|
||||||
ModelEvent: *event,
|
errEvent.App = app
|
||||||
Error: saveErr,
|
hookErr := app.OnModelAfterCreateError().Trigger(errEvent)
|
||||||
})
|
|
||||||
if hookErr != nil {
|
if hookErr != nil {
|
||||||
return errors.Join(saveErr, hookErr)
|
return errors.Join(saveErr, hookErr)
|
||||||
}
|
}
|
||||||
|
@ -417,10 +415,9 @@ func (app *BaseApp) update(ctx context.Context, model Model, withValidations boo
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
if saveErr != nil {
|
if saveErr != nil {
|
||||||
hookErr := app.OnModelAfterUpdateError().Trigger(&ModelErrorEvent{
|
errEvent := &ModelErrorEvent{ModelEvent: *event, Error: saveErr}
|
||||||
ModelEvent: *event,
|
errEvent.App = app
|
||||||
Error: saveErr,
|
hookErr := app.OnModelAfterUpdateError().Trigger(errEvent)
|
||||||
})
|
|
||||||
if hookErr != nil {
|
if hookErr != nil {
|
||||||
return errors.Join(saveErr, hookErr)
|
return errors.Join(saveErr, hookErr)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue