1
0
mirror of https://github.com/django/django.git synced 2025-07-05 18:29:11 +00:00

schema-evolution:

fixed postgresql constraint lookup after a table rename bug
pulled table renames into the backends
added postgresql unit tests



git-svn-id: http://code.djangoproject.com/svn/django/branches/schema-evolution@5784 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
Derek Anderson 2007-08-01 17:51:59 +00:00
parent 4629092d79
commit 0b4c2c7ab1
6 changed files with 72 additions and 5 deletions

View File

@ -5,6 +5,7 @@ import django
from django.core.exceptions import ImproperlyConfigured from django.core.exceptions import ImproperlyConfigured
from optparse import OptionParser from optparse import OptionParser
from django.utils import termcolors from django.utils import termcolors
from django.conf import settings
import os, re, shutil, sys, textwrap import os, re, shutil, sys, textwrap
try: try:
@ -591,9 +592,9 @@ def get_sql_evolution_check_for_changed_model_name(klass):
if klass._meta.db_table in table_list: if klass._meta.db_table in table_list:
return [], None return [], None
if klass._meta.aka in table_list: if klass._meta.aka in table_list:
return [ 'ALTER TABLE '+ backend.quote_name(klass._meta.aka) +' RENAME TO '+ backend.quote_name(klass._meta.db_table) + ';' ], klass._meta.aka return [ backend.get_change_table_name_sql( klass._meta.db_table, klass._meta.aka) ], klass._meta.aka
elif len(set(klass._meta.aka) & set(table_list))==1: elif len(set(klass._meta.aka) & set(table_list))==1:
return [ 'ALTER TABLE '+ backend.quote_name(klass._meta.aka[0]) +' RENAME TO '+ backend.quote_name(klass._meta.db_table) + ';' ], klass._meta.aka[0] return [ backend.get_change_table_name_sql( klass._meta.db_table, klass._meta.aka[0]) ], klass._meta.aka[0]
else: else:
return [], None return [], None
@ -643,6 +644,7 @@ def get_sql_evolution_check_for_changed_field_flags(klass, new_table_name):
db_table = new_table_name db_table = new_table_name
for f in opts.fields: for f in opts.fields:
existing_fields = introspection.get_columns(cursor,db_table) existing_fields = introspection.get_columns(cursor,db_table)
# print existing_fields
cf = None # current field, ie what it is before any renames cf = None # current field, ie what it is before any renames
if f.column in existing_fields: if f.column in existing_fields:
cf = f.column cf = f.column
@ -655,10 +657,11 @@ def get_sql_evolution_check_for_changed_field_flags(klass, new_table_name):
data_type = f.get_internal_type() data_type = f.get_internal_type()
if data_types.has_key(data_type): if data_types.has_key(data_type):
column_flags = introspection.get_known_column_flags(cursor, db_table, cf) column_flags = introspection.get_known_column_flags(cursor, db_table, cf)
# print db_table, cf, column_flags
if column_flags['allow_null']!=f.null or \ if column_flags['allow_null']!=f.null or \
( not f.primary_key and isinstance(f, CharField) and column_flags['maxlength']!=str(f.maxlength) ) or \ ( not f.primary_key and isinstance(f, CharField) and column_flags['maxlength']!=str(f.maxlength) ) or \
( not f.primary_key and isinstance(f, SlugField) and column_flags['maxlength']!=str(f.maxlength) ) or \ ( not f.primary_key and isinstance(f, SlugField) and column_flags['maxlength']!=str(f.maxlength) ) or \
column_flags['unique']!=f.unique or \ ( column_flags['unique']!=f.unique and ( settings.DATABASE_ENGINE!='postgresql' or not f.primary_key ) ) or \
column_flags['primary_key']!=f.primary_key: column_flags['primary_key']!=f.primary_key:
#column_flags['foreign_key']!=f.foreign_key: #column_flags['foreign_key']!=f.foreign_key:
# print # print

View File

@ -242,6 +242,9 @@ def get_sql_sequence_reset(style, model_list):
# No sequence reset required # No sequence reset required
return [] return []
def get_change_table_name_sql( table_name, old_table_name ):
return 'ALTER TABLE '+ quote_name(old_table_name) +' RENAME TO '+ quote_name(table_name) + ';'
def get_change_column_name_sql( table_name, indexes, old_col_name, new_col_name, col_def ): def get_change_column_name_sql( table_name, indexes, old_col_name, new_col_name, col_def ):
# mysql doesn't support column renames (AFAIK), so we fake it # mysql doesn't support column renames (AFAIK), so we fake it
# TODO: only supports a single primary key so far # TODO: only supports a single primary key so far

View File

@ -282,6 +282,11 @@ def typecast_string(s):
return s return s
return smart_unicode(s) return smart_unicode(s)
def get_change_table_name_sql( table_name, old_table_name ):
output = []
output.append('ALTER TABLE '+ quote_name(old_table_name) +' RENAME TO '+ quote_name(table_name) + ';')
return '\n'.join(output)
def get_change_column_name_sql( table_name, indexes, old_col_name, new_col_name, col_def ): def get_change_column_name_sql( table_name, indexes, old_col_name, new_col_name, col_def ):
# TODO: only supports a single primary key so far # TODO: only supports a single primary key so far
pk_name = None pk_name = None

View File

@ -97,8 +97,9 @@ def get_known_column_flags( cursor, table_name, column_name ):
# print "select pg_constraint.conname, pg_constraint.contype, pg_attribute.attname from pg_constraint, pg_attribute where pg_constraint.conrelid=pg_attribute.attrelid and pg_attribute.attnum=any(pg_constraint.conkey) and pg_constraint.conname~'^%s'" % table_name # print "select pg_constraint.conname, pg_constraint.contype, pg_attribute.attname from pg_constraint, pg_attribute where pg_constraint.conrelid=pg_attribute.attrelid and pg_attribute.attnum=any(pg_constraint.conkey) and pg_constraint.conname~'^%s'" % table_name
unique_conname = None unique_conname = None
shared_unique_connames = set() shared_unique_connames = set()
cursor.execute("select pg_constraint.conname, pg_constraint.contype, pg_attribute.attname from pg_constraint, pg_attribute where pg_constraint.conrelid=pg_attribute.attrelid and pg_attribute.attnum=any(pg_constraint.conkey) and pg_constraint.conname~'^%s'" % table_name ) cursor.execute("select pg_constraint.conname, pg_constraint.contype, pg_attribute.attname from pg_constraint, pg_attribute, pg_class where pg_constraint.conrelid=pg_class.oid and pg_constraint.conrelid=pg_attribute.attrelid and pg_attribute.attnum=any(pg_constraint.conkey) and pg_class.relname='%s'" % table_name )
for row in cursor.fetchall(): for row in cursor.fetchall():
# print row
if row[2] == column_name: if row[2] == column_name:
if row[1]=='p': dict['primary_key'] = True if row[1]=='p': dict['primary_key'] = True
if row[1]=='f': dict['foreign_key'] = True if row[1]=='f': dict['foreign_key'] = True

View File

@ -214,6 +214,9 @@ def _sqlite_regexp(re_pattern, re_string):
except: except:
return False return False
def get_change_table_name_sql( table_name, old_table_name ):
return 'ALTER TABLE '+ quote_name(old_table_name) +' RENAME TO '+ quote_name(table_name) + ';'
def get_change_column_name_sql( table_name, indexes, old_col_name, new_col_name, col_def ): def get_change_column_name_sql( table_name, indexes, old_col_name, new_col_name, col_def ):
# sqlite doesn't support column renames, so we fake it # sqlite doesn't support column renames, so we fake it
# TODO: only supports a single primary key so far # TODO: only supports a single primary key so far

View File

@ -3,6 +3,7 @@ Schema Evolution Tests
""" """
from django.db import models from django.db import models
from django.conf import settings
GENDER_CHOICES = ( GENDER_CHOICES = (
('M', 'Male'), ('M', 'Male'),
@ -27,7 +28,10 @@ __test__ = {'API_TESTS':"""
>>> from django.db import connection, get_introspection_module >>> from django.db import connection, get_introspection_module
>>> app = models.get_apps()[-1] >>> app = models.get_apps()[-1]
>>> cursor = connection.cursor() >>> cursor = connection.cursor()
"""}
if settings.DATABASE_ENGINE == 'mysql':
__test__['API_TESTS'] += """
# the table as it is supposed to be # the table as it is supposed to be
>>> create_table_sql = management.get_sql_all(app) >>> create_table_sql = management.get_sql_all(app)
>>> print create_table_sql >>> print create_table_sql
@ -85,7 +89,55 @@ ALTER TABLE `schema_evolution_person` DROP COLUMN `gender`;
0L 0L
>>> management.get_sql_evolution(app) >>> management.get_sql_evolution(app)
['ALTER TABLE `schema_evolution_person` MODIFY COLUMN `name` varchar(20) NOT NULL;'] ['ALTER TABLE `schema_evolution_person` MODIFY COLUMN `name` varchar(20) NOT NULL;']
"""
if settings.DATABASE_ENGINE == 'postgresql':
__test__['API_TESTS'] += """
# the table as it is supposed to be
>>> create_table_sql = management.get_sql_all(app)
>>> print create_table_sql
['CREATE TABLE "schema_evolution_person" (\\n "id" serial NOT NULL PRIMARY KEY,\\n "name" varchar(20) NOT NULL,\\n "gender" varchar(1) NOT NULL,\\n "gender2" varchar(1) NOT NULL\\n)\\n;']
"""} # make sure we don't evolve an unedited table
>>> management.get_sql_evolution(app)
[]
# delete a column, so it looks like we've recently added a field
>>> cursor.execute( backend.get_drop_column_sql( 'schema_evolution_person', 'gender' ) )
>>> management.get_sql_evolution(app)
['ALTER TABLE "schema_evolution_person" ADD COLUMN "gender" varchar(1);\\nALTER TABLE "schema_evolution_person" ALTER COLUMN "gender" SET NOT NULL;']
# reset the db
>>> cursor.execute('DROP TABLE schema_evolution_person;'); cursor.execute(create_table_sql[0])
# add a column, so it looks like we've recently deleted a field
>>> cursor.execute( backend.get_add_column_sql( 'schema_evolution_person', 'gender_nothere', 'varchar(1)', True, False, False ) )
>>> management.get_sql_evolution(app)
['-- warning: as the following may cause data loss, it/they must be run manually', u'-- ALTER TABLE "schema_evolution_person" DROP COLUMN "gender_nothere";', '-- end warning']
# reset the db
>>> cursor.execute('DROP TABLE schema_evolution_person;'); cursor.execute(create_table_sql[0])
# rename column, so it looks like we've recently renamed a field
>>> cursor.execute( backend.get_change_column_name_sql( 'schema_evolution_person', {}, 'gender2', 'gender_old', 'varchar(1)' ) )
>>> management.get_sql_evolution(app)
['ALTER TABLE "schema_evolution_person" RENAME COLUMN "gender_old" TO "gender2";']
# reset the db
>>> cursor.execute('DROP TABLE schema_evolution_person;'); cursor.execute(create_table_sql[0])
# rename table, so it looks like we've recently renamed a model
>>> cursor.execute( backend.get_change_table_name_sql( 'schema_evolution_personold', 'schema_evolution_person' ) )
>>> management.get_sql_evolution(app)
['ALTER TABLE "schema_evolution_personold" RENAME TO "schema_evolution_person";']
# reset the db
>>> cursor.execute(create_table_sql[0])
# change column flags, so it looks like we've recently changed a column flag
>>> cursor.execute( backend.get_change_column_def_sql( 'schema_evolution_person', 'name', 'varchar(10)', True, False, False ) )
>>> management.get_sql_evolution(app)
['ALTER TABLE "schema_evolution_person" ADD COLUMN "name_tmp" varchar(20);\\nUPDATE "schema_evolution_person" SET "name_tmp" = "name";\\nALTER TABLE "schema_evolution_person" DROP COLUMN "name";\\nALTER TABLE "schema_evolution_person" RENAME COLUMN "name_tmp" TO "name";\\nALTER TABLE "schema_evolution_person" ALTER COLUMN "name" SET NOT NULL;']
"""