Began implementing BaseDatabaseOperations class for every database backend. This class will be used to hold the database-specific methods that currently live at the module level in each backend. Only autoinc_sql() has been implemented so far.

git-svn-id: http://code.djangoproject.com/svn/django/trunk@5950 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
Adrian Holovaty 2007-08-19 22:29:57 +00:00
parent 7c41b19c8a
commit 38b5d7f23d
10 changed files with 85 additions and 47 deletions

View File

@ -213,7 +213,7 @@ def sql_model_create(model, style, known_models=set()):
Returns the SQL required to create a single model, as a tuple of: Returns the SQL required to create a single model, as a tuple of:
(list_of_sql, pending_references_dict) (list_of_sql, pending_references_dict)
""" """
from django.db import backend, models from django.db import backend, connection, models
opts = model._meta opts = model._meta
final_output = [] final_output = []
@ -267,9 +267,9 @@ def sql_model_create(model, style, known_models=set()):
full_statement.append(';') full_statement.append(';')
final_output.append('\n'.join(full_statement)) final_output.append('\n'.join(full_statement))
if opts.has_auto_field and hasattr(backend, 'get_autoinc_sql'): if opts.has_auto_field:
# Add any extra SQL needed to support auto-incrementing primary keys # Add any extra SQL needed to support auto-incrementing primary keys.
autoinc_sql = backend.get_autoinc_sql(opts.db_table) autoinc_sql = connection.ops.autoinc_sql(opts.db_table)
if autoinc_sql: if autoinc_sql:
for stmt in autoinc_sql: for stmt in autoinc_sql:
final_output.append(stmt) final_output.append(stmt)

View File

@ -6,6 +6,10 @@ except ImportError:
from django.utils._threading_local import local from django.utils._threading_local import local
class BaseDatabaseWrapper(local): class BaseDatabaseWrapper(local):
"""
Represents a database connection.
"""
ops = None
def __init__(self, **kwargs): def __init__(self, **kwargs):
self.connection = None self.connection = None
self.queries = [] self.queries = []
@ -34,3 +38,18 @@ class BaseDatabaseWrapper(local):
def make_debug_cursor(self, cursor): def make_debug_cursor(self, cursor):
from django.db.backends import util from django.db.backends import util
return util.CursorDebugWrapper(cursor, self) return util.CursorDebugWrapper(cursor, self)
class BaseDatabaseOperations(object):
"""
This class encapsulates all backend-specific differences, such as the way
a backend performs ordering or calculates the ID of a recently-inserted
row.
"""
def autoinc_sql(self, table):
"""
Returns any SQL needed to support auto-incrementing primary keys, or
None if no SQL is necessary.
This SQL is executed when a table is created.
"""
return None

View File

@ -4,7 +4,7 @@ ADO MSSQL database backend for Django.
Requires adodbapi 2.0.1: http://adodbapi.sourceforge.net/ Requires adodbapi 2.0.1: http://adodbapi.sourceforge.net/
""" """
from django.db.backends import BaseDatabaseWrapper, util from django.db.backends import BaseDatabaseWrapper, BaseDatabaseOperations, util
try: try:
import adodbapi as Database import adodbapi as Database
except ImportError, e: except ImportError, e:
@ -48,7 +48,12 @@ def variantToPython(variant, adType):
return res return res
Database.convertVariantToPython = variantToPython Database.convertVariantToPython = variantToPython
class DatabaseOperations(BaseDatabaseOperations):
pass
class DatabaseWrapper(BaseDatabaseWrapper): class DatabaseWrapper(BaseDatabaseWrapper):
ops = DatabaseOperations()
def _cursor(self, settings): def _cursor(self, settings):
if self.connection is None: if self.connection is None:
if settings.DATABASE_NAME == '' or settings.DATABASE_USER == '': if settings.DATABASE_NAME == '' or settings.DATABASE_USER == '':
@ -130,9 +135,6 @@ def get_start_transaction_sql():
def get_tablespace_sql(tablespace, inline=False): def get_tablespace_sql(tablespace, inline=False):
return "ON %s" % quote_name(tablespace) return "ON %s" % quote_name(tablespace)
def get_autoinc_sql(table):
return None
def get_sql_flush(style, tables, sequences): def get_sql_flush(style, tables, sequences):
"""Return a list of SQL statements required to remove all data from """Return a list of SQL statements required to remove all data from
all tables in the database (without actually removing the tables all tables in the database (without actually removing the tables

View File

@ -21,7 +21,12 @@ class DatabaseError(Exception):
class IntegrityError(DatabaseError): class IntegrityError(DatabaseError):
pass pass
class DatabaseWrapper: class DatabaseOperations(object):
def __getattr__(self, *args, **kwargs):
complain()
class DatabaseWrapper(object):
ops = DatabaseOperations()
cursor = complain cursor = complain
_commit = complain _commit = complain
_rollback = ignore _rollback = ignore
@ -50,7 +55,6 @@ get_drop_foreignkey_sql = complain
get_pk_default_value = complain get_pk_default_value = complain
get_max_name_length = ignore get_max_name_length = ignore
get_start_transaction_sql = complain get_start_transaction_sql = complain
get_autoinc_sql = complain
get_sql_flush = complain get_sql_flush = complain
get_sql_sequence_reset = complain get_sql_sequence_reset = complain

View File

@ -4,7 +4,7 @@ MySQL database backend for Django.
Requires MySQLdb: http://sourceforge.net/projects/mysql-python Requires MySQLdb: http://sourceforge.net/projects/mysql-python
""" """
from django.db.backends import BaseDatabaseWrapper, util from django.db.backends import BaseDatabaseWrapper, BaseDatabaseOperations, util
try: try:
import MySQLdb as Database import MySQLdb as Database
except ImportError, e: except ImportError, e:
@ -53,7 +53,12 @@ server_version_re = re.compile(r'(\d{1,2})\.(\d{1,2})\.(\d{1,2})')
# standard util.CursorDebugWrapper can be used. Also, using sql_mode # standard util.CursorDebugWrapper can be used. Also, using sql_mode
# TRADITIONAL will automatically cause most warnings to be treated as errors. # TRADITIONAL will automatically cause most warnings to be treated as errors.
class DatabaseOperations(BaseDatabaseOperations):
pass
class DatabaseWrapper(BaseDatabaseWrapper): class DatabaseWrapper(BaseDatabaseWrapper):
ops = DatabaseOperations()
def __init__(self, **kwargs): def __init__(self, **kwargs):
super(DatabaseWrapper, self).__init__(**kwargs) super(DatabaseWrapper, self).__init__(**kwargs)
self.server_version = None self.server_version = None
@ -181,9 +186,6 @@ def get_max_name_length():
def get_start_transaction_sql(): def get_start_transaction_sql():
return "BEGIN;" return "BEGIN;"
def get_autoinc_sql(table):
return None
def get_sql_flush(style, tables, sequences): def get_sql_flush(style, tables, sequences):
"""Return a list of SQL statements required to remove all data from """Return a list of SQL statements required to remove all data from
all tables in the database (without actually removing the tables all tables in the database (without actually removing the tables

View File

@ -4,7 +4,7 @@ MySQL database backend for Django.
Requires MySQLdb: http://sourceforge.net/projects/mysql-python Requires MySQLdb: http://sourceforge.net/projects/mysql-python
""" """
from django.db.backends import BaseDatabaseWrapper, util from django.db.backends import BaseDatabaseWrapper, BaseDatabaseOperations, util
from django.utils.encoding import force_unicode from django.utils.encoding import force_unicode
try: try:
import MySQLdb as Database import MySQLdb as Database
@ -63,7 +63,12 @@ class MysqlDebugWrapper:
else: else:
return getattr(self.cursor, attr) return getattr(self.cursor, attr)
class DatabaseOperations(BaseDatabaseOperations):
pass
class DatabaseWrapper(BaseDatabaseWrapper): class DatabaseWrapper(BaseDatabaseWrapper):
ops = DatabaseOperations()
def __init__(self, **kwargs): def __init__(self, **kwargs):
super(DatabaseWrapper, self).__init__(**kwargs) super(DatabaseWrapper, self).__init__(**kwargs)
self.server_version = None self.server_version = None
@ -200,9 +205,6 @@ def get_max_name_length():
def get_start_transaction_sql(): def get_start_transaction_sql():
return "BEGIN;" return "BEGIN;"
def get_autoinc_sql(table):
return None
def get_sql_flush(style, tables, sequences): def get_sql_flush(style, tables, sequences):
"""Return a list of SQL statements required to remove all data from """Return a list of SQL statements required to remove all data from
all tables in the database (without actually removing the tables all tables in the database (without actually removing the tables

View File

@ -4,7 +4,7 @@ Oracle database backend for Django.
Requires cx_Oracle: http://www.python.net/crew/atuining/cx_Oracle/ Requires cx_Oracle: http://www.python.net/crew/atuining/cx_Oracle/
""" """
from django.db.backends import BaseDatabaseWrapper, util from django.db.backends import BaseDatabaseWrapper, BaseDatabaseOperations, util
from django.utils.datastructures import SortedDict from django.utils.datastructures import SortedDict
from django.utils.encoding import smart_str, force_unicode from django.utils.encoding import smart_str, force_unicode
import datetime import datetime
@ -21,7 +21,26 @@ except ImportError, e:
DatabaseError = Database.Error DatabaseError = Database.Error
IntegrityError = Database.IntegrityError IntegrityError = Database.IntegrityError
class DatabaseOperations(BaseDatabaseOperations):
def autoinc_sql(self, table):
# To simulate auto-incrementing primary keys in Oracle, we have to
# create a sequence and a trigger.
sq_name = get_sequence_name(table)
tr_name = get_trigger_name(table)
sequence_sql = 'CREATE SEQUENCE %s;' % sq_name
trigger_sql = """
CREATE OR REPLACE TRIGGER %s
BEFORE INSERT ON %s
FOR EACH ROW
WHEN (new.id IS NULL)
BEGIN
SELECT %s.nextval INTO :new.id FROM dual;
END;/""" % (tr_name, quote_name(table), sq_name)
return sequence_sql, trigger_sql
class DatabaseWrapper(BaseDatabaseWrapper): class DatabaseWrapper(BaseDatabaseWrapper):
ops = DatabaseOperations()
def _valid_connection(self): def _valid_connection(self):
return self.connection is not None return self.connection is not None
@ -187,22 +206,6 @@ def get_start_transaction_sql():
def get_tablespace_sql(tablespace, inline=False): def get_tablespace_sql(tablespace, inline=False):
return "%sTABLESPACE %s" % ((inline and "USING INDEX " or ""), quote_name(tablespace)) return "%sTABLESPACE %s" % ((inline and "USING INDEX " or ""), quote_name(tablespace))
def get_autoinc_sql(table):
# To simulate auto-incrementing primary keys in Oracle, we have to
# create a sequence and a trigger.
sq_name = get_sequence_name(table)
tr_name = get_trigger_name(table)
sequence_sql = 'CREATE SEQUENCE %s;' % sq_name
trigger_sql = """CREATE OR REPLACE TRIGGER %s
BEFORE INSERT ON %s
FOR EACH ROW
WHEN (new.id IS NULL)
BEGIN
SELECT %s.nextval INTO :new.id FROM dual;
END;
/""" % (tr_name, quote_name(table), sq_name)
return sequence_sql, trigger_sql
def get_drop_sequence(table): def get_drop_sequence(table):
return "DROP SEQUENCE %s;" % quote_name(get_sequence_name(table)) return "DROP SEQUENCE %s;" % quote_name(get_sequence_name(table))

View File

@ -5,7 +5,7 @@ Requires psycopg 1: http://initd.org/projects/psycopg1
""" """
from django.utils.encoding import smart_str, smart_unicode from django.utils.encoding import smart_str, smart_unicode
from django.db.backends import BaseDatabaseWrapper, util from django.db.backends import BaseDatabaseWrapper, BaseDatabaseOperations, util
try: try:
import psycopg as Database import psycopg as Database
except ImportError, e: except ImportError, e:
@ -57,7 +57,12 @@ class UnicodeCursorWrapper(object):
postgres_version = None postgres_version = None
class DatabaseOperations(BaseDatabaseOperations):
pass
class DatabaseWrapper(BaseDatabaseWrapper): class DatabaseWrapper(BaseDatabaseWrapper):
ops = DatabaseOperations()
def _cursor(self, settings): def _cursor(self, settings):
set_tz = False set_tz = False
if self.connection is None: if self.connection is None:
@ -157,9 +162,6 @@ def get_max_name_length():
def get_start_transaction_sql(): def get_start_transaction_sql():
return "BEGIN;" return "BEGIN;"
def get_autoinc_sql(table):
return None
def get_sql_flush(style, tables, sequences): def get_sql_flush(style, tables, sequences):
"""Return a list of SQL statements required to remove all data from """Return a list of SQL statements required to remove all data from
all tables in the database (without actually removing the tables all tables in the database (without actually removing the tables

View File

@ -4,7 +4,7 @@ PostgreSQL database backend for Django.
Requires psycopg 2: http://initd.org/projects/psycopg2 Requires psycopg 2: http://initd.org/projects/psycopg2
""" """
from django.db.backends import BaseDatabaseWrapper, util from django.db.backends import BaseDatabaseWrapper, BaseDatabaseOperations, util
try: try:
import psycopg2 as Database import psycopg2 as Database
import psycopg2.extensions import psycopg2.extensions
@ -19,7 +19,12 @@ psycopg2.extensions.register_type(psycopg2.extensions.UNICODE)
postgres_version = None postgres_version = None
class DatabaseOperations(BaseDatabaseOperations):
pass
class DatabaseWrapper(BaseDatabaseWrapper): class DatabaseWrapper(BaseDatabaseWrapper):
ops = DatabaseOperations()
def _cursor(self, settings): def _cursor(self, settings):
set_tz = False set_tz = False
if self.connection is None: if self.connection is None:
@ -111,9 +116,6 @@ def get_max_name_length():
def get_start_transaction_sql(): def get_start_transaction_sql():
return "BEGIN;" return "BEGIN;"
def get_autoinc_sql(table):
return None
def get_sql_flush(style, tables, sequences): def get_sql_flush(style, tables, sequences):
"""Return a list of SQL statements required to remove all data from """Return a list of SQL statements required to remove all data from
all tables in the database (without actually removing the tables all tables in the database (without actually removing the tables

View File

@ -2,7 +2,7 @@
SQLite3 backend for django. Requires pysqlite2 (http://pysqlite.org/). SQLite3 backend for django. Requires pysqlite2 (http://pysqlite.org/).
""" """
from django.db.backends import BaseDatabaseWrapper, util from django.db.backends import BaseDatabaseWrapper, BaseDatabaseOperations, util
try: try:
try: try:
from sqlite3 import dbapi2 as Database from sqlite3 import dbapi2 as Database
@ -34,7 +34,12 @@ Database.register_converter("TIMESTAMP", util.typecast_timestamp)
Database.register_converter("decimal", util.typecast_decimal) Database.register_converter("decimal", util.typecast_decimal)
Database.register_adapter(decimal.Decimal, util.rev_typecast_decimal) Database.register_adapter(decimal.Decimal, util.rev_typecast_decimal)
class DatabaseOperations(BaseDatabaseOperations):
pass
class DatabaseWrapper(BaseDatabaseWrapper): class DatabaseWrapper(BaseDatabaseWrapper):
ops = DatabaseOperations()
def _cursor(self, settings): def _cursor(self, settings):
if self.connection is None: if self.connection is None:
kwargs = { kwargs = {
@ -143,9 +148,6 @@ def get_max_name_length():
def get_start_transaction_sql(): def get_start_transaction_sql():
return "BEGIN;" return "BEGIN;"
def get_autoinc_sql(table):
return None
def get_sql_flush(style, tables, sequences): def get_sql_flush(style, tables, sequences):
""" """
Return a list of SQL statements required to remove all data from Return a list of SQL statements required to remove all data from