Skip to content
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

fix: migrations to make postgresql compatible. #35762

Open
wants to merge 7 commits into
base: master
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
Original file line number Diff line number Diff line change
@@ -1,15 +1,40 @@
# Generated by Django 2.2.20 on 2021-05-07 18:29, manually modified to make "course_id" column case sensitive

from django.conf import settings
from django.db import migrations, models
from django.db import migrations, models, connection
import django.db.models.deletion
import opaque_keys.edx.django.models
import simple_history.models


def generate_split_module_sql(db_engine):
if 'mysql' in db_engine:
return 'ALTER TABLE split_modulestore_django_splitmodulestorecourseindex MODIFY course_id varchar(255) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL UNIQUE;'
elif 'postgresql' in db_engine:
return """
ALTER TABLE split_modulestore_django_splitmodulestorecourseindex
ALTER COLUMN course_id TYPE VARCHAR(255),
ALTER COLUMN course_id SET NOT NULL;

ALTER TABLE split_modulestore_django_splitmodulestorecourseindex
ADD CONSTRAINT course_id_unique UNIQUE (course_id);
"""


def generate_split_history_module_sql(db_engine):
if 'mysql' in db_engine:
return 'ALTER TABLE split_modulestore_django_historicalsplitmodulestorecourseindex MODIFY course_id varchar(255) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL;'
elif 'postgresql' in db_engine:
return """
ALTER TABLE split_modulestore_django_historicalsplitmodulestorecourseindex
ALTER COLUMN course_id TYPE VARCHAR(255),
ALTER COLUMN course_id SET NOT NULL,
ALTER COLUMN course_id SET DATA TYPE VARCHAR(255) COLLATE "C";
"""
class Migration(migrations.Migration):

initial = True
db_engine = connection.settings_dict['ENGINE']

dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
Expand Down Expand Up @@ -65,11 +90,11 @@ class Migration(migrations.Migration):
# Custom code: Convert columns to utf8_bin because we want to allow
# case-sensitive comparisons for CourseKeys, which were case-sensitive in MongoDB
migrations.RunSQL(
'ALTER TABLE split_modulestore_django_splitmodulestorecourseindex MODIFY course_id varchar(255) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL UNIQUE;',
generate_split_module_sql(db_engine),
reverse_sql=migrations.RunSQL.noop,
),
migrations.RunSQL(
'ALTER TABLE split_modulestore_django_historicalsplitmodulestorecourseindex MODIFY course_id varchar(255) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL;',
generate_split_history_module_sql(db_engine),
reverse_sql=migrations.RunSQL.noop,
),
]
Original file line number Diff line number Diff line change
@@ -1,22 +1,24 @@
from django.conf import settings
from django.contrib.auth import get_user_model
from django.contrib.auth.models import User
from django.db import migrations, models
from django.db import migrations, models, transaction

USERNAME = settings.ECOMMERCE_SERVICE_WORKER_USERNAME
EMAIL = USERNAME + '@fake.email'

def forwards(apps, schema_editor):
"""Add the service user."""
User = get_user_model()
user, created = User.objects.get_or_create(username=USERNAME, email=EMAIL)
if created:
user.set_unusable_password()
user.save()
with transaction.atomic():
user, created = User.objects.get_or_create(username=USERNAME, email=EMAIL)
if created:
user.set_unusable_password()
user.save()

def backwards(apps, schema_editor):
"""Remove the service user."""
User.objects.get(username=USERNAME, email=EMAIL).delete()
with transaction.atomic():
User.objects.get(username=USERNAME, email=EMAIL).delete()

class Migration(migrations.Migration):

Expand Down
21 changes: 16 additions & 5 deletions lms/djangoapps/courseware/migrations/0011_csm_id_bigint.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
# Generated by Django 1.11.23 on 2019-08-28 15:50


import lms.djangoapps.courseware.fields

from django.conf import settings
Expand All @@ -10,29 +9,41 @@
class CsmBigInt(AlterField):
'''
Subclass AlterField migration class to split SQL between two different databases
We can't use the normal AlterField migration operation because Django generate and routes migrations at the model
We can't use the normal AlterField migration operation because Django generates and routes migrations at the model
level and the coursewarehistoryextended_studentmodulehistoryextended table is in a different database
'''
def database_forwards(self, app_label, schema_editor, from_state, to_state):
if hasattr(schema_editor.connection, 'is_in_memory_db') and schema_editor.connection.is_in_memory_db():
# sqlite3 doesn't support 'MODIFY', so skipping during tests
return

to_model = to_state.apps.get_model(app_label, self.model_name)

if schema_editor.connection.alias == 'student_module_history':
if settings.FEATURES["ENABLE_CSMH_EXTENDED"]:
schema_editor.execute("ALTER TABLE `coursewarehistoryextended_studentmodulehistoryextended` MODIFY `student_module_id` bigint UNSIGNED NOT NULL;")
if schema_editor.connection.vendor == 'mysql':
schema_editor.execute("ALTER TABLE `coursewarehistoryextended_studentmodulehistoryextended` MODIFY `student_module_id` bigint UNSIGNED NOT NULL;")
elif schema_editor.connection.vendor == 'postgresql':
schema_editor.execute("ALTER TABLE coursewarehistoryextended_studentmodulehistoryextended ALTER COLUMN student_module_id TYPE bigint;")
elif self.allow_migrate_model(schema_editor.connection.alias, to_model):
schema_editor.execute("ALTER TABLE `courseware_studentmodule` MODIFY `id` bigint UNSIGNED AUTO_INCREMENT NOT NULL;")
if schema_editor.connection.vendor == 'postgresql':
# For PostgreSQL
schema_editor.execute("ALTER TABLE courseware_studentmodule ALTER COLUMN id SET DATA TYPE bigint;")
schema_editor.execute("ALTER TABLE courseware_studentmodule ALTER COLUMN id SET NOT NULL;")
else:
# For MySQL
schema_editor.execute("ALTER TABLE `courseware_studentmodule` MODIFY `id` bigint UNSIGNED AUTO_INCREMENT NOT NULL;")

def database_backwards(self, app_label, schema_editor, from_state, to_state):
# Make backwards migration a no-op, app will still work if column is wider than expected
# Make backwards migration a no-op; app will still work if column is wider than expected
pass

class Migration(migrations.Migration):

dependencies = [
('courseware', '0010_auto_20190709_1559'),
]

if settings.FEATURES["ENABLE_CSMH_EXTENDED"]:
dependencies.append(('coursewarehistoryextended', '0002_force_studentmodule_index'))

Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
# Generated by Django 1.11.20 on 2019-06-05 13:59


from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
import simple_history.models
from django.conf import settings
from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('grades', '0014_persistentsubsectiongradeoverridehistory'),
Expand All @@ -28,15 +27,24 @@ class Migration(migrations.Migration):
('history_id', models.AutoField(primary_key=True, serialize=False)),
('history_date', models.DateTimeField()),
('history_change_reason', models.CharField(max_length=100, null=True)),
('history_type', models.CharField(choices=[('+', 'Created'), ('~', 'Changed'), ('-', 'Deleted')], max_length=1)),
('grade', models.ForeignKey(blank=True, db_constraint=False, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', to='grades.PersistentSubsectionGrade')),
('history_user', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to=settings.AUTH_USER_MODEL)),
('history_type',
models.CharField(choices=[('+', 'Created'), ('~', 'Changed'), ('-', 'Deleted')], max_length=1)),
('history_user',
models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+',
to=settings.AUTH_USER_MODEL)),
],
options={
'ordering': ('-history_date', '-history_id'),
'get_latest_by': 'history_date',
'verbose_name': 'historical persistent subsection grade override',
},
bases=(simple_history.models.HistoricalChanges, models.Model),
options = {
'ordering': ('-history_date', '-history_id'),
'get_latest_by': 'history_date',
'verbose_name': 'historical persistent subsection grade override',
},
bases = (simple_history.models.HistoricalChanges, models.Model),
),
migrations.AddField(
model_name='historicalpersistentsubsectiongradeoverride',
name='grade',
field=models.ForeignKey(blank=True, db_constraint=False, null=True,
on_delete=django.db.models.deletion.DO_NOTHING, related_name='+',
to='grades.PersistentSubsectionGrade'),
),
]
Original file line number Diff line number Diff line change
@@ -1,45 +1,42 @@
from django.db import migrations, models, connection

def table_description():
"""Handle Mysql/Pg vs Sqlite"""
# django's mysql/pg introspection.get_table_description tries to select *
# from table and fails during initial migrations from scratch.
# sqlite does not have this failure, so we can use the API.
# For not-sqlite, query information-schema directly with code lifted
# from the internals of django.db.backends.mysql.introspection.py

"""Handle MySQL/Postgres vs SQLite compatibility for table introspection"""
if connection.vendor == 'sqlite':
fields = connection.introspection.get_table_description(connection.cursor(), 'course_overviews_courseoverview')
return [f.name for f in fields]
else:
cursor = connection.cursor()
cursor.execute("""
SELECT column_name
FROM information_schema.columns
WHERE table_name = 'course_overviews_courseoverview' AND table_schema = DATABASE()""")
if connection.vendor == 'mysql':
cursor.execute("""
SELECT column_name
FROM information_schema.columns
WHERE table_name = 'course_overviews_courseoverview' AND table_schema = DATABASE()
""")
elif connection.vendor == 'postgresql':
cursor.execute("""
SELECT column_name
FROM information_schema.columns
WHERE table_name = 'course_overviews_courseoverview' AND table_catalog = current_database()
""")
rows = cursor.fetchall()
return [r[0] for r in rows]


class Migration(migrations.Migration):

dependencies = [
('course_overviews', '0008_remove_courseoverview_facebook_url'),
]

# An original version of 0008 removed the facebook_url field We need to
# handle the case where our noop 0008 ran AND the case where the original
# 0008 ran. We do that by using the standard information_schema to find out
# what columns exist. _meta is unavailable as the column has already been
# removed from the model
operations = []
fields = table_description()

# during a migration from scratch, fields will be empty, but we do not want to add
# an additional facebook_url
# Ensure 'facebook_url' is added if it does not exist in the table
if fields and not any(f == 'facebook_url' for f in fields):
operations += migrations.AddField(
model_name='courseoverview',
name='facebook_url',
field=models.TextField(null=True),
),
operations.append(
migrations.AddField(
model_name='courseoverview',
name='facebook_url',
field=models.TextField(null=True),
)
)
Loading
Loading