# Generated by Django 2.2.11 on 2021-06-14 09:08
import hashlib

from django.db import connection, migrations


# noinspection DuplicatedCode
def split_identifier(identifier):
    """
    Split an SQL identifier into a two element tuple of (namespace, name).

    The identifier could be a table, column, or sequence name might be prefixed
    by a namespace.
    """
    try:
        namespace, name = identifier.split('"."')
    except ValueError:
        namespace, name = "", identifier
    return namespace.strip('"'), name.strip('"')


def names_digest(*args, length):
    """
    Generate a 32-bit digest of a set of arguments that can be used to shorten
    identifying names.
    """
    # Copied from django code for compat, no danger just used for naming indexes.
    h = hashlib.md5()  # nosec
    for arg in args:
        h.update(arg.encode())
    return h.hexdigest()[:length]


def _django_index_name(table, field_name):
    table_name = f"database_table_{table.id}"
    return _copied_django_internal_index_name_calculator(table_name, [field_name])


def _copied_django_internal_index_name_calculator(table_name, column_names, suffix=""):
    """
    COPIED FROM https://github.com/django/django/blob
    /ba9ced3e9a643a05bc521f0a2e6d02e3569de374/django/db/backends/base/schema.py#L989

    Generate a unique name for an index/unique constraint.
    The name is divided into 3 parts: the table name, the column names,
    and a unique digest and suffix.
    """
    _, table_name = split_identifier(table_name)
    hash_suffix_part = "%s%s" % (
        names_digest(table_name, *column_names, length=8),
        suffix,
    )
    max_length = connection.ops.max_name_length() or 200
    # If everything fits into max_length, use that name.
    index_name = "%s_%s_%s" % (table_name, "_".join(column_names), hash_suffix_part)
    if len(index_name) <= max_length:
        return index_name
    # Shorten a long suffix.
    if len(hash_suffix_part) > max_length / 3:
        hash_suffix_part = hash_suffix_part[: max_length // 3]
    other_length = (max_length - len(hash_suffix_part)) // 2 - 1
    index_name = "%s_%s_%s" % (
        table_name[:other_length],
        "_".join(column_names)[:other_length],
        hash_suffix_part,
    )
    # Prepend D if needed to prevent the name from starting with an
    # underscore or a number (not permitted on Oracle).
    if index_name[0] == "_" or index_name[0].isdigit():
        index_name = "D%s" % index_name[:-1]
    return index_name


# noinspection PyPep8Naming
def forward(apps, schema_editor):
    Table = apps.get_model("database", "Table")
    SingleSelectField = apps.get_model("database", "SingleSelectField")

    for table in Table.objects.all().order_by("id"):
        _add_index_for_field(schema_editor, table, "trashed")
        _add_index_for_field(schema_editor, table, "order")

    # Because table creation has previously not being creating Indexes any single
    # select fields installed via a template will be missing their index. We go
    # through and create them if missing.
    for single_select_field in SingleSelectField.objects.all().order_by("id"):
        _add_index_for_field(
            schema_editor, single_select_field.table, f"field_{single_select_field.id}"
        )


def _add_index_for_field(schema_editor, table, field_name):
    table_name = f"database_table_{table.id}"
    # Make the forward migration more idempotent / resilient to partially
    # applied migrations due to the lack of a transaction by using IF NOT
    # EXISTS.
    index_name = _django_index_name(table, field_name)
    schema_editor.execute(
        (
            f"CREATE INDEX CONCURRENTLY IF NOT EXISTS "
            f'"{index_name}" ON "{table_name}"("{field_name}");'
        )
    )


# noinspection PyPep8Naming
def reverse(apps, schema_editor):
    # We can't safely rollback the indexes made above as we can't tell which ones were
    # created separately from this migration by django itself.
    pass


class Migration(migrations.Migration):
    atomic = False

    dependencies = [
        ("database", "0040_formulafield_remove_field_by_id"),
    ]

    operations = [
        migrations.RunPython(forward, reverse),
    ]
