From 3e1c46d7ea734c6cf61ca3ec9697b40d53cb729e Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 3 Aug 2026 18:34:20 +0000 Subject: [PATCH] Match component_id column type to components.id when creating tables The component_checks and component_status_changes migrations hardcoded component_id as an unsigned integer, but components.id is a big integer on databases created by early 3.x builds. MySQL and MariaDB refuse a foreign key whose column type differs from the referenced column, so migrating those installations failed with errno 150. Both migrations now read the actual type of components.id and create component_id to match. Fixes cachethq/cachet#4625 Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01Poo3yQZ5oUvb7R7mzKjpXf --- ...4_000000_create_component_checks_table.php | 21 ++++++++++++++++++- ..._create_component_status_changes_table.php | 21 ++++++++++++++++++- .../ComponentStatusChangesTableTest.php | 20 ++++++++++++++++++ 3 files changed, 60 insertions(+), 2 deletions(-) create mode 100644 tests/Unit/Database/ComponentStatusChangesTableTest.php diff --git a/database/migrations/2025_09_14_000000_create_component_checks_table.php b/database/migrations/2025_09_14_000000_create_component_checks_table.php index 4f3d92d4..2462090b 100644 --- a/database/migrations/2025_09_14_000000_create_component_checks_table.php +++ b/database/migrations/2025_09_14_000000_create_component_checks_table.php @@ -13,7 +13,13 @@ public function up(): void { Schema::create('component_checks', function (Blueprint $table) { $table->id(); - $table->unsignedInteger('component_id'); + + if ($this->componentsTableUsesBigIntegerIds()) { + $table->unsignedBigInteger('component_id'); + } else { + $table->unsignedInteger('component_id'); + } + $table->unsignedTinyInteger('status'); $table->boolean('successful')->default(false); $table->unsignedSmallInteger('response_code')->nullable(); @@ -32,4 +38,17 @@ public function down(): void { Schema::dropIfExists('component_checks'); } + + /** + * Whether the components table keys its rows by big integers. + * + * components.id is an unsigned integer on fresh installations and on + * upgrades from 2.x, but a big integer on databases created by early 3.x + * builds. MySQL refuses a foreign key whose column type does not match + * the column it references, so component_id must mirror the parent. + */ + private function componentsTableUsesBigIntegerIds(): bool + { + return in_array(Schema::getColumnType('components', 'id'), ['bigint', 'int8'], true); + } }; diff --git a/database/migrations/2026_07_25_000001_create_component_status_changes_table.php b/database/migrations/2026_07_25_000001_create_component_status_changes_table.php index 1c805758..7d24fdc5 100644 --- a/database/migrations/2026_07_25_000001_create_component_status_changes_table.php +++ b/database/migrations/2026_07_25_000001_create_component_status_changes_table.php @@ -13,7 +13,13 @@ public function up(): void { Schema::create('component_status_changes', function (Blueprint $table) { $table->id(); - $table->unsignedInteger('component_id'); + + if ($this->componentsTableUsesBigIntegerIds()) { + $table->unsignedBigInteger('component_id'); + } else { + $table->unsignedInteger('component_id'); + } + $table->unsignedTinyInteger('old_status')->nullable(); $table->unsignedTinyInteger('new_status'); $table->string('source'); @@ -33,4 +39,17 @@ public function down(): void { Schema::dropIfExists('component_status_changes'); } + + /** + * Whether the components table keys its rows by big integers. + * + * components.id is an unsigned integer on fresh installations and on + * upgrades from 2.x, but a big integer on databases created by early 3.x + * builds. MySQL refuses a foreign key whose column type does not match + * the column it references, so component_id must mirror the parent. + */ + private function componentsTableUsesBigIntegerIds(): bool + { + return in_array(Schema::getColumnType('components', 'id'), ['bigint', 'int8'], true); + } }; diff --git a/tests/Unit/Database/ComponentStatusChangesTableTest.php b/tests/Unit/Database/ComponentStatusChangesTableTest.php new file mode 100644 index 00000000..e551c6f8 --- /dev/null +++ b/tests/Unit/Database/ComponentStatusChangesTableTest.php @@ -0,0 +1,20 @@ +firstWhere('name', 'id'); + $componentId = collect(Schema::getColumns('component_status_changes'))->firstWhere('name', 'component_id'); + + expect($componentId['type'])->toBe($componentsId['type']); +}); + +it('constrains component_id to components.id with cascading deletes', function () { + $foreignKey = collect(Schema::getForeignKeys('component_status_changes')) + ->first(fn (array $key) => $key['columns'] === ['component_id']); + + expect($foreignKey)->not->toBeNull() + ->and($foreignKey['foreign_table'])->toBe('components') + ->and($foreignKey['foreign_columns'])->toBe(['id']) + ->and(strtolower($foreignKey['on_delete']))->toBe('cascade'); +});