diff --git a/django/core/db/backends/__init__.py b/django/db/backends/__init__.py similarity index 100% rename from django/core/db/backends/__init__.py rename to django/db/backends/__init__.py diff --git a/django/db/backends/ado_mssql/__init__.py b/django/db/backends/ado_mssql/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/django/db/backends/ado_mssql/introspection.py b/django/db/backends/ado_mssql/introspection.py new file mode 100644 index 0000000000..a193b59cc1 --- /dev/null +++ b/django/db/backends/ado_mssql/introspection.py @@ -0,0 +1,10 @@ +def get_table_list(cursor): + raise NotImplementedError + +def get_table_description(cursor, table_name): + raise NotImplementedError + +def get_relations(cursor, table_name): + raise NotImplementedError + +DATA_TYPES_REVERSE = {} diff --git a/django/core/db/backends/ado_mssql.py b/django/db/backends/ado_mssql/wrapper.py similarity index 96% rename from django/core/db/backends/ado_mssql.py rename to django/db/backends/ado_mssql/wrapper.py index bb5b628aca..44fb4ec1c9 100644 --- a/django/core/db/backends/ado_mssql.py +++ b/django/db/backends/ado_mssql/wrapper.py @@ -109,15 +109,6 @@ def get_limit_offset_sql(limit, offset=None): 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 - OPERATOR_MAPPING = { 'exact': '= %s', 'iexact': 'LIKE %s', @@ -161,4 +152,3 @@ DATA_TYPES = { 'USStateField': 'varchar(2)', } -DATA_TYPES_REVERSE = {} diff --git a/django/db/backends/mysql/__init__.py b/django/db/backends/mysql/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/django/db/backends/mysql/introspection.py b/django/db/backends/mysql/introspection.py new file mode 100644 index 0000000000..28f3ac13ef --- /dev/null +++ b/django/db/backends/mysql/introspection.py @@ -0,0 +1,34 @@ +from MySQLdb.constants import FIELD_TYPE + +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" % table_name) + return cursor.description + +def get_relations(cursor, table_name): + raise NotImplementedError + +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/mysql.py b/django/db/backends/mysql/wrapper.py similarity index 83% rename from django/core/db/backends/mysql.py rename to django/db/backends/mysql/wrapper.py index 0e31af13fc..0f1f29bd82 100644 --- a/django/core/db/backends/mysql.py +++ b/django/db/backends/mysql/wrapper.py @@ -119,19 +119,6 @@ def get_limit_offset_sql(limit, offset=None): 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" % table_name) - return cursor.description - -def get_relations(cursor, table_name): - raise NotImplementedError - OPERATOR_MAPPING = { 'exact': '= %s', 'iexact': 'LIKE %s', @@ -178,23 +165,3 @@ DATA_TYPES = { '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/db/backends/postgresql/__init__.py b/django/db/backends/postgresql/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/django/db/backends/postgresql/introspection.py b/django/db/backends/postgresql/introspection.py new file mode 100644 index 0000000000..778a8e84cb --- /dev/null +++ b/django/db/backends/postgresql/introspection.py @@ -0,0 +1,52 @@ +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" % 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 + +# 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/postgresql.py b/django/db/backends/postgresql/wrapper.py similarity index 75% rename from django/core/db/backends/postgresql.py rename to django/db/backends/postgresql/wrapper.py index a4ce5b39ee..730d7d4649 100644 --- a/django/core/db/backends/postgresql.py +++ b/django/db/backends/postgresql/wrapper.py @@ -89,43 +89,6 @@ def get_limit_offset_sql(limit, offset=None): 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" % 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 - # 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. @@ -183,19 +146,3 @@ DATA_TYPES = { '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/db/backends/sqlite3/__init__.py b/django/db/backends/sqlite3/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/django/db/backends/sqlite3/introspection.py b/django/db/backends/sqlite3/introspection.py new file mode 100644 index 0000000000..499a4cba84 --- /dev/null +++ b/django/db/backends/sqlite3/introspection.py @@ -0,0 +1,44 @@ +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)" % table_name) + return [(row[1], row[2], None, None) for row in cursor.fetchall()] + +def get_relations(cursor, table_name): + raise NotImplementedError + +# 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': m.group(1)}) + raise KeyError + +DATA_TYPES_REVERSE = FlexibleFieldLookupDict() diff --git a/django/core/db/backends/sqlite3.py b/django/db/backends/sqlite3/wrapper.py similarity index 80% rename from django/core/db/backends/sqlite3.py rename to django/db/backends/sqlite3/wrapper.py index 1a4a80d632..8525ccce54 100644 --- a/django/core/db/backends/sqlite3.py +++ b/django/db/backends/sqlite3/wrapper.py @@ -123,17 +123,6 @@ def _sqlite_date_trunc(lookup_type, dt): 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)" % table_name) - return [(row[1], row[2], None, None) for row in cursor.fetchall()] - -def get_relations(cursor, table_name): - raise NotImplementedError - # Operators and fields ######################################################## # SQLite requires LIKE statements to include an ESCAPE clause if the value @@ -184,37 +173,3 @@ DATA_TYPES = { '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': m.group(1)}) - raise KeyError - -DATA_TYPES_REVERSE = FlexibleFieldLookupDict()