ticket_watchers (migrations/000_baseline.sql, ~lines 245-251) stores ticket_id as int(11), while every other satellite table (ticket_comments, ticket_attachments, ticket_dependencies, custom_field_values) stores it as varchar(9)/varchar(10) matching tickets.ticket_id. There's also no FK constraint from ticket_watchers.ticket_id to tickets.ticket_id at all — every other satellite table has one.
api/watch_ticket.php (~lines 37-49) and helpers/NotificationHelper.php (~lines 197-200) cast/bind ticket IDs as ints here, which only works today because ticket IDs happen to be numeric-only 9-digit strings that fit in int(11). If the ID format ever gains a non-numeric character (e.g. a prefix), this silently truncates/corrupts IDs, and orphaned watcher rows can never be caught by referential integrity since there's no FK.
Fix: Migrate ticket_watchers.ticket_id to varchar(9) matching tickets.ticket_id, and add the missing FK constraint (with ON DELETE CASCADE, matching the intent of a per-ticket subscription row).
**Severity:** High (data-integrity risk)
`ticket_watchers` (migrations/000_baseline.sql, ~lines 245-251) stores `ticket_id` as `int(11)`, while every other satellite table (`ticket_comments`, `ticket_attachments`, `ticket_dependencies`, `custom_field_values`) stores it as `varchar(9)`/`varchar(10)` matching `tickets.ticket_id`. There's also no FK constraint from `ticket_watchers.ticket_id` to `tickets.ticket_id` at all — every other satellite table has one.
`api/watch_ticket.php` (~lines 37-49) and `helpers/NotificationHelper.php` (~lines 197-200) cast/bind ticket IDs as ints here, which only works today because ticket IDs happen to be numeric-only 9-digit strings that fit in `int(11)`. If the ID format ever gains a non-numeric character (e.g. a prefix), this silently truncates/corrupts IDs, and orphaned watcher rows can never be caught by referential integrity since there's no FK.
**Fix:** Migrate `ticket_watchers.ticket_id` to `varchar(9)` matching `tickets.ticket_id`, and add the missing FK constraint (with `ON DELETE CASCADE`, matching the intent of a per-ticket subscription row).
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Severity: High (data-integrity risk)
ticket_watchers(migrations/000_baseline.sql, ~lines 245-251) storesticket_idasint(11), while every other satellite table (ticket_comments,ticket_attachments,ticket_dependencies,custom_field_values) stores it asvarchar(9)/varchar(10)matchingtickets.ticket_id. There's also no FK constraint fromticket_watchers.ticket_idtotickets.ticket_idat all — every other satellite table has one.api/watch_ticket.php(~lines 37-49) andhelpers/NotificationHelper.php(~lines 197-200) cast/bind ticket IDs as ints here, which only works today because ticket IDs happen to be numeric-only 9-digit strings that fit inint(11). If the ID format ever gains a non-numeric character (e.g. a prefix), this silently truncates/corrupts IDs, and orphaned watcher rows can never be caught by referential integrity since there's no FK.Fix: Migrate
ticket_watchers.ticket_idtovarchar(9)matchingtickets.ticket_id, and add the missing FK constraint (withON DELETE CASCADE, matching the intent of a per-ticket subscription row).Fixed and verified against real MariaDB (schema/migration/access-control cases). Merged to main in commit
3664719.