Manager: convert pragma foreign key command to sqlc
No functional changes. Ref: #104305
This commit is contained in:
parent
96cc8255e7
commit
476d4059bf
@ -134,7 +134,9 @@ func openDBWithConfig(dsn string, config *gorm.Config) (*DB, error) {
|
|||||||
sqlDB.SetMaxOpenConns(1) // Max num of open connections to the database.
|
sqlDB.SetMaxOpenConns(1) // Max num of open connections to the database.
|
||||||
|
|
||||||
// Always enable foreign key checks, to make SQLite behave like a real database.
|
// Always enable foreign key checks, to make SQLite behave like a real database.
|
||||||
if err := db.pragmaForeignKeys(true); err != nil {
|
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||||
|
defer cancel()
|
||||||
|
if err := db.pragmaForeignKeys(ctx, true); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -237,29 +239,22 @@ func (db *DB) now() sql.NullTime {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (db *DB) pragmaForeignKeys(enabled bool) error {
|
func (db *DB) pragmaForeignKeys(ctx context.Context, enabled bool) error {
|
||||||
var (
|
var noun string
|
||||||
value int
|
|
||||||
noun string
|
|
||||||
)
|
|
||||||
switch enabled {
|
switch enabled {
|
||||||
case false:
|
case false:
|
||||||
value = 0
|
|
||||||
noun = "disabl"
|
noun = "disabl"
|
||||||
case true:
|
case true:
|
||||||
value = 1
|
|
||||||
noun = "enabl"
|
noun = "enabl"
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Trace().Msgf("%sing SQLite foreign key checks", noun)
|
log.Trace().Msgf("%sing SQLite foreign key checks", noun)
|
||||||
|
|
||||||
// SQLite doesn't seem to like SQL parameters for `PRAGMA`, so `PRAGMA foreign_keys = ?` doesn't work.
|
queries := db.queries()
|
||||||
sql := fmt.Sprintf("PRAGMA foreign_keys = %d", value)
|
if err := queries.PragmaForeignKeysSet(ctx, enabled); err != nil {
|
||||||
|
return fmt.Errorf("%sing foreign keys: %w", noun, err)
|
||||||
if tx := db.gormDB.Exec(sql); tx.Error != nil {
|
|
||||||
return fmt.Errorf("%sing foreign keys: %w", noun, tx.Error)
|
|
||||||
}
|
}
|
||||||
fkEnabled, err := db.areForeignKeysEnabled()
|
fkEnabled, err := db.areForeignKeysEnabled(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -270,12 +265,13 @@ func (db *DB) pragmaForeignKeys(enabled bool) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (db *DB) areForeignKeysEnabled() (bool, error) {
|
func (db *DB) areForeignKeysEnabled(ctx context.Context) (bool, error) {
|
||||||
log.Trace().Msg("checking whether SQLite foreign key checks are enabled")
|
log.Trace().Msg("checking whether SQLite foreign key checks are enabled")
|
||||||
|
|
||||||
var fkEnabled int
|
queries := db.queries()
|
||||||
if tx := db.gormDB.Raw("PRAGMA foreign_keys").Scan(&fkEnabled); tx.Error != nil {
|
fkEnabled, err := queries.PragmaForeignKeysGet(ctx)
|
||||||
return false, fmt.Errorf("checking whether the database has foreign key checks are enabled: %w", tx.Error)
|
if err != nil {
|
||||||
|
return false, fmt.Errorf("checking whether the database has foreign key checks are enabled: %w", err)
|
||||||
}
|
}
|
||||||
return fkEnabled != 0, nil
|
return fkEnabled, nil
|
||||||
}
|
}
|
||||||
|
@ -41,7 +41,7 @@ func (db *DB) migrate(ctx context.Context) error {
|
|||||||
// of data, foreign keys are disabled here instead of in the migration SQL
|
// of data, foreign keys are disabled here instead of in the migration SQL
|
||||||
// files, so that it can't be forgotten.
|
// files, so that it can't be forgotten.
|
||||||
|
|
||||||
if err := db.pragmaForeignKeys(false); err != nil {
|
if err := db.pragmaForeignKeys(ctx, false); err != nil {
|
||||||
log.Fatal().AnErr("cause", err).Msgf("could not disable foreign key constraints before performing database migrations, please report a bug at %s", website.BugReportURL)
|
log.Fatal().AnErr("cause", err).Msgf("could not disable foreign key constraints before performing database migrations, please report a bug at %s", website.BugReportURL)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -52,7 +52,7 @@ func (db *DB) migrate(ctx context.Context) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Re-enable foreign key checks.
|
// Re-enable foreign key checks.
|
||||||
if err := db.pragmaForeignKeys(true); err != nil {
|
if err := db.pragmaForeignKeys(ctx, true); err != nil {
|
||||||
log.Fatal().AnErr("cause", err).Msgf("could not re-enable foreign key constraints after performing database migrations, please report a bug at %s", website.BugReportURL)
|
log.Fatal().AnErr("cause", err).Msgf("could not re-enable foreign key constraints after performing database migrations, please report a bug at %s", website.BugReportURL)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -74,7 +74,7 @@ func (db *DB) performIntegrityCheck(ctx context.Context) (ok bool) {
|
|||||||
|
|
||||||
log.Debug().Msg("database: performing integrity check")
|
log.Debug().Msg("database: performing integrity check")
|
||||||
|
|
||||||
db.ensureForeignKeysEnabled()
|
db.ensureForeignKeysEnabled(checkCtx)
|
||||||
|
|
||||||
if !db.pragmaIntegrityCheck(checkCtx) {
|
if !db.pragmaIntegrityCheck(checkCtx) {
|
||||||
return false
|
return false
|
||||||
@ -162,8 +162,8 @@ func (db *DB) pragmaForeignKeyCheck(ctx context.Context) (ok bool) {
|
|||||||
// connection to the low-level SQLite driver. Unfortunately the GORM-embedded
|
// connection to the low-level SQLite driver. Unfortunately the GORM-embedded
|
||||||
// SQLite doesn't have an 'on-connect' callback function to always enable
|
// SQLite doesn't have an 'on-connect' callback function to always enable
|
||||||
// foreign keys.
|
// foreign keys.
|
||||||
func (db *DB) ensureForeignKeysEnabled() {
|
func (db *DB) ensureForeignKeysEnabled(ctx context.Context) {
|
||||||
fkEnabled, err := db.areForeignKeysEnabled()
|
fkEnabled, err := db.areForeignKeysEnabled(ctx)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error().AnErr("cause", err).Msg("database: could not check whether foreign keys are enabled")
|
log.Error().AnErr("cause", err).Msg("database: could not check whether foreign keys are enabled")
|
||||||
@ -175,7 +175,7 @@ func (db *DB) ensureForeignKeysEnabled() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
log.Warn().Msg("database: foreign keys are disabled, re-enabling them")
|
log.Warn().Msg("database: foreign keys are disabled, re-enabling them")
|
||||||
if err := db.pragmaForeignKeys(true); err != nil {
|
if err := db.pragmaForeignKeys(ctx, true); err != nil {
|
||||||
log.Error().AnErr("cause", err).Msg("database: error re-enabling foreign keys")
|
log.Error().AnErr("cause", err).Msg("database: error re-enabling foreign keys")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -389,11 +389,11 @@ func (db *DB) FetchJobShamanCheckoutID(ctx context.Context, jobUUID string) (str
|
|||||||
// The deletion cascades to its tasks and other job-related tables.
|
// The deletion cascades to its tasks and other job-related tables.
|
||||||
func (db *DB) DeleteJob(ctx context.Context, jobUUID string) error {
|
func (db *DB) DeleteJob(ctx context.Context, jobUUID string) error {
|
||||||
// As a safety measure, refuse to delete jobs unless foreign key constraints are active.
|
// As a safety measure, refuse to delete jobs unless foreign key constraints are active.
|
||||||
fkEnabled, err := db.areForeignKeysEnabled()
|
fkEnabled, err := db.areForeignKeysEnabled(ctx)
|
||||||
if err != nil {
|
switch {
|
||||||
return fmt.Errorf("checking whether foreign keys are enabled: %w", err)
|
case err != nil:
|
||||||
}
|
return err
|
||||||
if !fkEnabled {
|
case !fkEnabled:
|
||||||
return ErrDeletingWithoutFK
|
return ErrDeletingWithoutFK
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -255,7 +255,7 @@ func TestDeleteJobWithoutFK(t *testing.T) {
|
|||||||
authJob.Name = "Job to delete"
|
authJob.Name = "Job to delete"
|
||||||
persistAuthoredJob(t, ctx, db, authJob)
|
persistAuthoredJob(t, ctx, db, authJob)
|
||||||
|
|
||||||
require.NoError(t, db.pragmaForeignKeys(false))
|
require.NoError(t, db.pragmaForeignKeys(ctx, false))
|
||||||
|
|
||||||
err := db.DeleteJob(ctx, authJob.JobID)
|
err := db.DeleteJob(ctx, authJob.JobID)
|
||||||
require.ErrorIs(t, err, ErrDeletingWithoutFK)
|
require.ErrorIs(t, err, ErrDeletingWithoutFK)
|
||||||
|
@ -38,3 +38,28 @@ func (q *Queries) PragmaIntegrityCheck(ctx context.Context) ([]PragmaIntegrityCh
|
|||||||
}
|
}
|
||||||
return items, nil
|
return items, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SQLite doesn't seem to like SQL parameters for `PRAGMA`, so `PRAGMA foreign_keys = ?` doesn't work.
|
||||||
|
const pragmaForeignKeysEnable = `PRAGMA foreign_keys = 1`
|
||||||
|
const pragmaForeignKeysDisable = `PRAGMA foreign_keys = 0`
|
||||||
|
|
||||||
|
func (q *Queries) PragmaForeignKeysSet(ctx context.Context, enable bool) error {
|
||||||
|
var sql string
|
||||||
|
if enable {
|
||||||
|
sql = pragmaForeignKeysEnable
|
||||||
|
} else {
|
||||||
|
sql = pragmaForeignKeysDisable
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := q.db.ExecContext(ctx, sql)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
const pragmaForeignKeys = `PRAGMA foreign_keys`
|
||||||
|
|
||||||
|
func (q *Queries) PragmaForeignKeysGet(ctx context.Context) (bool, error) {
|
||||||
|
row := q.db.QueryRowContext(ctx, pragmaForeignKeys)
|
||||||
|
var fkEnabled bool
|
||||||
|
err := row.Scan(&fkEnabled)
|
||||||
|
return fkEnabled, err
|
||||||
|
}
|
||||||
|
@ -73,11 +73,11 @@ func (db *DB) SaveWorkerTag(ctx context.Context, tag *WorkerTag) error {
|
|||||||
// DeleteWorkerTag deletes the given tag, after unassigning all workers from it.
|
// DeleteWorkerTag deletes the given tag, after unassigning all workers from it.
|
||||||
func (db *DB) DeleteWorkerTag(ctx context.Context, uuid string) error {
|
func (db *DB) DeleteWorkerTag(ctx context.Context, uuid string) error {
|
||||||
// As a safety measure, refuse to delete unless foreign key constraints are active.
|
// As a safety measure, refuse to delete unless foreign key constraints are active.
|
||||||
fkEnabled, err := db.areForeignKeysEnabled()
|
fkEnabled, err := db.areForeignKeysEnabled(ctx)
|
||||||
if err != nil {
|
switch {
|
||||||
return fmt.Errorf("checking whether foreign keys are enabled: %w", err)
|
case err != nil:
|
||||||
}
|
return err
|
||||||
if !fkEnabled {
|
case !fkEnabled:
|
||||||
return ErrDeletingWithoutFK
|
return ErrDeletingWithoutFK
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -85,7 +85,7 @@ func TestDeleteTagsWithoutFK(t *testing.T) {
|
|||||||
require.NoError(t, f.db.CreateWorkerTag(f.ctx, &secondTag))
|
require.NoError(t, f.db.CreateWorkerTag(f.ctx, &secondTag))
|
||||||
|
|
||||||
// Try deleting with foreign key constraints disabled.
|
// Try deleting with foreign key constraints disabled.
|
||||||
require.NoError(t, f.db.pragmaForeignKeys(false))
|
require.NoError(t, f.db.pragmaForeignKeys(f.ctx, false))
|
||||||
err = f.db.DeleteWorkerTag(f.ctx, f.tag.UUID)
|
err = f.db.DeleteWorkerTag(f.ctx, f.tag.UUID)
|
||||||
require.ErrorIs(t, err, ErrDeletingWithoutFK)
|
require.ErrorIs(t, err, ErrDeletingWithoutFK)
|
||||||
|
|
||||||
|
@ -139,11 +139,11 @@ func (db *DB) FetchWorker(ctx context.Context, uuid string) (*Worker, error) {
|
|||||||
|
|
||||||
func (db *DB) DeleteWorker(ctx context.Context, uuid string) error {
|
func (db *DB) DeleteWorker(ctx context.Context, uuid string) error {
|
||||||
// As a safety measure, refuse to delete unless foreign key constraints are active.
|
// As a safety measure, refuse to delete unless foreign key constraints are active.
|
||||||
fkEnabled, err := db.areForeignKeysEnabled()
|
fkEnabled, err := db.areForeignKeysEnabled(ctx)
|
||||||
if err != nil {
|
switch {
|
||||||
return fmt.Errorf("checking whether foreign keys are enabled: %w", err)
|
case err != nil:
|
||||||
}
|
return err
|
||||||
if !fkEnabled {
|
case !fkEnabled:
|
||||||
return ErrDeletingWithoutFK
|
return ErrDeletingWithoutFK
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -342,7 +342,7 @@ func TestDeleteWorkerNoForeignKeys(t *testing.T) {
|
|||||||
require.NoError(t, db.CreateWorker(ctx, &w1))
|
require.NoError(t, db.CreateWorker(ctx, &w1))
|
||||||
|
|
||||||
// Try deleting with foreign key constraints disabled.
|
// Try deleting with foreign key constraints disabled.
|
||||||
require.NoError(t, db.pragmaForeignKeys(false))
|
require.NoError(t, db.pragmaForeignKeys(ctx, false))
|
||||||
require.ErrorIs(t, ErrDeletingWithoutFK, db.DeleteWorker(ctx, w1.UUID))
|
require.ErrorIs(t, ErrDeletingWithoutFK, db.DeleteWorker(ctx, w1.UUID))
|
||||||
|
|
||||||
// The worker should still exist.
|
// The worker should still exist.
|
||||||
|
Loading…
x
Reference in New Issue
Block a user