Ticket #8901: 8901-r13323.diff
File 8901-r13323.diff, 8.4 KB (added by , 14 years ago) |
---|
-
django/db/backends/postgresql/operations.py
diff -r 90c15a5724dd django/db/backends/postgresql/operations.py
a b 54 54 return '%s' 55 55 56 56 def last_insert_id(self, cursor, table_name, pk_name): 57 cursor.execute("SELECT CURRVAL('\"%s_%s_seq\"')" % (table_name, pk_name)) 57 # Use pg_get_serial_sequence to get the underlying sequence name 58 # from the table name and column name (available since PostgreSQL 8) 59 cursor.execute("SELECT CURRVAL(pg_get_serial_sequence('%s','%s'))" % (table_name, pk_name)) 58 60 return cursor.fetchone()[0] 59 61 60 62 def no_limit_value(self): … … 90 92 for sequence_info in sequences: 91 93 table_name = sequence_info['table'] 92 94 column_name = sequence_info['column'] 93 if column_name and len(column_name) > 0:94 sequence_name = '%s_%s_seq' % (table_name, column_name)95 else:96 sequence_name = '%s_id_seq' % table_name97 sql.append("%s setval( '%s', 1, false);" % \95 if not (column_name and len(column_name) > 0): 96 # This will be the case if it's an m2m using an autogenerated 97 # intermediate table (see BaseDatabaseIntrospection.sequence_list) 98 column_name = 'id' 99 sql.append("%s setval(pg_get_serial_sequence('%s','%s'), 1, false);" % \ 98 100 (style.SQL_KEYWORD('SELECT'), 99 style.SQL_FIELD(self.quote_name(sequence_name))) 101 style.SQL_TABLE(table_name), 102 style.SQL_FIELD(column_name)) 100 103 ) 101 104 return sql 102 105 else: … … 110 113 # Use `coalesce` to set the sequence for each model to the max pk value if there are records, 111 114 # or 1 if there are none. Set the `is_called` property (the third argument to `setval`) to true 112 115 # if there are records (as the max pk value is already in use), otherwise set it to false. 116 # Use pg_get_serial_sequence to get the underlying sequence name from the table name 117 # and column name (available since PostgreSQL 8) 118 113 119 for f in model._meta.local_fields: 114 120 if isinstance(f, models.AutoField): 115 output.append("%s setval( '%s', coalesce(max(%s), 1), max(%s) %s null) %s %s;" % \121 output.append("%s setval(pg_get_serial_sequence('%s','%s'), coalesce(max(%s), 1), max(%s) %s null) %s %s;" % \ 116 122 (style.SQL_KEYWORD('SELECT'), 117 style.SQL_FIELD(qn('%s_%s_seq' % (model._meta.db_table, f.column))), 123 style.SQL_TABLE(model._meta.db_table), 124 style.SQL_FIELD(f.column), 118 125 style.SQL_FIELD(qn(f.column)), 119 126 style.SQL_FIELD(qn(f.column)), 120 127 style.SQL_KEYWORD('IS NOT'), … … 123 130 break # Only one AutoField is allowed per model, so don't bother continuing. 124 131 for f in model._meta.many_to_many: 125 132 if not f.rel.through: 126 output.append("%s setval( '%s', coalesce(max(%s), 1), max(%s) %s null) %s %s;" % \133 output.append("%s setval(pg_get_serial_sequence('%s','%s'), coalesce(max(%s), 1), max(%s) %s null) %s %s;" % \ 127 134 (style.SQL_KEYWORD('SELECT'), 128 style.SQL_FIELD(qn('%s_id_seq' % f.m2m_db_table())), 135 style.SQL_TABLE(model._meta.db_table), 136 style.SQL_FIELD('id'), 129 137 style.SQL_FIELD(qn('id')), 130 138 style.SQL_FIELD(qn('id')), 131 139 style.SQL_KEYWORD('IS NOT'), -
tests/regressiontests/backends/models.py
diff -r 90c15a5724dd tests/regressiontests/backends/models.py
a b 1 1 from django.db import models 2 2 from django.db import connection 3 3 4 4 5 class Square(models.Model): 5 6 root = models.IntegerField() 6 7 square = models.PositiveIntegerField() … … 8 9 def __unicode__(self): 9 10 return "%s ** 2 == %s" % (self.root, self.square) 10 11 12 11 13 class Person(models.Model): 12 14 first_name = models.CharField(max_length=20) 13 15 last_name = models.CharField(max_length=20) … … 15 17 def __unicode__(self): 16 18 return u'%s %s' % (self.first_name, self.last_name) 17 19 20 18 21 class SchoolClass(models.Model): 19 22 year = models.PositiveIntegerField() 20 23 day = models.CharField(max_length=9, blank=True) 21 24 last_updated = models.DateTimeField() 22 25 26 27 class VeryLongModelNameZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ(models.Model): 28 class Meta: 29 # We need to use a short actual table name or 30 # we hit issue #8548 which we're not testing! 31 verbose_name = 'model_with_long_table_name' 32 primary_key_is_quite_long_zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz = models.AutoField(primary_key=True) 33 m2m_also_quite_long_zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz = models.ManyToManyField(Person,blank=True) 34 35 23 36 qn = connection.ops.quote_name 24 37 25 38 __test__ = {'API_TESTS': """ -
tests/regressiontests/backends/tests.py
diff -r 90c15a5724dd tests/regressiontests/backends/tests.py
a b 1 1 # -*- coding: utf-8 -*- 2 2 # Unit and doctests for specific database backends. 3 3 import datetime 4 import models5 4 import unittest 5 6 from django.conf import settings 7 from django.core import management 8 from django.core.management.color import no_style 6 9 from django.db import backend, connection, DEFAULT_DB_ALIAS 7 10 from django.db.backends.signals import connection_created 8 from django.conf import settings9 11 from django.test import TestCase 10 12 13 from regressiontests.backends import models 14 11 15 class Callproc(unittest.TestCase): 12 16 13 17 def test_dbms_session(self): … … 88 92 self.assertRaises(Exception, cursor.executemany, query, [(1,2,3),]) 89 93 self.assertRaises(Exception, cursor.executemany, query, [(1,),]) 90 94 95 class LongNameTest(TestCase): 96 """Long primary keys and model names can result in a sequence name 97 that exceeds the database limits, which will result in truncation 98 on certain databases (e.g., Postgres). The backend needs to use 99 the correct sequence name in last_insert_id and other places, so 100 check it is. Refs #8901. 101 """ 102 103 def test_sequence_name_length_limits_create(self): 104 """Test creation of model with long name and long pk name doesn't error. Ref #8901""" 105 models.VeryLongModelNameZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ.objects.create() 106 107 def test_sequence_name_length_limits_m2m(self): 108 """Test an m2m save of a model with a long name and a long m2m field name doesn't error as on Django >=1.2 this now uses object saves. Ref #8901""" 109 obj = models.VeryLongModelNameZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ.objects.create() 110 rel_obj = models.Person.objects.create(first_name='Django', last_name='Reinhardt') 111 obj.m2m_also_quite_long_zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz.add(rel_obj) 112 113 def test_sequence_name_length_limits_flush(self): 114 """Test that sequence resetting as part of a flush with model with long name and long pk name doesn't error. Ref #8901""" 115 # A full flush is expensive to the full test, so we dig into the 116 # internals to generate the likely offending SQL and run it manually 117 118 # Some convenience aliases 119 VLM = models.VeryLongModelNameZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ 120 VLM_m2m = VLM.m2m_also_quite_long_zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz.through 121 tables = [ 122 VLM._meta.db_table, 123 VLM_m2m._meta.db_table, 124 ] 125 sequences = [ 126 { 127 'column': VLM._meta.pk.attname, 128 'table': VLM._meta.db_table 129 }, 130 ] 131 cursor = connection.cursor() 132 for statement in connection.ops.sql_flush(no_style(), tables, sequences): 133 cursor.execute(statement) 134 91 135 92 136 def connection_created_test(sender, **kwargs): 93 137 print 'connection_created signal'