web/lib/django/db/backends/postgresql/operations.py
changeset 0 0d40e90630ef
child 29 cc9b7e14412b
equal deleted inserted replaced
-1:000000000000 0:0d40e90630ef
       
     1 import re
       
     2 
       
     3 from django.db.backends import BaseDatabaseOperations
       
     4 
       
     5 # This DatabaseOperations class lives in here instead of base.py because it's
       
     6 # used by both the 'postgresql' and 'postgresql_psycopg2' backends.
       
     7 
       
     8 class DatabaseOperations(BaseDatabaseOperations):
       
     9     def __init__(self):
       
    10         self._postgres_version = None
       
    11 
       
    12     def _get_postgres_version(self):
       
    13         if self._postgres_version is None:
       
    14             from django.db import connection
       
    15             from django.db.backends.postgresql.version import get_version
       
    16             cursor = connection.cursor()
       
    17             self._postgres_version = get_version(cursor)
       
    18         return self._postgres_version
       
    19     postgres_version = property(_get_postgres_version)
       
    20 
       
    21     def date_extract_sql(self, lookup_type, field_name):
       
    22         # http://www.postgresql.org/docs/8.0/static/functions-datetime.html#FUNCTIONS-DATETIME-EXTRACT
       
    23         if lookup_type == 'week_day':
       
    24             # For consistency across backends, we return Sunday=1, Saturday=7.
       
    25             return "EXTRACT('dow' FROM %s) + 1" % field_name
       
    26         else:
       
    27             return "EXTRACT('%s' FROM %s)" % (lookup_type, field_name)
       
    28 
       
    29     def date_trunc_sql(self, lookup_type, field_name):
       
    30         # http://www.postgresql.org/docs/8.0/static/functions-datetime.html#FUNCTIONS-DATETIME-TRUNC
       
    31         return "DATE_TRUNC('%s', %s)" % (lookup_type, field_name)
       
    32 
       
    33     def deferrable_sql(self):
       
    34         return " DEFERRABLE INITIALLY DEFERRED"
       
    35 
       
    36     def lookup_cast(self, lookup_type):
       
    37         lookup = '%s'
       
    38 
       
    39         # Cast text lookups to text to allow things like filter(x__contains=4)
       
    40         if lookup_type in ('iexact', 'contains', 'icontains', 'startswith',
       
    41                            'istartswith', 'endswith', 'iendswith'):
       
    42             lookup = "%s::text"
       
    43 
       
    44         # Use UPPER(x) for case-insensitive lookups; it's faster.
       
    45         if lookup_type in ('iexact', 'icontains', 'istartswith', 'iendswith'):
       
    46             lookup = 'UPPER(%s)' % lookup
       
    47 
       
    48         return lookup
       
    49 
       
    50     def field_cast_sql(self, db_type):
       
    51         if db_type == 'inet':
       
    52             return 'HOST(%s)'
       
    53         return '%s'
       
    54 
       
    55     def last_insert_id(self, cursor, table_name, pk_name):
       
    56         cursor.execute("SELECT CURRVAL('\"%s_%s_seq\"')" % (table_name, pk_name))
       
    57         return cursor.fetchone()[0]
       
    58 
       
    59     def no_limit_value(self):
       
    60         return None
       
    61 
       
    62     def quote_name(self, name):
       
    63         if name.startswith('"') and name.endswith('"'):
       
    64             return name # Quoting once is enough.
       
    65         return '"%s"' % name
       
    66 
       
    67     def sql_flush(self, style, tables, sequences):
       
    68         if tables:
       
    69             if self.postgres_version[0:2] >= (8,1):
       
    70                 # Postgres 8.1+ can do 'TRUNCATE x, y, z...;'. In fact, it *has to*
       
    71                 # in order to be able to truncate tables referenced by a foreign
       
    72                 # key in any other table. The result is a single SQL TRUNCATE
       
    73                 # statement.
       
    74                 sql = ['%s %s;' % \
       
    75                     (style.SQL_KEYWORD('TRUNCATE'),
       
    76                      style.SQL_FIELD(', '.join([self.quote_name(table) for table in tables]))
       
    77                 )]
       
    78             else:
       
    79                 # Older versions of Postgres can't do TRUNCATE in a single call, so
       
    80                 # they must use a simple delete.
       
    81                 sql = ['%s %s %s;' % \
       
    82                         (style.SQL_KEYWORD('DELETE'),
       
    83                          style.SQL_KEYWORD('FROM'),
       
    84                          style.SQL_FIELD(self.quote_name(table))
       
    85                          ) for table in tables]
       
    86 
       
    87             # 'ALTER SEQUENCE sequence_name RESTART WITH 1;'... style SQL statements
       
    88             # to reset sequence indices
       
    89             for sequence_info in sequences:
       
    90                 table_name = sequence_info['table']
       
    91                 column_name = sequence_info['column']
       
    92                 if column_name and len(column_name) > 0:
       
    93                     sequence_name = '%s_%s_seq' % (table_name, column_name)
       
    94                 else:
       
    95                     sequence_name = '%s_id_seq' % table_name
       
    96                 sql.append("%s setval('%s', 1, false);" % \
       
    97                     (style.SQL_KEYWORD('SELECT'),
       
    98                     style.SQL_FIELD(self.quote_name(sequence_name)))
       
    99                 )
       
   100             return sql
       
   101         else:
       
   102             return []
       
   103 
       
   104     def sequence_reset_sql(self, style, model_list):
       
   105         from django.db import models
       
   106         output = []
       
   107         qn = self.quote_name
       
   108         for model in model_list:
       
   109             # Use `coalesce` to set the sequence for each model to the max pk value if there are records,
       
   110             # or 1 if there are none. Set the `is_called` property (the third argument to `setval`) to true
       
   111             # if there are records (as the max pk value is already in use), otherwise set it to false.
       
   112             for f in model._meta.local_fields:
       
   113                 if isinstance(f, models.AutoField):
       
   114                     output.append("%s setval('%s', coalesce(max(%s), 1), max(%s) %s null) %s %s;" % \
       
   115                         (style.SQL_KEYWORD('SELECT'),
       
   116                         style.SQL_FIELD(qn('%s_%s_seq' % (model._meta.db_table, f.column))),
       
   117                         style.SQL_FIELD(qn(f.column)),
       
   118                         style.SQL_FIELD(qn(f.column)),
       
   119                         style.SQL_KEYWORD('IS NOT'),
       
   120                         style.SQL_KEYWORD('FROM'),
       
   121                         style.SQL_TABLE(qn(model._meta.db_table))))
       
   122                     break # Only one AutoField is allowed per model, so don't bother continuing.
       
   123             for f in model._meta.many_to_many:
       
   124                 if not f.rel.through:
       
   125                     output.append("%s setval('%s', coalesce(max(%s), 1), max(%s) %s null) %s %s;" % \
       
   126                         (style.SQL_KEYWORD('SELECT'),
       
   127                         style.SQL_FIELD(qn('%s_id_seq' % f.m2m_db_table())),
       
   128                         style.SQL_FIELD(qn('id')),
       
   129                         style.SQL_FIELD(qn('id')),
       
   130                         style.SQL_KEYWORD('IS NOT'),
       
   131                         style.SQL_KEYWORD('FROM'),
       
   132                         style.SQL_TABLE(qn(f.m2m_db_table()))))
       
   133         return output
       
   134 
       
   135     def savepoint_create_sql(self, sid):
       
   136         return "SAVEPOINT %s" % sid
       
   137 
       
   138     def savepoint_commit_sql(self, sid):
       
   139         return "RELEASE SAVEPOINT %s" % sid
       
   140 
       
   141     def savepoint_rollback_sql(self, sid):
       
   142         return "ROLLBACK TO SAVEPOINT %s" % sid
       
   143 
       
   144     def prep_for_iexact_query(self, x):
       
   145         return x
       
   146 
       
   147     def check_aggregate_support(self, aggregate):
       
   148         """Check that the backend fully supports the provided aggregate.
       
   149 
       
   150         The population and sample statistics (STDDEV_POP, STDDEV_SAMP,
       
   151         VAR_POP, VAR_SAMP) were first implemented in Postgres 8.2.
       
   152 
       
   153         The implementation of population statistics (STDDEV_POP and VAR_POP)
       
   154         under Postgres 8.2 - 8.2.4 is known to be faulty. Raise
       
   155         NotImplementedError if this is the database in use.
       
   156         """
       
   157         if aggregate.sql_function in ('STDDEV_POP', 'STDDEV_SAMP', 'VAR_POP', 'VAR_SAMP'):
       
   158             if self.postgres_version[0:2] < (8,2):
       
   159                 raise NotImplementedError('PostgreSQL does not support %s prior to version 8.2. Please upgrade your version of PostgreSQL.' % aggregate.sql_function)
       
   160 
       
   161         if aggregate.sql_function in ('STDDEV_POP', 'VAR_POP'):
       
   162             if self.postgres_version[0:2] == (8,2):
       
   163                 if self.postgres_version[2] is None or self.postgres_version[2] <= 4:
       
   164                     raise NotImplementedError('PostgreSQL 8.2 to 8.2.4 is known to have a faulty implementation of %s. Please upgrade your version of PostgreSQL.' % aggregate.sql_function)