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