web/lib/django/db/backends/postgresql/base.py
changeset 38 77b6da96e6f1
equal deleted inserted replaced
37:8d941af65caf 38:77b6da96e6f1
       
     1 """
       
     2 PostgreSQL database backend for Django.
       
     3 
       
     4 Requires psycopg 1: http://initd.org/projects/psycopg1
       
     5 """
       
     6 
       
     7 import sys
       
     8 
       
     9 from django.db import utils
       
    10 from django.db.backends import *
       
    11 from django.db.backends.signals import connection_created
       
    12 from django.db.backends.postgresql.client import DatabaseClient
       
    13 from django.db.backends.postgresql.creation import DatabaseCreation
       
    14 from django.db.backends.postgresql.introspection import DatabaseIntrospection
       
    15 from django.db.backends.postgresql.operations import DatabaseOperations
       
    16 from django.db.backends.postgresql.version import get_version
       
    17 from django.utils.encoding import smart_str, smart_unicode
       
    18 
       
    19 try:
       
    20     import psycopg as Database
       
    21 except ImportError, e:
       
    22     from django.core.exceptions import ImproperlyConfigured
       
    23     raise ImproperlyConfigured("Error loading psycopg module: %s" % e)
       
    24 
       
    25 DatabaseError = Database.DatabaseError
       
    26 IntegrityError = Database.IntegrityError
       
    27 
       
    28 class UnicodeCursorWrapper(object):
       
    29     """
       
    30     A thin wrapper around psycopg cursors that allows them to accept Unicode
       
    31     strings as params.
       
    32 
       
    33     This is necessary because psycopg doesn't apply any DB quoting to
       
    34     parameters that are Unicode strings. If a param is Unicode, this will
       
    35     convert it to a bytestring using database client's encoding before passing
       
    36     it to psycopg.
       
    37 
       
    38     All results retrieved from the database are converted into Unicode strings
       
    39     before being returned to the caller.
       
    40     """
       
    41     def __init__(self, cursor, charset):
       
    42         self.cursor = cursor
       
    43         self.charset = charset
       
    44 
       
    45     def format_params(self, params):
       
    46         if isinstance(params, dict):
       
    47             result = {}
       
    48             charset = self.charset
       
    49             for key, value in params.items():
       
    50                 result[smart_str(key, charset)] = smart_str(value, charset)
       
    51             return result
       
    52         else:
       
    53             return tuple([smart_str(p, self.charset, True) for p in params])
       
    54 
       
    55     def execute(self, sql, params=()):
       
    56         try:
       
    57             return self.cursor.execute(smart_str(sql, self.charset), self.format_params(params))
       
    58         except Database.IntegrityError, e:
       
    59             raise utils.IntegrityError, utils.IntegrityError(*tuple(e)), sys.exc_info()[2]
       
    60         except Database.DatabaseError, e:
       
    61             raise utils.DatabaseError, utils.DatabaseError(*tuple(e)), sys.exc_info()[2]
       
    62 
       
    63     def executemany(self, sql, param_list):
       
    64         try:
       
    65             new_param_list = [self.format_params(params) for params in param_list]
       
    66             return self.cursor.executemany(sql, new_param_list)
       
    67         except Database.IntegrityError, e:
       
    68             raise utils.IntegrityError, utils.IntegrityError(*tuple(e)), sys.exc_info()[2]
       
    69         except Database.DatabaseError, e:
       
    70             raise utils.DatabaseError, utils.DatabaseError(*tuple(e)), sys.exc_info()[2]
       
    71 
       
    72     def __getattr__(self, attr):
       
    73         if attr in self.__dict__:
       
    74             return self.__dict__[attr]
       
    75         else:
       
    76             return getattr(self.cursor, attr)
       
    77 
       
    78     def __iter__(self):
       
    79         return iter(self.cursor.fetchall())
       
    80 
       
    81 class DatabaseFeatures(BaseDatabaseFeatures):
       
    82     uses_savepoints = True
       
    83 
       
    84 class DatabaseWrapper(BaseDatabaseWrapper):
       
    85     operators = {
       
    86         'exact': '= %s',
       
    87         'iexact': '= UPPER(%s)',
       
    88         'contains': 'LIKE %s',
       
    89         'icontains': 'LIKE UPPER(%s)',
       
    90         'regex': '~ %s',
       
    91         'iregex': '~* %s',
       
    92         'gt': '> %s',
       
    93         'gte': '>= %s',
       
    94         'lt': '< %s',
       
    95         'lte': '<= %s',
       
    96         'startswith': 'LIKE %s',
       
    97         'endswith': 'LIKE %s',
       
    98         'istartswith': 'LIKE UPPER(%s)',
       
    99         'iendswith': 'LIKE UPPER(%s)',
       
   100     }
       
   101 
       
   102     def __init__(self, *args, **kwargs):
       
   103         super(DatabaseWrapper, self).__init__(*args, **kwargs)
       
   104 
       
   105         import warnings
       
   106         warnings.warn(
       
   107             'The "postgresql" backend has been deprecated. Use "postgresql_psycopg2" instead.',
       
   108             PendingDeprecationWarning
       
   109         )
       
   110 
       
   111         self.features = DatabaseFeatures()
       
   112         self.ops = DatabaseOperations(self)
       
   113         self.client = DatabaseClient(self)
       
   114         self.creation = DatabaseCreation(self)
       
   115         self.introspection = DatabaseIntrospection(self)
       
   116         self.validation = BaseDatabaseValidation(self)
       
   117 
       
   118     def _cursor(self):
       
   119         new_connection = False
       
   120         set_tz = False
       
   121         settings_dict = self.settings_dict
       
   122         if self.connection is None:
       
   123             new_connection = True
       
   124             set_tz = settings_dict.get('TIME_ZONE')
       
   125             if settings_dict['NAME'] == '':
       
   126                 from django.core.exceptions import ImproperlyConfigured
       
   127                 raise ImproperlyConfigured("You need to specify NAME in your Django settings file.")
       
   128             conn_string = "dbname=%s" % settings_dict['NAME']
       
   129             if settings_dict['USER']:
       
   130                 conn_string = "user=%s %s" % (settings_dict['USER'], conn_string)
       
   131             if settings_dict['PASSWORD']:
       
   132                 conn_string += " password='%s'" % settings_dict['PASSWORD']
       
   133             if settings_dict['HOST']:
       
   134                 conn_string += " host=%s" % settings_dict['HOST']
       
   135             if settings_dict['PORT']:
       
   136                 conn_string += " port=%s" % settings_dict['PORT']
       
   137             self.connection = Database.connect(conn_string, **settings_dict['OPTIONS'])
       
   138             self.connection.set_isolation_level(1) # make transactions transparent to all cursors
       
   139             connection_created.send(sender=self.__class__)
       
   140         cursor = self.connection.cursor()
       
   141         if new_connection:
       
   142             if set_tz:
       
   143                 cursor.execute("SET TIME ZONE %s", [settings_dict['TIME_ZONE']])
       
   144             if not hasattr(self, '_version'):
       
   145                 self.__class__._version = get_version(cursor)
       
   146             if self._version[0:2] < (8, 0):
       
   147                 # No savepoint support for earlier version of PostgreSQL.
       
   148                 self.features.uses_savepoints = False
       
   149             cursor.execute("SET client_encoding to 'UNICODE'")
       
   150         return UnicodeCursorWrapper(cursor, 'utf-8')
       
   151 
       
   152 def typecast_string(s):
       
   153     """
       
   154     Cast all returned strings to unicode strings.
       
   155     """
       
   156     if not s and not isinstance(s, str):
       
   157         return s
       
   158     return smart_unicode(s)
       
   159 
       
   160 # Register these custom typecasts, because Django expects dates/times to be
       
   161 # in Python's native (standard-library) datetime/time format, whereas psycopg
       
   162 # use mx.DateTime by default.
       
   163 try:
       
   164     Database.register_type(Database.new_type((1082,), "DATE", util.typecast_date))
       
   165 except AttributeError:
       
   166     raise Exception("You appear to be using psycopg version 2. Set your DATABASES.ENGINE to 'postgresql_psycopg2' instead of 'postgresql'.")
       
   167 Database.register_type(Database.new_type((1083,1266), "TIME", util.typecast_time))
       
   168 Database.register_type(Database.new_type((1114,1184), "TIMESTAMP", util.typecast_timestamp))
       
   169 Database.register_type(Database.new_type((16,), "BOOLEAN", util.typecast_boolean))
       
   170 Database.register_type(Database.new_type((1700,), "NUMERIC", util.typecast_decimal))
       
   171 Database.register_type(Database.new_type(Database.types[1043].values, 'STRING', typecast_string))