""" MySQL composite indexing. Also kinda works with SQLite3.

Looks for a _index_together tuple of column-name-tuples in the model class
Creates non-unique composite index based on that

Important : That tuple must be in the model, not the Meta class!
It's not a very Django-ish way of doing things, but I wasn't really willing
to hacking around in Django's Meta options. Meta ... it sounds so ... Ruby.

Usage is like so ...

class MyModel(models.Model):
    field_1 = models.IntegerField()
    field_2 = models.IntegerField()
    
    field_3 = models.IntegerField()
    field_4 = models.IntegerField()
    
    _index_together = (('field_1', 'field_2'), ('field3', 'field4'))

"""
from django.db import connection
from django.conf import settings


def create_index(model):
    meta = getattr(model, '_meta', None)
    if not meta: return 0
    
    if settings.DATABASE_ENGINE == 'mysql':
        func = create_index_mysql
    elif settings.DATABASE_ENGINE == 'sqlite3':
        func = create_index_sqlite3
    else:
        return 0
    
    successes = 0
    
    index_tuples = getattr(model, '_index_together', [])
    for index_tuple in index_tuples:
        columns = [meta.get_field(field).column for field in index_tuple]
        name = '_'.join(columns)[:63]
        table = meta.db_table
        successes += func(name, table, columns)
        
    if successes:
        print '%d indices created' % successes
    return successes


def create_index_mysql(name, table, columns):
    cursor = connection.cursor()    
    sql = "CREATE INDEX %s ON %s (%s)" % (
        name, table, ', '.join(columns))
        
    from MySQLdb import OperationalError
    try:
        cursor.execute(sql)
        return 1
    except OperationalError as x:
        if x.args[0] != 1061: # 1061 means duplicate key name / we can ignore
            raise
        return 0
    finally:
        cursor.close()


def create_index_sqlite3(name, table, columns):
    cursor = connection.cursor()
    sql = "CREATE INDEX IF NOT EXISTS %s ON %s (%s)" % (
        name, table, ', '.join(columns))
    try:
        cursor.execute(sql)
        return 1
    finally:
        cursor.close()


def create_all_indices(sender, *args, **kwds):
    from django.db import models
    model_list = models.get_models(sender)
    for model in model_list:
        create_index(model)


from django.db.models import signals
signals.post_syncdb.connect(create_all_indices)

