Skip to content

fix: MySQL DATABASE migrate error while using exists **/migrations/*.py files #8003

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 3 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions label_studio/core/__init__.py
Original file line number Diff line number Diff line change
@@ -1,2 +1,5 @@
"""This file and its contents are licensed under the Apache License 2.0. Please see the included NOTICE for copyright information and LICENSE for a copy of the license.
"""
import pymysql

pymysql.install_as_MySQLdb()
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
logger = logging.getLogger(__name__)


IS_SQLITE = connection.vendor == 'sqlite'
IS_SQLITE = settings.DJANGO_DB == settings.DJANGO_DB_SQLITE or settings.DJANGO_DB == settings.DJANGO_DB_MYSQL
migration_name = '0017_auto_20240731_1638'

def create_index_sql(table_name, index_name, column_name):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

logger = logging.getLogger(__name__)

IS_SQLITE = connection.vendor == 'sqlite'
IS_SQLITE = settings.DJANGO_DB == settings.DJANGO_DB_SQLITE or settings.DJANGO_DB == settings.DJANGO_DB_MYSQL


class Migration(migrations.Migration):
Expand Down
4 changes: 2 additions & 2 deletions label_studio/labels_manager/migrations/0001_initial.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ class Migration(migrations.Migration):
('created_at', models.DateTimeField(auto_now_add=True, help_text='Time of label creation', verbose_name='Created at')),
('updated_at', models.DateTimeField(auto_now=True, verbose_name='Updated at')),
('value', models.JSONField(verbose_name='value')),
('title', models.CharField(help_text='Label title', max_length=2048, verbose_name='Title')),
('title', models.CharField(help_text='Label title', max_length=255, verbose_name='Title')),
('description', models.TextField(blank=True, help_text='Label description', null=True, verbose_name='Description')),
('approved', models.BooleanField(default=False, help_text='Status of label')),
('approved_by', models.ForeignKey(help_text='User who approved this label', null=True, on_delete=django.db.models.deletion.CASCADE, related_name='labels_approved', to=settings.AUTH_USER_MODEL)),
Expand All @@ -36,7 +36,7 @@ class Migration(migrations.Migration):
name='LabelLink',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('from_name', models.CharField(help_text='Label title', max_length=2048, verbose_name='Title')),
('from_name', models.CharField(help_text='Label title', max_length=255, verbose_name='Title')),
('label', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='links', to='labels_manager.label')),
('project', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='projects.project')),
],
Expand Down
2 changes: 1 addition & 1 deletion label_studio/labels_manager/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ class Label(models.Model):
settings.AUTH_USER_MODEL, related_name='labels', on_delete=models.CASCADE, help_text='User who made this label'
)
value = models.JSONField('value', null=False, help_text='Label value')
title = models.CharField(_('Title'), max_length=2048, help_text='Label title')
title = models.CharField(_('Title'), max_length=255, help_text='Label title')
description = models.TextField(_('Description'), help_text='Label description', blank=True, null=True)
approved_by = models.ForeignKey(
settings.AUTH_USER_MODEL,
Expand Down
2 changes: 1 addition & 1 deletion label_studio/tasks/migrations/0044_auto_20230907_0155.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
from django.db import migrations, models
from django.conf import settings

IS_SQLITE = settings.DJANGO_DB == settings.DJANGO_DB_SQLITE
IS_SQLITE = settings.DJANGO_DB == settings.DJANGO_DB_SQLITE or settings.DJANGO_DB == settings.DJANGO_DB_MYSQL

if IS_SQLITE:
from django.db.migrations import AddIndex
Expand Down
6 changes: 6 additions & 0 deletions label_studio/tasks/migrations/0052_auto_20241030_1757.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,12 @@
SET created_at = datetime(expire_at, %s);
"""
sql_params = (f'-{settings.TASK_LOCK_TTL} seconds',)
elif settings.DJANGO_DB == settings.DJANGO_DB_MYSQL:
sql_update_created_at = """
UPDATE tasks_tasklock
SET created_at = expire_at - INTERVAL %s SECOND;
"""
sql_params = (settings.TASK_LOCK_TTL,)
else:
sql_update_created_at = """
UPDATE tasks_tasklock
Expand Down
44 changes: 31 additions & 13 deletions label_studio/tasks/migrations/0054_add_brin_index_updated_at.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,15 @@

migration_name = '0054_add_brin_index_updated_at'

def index_exists_mysql(cursor, index_name, table_name):
"""MySQL only, check if index exists"""
cursor.execute(
"SELECT COUNT(*) FROM information_schema.statistics "
"WHERE table_schema = DATABASE() AND table_name = %s AND index_name = %s",
[table_name, index_name],
)
return cursor.fetchone()[0] > 0

def forward_migration(migration_name):
migration = AsyncMigrationStatus.objects.create(
name=migration_name,
Expand All @@ -18,20 +27,27 @@ def forward_migration(migration_name):
logger.debug(f'Start async migration {migration_name}')

# Check database backend and use appropriate SQL
if connection.vendor == 'postgresql':
# Create BRIN index concurrently to avoid blocking writes
sql = '''
CREATE INDEX CONCURRENTLY IF NOT EXISTS "task_updated_at_brin_idx"
ON "task" USING BRIN ("updated_at");
'''
else:
# SQLite fallback - regular B-tree index
sql = '''
CREATE INDEX IF NOT EXISTS "task_updated_at_brin_idx"
ON "task" ("updated_at");
'''

with connection.cursor() as cursor:
if connection.vendor == 'postgresql':
# Create BRIN index concurrently to avoid blocking writes
sql = '''
CREATE INDEX CONCURRENTLY IF NOT EXISTS "task_updated_at_brin_idx"
ON "task" USING BRIN ("updated_at");
'''
elif connection.vendor == 'mysql':
idx_name = 'task_updated_at_brin_idx'
if not index_exists_mysql(cursor, idx_name, 'task'):
sql = f'''
CREATE INDEX `{idx_name}`
ON `task` (`updated_at`) ALGORITHM=INPLACE LOCK=NONE;
'''
else:
# SQLite fallback - regular B-tree index
sql = '''
CREATE INDEX IF NOT EXISTS "task_updated_at_brin_idx"
ON "task" ("updated_at");
'''

cursor.execute(sql)

migration.status = AsyncMigrationStatus.STATUS_FINISHED
Expand All @@ -48,6 +64,8 @@ def reverse_migration(migration_name):
# Drop index (works for both PostgreSQL and SQLite)
if connection.vendor == 'postgresql':
sql = 'DROP INDEX CONCURRENTLY IF EXISTS "task_updated_at_brin_idx";'
elif connection.vendor == 'mysql':
sql = 'DROP INDEX IF EXISTS `task_updated_at_brin_idx` ON `task`;'
else:
sql = 'DROP INDEX IF EXISTS "task_updated_at_brin_idx";'

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
import logging
logger = logging.getLogger(__name__)

IS_SQLITE = settings.DJANGO_DB == settings.DJANGO_DB_SQLITE
IS_SQLITE = settings.DJANGO_DB == settings.DJANGO_DB_SQLITE or settings.DJANGO_DB == settings.DJANGO_DB_MYSQL

migration_name = '0055_task_proj_octlen_idx_async'

Expand Down
1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ dependencies = [
## HumanSignal repo dependencies :start
"label-studio-sdk @ https://github.com/HumanSignal/label-studio-sdk/archive/fb9acd56a1a46d1cc82d115c76ac157382837034.zip",
## HumanSignal repo dependencies :end
"pymysql (>=1.1.1)",
]

[project.urls]
Expand Down
Loading