mirror of https://github.com/pixelfed/pixelfed
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
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…
Reference in New Issue