2008-04-13 10:04:10 +08:00
import re
2007-08-20 09:26:46 +08:00
from django . db . backends import BaseDatabaseOperations
# 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 ) :
2009-12-22 23:18:51 +08:00
def __init__ ( self , connection ) :
super ( DatabaseOperations , self ) . __init__ ( )
2007-08-26 03:24:47 +08:00
self . _postgres_version = None
2009-12-22 23:18:51 +08:00
self . connection = connection
2007-08-26 03:24:47 +08:00
def _get_postgres_version ( self ) :
if self . _postgres_version is None :
2009-05-10 17:22:06 +08:00
from django . db . backends . postgresql . version import get_version
2009-12-22 23:18:51 +08:00
cursor = self . connection . cursor ( )
2009-05-10 17:22:06 +08:00
self . _postgres_version = get_version ( cursor )
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 ' :
2009-04-12 10:00:58 +08:00
# For consistency across backends, we return Sunday=1, Saturday=7.
return " EXTRACT( ' dow ' FROM %s ) + 1 " % field_name
2009-02-08 13:08:06 +08:00
else :
return " EXTRACT( ' %s ' FROM %s ) " % ( lookup_type , field_name )
2007-08-20 09:26:46 +08:00
2010-12-22 11:34:04 +08:00
def date_interval_sql ( self , sql , connector , timedelta ) :
"""
implements the interval functionality for expressions
format for Postgres :
( datefield + interval ' 3 days 200 seconds 5 microseconds ' )
"""
modifiers = [ ]
if timedelta . days :
modifiers . append ( u ' %s days ' % timedelta . days )
if timedelta . seconds :
modifiers . append ( u ' %s seconds ' % timedelta . seconds )
if timedelta . microseconds :
modifiers . append ( u ' %s microseconds ' % timedelta . microseconds )
mods = u ' ' . join ( modifiers )
conn = u ' %s ' % connector
return u ' ( %s ) ' % conn . join ( [ sql , u ' interval \' %s \' ' % mods ] )
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 ) :
2010-06-21 19:48:45 +08:00
# Use pg_get_serial_sequence to get the underlying sequence name
# from the table name and column name (available since PostgreSQL 8)
2010-07-30 10:43:01 +08:00
cursor . execute ( " SELECT CURRVAL(pg_get_serial_sequence( ' %s ' , ' %s ' )) " % (
self . quote_name ( table_name ) , pk_name ) )
2007-08-20 09:26:46 +08:00
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 :
2009-05-11 09:46:26 +08:00
if self . postgres_version [ 0 : 2 ] > = ( 8 , 1 ) :
2007-08-20 09:26:46 +08:00
# 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 ' ]
2010-06-21 19:48:45 +08:00
if not ( column_name and len ( column_name ) > 0 ) :
# This will be the case if it's an m2m using an autogenerated
# intermediate table (see BaseDatabaseIntrospection.sequence_list)
column_name = ' id '
sql . append ( " %s setval(pg_get_serial_sequence( ' %s ' , ' %s ' ), 1, false); " % \
2007-08-26 02:56:43 +08:00
( style . SQL_KEYWORD ( ' SELECT ' ) ,
2010-07-30 10:43:01 +08:00
style . SQL_TABLE ( self . quote_name ( table_name ) ) ,
2010-06-21 19:48:45 +08:00
style . SQL_FIELD ( column_name ) )
2007-08-26 02:56:43 +08:00
)
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.
2010-06-21 19:48:45 +08:00
# Use pg_get_serial_sequence to get the underlying sequence name from the table name
# and column name (available since PostgreSQL 8)
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 ) :
2010-06-21 19:48:45 +08:00
output . append ( " %s setval(pg_get_serial_sequence( ' %s ' , ' %s ' ), coalesce(max( %s ), 1), max( %s ) %s null) %s %s ; " % \
2007-08-20 09:26:46 +08:00
( style . SQL_KEYWORD ( ' SELECT ' ) ,
2010-07-30 10:43:01 +08:00
style . SQL_TABLE ( qn ( model . _meta . db_table ) ) ,
2010-06-21 19:48:45 +08:00
style . SQL_FIELD ( f . column ) ,
2007-08-20 09:26:46 +08:00
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 :
2009-07-11 22:22:52 +08:00
if not f . rel . through :
2010-06-21 19:48:45 +08:00
output . append ( " %s setval(pg_get_serial_sequence( ' %s ' , ' %s ' ), coalesce(max( %s ), 1), max( %s ) %s null) %s %s ; " % \
2009-07-11 22:22:52 +08:00
( style . SQL_KEYWORD ( ' SELECT ' ) ,
2010-07-30 10:43:01 +08:00
style . SQL_TABLE ( qn ( f . m2m_db_table ( ) ) ) ,
2010-06-21 19:48:45 +08:00
style . SQL_FIELD ( ' id ' ) ,
2009-07-11 22:22:52 +08:00
style . SQL_FIELD ( qn ( ' id ' ) ) ,
style . SQL_FIELD ( qn ( ' id ' ) ) ,
style . SQL_KEYWORD ( ' IS NOT ' ) ,
style . SQL_KEYWORD ( ' FROM ' ) ,
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.
2009-05-10 17:22:53 +08:00
The population and sample statistics ( STDDEV_POP , STDDEV_SAMP ,
VAR_POP , VAR_SAMP ) were first implemented in Postgres 8.2 .
2009-02-02 20:03:31 +08:00
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 .
"""
2009-05-10 17:22:53 +08:00
if aggregate . sql_function in ( ' STDDEV_POP ' , ' STDDEV_SAMP ' , ' VAR_POP ' , ' VAR_SAMP ' ) :
2009-05-11 09:46:26 +08:00
if self . postgres_version [ 0 : 2 ] < ( 8 , 2 ) :
2009-05-10 17:22:53 +08:00
raise NotImplementedError ( ' PostgreSQL does not support %s prior to version 8.2. Please upgrade your version of PostgreSQL. ' % aggregate . sql_function )
if aggregate . sql_function in ( ' STDDEV_POP ' , ' VAR_POP ' ) :
2009-05-11 09:46:26 +08:00
if self . postgres_version [ 0 : 2 ] == ( 8 , 2 ) :
2009-05-10 17:22:06 +08:00
if self . postgres_version [ 2 ] is None or self . postgres_version [ 2 ] < = 4 :
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 )
2010-04-29 09:22:50 +08:00
def max_name_length ( self ) :
"""
Returns the maximum length of an identifier .
Note that the maximum length of an identifier is 63 by default , but can
be changed by recompiling PostgreSQL after editing the NAMEDATALEN
macro in src / include / pg_config_manual . h .
This implementation simply returns 63 , but can easily be overridden by a
custom database backend that inherits most of its behavior from this one .
"""
return 63