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.
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
@ -237,29 +239,22 @@ func (db *DB) now() sql.NullTime {
|
||||
}
|
||||
}
|
||||
|
||||
func (db *DB) pragmaForeignKeys(enabled bool) error {
|
||||
var (
|
||||
value int
|
||||
noun string
|
||||
)
|
||||
func (db *DB) pragmaForeignKeys(ctx context.Context, enabled bool) error {
|
||||
var noun string
|
||||
switch enabled {
|
||||
case false:
|
||||
value = 0
|
||||
noun = "disabl"
|
||||
case true:
|
||||
value = 1
|
||||
noun = "enabl"
|
||||
}
|
||||
|
||||
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.
|
||||
sql := fmt.Sprintf("PRAGMA foreign_keys = %d", value)
|
||||
|
||||
if tx := db.gormDB.Exec(sql); tx.Error != nil {
|
||||
return fmt.Errorf("%sing foreign keys: %w", noun, tx.Error)
|
||||
queries := db.queries()
|
||||
if err := queries.PragmaForeignKeysSet(ctx, enabled); err != nil {
|
||||
return fmt.Errorf("%sing foreign keys: %w", noun, err)
|
||||
}
|
||||
fkEnabled, err := db.areForeignKeysEnabled()
|
||||
fkEnabled, err := db.areForeignKeysEnabled(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -270,12 +265,13 @@ func (db *DB) pragmaForeignKeys(enabled bool) error {
|
||||
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")
|
||||
|
||||
var fkEnabled int
|
||||
if tx := db.gormDB.Raw("PRAGMA foreign_keys").Scan(&fkEnabled); tx.Error != nil {
|
||||
return false, fmt.Errorf("checking whether the database has foreign key checks are enabled: %w", tx.Error)
|
||||
queries := db.queries()
|
||||
fkEnabled, err := queries.PragmaForeignKeysGet(ctx)
|
||||
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
|
||||
// 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)
|
||||
}
|
||||
|
||||
@ -52,7 +52,7 @@ func (db *DB) migrate(ctx context.Context) error {
|
||||
}
|
||||
|
||||
// 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)
|
||||
}
|
||||
|
||||
|
@ -74,7 +74,7 @@ func (db *DB) performIntegrityCheck(ctx context.Context) (ok bool) {
|
||||
|
||||
log.Debug().Msg("database: performing integrity check")
|
||||
|
||||
db.ensureForeignKeysEnabled()
|
||||
db.ensureForeignKeysEnabled(checkCtx)
|
||||
|
||||
if !db.pragmaIntegrityCheck(checkCtx) {
|
||||
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
|
||||
// SQLite doesn't have an 'on-connect' callback function to always enable
|
||||
// foreign keys.
|
||||
func (db *DB) ensureForeignKeysEnabled() {
|
||||
fkEnabled, err := db.areForeignKeysEnabled()
|
||||
func (db *DB) ensureForeignKeysEnabled(ctx context.Context) {
|
||||
fkEnabled, err := db.areForeignKeysEnabled(ctx)
|
||||
|
||||
if err != nil {
|
||||
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")
|
||||
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")
|
||||
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.
|
||||
func (db *DB) DeleteJob(ctx context.Context, jobUUID string) error {
|
||||
// As a safety measure, refuse to delete jobs unless foreign key constraints are active.
|
||||
fkEnabled, err := db.areForeignKeysEnabled()
|
||||
if err != nil {
|
||||
return fmt.Errorf("checking whether foreign keys are enabled: %w", err)
|
||||
}
|
||||
if !fkEnabled {
|
||||
fkEnabled, err := db.areForeignKeysEnabled(ctx)
|
||||
switch {
|
||||
case err != nil:
|
||||
return err
|
||||
case !fkEnabled:
|
||||
return ErrDeletingWithoutFK
|
||||
}
|
||||
|
||||
|
@ -255,7 +255,7 @@ func TestDeleteJobWithoutFK(t *testing.T) {
|
||||
authJob.Name = "Job to delete"
|
||||
persistAuthoredJob(t, ctx, db, authJob)
|
||||
|
||||
require.NoError(t, db.pragmaForeignKeys(false))
|
||||
require.NoError(t, db.pragmaForeignKeys(ctx, false))
|
||||
|
||||
err := db.DeleteJob(ctx, authJob.JobID)
|
||||
require.ErrorIs(t, err, ErrDeletingWithoutFK)
|
||||
|
@ -38,3 +38,28 @@ func (q *Queries) PragmaIntegrityCheck(ctx context.Context) ([]PragmaIntegrityCh
|
||||
}
|
||||
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.
|
||||
func (db *DB) DeleteWorkerTag(ctx context.Context, uuid string) error {
|
||||
// As a safety measure, refuse to delete unless foreign key constraints are active.
|
||||
fkEnabled, err := db.areForeignKeysEnabled()
|
||||
if err != nil {
|
||||
return fmt.Errorf("checking whether foreign keys are enabled: %w", err)
|
||||
}
|
||||
if !fkEnabled {
|
||||
fkEnabled, err := db.areForeignKeysEnabled(ctx)
|
||||
switch {
|
||||
case err != nil:
|
||||
return err
|
||||
case !fkEnabled:
|
||||
return ErrDeletingWithoutFK
|
||||
}
|
||||
|
||||
|
@ -85,7 +85,7 @@ func TestDeleteTagsWithoutFK(t *testing.T) {
|
||||
require.NoError(t, f.db.CreateWorkerTag(f.ctx, &secondTag))
|
||||
|
||||
// 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)
|
||||
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 {
|
||||
// As a safety measure, refuse to delete unless foreign key constraints are active.
|
||||
fkEnabled, err := db.areForeignKeysEnabled()
|
||||
if err != nil {
|
||||
return fmt.Errorf("checking whether foreign keys are enabled: %w", err)
|
||||
}
|
||||
if !fkEnabled {
|
||||
fkEnabled, err := db.areForeignKeysEnabled(ctx)
|
||||
switch {
|
||||
case err != nil:
|
||||
return err
|
||||
case !fkEnabled:
|
||||
return ErrDeletingWithoutFK
|
||||
}
|
||||
|
||||
|
@ -342,7 +342,7 @@ func TestDeleteWorkerNoForeignKeys(t *testing.T) {
|
||||
require.NoError(t, db.CreateWorker(ctx, &w1))
|
||||
|
||||
// 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))
|
||||
|
||||
// The worker should still exist.
|
||||
|
Loading…
x
Reference in New Issue
Block a user