diff options
| author | Adrian Holovaty <adrian@holovaty.com> | 2006-05-02 01:31:56 +0000 |
|---|---|---|
| committer | Adrian Holovaty <adrian@holovaty.com> | 2006-05-02 01:31:56 +0000 |
| commit | f69cf70ed813a8cd7e1f963a14ae39103e8d5265 (patch) | |
| tree | d3b32e84cd66573b3833ddf662af020f8ef2f7a8 /django/core/db | |
| parent | d5dbeaa9be359a4c794885c2e9f1b5a7e5e51fb8 (diff) | |
MERGED MAGIC-REMOVAL BRANCH TO TRUNK. This change is highly backwards-incompatible. Please read http://code.djangoproject.com/wiki/RemovingTheMagic for upgrade instructions.
git-svn-id: http://code.djangoproject.com/svn/django/trunk@2809 bcc190cf-cafb-0310-a4f2-bffc1f526a37
Diffstat (limited to 'django/core/db')
| -rw-r--r-- | django/core/db/__init__.py | 43 | ||||
| -rw-r--r-- | django/core/db/backends/__init__.py | 0 | ||||
| -rw-r--r-- | django/core/db/backends/ado_mssql.py | 174 | ||||
| -rw-r--r-- | django/core/db/backends/mysql.py | 235 | ||||
| -rw-r--r-- | django/core/db/backends/postgresql.py | 238 | ||||
| -rw-r--r-- | django/core/db/backends/sqlite3.py | 230 | ||||
| -rw-r--r-- | django/core/db/base.py | 32 | ||||
| -rw-r--r-- | django/core/db/dicthelpers.py | 24 | ||||
| -rw-r--r-- | django/core/db/typecasts.py | 55 |
9 files changed, 0 insertions, 1031 deletions
diff --git a/django/core/db/__init__.py b/django/core/db/__init__.py deleted file mode 100644 index 1d2f04b367..0000000000 --- a/django/core/db/__init__.py +++ /dev/null @@ -1,43 +0,0 @@ -""" -This is the core database connection. - -All Django code assumes database SELECT statements cast the resulting values as such: - * booleans are mapped to Python booleans - * dates are mapped to Python datetime.date objects - * times are mapped to Python datetime.time objects - * timestamps are mapped to Python datetime.datetime objects -""" - -from django.conf.settings import DATABASE_ENGINE - -try: - dbmod = __import__('django.core.db.backends.%s' % DATABASE_ENGINE, '', '', ['']) -except ImportError, exc: - # The database backend wasn't found. Display a helpful error message - # listing all possible database backends. - from django.core.exceptions import ImproperlyConfigured - import os - backend_dir = os.path.join(__path__[0], 'backends') - available_backends = [f[:-3] for f in os.listdir(backend_dir) if f.endswith('.py') and not f.startswith('__init__')] - available_backends.sort() - raise ImproperlyConfigured, "Could not load database backend: %s. Is your DATABASE_ENGINE setting (currently, %r) spelled correctly? Available options are: %s" % \ - (exc, DATABASE_ENGINE, ", ".join(map(repr, available_backends))) - -DatabaseError = dbmod.DatabaseError -db = dbmod.DatabaseWrapper() -dictfetchone = dbmod.dictfetchone -dictfetchmany = dbmod.dictfetchmany -dictfetchall = dbmod.dictfetchall -dictfetchall = dbmod.dictfetchall -get_last_insert_id = dbmod.get_last_insert_id -get_date_extract_sql = dbmod.get_date_extract_sql -get_date_trunc_sql = dbmod.get_date_trunc_sql -get_limit_offset_sql = dbmod.get_limit_offset_sql -get_random_function_sql = dbmod.get_random_function_sql -get_table_list = dbmod.get_table_list -get_table_description = dbmod.get_table_description -get_relations = dbmod.get_relations -get_indexes = dbmod.get_indexes -OPERATOR_MAPPING = dbmod.OPERATOR_MAPPING -DATA_TYPES = dbmod.DATA_TYPES -DATA_TYPES_REVERSE = dbmod.DATA_TYPES_REVERSE diff --git a/django/core/db/backends/__init__.py b/django/core/db/backends/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 --- a/django/core/db/backends/__init__.py +++ /dev/null diff --git a/django/core/db/backends/ado_mssql.py b/django/core/db/backends/ado_mssql.py deleted file mode 100644 index 4afe0cef70..0000000000 --- a/django/core/db/backends/ado_mssql.py +++ /dev/null @@ -1,174 +0,0 @@ -""" -ADO MSSQL database backend for Django. - -Requires adodbapi 2.0.1: http://adodbapi.sourceforge.net/ -""" - -from django.core.db import base -from django.core.db.dicthelpers import * -import adodbapi as Database -import datetime -try: - import mx -except ImportError: - mx = None - -DatabaseError = Database.DatabaseError - -# We need to use a special Cursor class because adodbapi expects question-mark -# param style, but Django expects "%s". This cursor converts question marks to -# format-string style. -class Cursor(Database.Cursor): - def executeHelper(self, operation, isStoredProcedureCall, parameters=None): - if parameters is not None and "%s" in operation: - operation = operation.replace("%s", "?") - Database.Cursor.executeHelper(self, operation, isStoredProcedureCall, parameters) - -class Connection(Database.Connection): - def cursor(self): - return Cursor(self) -Database.Connection = Connection - -origCVtoP = Database.convertVariantToPython -def variantToPython(variant, adType): - if type(variant) == bool and adType == 11: - return variant # bool not 1/0 - res = origCVtoP(variant, adType) - if mx is not None and type(res) == mx.DateTime.mxDateTime.DateTimeType: - # Convert ms.DateTime objects to Python datetime.datetime objects. - tv = list(res.tuple()[:7]) - tv[-2] = int(tv[-2]) - return datetime.datetime(*tuple(tv)) - if type(res) == float and str(res)[-2:] == ".0": - return int(res) # If float but int, then int. - return res -Database.convertVariantToPython = variantToPython - -try: - # Only exists in python 2.4+ - from threading import local -except ImportError: - # Import copy of _thread_local.py from python 2.4 - from django.utils._threading_local import local - -class DatabaseWrapper(local): - def __init__(self): - self.connection = None - self.queries = [] - - def cursor(self): - from django.conf.settings import DATABASE_USER, DATABASE_NAME, DATABASE_HOST, DATABASE_PORT, DATABASE_PASSWORD, DEBUG - if self.connection is None: - if DATABASE_NAME == '' or DATABASE_USER == '': - from django.core.exceptions import ImproperlyConfigured - raise ImproperlyConfigured, "You need to specify both DATABASE_NAME and DATABASE_USER in your Django settings file." - if not DATABASE_HOST: - DATABASE_HOST = "127.0.0.1" - # TODO: Handle DATABASE_PORT. - conn_string = "PROVIDER=SQLOLEDB;DATA SOURCE=%s;UID=%s;PWD=%s;DATABASE=%s" % (DATABASE_HOST, DATABASE_USER, DATABASE_PASSWORD, DATABASE_NAME) - self.connection = Database.connect(conn_string) - cursor = self.connection.cursor() - if DEBUG: - return base.CursorDebugWrapper(cursor, self) - return cursor - - def commit(self): - return self.connection.commit() - - def rollback(self): - if self.connection: - return self.connection.rollback() - - def close(self): - if self.connection is not None: - self.connection.close() - self.connection = None - - def quote_name(self, name): - if name.startswith('[') and name.endswith(']'): - return name # Quoting once is enough. - return '[%s]' % name - -def get_last_insert_id(cursor, table_name, pk_name): - cursor.execute("SELECT %s FROM %s WHERE %s = @@IDENTITY" % (pk_name, table_name, pk_name)) - return cursor.fetchone()[0] - -def get_date_extract_sql(lookup_type, table_name): - # lookup_type is 'year', 'month', 'day' - return "DATEPART(%s, %s)" % (lookup_type, table_name) - -def get_date_trunc_sql(lookup_type, field_name): - # lookup_type is 'year', 'month', 'day' - if lookup_type=='year': - return "Convert(datetime, Convert(varchar, DATEPART(year, %s)) + '/01/01')" % field_name - if lookup_type=='month': - return "Convert(datetime, Convert(varchar, DATEPART(year, %s)) + '/' + Convert(varchar, DATEPART(month, %s)) + '/01')" % (field_name, field_name) - if lookup_type=='day': - return "Convert(datetime, Convert(varchar(12), %s))" % field_name - -def get_limit_offset_sql(limit, offset=None): - # TODO: This is a guess. Make sure this is correct. - sql = "LIMIT %s" % limit - if offset and offset != 0: - sql += " OFFSET %s" % offset - return sql - -def get_random_function_sql(): - return "RAND()" - -def get_table_list(cursor): - raise NotImplementedError - -def get_table_description(cursor, table_name): - raise NotImplementedError - -def get_relations(cursor, table_name): - raise NotImplementedError - -def get_indexes(cursor, table_name): - raise NotImplementedError - -OPERATOR_MAPPING = { - 'exact': '= %s', - 'iexact': 'LIKE %s', - 'contains': 'LIKE %s', - 'icontains': 'LIKE %s', - 'ne': '!= %s', - 'gt': '> %s', - 'gte': '>= %s', - 'lt': '< %s', - 'lte': '<= %s', - 'startswith': 'LIKE %s', - 'endswith': 'LIKE %s', - 'istartswith': 'LIKE %s', - 'iendswith': 'LIKE %s', -} - -DATA_TYPES = { - 'AutoField': 'int IDENTITY (1, 1)', - 'BooleanField': 'bit', - 'CharField': 'varchar(%(maxlength)s)', - 'CommaSeparatedIntegerField': 'varchar(%(maxlength)s)', - 'DateField': 'smalldatetime', - 'DateTimeField': 'smalldatetime', - 'FileField': 'varchar(100)', - 'FilePathField': 'varchar(100)', - 'FloatField': 'numeric(%(max_digits)s, %(decimal_places)s)', - 'ImageField': 'varchar(100)', - 'IntegerField': 'int', - 'IPAddressField': 'char(15)', - 'ManyToManyField': None, - 'NullBooleanField': 'bit', - 'OneToOneField': 'int', - 'PhoneNumberField': 'varchar(20)', - 'PositiveIntegerField': 'int CONSTRAINT [CK_int_pos_%(column)s] CHECK ([%(column)s] > 0)', - 'PositiveSmallIntegerField': 'smallint CONSTRAINT [CK_smallint_pos_%(column)s] CHECK ([%(column)s] > 0)', - 'SlugField': 'varchar(%(maxlength)s)', - 'SmallIntegerField': 'smallint', - 'TextField': 'text', - 'TimeField': 'time', - 'URLField': 'varchar(200)', - 'USStateField': 'varchar(2)', -} - -DATA_TYPES_REVERSE = {} diff --git a/django/core/db/backends/mysql.py b/django/core/db/backends/mysql.py deleted file mode 100644 index d0e27e6f44..0000000000 --- a/django/core/db/backends/mysql.py +++ /dev/null @@ -1,235 +0,0 @@ -""" -MySQL database backend for Django. - -Requires MySQLdb: http://sourceforge.net/projects/mysql-python -""" - -from django.core.db import base, typecasts -from django.core.db.dicthelpers import * -import MySQLdb as Database -from MySQLdb.converters import conversions -from MySQLdb.constants import FIELD_TYPE -import types - -DatabaseError = Database.DatabaseError - -django_conversions = conversions.copy() -django_conversions.update({ - types.BooleanType: typecasts.rev_typecast_boolean, - FIELD_TYPE.DATETIME: typecasts.typecast_timestamp, - FIELD_TYPE.DATE: typecasts.typecast_date, - FIELD_TYPE.TIME: typecasts.typecast_time, -}) - -# This is an extra debug layer over MySQL queries, to display warnings. -# It's only used when DEBUG=True. -class MysqlDebugWrapper: - def __init__(self, cursor): - self.cursor = cursor - - def execute(self, sql, params=()): - try: - return self.cursor.execute(sql, params) - except Database.Warning, w: - self.cursor.execute("SHOW WARNINGS") - raise Database.Warning, "%s: %s" % (w, self.cursor.fetchall()) - - def executemany(self, sql, param_list): - try: - return self.cursor.executemany(sql, param_list) - except Database.Warning: - self.cursor.execute("SHOW WARNINGS") - raise Database.Warning, "%s: %s" % (w, self.cursor.fetchall()) - - def __getattr__(self, attr): - if self.__dict__.has_key(attr): - return self.__dict__[attr] - else: - return getattr(self.cursor, attr) - -try: - # Only exists in python 2.4+ - from threading import local -except ImportError: - # Import copy of _thread_local.py from python 2.4 - from django.utils._threading_local import local - -class DatabaseWrapper(local): - def __init__(self): - self.connection = None - self.queries = [] - - def _valid_connection(self): - if self.connection is not None: - try: - self.connection.ping() - return True - except DatabaseError: - self.connection.close() - self.connection = None - return False - - def cursor(self): - from django.conf.settings import DATABASE_USER, DATABASE_NAME, DATABASE_HOST, DATABASE_PORT, DATABASE_PASSWORD, DEBUG - if not self._valid_connection(): - kwargs = { - 'user': DATABASE_USER, - 'db': DATABASE_NAME, - 'passwd': DATABASE_PASSWORD, - 'host': DATABASE_HOST, - 'conv': django_conversions, - } - if DATABASE_PORT: - kwargs['port'] = DATABASE_PORT - self.connection = Database.connect(**kwargs) - cursor = self.connection.cursor() - if self.connection.get_server_info() >= '4.1': - cursor.execute("SET NAMES utf8") - if DEBUG: - return base.CursorDebugWrapper(MysqlDebugWrapper(cursor), self) - return cursor - - def commit(self): - self.connection.commit() - - def rollback(self): - if self.connection: - try: - self.connection.rollback() - except Database.NotSupportedError: - pass - - def close(self): - if self.connection is not None: - self.connection.close() - self.connection = None - - def quote_name(self, name): - if name.startswith("`") and name.endswith("`"): - return name # Quoting once is enough. - return "`%s`" % name - -def get_last_insert_id(cursor, table_name, pk_name): - return cursor.lastrowid - -def get_date_extract_sql(lookup_type, table_name): - # lookup_type is 'year', 'month', 'day' - # http://dev.mysql.com/doc/mysql/en/date-and-time-functions.html - return "EXTRACT(%s FROM %s)" % (lookup_type.upper(), table_name) - -def get_date_trunc_sql(lookup_type, field_name): - # lookup_type is 'year', 'month', 'day' - # http://dev.mysql.com/doc/mysql/en/date-and-time-functions.html - # MySQL doesn't support DATE_TRUNC, so we fake it by subtracting intervals. - # If you know of a better way to do this, please file a Django ticket. - # Note that we can't use DATE_FORMAT directly because that causes the output - # to be a string rather than a datetime object, and we need MySQL to return - # a date so that it's typecasted properly into a Python datetime object. - subtractions = ["interval (DATE_FORMAT(%s, '%%%%s')) second - interval (DATE_FORMAT(%s, '%%%%i')) minute - interval (DATE_FORMAT(%s, '%%%%H')) hour" % (field_name, field_name, field_name)] - if lookup_type in ('year', 'month'): - subtractions.append(" - interval (DATE_FORMAT(%s, '%%%%e')-1) day" % field_name) - if lookup_type == 'year': - subtractions.append(" - interval (DATE_FORMAT(%s, '%%%%m')-1) month" % field_name) - return "(%s - %s)" % (field_name, ''.join(subtractions)) - -def get_limit_offset_sql(limit, offset=None): - sql = "LIMIT " - if offset and offset != 0: - sql += "%s," % offset - return sql + str(limit) - -def get_random_function_sql(): - return "RAND()" - -def get_table_list(cursor): - "Returns a list of table names in the current database." - cursor.execute("SHOW TABLES") - return [row[0] for row in cursor.fetchall()] - -def get_table_description(cursor, table_name): - "Returns a description of the table, with the DB-API cursor.description interface." - cursor.execute("SELECT * FROM %s LIMIT 1" % DatabaseWrapper().quote_name(table_name)) - return cursor.description - -def get_relations(cursor, table_name): - raise NotImplementedError - -def get_indexes(cursor, table_name): - """ - Returns a dictionary of fieldname -> infodict for the given table, - where each infodict is in the format: - {'primary_key': boolean representing whether it's the primary key, - 'unique': boolean representing whether it's a unique index} - """ - cursor.execute("SHOW INDEX FROM %s" % DatabaseWrapper().quote_name(table_name)) - indexes = {} - for row in cursor.fetchall(): - indexes[row[4]] = {'primary_key': (row[2] == 'PRIMARY'), 'unique': not bool(row[1])} - return indexes - -OPERATOR_MAPPING = { - 'exact': '= %s', - 'iexact': 'LIKE %s', - 'contains': 'LIKE BINARY %s', - 'icontains': 'LIKE %s', - 'ne': '!= %s', - 'gt': '> %s', - 'gte': '>= %s', - 'lt': '< %s', - 'lte': '<= %s', - 'startswith': 'LIKE BINARY %s', - 'endswith': 'LIKE BINARY %s', - 'istartswith': 'LIKE %s', - 'iendswith': 'LIKE %s', -} - -# This dictionary maps Field objects to their associated MySQL column -# types, as strings. Column-type strings can contain format strings; they'll -# be interpolated against the values of Field.__dict__ before being output. -# If a column type is set to None, it won't be included in the output. -DATA_TYPES = { - 'AutoField': 'integer AUTO_INCREMENT', - 'BooleanField': 'bool', - 'CharField': 'varchar(%(maxlength)s)', - 'CommaSeparatedIntegerField': 'varchar(%(maxlength)s)', - 'DateField': 'date', - 'DateTimeField': 'datetime', - 'FileField': 'varchar(100)', - 'FilePathField': 'varchar(100)', - 'FloatField': 'numeric(%(max_digits)s, %(decimal_places)s)', - 'ImageField': 'varchar(100)', - 'IntegerField': 'integer', - 'IPAddressField': 'char(15)', - 'ManyToManyField': None, - 'NullBooleanField': 'bool', - 'OneToOneField': 'integer', - 'PhoneNumberField': 'varchar(20)', - 'PositiveIntegerField': 'integer UNSIGNED', - 'PositiveSmallIntegerField': 'smallint UNSIGNED', - 'SlugField': 'varchar(%(maxlength)s)', - 'SmallIntegerField': 'smallint', - 'TextField': 'longtext', - 'TimeField': 'time', - 'URLField': 'varchar(200)', - 'USStateField': 'varchar(2)', -} - -DATA_TYPES_REVERSE = { - FIELD_TYPE.BLOB: 'TextField', - FIELD_TYPE.CHAR: 'CharField', - FIELD_TYPE.DECIMAL: 'FloatField', - FIELD_TYPE.DATE: 'DateField', - FIELD_TYPE.DATETIME: 'DateTimeField', - FIELD_TYPE.DOUBLE: 'FloatField', - FIELD_TYPE.FLOAT: 'FloatField', - FIELD_TYPE.INT24: 'IntegerField', - FIELD_TYPE.LONG: 'IntegerField', - FIELD_TYPE.LONGLONG: 'IntegerField', - FIELD_TYPE.SHORT: 'IntegerField', - FIELD_TYPE.STRING: 'TextField', - FIELD_TYPE.TIMESTAMP: 'DateTimeField', - FIELD_TYPE.TINY_BLOB: 'TextField', - FIELD_TYPE.MEDIUM_BLOB: 'TextField', - FIELD_TYPE.LONG_BLOB: 'TextField', - FIELD_TYPE.VAR_STRING: 'CharField', -} diff --git a/django/core/db/backends/postgresql.py b/django/core/db/backends/postgresql.py deleted file mode 100644 index 0bc799c247..0000000000 --- a/django/core/db/backends/postgresql.py +++ /dev/null @@ -1,238 +0,0 @@ -""" -PostgreSQL database backend for Django. - -Requires psycopg 1: http://initd.org/projects/psycopg1 -""" - -from django.core.db import base, typecasts -import psycopg as Database - -DatabaseError = Database.DatabaseError - -try: - # Only exists in python 2.4+ - from threading import local -except ImportError: - # Import copy of _thread_local.py from python 2.4 - from django.utils._threading_local import local - -class DatabaseWrapper(local): - def __init__(self): - self.connection = None - self.queries = [] - - def cursor(self): - from django.conf.settings import DATABASE_USER, DATABASE_NAME, DATABASE_HOST, DATABASE_PORT, DATABASE_PASSWORD, DEBUG, TIME_ZONE - if self.connection is None: - if DATABASE_NAME == '': - from django.core.exceptions import ImproperlyConfigured - raise ImproperlyConfigured, "You need to specify DATABASE_NAME in your Django settings file." - conn_string = "dbname=%s" % DATABASE_NAME - if DATABASE_USER: - conn_string = "user=%s %s" % (DATABASE_USER, conn_string) - if DATABASE_PASSWORD: - conn_string += " password='%s'" % DATABASE_PASSWORD - if DATABASE_HOST: - conn_string += " host=%s" % DATABASE_HOST - if DATABASE_PORT: - conn_string += " port=%s" % DATABASE_PORT - self.connection = Database.connect(conn_string) - self.connection.set_isolation_level(1) # make transactions transparent to all cursors - cursor = self.connection.cursor() - cursor.execute("SET TIME ZONE %s", [TIME_ZONE]) - if DEBUG: - return base.CursorDebugWrapper(cursor, self) - return cursor - - def commit(self): - return self.connection.commit() - - def rollback(self): - if self.connection: - return self.connection.rollback() - - def close(self): - if self.connection is not None: - self.connection.close() - self.connection = None - - def quote_name(self, name): - if name.startswith('"') and name.endswith('"'): - return name # Quoting once is enough. - return '"%s"' % name - -def dictfetchone(cursor): - "Returns a row from the cursor as a dict" - return cursor.dictfetchone() - -def dictfetchmany(cursor, number): - "Returns a certain number of rows from a cursor as a dict" - return cursor.dictfetchmany(number) - -def dictfetchall(cursor): - "Returns all rows from a cursor as a dict" - return cursor.dictfetchall() - -def get_last_insert_id(cursor, table_name, pk_name): - cursor.execute("SELECT CURRVAL('\"%s_%s_seq\"')" % (table_name, pk_name)) - return cursor.fetchone()[0] - -def get_date_extract_sql(lookup_type, table_name): - # lookup_type is 'year', 'month', 'day' - # http://www.postgresql.org/docs/8.0/static/functions-datetime.html#FUNCTIONS-DATETIME-EXTRACT - return "EXTRACT('%s' FROM %s)" % (lookup_type, table_name) - -def get_date_trunc_sql(lookup_type, field_name): - # lookup_type is 'year', 'month', 'day' - # http://www.postgresql.org/docs/8.0/static/functions-datetime.html#FUNCTIONS-DATETIME-TRUNC - return "DATE_TRUNC('%s', %s)" % (lookup_type, field_name) - -def get_limit_offset_sql(limit, offset=None): - sql = "LIMIT %s" % limit - if offset and offset != 0: - sql += " OFFSET %s" % offset - return sql - -def get_random_function_sql(): - return "RANDOM()" - -def get_table_list(cursor): - "Returns a list of table names in the current database." - cursor.execute(""" - SELECT c.relname - FROM pg_catalog.pg_class c - LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace - WHERE c.relkind IN ('r', 'v', '') - AND n.nspname NOT IN ('pg_catalog', 'pg_toast') - AND pg_catalog.pg_table_is_visible(c.oid)""") - return [row[0] for row in cursor.fetchall()] - -def get_table_description(cursor, table_name): - "Returns a description of the table, with the DB-API cursor.description interface." - cursor.execute("SELECT * FROM %s LIMIT 1" % DatabaseWrapper().quote_name(table_name)) - return cursor.description - -def get_relations(cursor, table_name): - """ - Returns a dictionary of {field_index: (field_index_other_table, other_table)} - representing all relationships to the given table. Indexes are 0-based. - """ - cursor.execute(""" - SELECT con.conkey, con.confkey, c2.relname - FROM pg_constraint con, pg_class c1, pg_class c2 - WHERE c1.oid = con.conrelid - AND c2.oid = con.confrelid - AND c1.relname = %s - AND con.contype = 'f'""", [table_name]) - relations = {} - for row in cursor.fetchall(): - try: - # row[0] and row[1] are like "{2}", so strip the curly braces. - relations[int(row[0][1:-1]) - 1] = (int(row[1][1:-1]) - 1, row[2]) - except ValueError: - continue - return relations - -def get_indexes(cursor, table_name): - """ - Returns a dictionary of fieldname -> infodict for the given table, - where each infodict is in the format: - {'primary_key': boolean representing whether it's the primary key, - 'unique': boolean representing whether it's a unique index} - """ - # Get the table description because we only have the column indexes, and we - # need the column names. - desc = get_table_description(cursor, table_name) - # This query retrieves each index on the given table. - cursor.execute(""" - SELECT idx.indkey, idx.indisunique, idx.indisprimary - FROM pg_catalog.pg_class c, pg_catalog.pg_class c2, - pg_catalog.pg_index idx - WHERE c.oid = idx.indrelid - AND idx.indexrelid = c2.oid - AND c.relname = %s""", [table_name]) - indexes = {} - for row in cursor.fetchall(): - # row[0] (idx.indkey) is stored in the DB as an array. It comes out as - # a string of space-separated integers. This designates the field - # indexes (1-based) of the fields that have indexes on the table. - # Here, we skip any indexes across multiple fields. - if ' ' in row[0]: - continue - col_name = desc[int(row[0])-1][0] - indexes[col_name] = {'primary_key': row[2], 'unique': row[1]} - return indexes - -# Register these custom typecasts, because Django expects dates/times to be -# in Python's native (standard-library) datetime/time format, whereas psycopg -# use mx.DateTime by default. -try: - Database.register_type(Database.new_type((1082,), "DATE", typecasts.typecast_date)) -except AttributeError: - raise Exception, "You appear to be using psycopg version 2, which isn't supported yet, because it's still in beta. Use psycopg version 1 instead: http://initd.org/projects/psycopg1" -Database.register_type(Database.new_type((1083,1266), "TIME", typecasts.typecast_time)) -Database.register_type(Database.new_type((1114,1184), "TIMESTAMP", typecasts.typecast_timestamp)) -Database.register_type(Database.new_type((16,), "BOOLEAN", typecasts.typecast_boolean)) - -OPERATOR_MAPPING = { - 'exact': '= %s', - 'iexact': 'ILIKE %s', - 'contains': 'LIKE %s', - 'icontains': 'ILIKE %s', - 'ne': '!= %s', - 'gt': '> %s', - 'gte': '>= %s', - 'lt': '< %s', - 'lte': '<= %s', - 'startswith': 'LIKE %s', - 'endswith': 'LIKE %s', - 'istartswith': 'ILIKE %s', - 'iendswith': 'ILIKE %s', -} - -# This dictionary maps Field objects to their associated PostgreSQL column -# types, as strings. Column-type strings can contain format strings; they'll -# be interpolated against the values of Field.__dict__ before being output. -# If a column type is set to None, it won't be included in the output. -DATA_TYPES = { - 'AutoField': 'serial', - 'BooleanField': 'boolean', - 'CharField': 'varchar(%(maxlength)s)', - 'CommaSeparatedIntegerField': 'varchar(%(maxlength)s)', - 'DateField': 'date', - 'DateTimeField': 'timestamp with time zone', - 'FileField': 'varchar(100)', - 'FilePathField': 'varchar(100)', - 'FloatField': 'numeric(%(max_digits)s, %(decimal_places)s)', - 'ImageField': 'varchar(100)', - 'IntegerField': 'integer', - 'IPAddressField': 'inet', - 'ManyToManyField': None, - 'NullBooleanField': 'boolean', - 'OneToOneField': 'integer', - 'PhoneNumberField': 'varchar(20)', - 'PositiveIntegerField': 'integer CHECK ("%(column)s" >= 0)', - 'PositiveSmallIntegerField': 'smallint CHECK ("%(column)s" >= 0)', - 'SlugField': 'varchar(%(maxlength)s)', - 'SmallIntegerField': 'smallint', - 'TextField': 'text', - 'TimeField': 'time', - 'URLField': 'varchar(200)', - 'USStateField': 'varchar(2)', -} - -# Maps type codes to Django Field types. -DATA_TYPES_REVERSE = { - 16: 'BooleanField', - 21: 'SmallIntegerField', - 23: 'IntegerField', - 25: 'TextField', - 869: 'IPAddressField', - 1043: 'CharField', - 1082: 'DateField', - 1083: 'TimeField', - 1114: 'DateTimeField', - 1184: 'DateTimeField', - 1266: 'TimeField', - 1700: 'FloatField', -} diff --git a/django/core/db/backends/sqlite3.py b/django/core/db/backends/sqlite3.py deleted file mode 100644 index 1b2ae8cd84..0000000000 --- a/django/core/db/backends/sqlite3.py +++ /dev/null @@ -1,230 +0,0 @@ -""" -SQLite3 backend for django. Requires pysqlite2 (http://pysqlite.org/). -""" - -from django.core.db import base, typecasts -from django.core.db.dicthelpers import * -from pysqlite2 import dbapi2 as Database -DatabaseError = Database.DatabaseError - -# Register adaptors ########################################################### - -Database.register_converter("bool", lambda s: str(s) == '1') -Database.register_converter("time", typecasts.typecast_time) -Database.register_converter("date", typecasts.typecast_date) -Database.register_converter("datetime", typecasts.typecast_timestamp) - -# Database wrapper ############################################################ - -def utf8rowFactory(cursor, row): - def utf8(s): - if type(s) == unicode: - return s.encode("utf-8") - else: - return s - return [utf8(r) for r in row] - -try: - # Only exists in python 2.4+ - from threading import local -except ImportError: - # Import copy of _thread_local.py from python 2.4 - from django.utils._threading_local import local - -class DatabaseWrapper(local): - def __init__(self): - self.connection = None - self.queries = [] - - def cursor(self): - from django.conf.settings import DATABASE_NAME, DEBUG - if self.connection is None: - self.connection = Database.connect(DATABASE_NAME, detect_types=Database.PARSE_DECLTYPES) - # register extract and date_trun functions - self.connection.create_function("django_extract", 2, _sqlite_extract) - self.connection.create_function("django_date_trunc", 2, _sqlite_date_trunc) - cursor = self.connection.cursor(factory=SQLiteCursorWrapper) - cursor.row_factory = utf8rowFactory - if DEBUG: - return base.CursorDebugWrapper(cursor, self) - else: - return cursor - - def commit(self): - self.connection.commit() - - def rollback(self): - if self.connection: - self.connection.rollback() - - def close(self): - if self.connection is not None: - self.connection.close() - self.connection = None - - def quote_name(self, name): - if name.startswith('"') and name.endswith('"'): - return name # Quoting once is enough. - return '"%s"' % name - -class SQLiteCursorWrapper(Database.Cursor): - """ - Django uses "format" style placeholders, but pysqlite2 uses "qmark" style. - This fixes it -- but note that if you want to use a literal "%s" in a query, - you'll need to use "%%s" (which I belive is true of other wrappers as well). - """ - - def execute(self, query, params=[]): - query = self.convert_query(query, len(params)) - return Database.Cursor.execute(self, query, params) - - def executemany(self, query, params=[]): - query = self.convert_query(query, len(params[0])) - return Database.Cursor.executemany(self, query, params) - - def convert_query(self, query, num_params): - # XXX this seems too simple to be correct... is this right? - return query % tuple("?" * num_params) - -# Helper functions ############################################################ - -def get_last_insert_id(cursor, table_name, pk_name): - return cursor.lastrowid - -def get_date_extract_sql(lookup_type, table_name): - # lookup_type is 'year', 'month', 'day' - # sqlite doesn't support extract, so we fake it with the user-defined - # function _sqlite_extract that's registered in connect(), above. - return 'django_extract("%s", %s)' % (lookup_type.lower(), table_name) - -def _sqlite_extract(lookup_type, dt): - try: - dt = typecasts.typecast_timestamp(dt) - except (ValueError, TypeError): - return None - return str(getattr(dt, lookup_type)) - -def get_date_trunc_sql(lookup_type, field_name): - # lookup_type is 'year', 'month', 'day' - # sqlite doesn't support DATE_TRUNC, so we fake it as above. - return 'django_date_trunc("%s", %s)' % (lookup_type.lower(), field_name) - -def get_limit_offset_sql(limit, offset=None): - sql = "LIMIT %s" % limit - if offset and offset != 0: - sql += " OFFSET %s" % offset - return sql - -def get_random_function_sql(): - return "RANDOM()" - -def _sqlite_date_trunc(lookup_type, dt): - try: - dt = typecasts.typecast_timestamp(dt) - except (ValueError, TypeError): - return None - if lookup_type == 'year': - return "%i-01-01 00:00:00" % dt.year - elif lookup_type == 'month': - return "%i-%02i-01 00:00:00" % (dt.year, dt.month) - elif lookup_type == 'day': - return "%i-%02i-%02i 00:00:00" % (dt.year, dt.month, dt.day) - -def get_table_list(cursor): - cursor.execute("SELECT name FROM sqlite_master WHERE type='table' ORDER BY name") - return [row[0] for row in cursor.fetchall()] - -def get_table_description(cursor, table_name): - cursor.execute("PRAGMA table_info(%s)" % DatabaseWrapper().quote_name(table_name)) - return [(row[1], row[2], None, None) for row in cursor.fetchall()] - -def get_relations(cursor, table_name): - raise NotImplementedError - -def get_indexes(cursor, table_name): - raise NotImplementedError - -# Operators and fields ######################################################## - -# SQLite requires LIKE statements to include an ESCAPE clause if the value -# being escaped has a percent or underscore in it. -# See http://www.sqlite.org/lang_expr.html for an explanation. -OPERATOR_MAPPING = { - 'exact': '= %s', - 'iexact': "LIKE %s ESCAPE '\\'", - 'contains': "LIKE %s ESCAPE '\\'", - 'icontains': "LIKE %s ESCAPE '\\'", - 'ne': '!= %s', - 'gt': '> %s', - 'gte': '>= %s', - 'lt': '< %s', - 'lte': '<= %s', - 'startswith': "LIKE %s ESCAPE '\\'", - 'endswith': "LIKE %s ESCAPE '\\'", - 'istartswith': "LIKE %s ESCAPE '\\'", - 'iendswith': "LIKE %s ESCAPE '\\'", -} - -# SQLite doesn't actually support most of these types, but it "does the right -# thing" given more verbose field definitions, so leave them as is so that -# schema inspection is more useful. -DATA_TYPES = { - 'AutoField': 'integer', - 'BooleanField': 'bool', - 'CharField': 'varchar(%(maxlength)s)', - 'CommaSeparatedIntegerField': 'varchar(%(maxlength)s)', - 'DateField': 'date', - 'DateTimeField': 'datetime', - 'FileField': 'varchar(100)', - 'FilePathField': 'varchar(100)', - 'FloatField': 'numeric(%(max_digits)s, %(decimal_places)s)', - 'ImageField': 'varchar(100)', - 'IntegerField': 'integer', - 'IPAddressField': 'char(15)', - 'ManyToManyField': None, - 'NullBooleanField': 'bool', - 'OneToOneField': 'integer', - 'PhoneNumberField': 'varchar(20)', - 'PositiveIntegerField': 'integer unsigned', - 'PositiveSmallIntegerField': 'smallint unsigned', - 'SlugField': 'varchar(%(maxlength)s)', - 'SmallIntegerField': 'smallint', - 'TextField': 'text', - 'TimeField': 'time', - 'URLField': 'varchar(200)', - 'USStateField': 'varchar(2)', -} - -# Maps SQL types to Django Field types. Some of the SQL types have multiple -# entries here because SQLite allows for anything and doesn't normalize the -# field type; it uses whatever was given. -BASE_DATA_TYPES_REVERSE = { - 'bool': 'BooleanField', - 'boolean': 'BooleanField', - 'smallint': 'SmallIntegerField', - 'smallinteger': 'SmallIntegerField', - 'int': 'IntegerField', - 'integer': 'IntegerField', - 'text': 'TextField', - 'char': 'CharField', - 'date': 'DateField', - 'datetime': 'DateTimeField', - 'time': 'TimeField', -} - -# This light wrapper "fakes" a dictionary interface, because some SQLite data -# types include variables in them -- e.g. "varchar(30)" -- and can't be matched -# as a simple dictionary lookup. -class FlexibleFieldLookupDict: - def __getitem__(self, key): - key = key.lower() - try: - return BASE_DATA_TYPES_REVERSE[key] - except KeyError: - import re - m = re.search(r'^\s*(?:var)?char\s*\(\s*(\d+)\s*\)\s*$', key) - if m: - return ('CharField', {'maxlength': int(m.group(1))}) - raise KeyError - -DATA_TYPES_REVERSE = FlexibleFieldLookupDict() diff --git a/django/core/db/base.py b/django/core/db/base.py deleted file mode 100644 index 62cca9d0be..0000000000 --- a/django/core/db/base.py +++ /dev/null @@ -1,32 +0,0 @@ -from time import time - -class CursorDebugWrapper: - def __init__(self, cursor, db): - self.cursor = cursor - self.db = db - - def execute(self, sql, params=[]): - start = time() - result = self.cursor.execute(sql, params) - stop = time() - self.db.queries.append({ - 'sql': sql % tuple(params), - 'time': "%.3f" % (stop - start), - }) - return result - - def executemany(self, sql, param_list): - start = time() - result = self.cursor.executemany(sql, param_list) - stop = time() - self.db.queries.append({ - 'sql': 'MANY: ' + sql + ' ' + str(tuple(param_list)), - 'time': "%.3f" % (stop - start), - }) - return result - - def __getattr__(self, attr): - if self.__dict__.has_key(attr): - return self.__dict__[attr] - else: - return getattr(self.cursor, attr) diff --git a/django/core/db/dicthelpers.py b/django/core/db/dicthelpers.py deleted file mode 100644 index 5aedc51aed..0000000000 --- a/django/core/db/dicthelpers.py +++ /dev/null @@ -1,24 +0,0 @@ -""" -Helper functions for dictfetch* for databases that don't natively support them. -""" - -def _dict_helper(desc, row): - "Returns a dictionary for the given cursor.description and result row." - return dict([(desc[col[0]][0], col[1]) for col in enumerate(row)]) - -def dictfetchone(cursor): - "Returns a row from the cursor as a dict" - row = cursor.fetchone() - if not row: - return None - return _dict_helper(cursor.description, row) - -def dictfetchmany(cursor, number): - "Returns a certain number of rows from a cursor as a dict" - desc = cursor.description - return [_dict_helper(desc, row) for row in cursor.fetchmany(number)] - -def dictfetchall(cursor): - "Returns all rows from a cursor as a dict" - desc = cursor.description - return [_dict_helper(desc, row) for row in cursor.fetchall()] diff --git a/django/core/db/typecasts.py b/django/core/db/typecasts.py deleted file mode 100644 index 9be9062626..0000000000 --- a/django/core/db/typecasts.py +++ /dev/null @@ -1,55 +0,0 @@ -import datetime - -############################################### -# Converters from database (string) to Python # -############################################### - -def typecast_date(s): - return s and datetime.date(*map(int, s.split('-'))) or None # returns None if s is null - -def typecast_time(s): # does NOT store time zone information - if not s: return None - hour, minutes, seconds = s.split(':') - if '.' in seconds: # check whether seconds have a fractional part - seconds, microseconds = seconds.split('.') - else: - microseconds = '0' - return datetime.time(int(hour), int(minutes), int(seconds), int(float('.'+microseconds) * 1000000)) - -def typecast_timestamp(s): # does NOT store time zone information - # "2005-07-29 15:48:00.590358-05" - # "2005-07-29 09:56:00-05" - if not s: return None - if not ' ' in s: return typecast_date(s) - d, t = s.split() - # Extract timezone information, if it exists. Currently we just throw - # it away, but in the future we may make use of it. - if '-' in t: - t, tz = t.split('-', 1) - tz = '-' + tz - elif '+' in t: - t, tz = t.split('+', 1) - tz = '+' + tz - else: - tz = '' - dates = d.split('-') - times = t.split(':') - seconds = times[2] - if '.' in seconds: # check whether seconds have a fractional part - seconds, microseconds = seconds.split('.') - else: - microseconds = '0' - return datetime.datetime(int(dates[0]), int(dates[1]), int(dates[2]), - int(times[0]), int(times[1]), int(seconds), int(float('.'+microseconds) * 1000000)) - -def typecast_boolean(s): - if s is None: return None - if not s: return False - return str(s)[0].lower() == 't' - -############################################### -# Converters from Python to database (string) # -############################################### - -def rev_typecast_boolean(obj, d): - return obj and '1' or '0' |
