Fix inconsistent FK ON DELETE behavior on bulk_operations/ticket_templates (#40)

bulk_operations.performed_by and ticket_templates.created_by had no
ON DELETE clause (defaulting to RESTRICT), unlike every other
user-reference FK in the schema (tickets.*, ticket_attachments,
ticket_dependencies, recurring_tickets, api_keys), which all use
SET NULL. Deleting a user who ever ran a bulk operation or created a
template hard-failed at the DB level instead of nulling the
reference, breaking the pattern used everywhere else.

performed_by was NOT NULL, so it had to become nullable to support
SET NULL, matching how every other SET NULL column is defined.

- Fixed 000_baseline.sql for fresh installs.
- Added 003_fk_on_delete_set_null.sql for existing deployments.

Verified against a local MariaDB instance: reproduced the old RESTRICT
schema, ran the migration (twice, for idempotency), then confirmed
deleting a user with rows in both tables now nulls the references
instead of failing.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MGDKHiU5RJdo3dqQUDow3X
This commit is contained in:
2026-09-08 11:56:33 -04:00
co-authored by Claude Sonnet 5
parent 3221ccfd29
commit 700048337f
2 changed files with 35 additions and 3 deletions
+3 -3
View File
@@ -57,7 +57,7 @@ CREATE TABLE IF NOT EXISTS `bulk_operations` (
`operation_id` int(11) NOT NULL AUTO_INCREMENT, `operation_id` int(11) NOT NULL AUTO_INCREMENT,
`operation_type` varchar(50) NOT NULL, `operation_type` varchar(50) NOT NULL,
`ticket_ids` text NOT NULL, `ticket_ids` text NOT NULL,
`performed_by` int(11) NOT NULL, `performed_by` int(11) DEFAULT NULL,
`parameters` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL CHECK (json_valid(`parameters`)), `parameters` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL CHECK (json_valid(`parameters`)),
-- 32, not 20: 'completed_with_errors' is 21 chars (see 001_widen_bulk_operations_status.sql) -- 32, not 20: 'completed_with_errors' is 21 chars (see 001_widen_bulk_operations_status.sql)
`status` varchar(32) DEFAULT 'pending', `status` varchar(32) DEFAULT 'pending',
@@ -69,7 +69,7 @@ CREATE TABLE IF NOT EXISTS `bulk_operations` (
PRIMARY KEY (`operation_id`), PRIMARY KEY (`operation_id`),
KEY `idx_performed_by` (`performed_by`), KEY `idx_performed_by` (`performed_by`),
KEY `idx_created_at` (`created_at`), KEY `idx_created_at` (`created_at`),
CONSTRAINT `bulk_operations_ibfk_1` FOREIGN KEY (`performed_by`) REFERENCES `users` (`user_id`) CONSTRAINT `bulk_operations_ibfk_1` FOREIGN KEY (`performed_by`) REFERENCES `users` (`user_id`) ON DELETE SET NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
-- ============ custom_field_definitions ============ -- ============ custom_field_definitions ============
@@ -238,7 +238,7 @@ CREATE TABLE IF NOT EXISTS `ticket_templates` (
PRIMARY KEY (`template_id`), PRIMARY KEY (`template_id`),
KEY `created_by` (`created_by`), KEY `created_by` (`created_by`),
KEY `idx_template_name` (`template_name`), KEY `idx_template_name` (`template_name`),
CONSTRAINT `ticket_templates_ibfk_1` FOREIGN KEY (`created_by`) REFERENCES `users` (`user_id`) CONSTRAINT `ticket_templates_ibfk_1` FOREIGN KEY (`created_by`) REFERENCES `users` (`user_id`) ON DELETE SET NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
-- ============ ticket_watchers ============ -- ============ ticket_watchers ============
+32
View File
@@ -0,0 +1,32 @@
-- Fix inconsistent FK ON DELETE behavior on bulk_operations.performed_by and
-- ticket_templates.created_by
--
-- Every other user-reference FK in the schema (tickets.created_by/updated_by/
-- assigned_to, ticket_attachments.uploaded_by, ticket_dependencies.created_by,
-- recurring_tickets.created_by/assigned_to, api_keys.created_by, etc.) uses
-- ON DELETE SET NULL. These two had no ON DELETE clause at all, which
-- defaults to RESTRICT — so deleting a user who ever ran a bulk operation or
-- created a template hard-fails at the DB level instead of nulling the
-- reference, breaking the pattern used everywhere else and potentially
-- blocking legitimate user offboarding/cleanup.
--
-- bulk_operations.performed_by is NOT NULL today; it must become nullable to
-- support SET NULL, matching how every other SET NULL column in the schema
-- is defined.
--
-- Safe to re-run.
ALTER TABLE `bulk_operations`
MODIFY COLUMN `performed_by` int(11) DEFAULT NULL;
ALTER TABLE `bulk_operations`
DROP FOREIGN KEY IF EXISTS `bulk_operations_ibfk_1`;
ALTER TABLE `bulk_operations`
ADD CONSTRAINT `bulk_operations_ibfk_1` FOREIGN KEY (`performed_by`) REFERENCES `users` (`user_id`) ON DELETE SET NULL;
ALTER TABLE `ticket_templates`
DROP FOREIGN KEY IF EXISTS `ticket_templates_ibfk_1`;
ALTER TABLE `ticket_templates`
ADD CONSTRAINT `ticket_templates_ibfk_1` FOREIGN KEY (`created_by`) REFERENCES `users` (`user_id`) ON DELETE SET NULL;