Ticket #8901: django8901_msh_patch1.diff

File django8901_msh_patch1.diff, 7.6 KB (added by Skaffen, 5 years ago)

Use pg_get_serial_sequence to fetch sequence name for a given serial column (includes test patches)

  • django/db/backends/postgresql/operations.py

    diff --unified -r a/django/db/backends/postgresql/operations.py b/django/db/backends/postgresql/operations.py
    a b  
    5454        return '%s'
    5555
    5656    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))
    5859        return cursor.fetchone()[0]
    5960
    6061    def no_limit_value(self):
     
    9091            for sequence_info in sequences:
    9192                table_name = sequence_info['table']
    9293                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);" % \
    98102                    (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))
    100105                )
    101106            return sql
    102107        else:
     
    109114        for model in model_list:
    110115            # Use `coalesce` to set the sequence for each model to the max pk value if there are records,
    111116            # 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           
    113120            for f in model._meta.local_fields:
    114121                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;" % \
    116123                        (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),
    118126                        style.SQL_FIELD(qn(f.column)),
    119127                        style.SQL_FIELD(qn(f.column)),
    120128                        style.SQL_KEYWORD('IS NOT'),
     
    123131                    break # Only one AutoField is allowed per model, so don't bother continuing.
    124132            for f in model._meta.many_to_many:
    125133                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;" % \
    127135                        (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'),
    129138                        style.SQL_FIELD(qn('id')),
    130139                        style.SQL_FIELD(qn('id')),
    131140                        style.SQL_KEYWORD('IS NOT'),
  • tests/regressiontests/backends/models.py

    diff --unified -r a/tests/regressiontests/backends/models.py b/tests/regressiontests/backends/models.py
    a b  
    1919    year = models.PositiveIntegerField()
    2020    day = models.CharField(max_length=9, blank=True)
    2121    last_updated = models.DateTimeField()
     22   
     23class OtherRelatedThingTestBug8901(models.Model):
     24    pass
     25
     26class 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)
    2231
    2332qn = connection.ops.quote_name
    2433
  • tests/regressiontests/backends/tests.py

    diff --unified -r a/tests/regressiontests/backends/tests.py b/tests/regressiontests/backends/tests.py
    a b  
    77from django.db.backends.signals import connection_created
    88from django.conf import settings
    99from django.test import TestCase
     10from django.core import management
    1011
    1112class Callproc(unittest.TestCase):
    1213
     
    8889        self.assertRaises(Exception, cursor.executemany, query, [(1,2,3),])
    8990        self.assertRaises(Exception, cursor.executemany, query, [(1,),])
    9091
     92class 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)
    91112
    92113def connection_created_test(sender, **kwargs):
    93114    print 'connection_created signal'
Back to Top