Fix index migrations to support PostgreSQL and MariaDB

The three recent index migrations used raw MySQL-only DDL (backtick
identifiers, ADD INDEX inside ALTER TABLE, ALGORITHM=INPLACE/LOCK=NONE)
guarded only against sqlite, so PostgreSQL instances failed with
SQLSTATE[42601] on migrate (#7177).

Each migration now branches on the driver:
- mysql/mariadb keep the online-DDL fast path (non-blocking on large
  instances)
- other drivers use the portable Schema::table builder

Table names, index names, and columns are unchanged so already-migrated
MySQL instances are unaffected.
pull/7178/head
Your Name 2 weeks ago
parent dc56ac6d33
commit cd4d9e5f36

@ -1,39 +1,59 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
protected string $table = 'notifications';
protected string $index = 'notifications_profile_deleted_id_index';
protected array $columns = ['profile_id', 'deleted_at', 'id'];
public function up(): void
{
if (DB::getDriverName() === 'sqlite') {
// MySQL/MariaDB: use online DDL (INPLACE/LOCK=NONE) so index creation
// does not block writes on large instances. Other drivers (pgsql,
// sqlite) use the portable schema builder, which emits correct
// dialect-specific SQL.
if (in_array(DB::getDriverName(), ['mysql', 'mariadb'], true)) {
$columns = collect($this->columns)
->map(fn ($column) => "`{$column}`")
->implode(', ');
DB::statement("
ALTER TABLE `{$this->table}`
ADD INDEX `{$this->index}` ({$columns}),
ALGORITHM=INPLACE,
LOCK=NONE
");
return;
}
DB::statement('
ALTER TABLE `notifications`
ADD INDEX `notifications_profile_deleted_id_index`
(`profile_id`, `deleted_at`, `id`),
ALGORITHM=INPLACE,
LOCK=NONE
');
Schema::table($this->table, function (Blueprint $table) {
$table->index($this->columns, $this->index);
});
}
public function down(): void
{
if (DB::getDriverName() === 'sqlite') {
if (in_array(DB::getDriverName(), ['mysql', 'mariadb'], true)) {
DB::statement("
ALTER TABLE `{$this->table}`
DROP INDEX `{$this->index}`,
ALGORITHM=INPLACE,
LOCK=NONE
");
return;
}
DB::statement('
ALTER TABLE `notifications`
DROP INDEX `notifications_profile_deleted_id_index`,
ALGORITHM=INPLACE,
LOCK=NONE
');
Schema::table($this->table, function (Blueprint $table) {
$table->dropIndex($this->index);
});
}
};

@ -1,36 +1,59 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
protected string $table = 'media';
protected string $index = 'media_unoptimized_recent_index';
protected array $columns = ['processed_at', 'remote_url', 'deleted_at', 'created_at', 'id'];
public function up(): void
{
if (DB::getDriverName() === 'sqlite') {
// MySQL/MariaDB: use online DDL (INPLACE/LOCK=NONE) so index creation
// does not block writes on large instances. Other drivers (pgsql,
// sqlite) use the portable schema builder, which emits correct
// dialect-specific SQL.
if (in_array(DB::getDriverName(), ['mysql', 'mariadb'], true)) {
$columns = collect($this->columns)
->map(fn ($column) => "`{$column}`")
->implode(', ');
DB::statement("
ALTER TABLE `{$this->table}`
ADD INDEX `{$this->index}` ({$columns}),
ALGORITHM=INPLACE,
LOCK=NONE
");
return;
}
DB::statement('
ALTER TABLE `media`
ADD INDEX `media_unoptimized_recent_index`
(`processed_at`, `remote_url`, `deleted_at`, `created_at`, `id`),
ALGORITHM=INPLACE,
LOCK=NONE
');
Schema::table($this->table, function (Blueprint $table) {
$table->index($this->columns, $this->index);
});
}
public function down(): void
{
if (DB::getDriverName() === 'sqlite') {
if (in_array(DB::getDriverName(), ['mysql', 'mariadb'], true)) {
DB::statement("
ALTER TABLE `{$this->table}`
DROP INDEX `{$this->index}`,
ALGORITHM=INPLACE,
LOCK=NONE
");
return;
}
DB::statement('
ALTER TABLE `media`
DROP INDEX `media_unoptimized_recent_index`,
ALGORITHM=INPLACE,
LOCK=NONE
');
Schema::table($this->table, function (Blueprint $table) {
$table->dropIndex($this->index);
});
}
};

@ -1,44 +1,65 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
protected string $table = 'media';
protected string $index = 'media_user_id_size_index';
protected array $columns = ['user_id', 'size'];
/**
* Add a covering index for per-user storage aggregation.
*
* `SUM(size) WHERE user_id = ?` (UserStorageService::calculateStorageUsed)
* previously required a full table scan because media.user_id was not
* indexed. The composite (user_id, size) lets the aggregate be served
* entirely from the index. Uses INPLACE/LOCK=NONE so it does not block
* writes on large instances.
* entirely from the index. On MySQL/MariaDB this uses INPLACE/LOCK=NONE so
* it does not block writes on large instances; other drivers use the
* portable schema builder.
*/
public function up(): void
{
if (DB::getDriverName() === 'sqlite') {
if (in_array(DB::getDriverName(), ['mysql', 'mariadb'], true)) {
$columns = collect($this->columns)
->map(fn ($column) => "`{$column}`")
->implode(', ');
DB::statement("
ALTER TABLE `{$this->table}`
ADD INDEX `{$this->index}` ({$columns}),
ALGORITHM=INPLACE,
LOCK=NONE
");
return;
}
DB::statement('
ALTER TABLE `media`
ADD INDEX `media_user_id_size_index` (`user_id`, `size`),
ALGORITHM=INPLACE,
LOCK=NONE
');
Schema::table($this->table, function (Blueprint $table) {
$table->index($this->columns, $this->index);
});
}
public function down(): void
{
if (DB::getDriverName() === 'sqlite') {
if (in_array(DB::getDriverName(), ['mysql', 'mariadb'], true)) {
DB::statement("
ALTER TABLE `{$this->table}`
DROP INDEX `{$this->index}`,
ALGORITHM=INPLACE,
LOCK=NONE
");
return;
}
DB::statement('
ALTER TABLE `media`
DROP INDEX `media_user_id_size_index`,
ALGORITHM=INPLACE,
LOCK=NONE
');
Schema::table($this->table, function (Blueprint $table) {
$table->dropIndex($this->index);
});
}
};

Loading…
Cancel
Save