/django/db/backends/oracle/introspection.py

https://github.com/insane/django · Python · 136 lines · 110 code · 16 blank · 10 comment · 16 complexity · a1acbd0345f56c15659d60412a9a228e MD5 · raw file

  1. import re
  2. import cx_Oracle
  3. from django.db.backends import BaseDatabaseIntrospection, FieldInfo
  4. from django.utils.encoding import force_text
  5. foreign_key_re = re.compile(r"\sCONSTRAINT `[^`]*` FOREIGN KEY \(`([^`]*)`\) REFERENCES `([^`]*)` \(`([^`]*)`\)")
  6. class DatabaseIntrospection(BaseDatabaseIntrospection):
  7. # Maps type objects to Django Field types.
  8. data_types_reverse = {
  9. cx_Oracle.BLOB: 'BinaryField',
  10. cx_Oracle.CLOB: 'TextField',
  11. cx_Oracle.DATETIME: 'DateField',
  12. cx_Oracle.FIXED_CHAR: 'CharField',
  13. cx_Oracle.NCLOB: 'TextField',
  14. cx_Oracle.NUMBER: 'DecimalField',
  15. cx_Oracle.STRING: 'CharField',
  16. cx_Oracle.TIMESTAMP: 'DateTimeField',
  17. }
  18. try:
  19. data_types_reverse[cx_Oracle.NATIVE_FLOAT] = 'FloatField'
  20. except AttributeError:
  21. pass
  22. try:
  23. data_types_reverse[cx_Oracle.UNICODE] = 'CharField'
  24. except AttributeError:
  25. pass
  26. def get_field_type(self, data_type, description):
  27. # If it's a NUMBER with scale == 0, consider it an IntegerField
  28. if data_type == cx_Oracle.NUMBER and description[5] == 0:
  29. if description[4] > 11:
  30. return 'BigIntegerField'
  31. else:
  32. return 'IntegerField'
  33. else:
  34. return super(DatabaseIntrospection, self).get_field_type(
  35. data_type, description)
  36. def get_table_list(self, cursor):
  37. "Returns a list of table names in the current database."
  38. cursor.execute("SELECT TABLE_NAME FROM USER_TABLES")
  39. return [row[0].lower() for row in cursor.fetchall()]
  40. def get_table_description(self, cursor, table_name):
  41. "Returns a description of the table, with the DB-API cursor.description interface."
  42. cursor.execute("SELECT * FROM %s WHERE ROWNUM < 2" % self.connection.ops.quote_name(table_name))
  43. description = []
  44. for desc in cursor.description:
  45. name = force_text(desc[0]) # cx_Oracle always returns a 'str' on both Python 2 and 3
  46. name = name % {} # cx_Oracle, for some reason, doubles percent signs.
  47. description.append(FieldInfo(*(name.lower(),) + desc[1:]))
  48. return description
  49. def table_name_converter(self, name):
  50. "Table name comparison is case insensitive under Oracle"
  51. return name.lower()
  52. def _name_to_index(self, cursor, table_name):
  53. """
  54. Returns a dictionary of {field_name: field_index} for the given table.
  55. Indexes are 0-based.
  56. """
  57. return dict([(d[0], i) for i, d in enumerate(self.get_table_description(cursor, table_name))])
  58. def get_relations(self, cursor, table_name):
  59. """
  60. Returns a dictionary of {field_index: (field_index_other_table, other_table)}
  61. representing all relationships to the given table. Indexes are 0-based.
  62. """
  63. table_name = table_name.upper()
  64. cursor.execute("""
  65. SELECT ta.column_id - 1, tb.table_name, tb.column_id - 1
  66. FROM user_constraints, USER_CONS_COLUMNS ca, USER_CONS_COLUMNS cb,
  67. user_tab_cols ta, user_tab_cols tb
  68. WHERE user_constraints.table_name = %s AND
  69. ta.table_name = user_constraints.table_name AND
  70. ta.column_name = ca.column_name AND
  71. ca.table_name = ta.table_name AND
  72. user_constraints.constraint_name = ca.constraint_name AND
  73. user_constraints.r_constraint_name = cb.constraint_name AND
  74. cb.table_name = tb.table_name AND
  75. cb.column_name = tb.column_name AND
  76. ca.position = cb.position""", [table_name])
  77. relations = {}
  78. for row in cursor.fetchall():
  79. relations[row[0]] = (row[2], row[1].lower())
  80. return relations
  81. def get_key_columns(self, cursor, table_name):
  82. cursor.execute("""
  83. SELECT ccol.column_name, rcol.table_name AS referenced_table, rcol.column_name AS referenced_column
  84. FROM user_constraints c
  85. JOIN user_cons_columns ccol
  86. ON ccol.constraint_name = c.constraint_name
  87. JOIN user_cons_columns rcol
  88. ON rcol.constraint_name = c.r_constraint_name
  89. WHERE c.table_name = %s AND c.constraint_type = 'R'""", [table_name.upper()])
  90. return [tuple(cell.lower() for cell in row)
  91. for row in cursor.fetchall()]
  92. def get_indexes(self, cursor, table_name):
  93. sql = """
  94. SELECT LOWER(uic1.column_name) AS column_name,
  95. CASE user_constraints.constraint_type
  96. WHEN 'P' THEN 1 ELSE 0
  97. END AS is_primary_key,
  98. CASE user_indexes.uniqueness
  99. WHEN 'UNIQUE' THEN 1 ELSE 0
  100. END AS is_unique
  101. FROM user_constraints, user_indexes, user_ind_columns uic1
  102. WHERE user_constraints.constraint_type (+) = 'P'
  103. AND user_constraints.index_name (+) = uic1.index_name
  104. AND user_indexes.uniqueness (+) = 'UNIQUE'
  105. AND user_indexes.index_name (+) = uic1.index_name
  106. AND uic1.table_name = UPPER(%s)
  107. AND uic1.column_position = 1
  108. AND NOT EXISTS (
  109. SELECT 1
  110. FROM user_ind_columns uic2
  111. WHERE uic2.index_name = uic1.index_name
  112. AND uic2.column_position = 2
  113. )
  114. """
  115. cursor.execute(sql, [table_name])
  116. indexes = {}
  117. for row in cursor.fetchall():
  118. indexes[row[0]] = {'primary_key': bool(row[1]),
  119. 'unique': bool(row[2])}
  120. return indexes