Skip to content
This repository has been archived by the owner on Feb 7, 2019. It is now read-only.

Commit

Permalink
Merge pull request #5 from brki/add-postgresql-utilities
Browse files Browse the repository at this point in the history
Add postgresql utilities
  • Loading branch information
maennel committed Sep 15, 2014
2 parents 11a4efd + af0d7dc commit 0d43afa
Show file tree
Hide file tree
Showing 3 changed files with 120 additions and 0 deletions.
Empty file added versions/util/__init__.py
Empty file.
26 changes: 26 additions & 0 deletions versions/util/helper.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
from __future__ import absolute_import
from django.db import connection, connections
from django import VERSION

if VERSION >= (1, 7):
from django.apps import apps
else:
from django.db.models import get_app, get_models

from ..models import Versionable

def database_connection(dbname=None):
if dbname:
return connections[dbname]
else:
return connection

def get_app_models(app_name, include_auto_created=False):
if VERSION >= (1, 7):
return apps.get_app_config(app_name).get_models(include_auto_created=include_auto_created)
else:
return get_models(get_app(app_name), include_auto_created=include_auto_created)


def versionable_models(app_name, include_auto_created=False):
return [m for m in get_app_models(app_name, include_auto_created) if issubclass(m, Versionable)]
94 changes: 94 additions & 0 deletions versions/util/postgresql.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
from __future__ import absolute_import
from versions.models import VersionedForeignKey
from .helper import database_connection, versionable_models

def index_exists(connection, index_name):
cursor = connection.cursor()
cursor.execute("SELECT COUNT(1) FROM pg_indexes WHERE indexname = %s",[index_name])
return cursor.fetchone()[0] > 0

def remove_uuid_id_like_indexes(app_name, database=None):
"""
Remove all of varchar_pattern_ops indexes that django created for uuid columns.
A search is never done with a filter of the style (uuid__like='1ae3c%'), so
all such indexes can be removed from Versionable models.
This will only try to remove indexes if they exist in the database, so it
should be safe to run in a post_migrate signal handler. Running it several
times should leave the database in the same state as running it once.
:param str app_name: application name whose Versionable models will be acted on.
:param str database: database alias to use. If None, use default connection.
:return: number of indexes removed
:rtype: int
"""

removed_indexes = 0
cursor = database_connection(database).cursor()
for model in versionable_models(app_name, include_auto_created=True):
# VersionedForeignKey fields as well as the id fields have these useless like indexes
field_names = ["'%s'" % f.column for f in model._meta.fields if isinstance(f, VersionedForeignKey)]
field_names.append("'id'")

sql = """
select i.relname as index_name
from pg_class t,
pg_class i,
pg_index ix,
pg_attribute a
where t.oid = ix.indrelid
and i.oid = ix.indexrelid
and a.attrelid = t.oid
and a.attnum = ANY(ix.indkey)
and t.relkind = 'r'
and t.relname = '{0}'
and a.attname in ({1})
and i.relname like '%_like'
""".format(model._meta.db_table, ','.join(field_names))

cursor.execute(sql)
indexes = cursor.fetchall()
if indexes:
index_list = ','.join(['"%s"' % r[0] for r in indexes])
cursor.execute("DROP INDEX %s" % index_list)
removed_indexes += len(indexes)

return removed_indexes

def create_current_version_unique_indexes(app_name, database=None):
"""
Add unique indexes for models which have a VERSION_UNIQUE attribute.
These must be defined as partially unique indexes, which django
does not support.
The unique indexes are defined so that no two *current* versions can have
the same value.
This will only try to create indexes if they do not exist in the database, so it
should be safe to run in a post_migrate signal handler. Running it several
times should leave the database in the same state as running it once.
:param str app_name: application name whose Versionable models will be acted on.
:param str database: database alias to use. If None, use default connection.
:return: number of partial unique indexes created
:rtype: int
"""

indexes_created = 0
connection = database_connection(database)
cursor = connection.cursor()
for model in versionable_models(app_name):
unique_field_groups = getattr(model, 'VERSION_UNIQUE', None)
if not unique_field_groups:
continue

table_name = model._meta.db_table
for group in unique_field_groups:
col_prefixes = []
columns = []
for field in group:
column = model._meta.get_field_by_name(field)[0].column
col_prefixes.append(column[0:3])
columns.append(column)
index_name = '%s_%s_%s_v_uniq' % (app_name, table_name, '_'.join(col_prefixes))
if not index_exists(connection, index_name):
cursor.execute("CREATE UNIQUE INDEX %s ON %s(%s) WHERE version_end_date IS NULL" % (index_name, table_name, ','.join(columns)))
indexes_created += 1

return indexes_created

0 comments on commit 0d43afa

Please sign in to comment.