package db import ( "errors" "fmt" "time" gormmysql "gorm.io/driver/mysql" "gorm.io/gorm" "gorm.io/gorm/logger" ) var errPermissionProbeRollback = errors.New("permission probe rollback") // ValidateMariaDBConnection opens a one-off connection using dsn, pings, checks // the required lot table exists, and probes write access to qt_client_schema_state. // Returns (lot row count, canWrite, error). func ValidateMariaDBConnection(dsn string) (int64, bool, error) { db, err := gorm.Open(gormmysql.Open(dsn), &gorm.Config{ Logger: logger.Default.LogMode(logger.Silent), }) if err != nil { return 0, false, fmt.Errorf("open MariaDB connection: %w", err) } sqlDB, err := db.DB() if err != nil { return 0, false, fmt.Errorf("get database handle: %w", err) } defer sqlDB.Close() if err := sqlDB.Ping(); err != nil { return 0, false, fmt.Errorf("ping MariaDB: %w", err) } var lotCount int64 if err := db.Table("lot").Count(&lotCount).Error; err != nil { return 0, false, fmt.Errorf("check required table lot: %w", err) } return lotCount, testSyncWritePermission(db), nil } func testSyncWritePermission(db *gorm.DB) bool { sentinel := fmt.Sprintf("quoteforge-permission-check-%d", time.Now().UnixNano()) err := db.Transaction(func(tx *gorm.DB) error { if err := tx.Exec(` INSERT INTO qt_client_schema_state (username, hostname, last_checked_at, updated_at) VALUES (?, ?, NOW(), NOW()) ON DUPLICATE KEY UPDATE last_checked_at = VALUES(last_checked_at), updated_at = VALUES(updated_at) `, sentinel, "setup-check").Error; err != nil { return err } return errPermissionProbeRollback }) return errors.Is(err, errPermissionProbeRollback) }