2008-04-13 10:04:10 +08:00
import re
2007-08-20 09:26:46 +08:00
from django . db . backends import BaseDatabaseOperations
2008-04-13 10:04:10 +08:00
server_version_re = re . compile ( r ' PostgreSQL ( \ d { 1,2}) \ .( \ d { 1,2}) \ .?( \ d { 1,2})? ' )
2007-08-20 09:26:46 +08:00
# This DatabaseOperations class lives in here instead of base.py because it's
# used by both the 'postgresql' and 'postgresql_psycopg2' backends.
class DatabaseOperations ( BaseDatabaseOperations ) :
2007-08-26 03:24:47 +08:00
def __init__ ( self ) :
self . _postgres_version = None
def _get_postgres_version ( self ) :
if self . _postgres_version is None :
from django . db import connection
cursor = connection . cursor ( )
cursor . execute ( " SELECT version() " )
2008-04-13 10:04:10 +08:00
version_string = cursor . fetchone ( ) [ 0 ]
m = server_version_re . match ( version_string )
if not m :
raise Exception ( ' Unable to determine PostgreSQL version from version() function string: %r ' % version_string )
self . _postgres_version = [ int ( val ) for val in m . groups ( ) if val ]
2007-08-26 03:24:47 +08:00
return self . _postgres_version
postgres_version = property ( _get_postgres_version )
2007-08-20 09:26:46 +08:00
def date_extract_sql ( self , lookup_type , field_name ) :
# http://www.postgresql.org/docs/8.0/static/functions-datetime.html#FUNCTIONS-DATETIME-EXTRACT
2009-02-08 13:08:06 +08:00
if lookup_type == ' week_day ' :
# Using EXTRACT(), PostgreSQL days are indexed as Sunday=0, Saturday=6.
# If we instead us TO_CHAR, they're indexed with Sunday=1, Saturday=7
return " TO_CHAR( %s , ' D ' ) " % field_name
else :
return " EXTRACT( ' %s ' FROM %s ) " % ( lookup_type , field_name )
2007-08-20 09:26:46 +08:00
def date_trunc_sql ( self , lookup_type , field_name ) :
# http://www.postgresql.org/docs/8.0/static/functions-datetime.html#FUNCTIONS-DATETIME-TRUNC
return " DATE_TRUNC( ' %s ' , %s ) " % ( lookup_type , field_name )
def deferrable_sql ( self ) :
return " DEFERRABLE INITIALLY DEFERRED "
2008-08-09 04:09:53 +08:00
def lookup_cast ( self , lookup_type ) :
2008-08-25 20:56:06 +08:00
lookup = ' %s '
# Cast text lookups to text to allow things like filter(x__contains=4)
if lookup_type in ( ' iexact ' , ' contains ' , ' icontains ' , ' startswith ' ,
' istartswith ' , ' endswith ' , ' iendswith ' ) :
lookup = " %s ::text "
# Use UPPER(x) for case-insensitive lookups; it's faster.
if lookup_type in ( ' iexact ' , ' icontains ' , ' istartswith ' , ' iendswith ' ) :
lookup = ' UPPER( %s ) ' % lookup
return lookup
2008-08-09 04:09:53 +08:00
2008-02-27 07:12:47 +08:00
def field_cast_sql ( self , db_type ) :
if db_type == ' inet ' :
return ' HOST( %s ) '
return ' %s '
2007-08-20 09:26:46 +08:00
def last_insert_id ( self , cursor , table_name , pk_name ) :
cursor . execute ( " SELECT CURRVAL( ' \" %s _ %s _seq \" ' ) " % ( table_name , pk_name ) )
return cursor . fetchone ( ) [ 0 ]
Merged the queryset-refactor branch into trunk.
This is a big internal change, but mostly backwards compatible with existing
code. Also adds a couple of new features.
Fixed #245, #1050, #1656, #1801, #2076, #2091, #2150, #2253, #2306, #2400, #2430, #2482, #2496, #2676, #2737, #2874, #2902, #2939, #3037, #3141, #3288, #3440, #3592, #3739, #4088, #4260, #4289, #4306, #4358, #4464, #4510, #4858, #5012, #5020, #5261, #5295, #5321, #5324, #5325, #5555, #5707, #5796, #5817, #5987, #6018, #6074, #6088, #6154, #6177, #6180, #6203, #6658
git-svn-id: http://code.djangoproject.com/svn/django/trunk@7477 bcc190cf-cafb-0310-a4f2-bffc1f526a37
2008-04-27 10:50:16 +08:00
def no_limit_value ( self ) :
return None
2007-08-20 09:26:46 +08:00
def quote_name ( self , name ) :
if name . startswith ( ' " ' ) and name . endswith ( ' " ' ) :
return name # Quoting once is enough.
return ' " %s " ' % name
def sql_flush ( self , style , tables , sequences ) :
if tables :
if self . postgres_version [ 0 ] > = 8 and self . postgres_version [ 1 ] > = 1 :
# Postgres 8.1+ can do 'TRUNCATE x, y, z...;'. In fact, it *has to*
# in order to be able to truncate tables referenced by a foreign
# key in any other table. The result is a single SQL TRUNCATE
# statement.
sql = [ ' %s %s ; ' % \
( style . SQL_KEYWORD ( ' TRUNCATE ' ) ,
style . SQL_FIELD ( ' , ' . join ( [ self . quote_name ( table ) for table in tables ] ) )
) ]
else :
# Older versions of Postgres can't do TRUNCATE in a single call, so
# they must use a simple delete.
sql = [ ' %s %s %s ; ' % \
( style . SQL_KEYWORD ( ' DELETE ' ) ,
style . SQL_KEYWORD ( ' FROM ' ) ,
style . SQL_FIELD ( self . quote_name ( table ) )
) for table in tables ]
# 'ALTER SEQUENCE sequence_name RESTART WITH 1;'... style SQL statements
# to reset sequence indices
for sequence_info in sequences :
table_name = sequence_info [ ' table ' ]
column_name = sequence_info [ ' column ' ]
2007-08-26 02:56:43 +08:00
if column_name and len ( column_name ) > 0 :
sequence_name = ' %s _ %s _seq ' % ( table_name , column_name )
2007-08-20 09:26:46 +08:00
else :
2007-08-26 02:56:43 +08:00
sequence_name = ' %s _id_seq ' % table_name
sql . append ( " %s setval( ' %s ' , 1, false); " % \
( style . SQL_KEYWORD ( ' SELECT ' ) ,
style . SQL_FIELD ( self . quote_name ( sequence_name ) ) )
)
2007-08-20 09:26:46 +08:00
return sql
else :
return [ ]
def sequence_reset_sql ( self , style , model_list ) :
from django . db import models
output = [ ]
qn = self . quote_name
for model in model_list :
# Use `coalesce` to set the sequence for each model to the max pk value if there are records,
# or 1 if there are none. Set the `is_called` property (the third argument to `setval`) to true
# if there are records (as the max pk value is already in use), otherwise set it to false.
2008-06-30 08:38:14 +08:00
for f in model . _meta . local_fields :
2007-08-20 09:26:46 +08:00
if isinstance ( f , models . AutoField ) :
output . append ( " %s setval( ' %s ' , coalesce(max( %s ), 1), max( %s ) %s null) %s %s ; " % \
( style . SQL_KEYWORD ( ' SELECT ' ) ,
style . SQL_FIELD ( qn ( ' %s _ %s _seq ' % ( model . _meta . db_table , f . column ) ) ) ,
style . SQL_FIELD ( qn ( f . column ) ) ,
style . SQL_FIELD ( qn ( f . column ) ) ,
style . SQL_KEYWORD ( ' IS NOT ' ) ,
style . SQL_KEYWORD ( ' FROM ' ) ,
style . SQL_TABLE ( qn ( model . _meta . db_table ) ) ) )
break # Only one AutoField is allowed per model, so don't bother continuing.
for f in model . _meta . many_to_many :
output . append ( " %s setval( ' %s ' , coalesce(max( %s ), 1), max( %s ) %s null) %s %s ; " % \
( style . SQL_KEYWORD ( ' SELECT ' ) ,
style . SQL_FIELD ( qn ( ' %s _id_seq ' % f . m2m_db_table ( ) ) ) ,
style . SQL_FIELD ( qn ( ' id ' ) ) ,
style . SQL_FIELD ( qn ( ' id ' ) ) ,
style . SQL_KEYWORD ( ' IS NOT ' ) ,
style . SQL_KEYWORD ( ' FROM ' ) ,
2007-10-14 13:53:56 +08:00
style . SQL_TABLE ( qn ( f . m2m_db_table ( ) ) ) ) )
2007-08-26 03:24:47 +08:00
return output
2008-08-12 13:34:56 +08:00
def savepoint_create_sql ( self , sid ) :
return " SAVEPOINT %s " % sid
def savepoint_commit_sql ( self , sid ) :
return " RELEASE SAVEPOINT %s " % sid
def savepoint_rollback_sql ( self , sid ) :
return " ROLLBACK TO SAVEPOINT %s " % sid
2008-08-28 13:42:05 +08:00
def prep_for_iexact_query ( self , x ) :
return x
2009-02-02 20:03:31 +08:00
def check_aggregate_support ( self , aggregate ) :
""" Check that the backend fully supports the provided aggregate.
The implementation of population statistics ( STDDEV_POP and VAR_POP )
under Postgres 8.2 - 8.2 .4 is known to be faulty . Raise
NotImplementedError if this is the database in use .
"""
if aggregate . sql_function == ' STDDEV_POP ' or aggregate . sql_function == ' VAR_POP ' :
2009-02-18 07:33:30 +08:00
if self . postgres_version [ 0 ] == 8 and self . postgres_version [ 1 ] == 2 and self . postgres_version [ 2 ] < = 4 :
2009-02-02 20:03:31 +08:00
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 )