diff --unified -r a/django/db/backends/postgresql/operations.py b/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 from the table name and column name (available since postgres 8) |
| 58 | cursor.execute("SELECT CURRVAL(pg_get_serial_sequence('%s','%s'))" % (table_name, pk_name)) |
58 | 59 | return cursor.fetchone()[0] |
59 | 60 | |
60 | 61 | def no_limit_value(self): |
… |
… |
|
90 | 91 | for sequence_info in sequences: |
91 | 92 | table_name = sequence_info['table'] |
92 | 93 | 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_name |
97 | | sql.append("%s setval('%s', 1, false);" % \ |
| 94 | if not (column_name and len(column_name) > 0): |
| 95 | # This will be the case if it's an m2m using an intermediate table (see BaseDatabaseIntrospection.sequence_list) |
| 96 | # - that function claims we don't need to reset the sequence if that's the case, although the code prior to the #8901 |
| 97 | # fix did reset it so I'm retaining resetting it - the pk will be id in those cases. (Note that sequence_reset_sql below also |
| 98 | # seems to include resetting sequences of m2m with in intermediate table) |
| 99 | column_name = 'id' |
| 100 | # Use pg_get_serial_sequence to get the underlying sequence name from the table name and column name (available since postgres 8) |
| 101 | sql.append("%s setval(pg_get_serial_sequence('%s','%s'), 1, false);" % \ |
98 | 102 | (style.SQL_KEYWORD('SELECT'), |
99 | | style.SQL_FIELD(self.quote_name(sequence_name))) |
| 103 | style.SQL_TABLE(table_name), |
| 104 | style.SQL_FIELD(column_name)) |
100 | 105 | ) |
101 | 106 | return sql |
102 | 107 | else: |
… |
… |
|
109 | 114 | for model in model_list: |
110 | 115 | # Use `coalesce` to set the sequence for each model to the max pk value if there are records, |
111 | 116 | # or 1 if there are none. Set the `is_called` property (the third argument to `setval`) to true |
112 | | # if there are records (as the max pk value is already in use), otherwise set it to false. |
| 117 | # if there are records (as the max pk value is already in use), otherwise set it to false. |
| 118 | # Use pg_get_serial_sequence to get the underlying sequence name from the table name and column name (available since postgres 8) |
| 119 | |
113 | 120 | for f in model._meta.local_fields: |
114 | 121 | if isinstance(f, models.AutoField): |
115 | | output.append("%s setval('%s', coalesce(max(%s), 1), max(%s) %s null) %s %s;" % \ |
| 122 | output.append("%s setval(pg_get_serial_sequence('%s','%s'), coalesce(max(%s), 1), max(%s) %s null) %s %s;" % \ |
116 | 123 | (style.SQL_KEYWORD('SELECT'), |
117 | | style.SQL_FIELD(qn('%s_%s_seq' % (model._meta.db_table, f.column))), |
| 124 | style.SQL_TABLE(model._meta.db_table), |
| 125 | style.SQL_FIELD(f.column), |
118 | 126 | style.SQL_FIELD(qn(f.column)), |
119 | 127 | style.SQL_FIELD(qn(f.column)), |
120 | 128 | style.SQL_KEYWORD('IS NOT'), |
… |
… |
|
123 | 131 | break # Only one AutoField is allowed per model, so don't bother continuing. |
124 | 132 | for f in model._meta.many_to_many: |
125 | 133 | if not f.rel.through: |
126 | | output.append("%s setval('%s', coalesce(max(%s), 1), max(%s) %s null) %s %s;" % \ |
| 134 | output.append("%s setval(pg_get_serial_sequence('%s','%s'), coalesce(max(%s), 1), max(%s) %s null) %s %s;" % \ |
127 | 135 | (style.SQL_KEYWORD('SELECT'), |
128 | | style.SQL_FIELD(qn('%s_id_seq' % f.m2m_db_table())), |
| 136 | style.SQL_TABLE(model._meta.db_table), |
| 137 | style.SQL_FIELD('id'), |
129 | 138 | style.SQL_FIELD(qn('id')), |
130 | 139 | style.SQL_FIELD(qn('id')), |
131 | 140 | style.SQL_KEYWORD('IS NOT'), |
diff --unified -r a/tests/regressiontests/backends/models.py b/tests/regressiontests/backends/models.py
a
|
b
|
|
19 | 19 | year = models.PositiveIntegerField() |
20 | 20 | day = models.CharField(max_length=9, blank=True) |
21 | 21 | last_updated = models.DateTimeField() |
| 22 | |
| 23 | class OtherRelatedThingTestBug8901(models.Model): |
| 24 | pass |
| 25 | |
| 26 | class VeryLongModelNameToTestBug8901ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ(models.Model): |
| 27 | class Meta: |
| 28 | verbose_name = 'TestBug8901' # Short verbose name or we hit issue in #8548 which we're not testing! |
| 29 | primary_key_is_quite_long_zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz = models.AutoField(primary_key=True) |
| 30 | m2m_also_quite_long_zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz = models.ManyToManyField(OtherRelatedThingTestBug8901,blank=True) |
22 | 31 | |
23 | 32 | qn = connection.ops.quote_name |
24 | 33 | |
diff --unified -r a/tests/regressiontests/backends/tests.py b/tests/regressiontests/backends/tests.py
a
|
b
|
|
7 | 7 | from django.db.backends.signals import connection_created |
8 | 8 | from django.conf import settings |
9 | 9 | from django.test import TestCase |
| 10 | from django.core import management |
10 | 11 | |
11 | 12 | class Callproc(unittest.TestCase): |
12 | 13 | |
… |
… |
|
88 | 89 | self.assertRaises(Exception, cursor.executemany, query, [(1,2,3),]) |
89 | 90 | self.assertRaises(Exception, cursor.executemany, query, [(1,),]) |
90 | 91 | |
| 92 | class PostgresqlSequenceNameLimitsTest(TestCase): |
| 93 | """Long primary keys and model names can result in a sequence name that's longer than |
| 94 | postgresql's limit of 63 characters so it truncates them. The backend needs to use the correct |
| 95 | sequence name in last_insert_id and other places, so check it is. Ref #8901.""" |
| 96 | |
| 97 | def test_sequence_name_length_limits_create(self): |
| 98 | """Test creation of model with long name and long pk name doesn't error. Ref #8901""" |
| 99 | #import time |
| 100 | #time.sleep(1000) |
| 101 | models.VeryLongModelNameToTestBug8901ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ.objects.create() |
| 102 | |
| 103 | def test_sequence_name_length_limits_m2m(self): |
| 104 | """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""" |
| 105 | obj = models.VeryLongModelNameToTestBug8901ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ.objects.create() |
| 106 | rel_obj = models.OtherRelatedThingTestBug8901.objects.create() |
| 107 | obj.m2m_also_quite_long_zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz.add(rel_obj) |
| 108 | |
| 109 | def test_sequence_name_length_limits_flush(self): |
| 110 | """Test management flush command with model with long name and long pk name doesn't error. Ref #8901""" |
| 111 | management.call_command('flush', verbosity=0, interactive=False) |
91 | 112 | |
92 | 113 | def connection_created_test(sender, **kwargs): |
93 | 114 | print 'connection_created signal' |