web/lib/django/db/backends/oracle/introspection.py
author ymh <ymh.work@gmail.com>
Wed, 02 Jun 2010 18:57:35 +0200
changeset 38 77b6da96e6f1
permissions -rw-r--r--
update django
Ignore whitespace changes - Everywhere: Within whitespace: At end of lines:
38
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
     1
from django.db.backends import BaseDatabaseIntrospection
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
     2
import cx_Oracle
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
     3
import re
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
     4
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
     5
foreign_key_re = re.compile(r"\sCONSTRAINT `[^`]*` FOREIGN KEY \(`([^`]*)`\) REFERENCES `([^`]*)` \(`([^`]*)`\)")
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
     6
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
     7
class DatabaseIntrospection(BaseDatabaseIntrospection):
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
     8
    # Maps type objects to Django Field types.
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
     9
    data_types_reverse = {
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    10
        cx_Oracle.CLOB: 'TextField',
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    11
        cx_Oracle.DATETIME: 'DateField',
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    12
        cx_Oracle.FIXED_CHAR: 'CharField',
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    13
        cx_Oracle.NCLOB: 'TextField',
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    14
        cx_Oracle.NUMBER: 'DecimalField',
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    15
        cx_Oracle.STRING: 'CharField',
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    16
        cx_Oracle.TIMESTAMP: 'DateTimeField',
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    17
    }
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    18
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    19
    try:
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    20
        data_types_reverse[cx_Oracle.NATIVE_FLOAT] = 'FloatField'
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    21
    except AttributeError:
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    22
        pass
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    23
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    24
    try:
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    25
        data_types_reverse[cx_Oracle.UNICODE] = 'CharField'
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    26
    except AttributeError:
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    27
        pass
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    28
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    29
    def get_field_type(self, data_type, description):
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    30
        # If it's a NUMBER with scale == 0, consider it an IntegerField
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    31
        if data_type == cx_Oracle.NUMBER and description[5] == 0:
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    32
            if description[4] > 11:
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    33
                return 'BigIntegerField'
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    34
            else:
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    35
                return 'IntegerField'
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    36
        else:
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    37
            return super(DatabaseIntrospection, self).get_field_type(
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    38
                data_type, description)
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    39
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    40
    def get_table_list(self, cursor):
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    41
        "Returns a list of table names in the current database."
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    42
        cursor.execute("SELECT TABLE_NAME FROM USER_TABLES")
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    43
        return [row[0].lower() for row in cursor.fetchall()]
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    44
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    45
    def get_table_description(self, cursor, table_name):
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    46
        "Returns a description of the table, with the DB-API cursor.description interface."
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    47
        cursor.execute("SELECT * FROM %s WHERE ROWNUM < 2" % self.connection.ops.quote_name(table_name))
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    48
        description = []
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    49
        for desc in cursor.description:
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    50
            description.append((desc[0].lower(),) + desc[1:])
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    51
        return description
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    52
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    53
    def table_name_converter(self, name):
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    54
        "Table name comparison is case insensitive under Oracle"
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    55
        return name.lower()
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    56
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    57
    def _name_to_index(self, cursor, table_name):
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    58
        """
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    59
        Returns a dictionary of {field_name: field_index} for the given table.
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    60
        Indexes are 0-based.
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    61
        """
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    62
        return dict([(d[0], i) for i, d in enumerate(self.get_table_description(cursor, table_name))])
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    63
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    64
    def get_relations(self, cursor, table_name):
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    65
        """
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    66
        Returns a dictionary of {field_index: (field_index_other_table, other_table)}
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    67
        representing all relationships to the given table. Indexes are 0-based.
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    68
        """
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    69
        cursor.execute("""
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    70
    SELECT ta.column_id - 1, tb.table_name, tb.column_id - 1
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    71
    FROM   user_constraints, USER_CONS_COLUMNS ca, USER_CONS_COLUMNS cb,
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    72
           user_tab_cols ta, user_tab_cols tb
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    73
    WHERE  user_constraints.table_name = %s AND
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    74
           ta.table_name = %s AND
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    75
           ta.column_name = ca.column_name AND
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    76
           ca.table_name = %s AND
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    77
           user_constraints.constraint_name = ca.constraint_name AND
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    78
           user_constraints.r_constraint_name = cb.constraint_name AND
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    79
           cb.table_name = tb.table_name AND
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    80
           cb.column_name = tb.column_name AND
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    81
           ca.position = cb.position""", [table_name, table_name, table_name])
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    82
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    83
        relations = {}
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    84
        for row in cursor.fetchall():
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    85
            relations[row[0]] = (row[2], row[1])
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    86
        return relations
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    87
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    88
    def get_indexes(self, cursor, table_name):
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    89
        """
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    90
        Returns a dictionary of fieldname -> infodict for the given table,
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    91
        where each infodict is in the format:
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    92
            {'primary_key': boolean representing whether it's the primary key,
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    93
             'unique': boolean representing whether it's a unique index}
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    94
        """
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    95
        # This query retrieves each index on the given table, including the
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    96
        # first associated field name
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    97
        # "We were in the nick of time; you were in great peril!"
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    98
        sql = """\
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    99
SELECT LOWER(all_tab_cols.column_name) AS column_name,
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   100
       CASE user_constraints.constraint_type
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   101
           WHEN 'P' THEN 1 ELSE 0
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   102
       END AS is_primary_key,
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   103
       CASE user_indexes.uniqueness
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   104
           WHEN 'UNIQUE' THEN 1 ELSE 0
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   105
       END AS is_unique
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   106
FROM   all_tab_cols, user_cons_columns, user_constraints, user_ind_columns, user_indexes
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   107
WHERE  all_tab_cols.column_name = user_cons_columns.column_name (+)
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   108
  AND  all_tab_cols.table_name = user_cons_columns.table_name (+)
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   109
  AND  user_cons_columns.constraint_name = user_constraints.constraint_name (+)
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   110
  AND  user_constraints.constraint_type (+) = 'P'
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   111
  AND  user_ind_columns.column_name (+) = all_tab_cols.column_name
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   112
  AND  user_ind_columns.table_name (+) = all_tab_cols.table_name
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   113
  AND  user_indexes.uniqueness (+) = 'UNIQUE'
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   114
  AND  user_indexes.index_name (+) = user_ind_columns.index_name
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   115
  AND  all_tab_cols.table_name = UPPER(%s)
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   116
"""
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   117
        cursor.execute(sql, [table_name])
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   118
        indexes = {}
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   119
        for row in cursor.fetchall():
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   120
            indexes[row[0]] = {'primary_key': row[1], 'unique': row[2]}
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   121
        return indexes