PageRenderTime 111ms CodeModel.GetById 25ms RepoModel.GetById 1ms app.codeStats 0ms

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

https://github.com/andnils/django
Python | 281 lines | 257 code | 14 blank | 10 comment | 13 complexity | 4246961cfffed71a5943040b612e72a0 MD5 | raw file
Possible License(s): BSD-3-Clause
  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:
  29. precision, scale = description[4:6]
  30. if scale == 0:
  31. if precision > 11:
  32. return 'BigIntegerField'
  33. elif precision == 1:
  34. return 'BooleanField'
  35. else:
  36. return 'IntegerField'
  37. elif scale == -127:
  38. return 'FloatField'
  39. return super(DatabaseIntrospection, self).get_field_type(data_type, description)
  40. def get_table_list(self, cursor):
  41. "Returns a list of table names in the current database."
  42. cursor.execute("SELECT TABLE_NAME FROM USER_TABLES")
  43. return [row[0].lower() for row in cursor.fetchall()]
  44. def get_table_description(self, cursor, table_name):
  45. "Returns a description of the table, with the DB-API cursor.description interface."
  46. cursor.execute("SELECT * FROM %s WHERE ROWNUM < 2" % self.connection.ops.quote_name(table_name))
  47. description = []
  48. for desc in cursor.description:
  49. name = force_text(desc[0]) # cx_Oracle always returns a 'str' on both Python 2 and 3
  50. name = name % {} # cx_Oracle, for some reason, doubles percent signs.
  51. description.append(FieldInfo(*(name.lower(),) + desc[1:]))
  52. return description
  53. def table_name_converter(self, name):
  54. "Table name comparison is case insensitive under Oracle"
  55. return name.lower()
  56. def _name_to_index(self, cursor, table_name):
  57. """
  58. Returns a dictionary of {field_name: field_index} for the given table.
  59. Indexes are 0-based.
  60. """
  61. return dict((d[0], i) for i, d in enumerate(self.get_table_description(cursor, table_name)))
  62. def get_relations(self, cursor, table_name):
  63. """
  64. Returns a dictionary of {field_index: (field_index_other_table, other_table)}
  65. representing all relationships to the given table. Indexes are 0-based.
  66. """
  67. table_name = table_name.upper()
  68. cursor.execute("""
  69. SELECT ta.column_id - 1, tb.table_name, tb.column_id - 1
  70. FROM user_constraints, USER_CONS_COLUMNS ca, USER_CONS_COLUMNS cb,
  71. user_tab_cols ta, user_tab_cols tb
  72. WHERE user_constraints.table_name = %s AND
  73. ta.table_name = user_constraints.table_name AND
  74. ta.column_name = ca.column_name AND
  75. ca.table_name = ta.table_name AND
  76. user_constraints.constraint_name = ca.constraint_name AND
  77. user_constraints.r_constraint_name = cb.constraint_name AND
  78. cb.table_name = tb.table_name AND
  79. cb.column_name = tb.column_name AND
  80. ca.position = cb.position""", [table_name])
  81. relations = {}
  82. for row in cursor.fetchall():
  83. relations[row[0]] = (row[2], row[1].lower())
  84. return relations
  85. def get_key_columns(self, cursor, table_name):
  86. cursor.execute("""
  87. SELECT ccol.column_name, rcol.table_name AS referenced_table, rcol.column_name AS referenced_column
  88. FROM user_constraints c
  89. JOIN user_cons_columns ccol
  90. ON ccol.constraint_name = c.constraint_name
  91. JOIN user_cons_columns rcol
  92. ON rcol.constraint_name = c.r_constraint_name
  93. WHERE c.table_name = %s AND c.constraint_type = 'R'""", [table_name.upper()])
  94. return [tuple(cell.lower() for cell in row)
  95. for row in cursor.fetchall()]
  96. def get_indexes(self, cursor, table_name):
  97. sql = """
  98. SELECT LOWER(uic1.column_name) AS column_name,
  99. CASE user_constraints.constraint_type
  100. WHEN 'P' THEN 1 ELSE 0
  101. END AS is_primary_key,
  102. CASE user_indexes.uniqueness
  103. WHEN 'UNIQUE' THEN 1 ELSE 0
  104. END AS is_unique
  105. FROM user_constraints, user_indexes, user_ind_columns uic1
  106. WHERE user_constraints.constraint_type (+) = 'P'
  107. AND user_constraints.index_name (+) = uic1.index_name
  108. AND user_indexes.uniqueness (+) = 'UNIQUE'
  109. AND user_indexes.index_name (+) = uic1.index_name
  110. AND uic1.table_name = UPPER(%s)
  111. AND uic1.column_position = 1
  112. AND NOT EXISTS (
  113. SELECT 1
  114. FROM user_ind_columns uic2
  115. WHERE uic2.index_name = uic1.index_name
  116. AND uic2.column_position = 2
  117. )
  118. """
  119. cursor.execute(sql, [table_name])
  120. indexes = {}
  121. for row in cursor.fetchall():
  122. indexes[row[0]] = {'primary_key': bool(row[1]),
  123. 'unique': bool(row[2])}
  124. return indexes
  125. def get_constraints(self, cursor, table_name):
  126. """
  127. Retrieves any constraints or keys (unique, pk, fk, check, index) across one or more columns.
  128. """
  129. constraints = {}
  130. # Loop over the constraints, getting PKs and uniques
  131. cursor.execute("""
  132. SELECT
  133. user_constraints.constraint_name,
  134. LOWER(cols.column_name) AS column_name,
  135. CASE user_constraints.constraint_type
  136. WHEN 'P' THEN 1
  137. ELSE 0
  138. END AS is_primary_key,
  139. CASE user_indexes.uniqueness
  140. WHEN 'UNIQUE' THEN 1
  141. ELSE 0
  142. END AS is_unique,
  143. CASE user_constraints.constraint_type
  144. WHEN 'C' THEN 1
  145. ELSE 0
  146. END AS is_check_constraint
  147. FROM
  148. user_constraints
  149. INNER JOIN
  150. user_indexes ON user_indexes.index_name = user_constraints.index_name
  151. LEFT OUTER JOIN
  152. user_cons_columns cols ON user_constraints.constraint_name = cols.constraint_name
  153. WHERE
  154. (
  155. user_constraints.constraint_type = 'P' OR
  156. user_constraints.constraint_type = 'U'
  157. )
  158. AND user_constraints.table_name = UPPER(%s)
  159. ORDER BY cols.position
  160. """, [table_name])
  161. for constraint, column, pk, unique, check in cursor.fetchall():
  162. # If we're the first column, make the record
  163. if constraint not in constraints:
  164. constraints[constraint] = {
  165. "columns": [],
  166. "primary_key": pk,
  167. "unique": unique,
  168. "foreign_key": None,
  169. "check": check,
  170. "index": True, # All P and U come with index, see inner join above
  171. }
  172. # Record the details
  173. constraints[constraint]['columns'].append(column)
  174. # Check constraints
  175. cursor.execute("""
  176. SELECT
  177. cons.constraint_name,
  178. LOWER(cols.column_name) AS column_name
  179. FROM
  180. user_constraints cons
  181. LEFT OUTER JOIN
  182. user_cons_columns cols ON cons.constraint_name = cols.constraint_name
  183. WHERE
  184. cons.constraint_type = 'C' AND
  185. cons.table_name = UPPER(%s)
  186. ORDER BY cols.position
  187. """, [table_name])
  188. for constraint, column in cursor.fetchall():
  189. # If we're the first column, make the record
  190. if constraint not in constraints:
  191. constraints[constraint] = {
  192. "columns": [],
  193. "primary_key": False,
  194. "unique": False,
  195. "foreign_key": None,
  196. "check": True,
  197. "index": False,
  198. }
  199. # Record the details
  200. constraints[constraint]['columns'].append(column)
  201. # Foreign key constraints
  202. cursor.execute("""
  203. SELECT
  204. cons.constraint_name,
  205. LOWER(cols.column_name) AS column_name,
  206. LOWER(rcons.table_name),
  207. LOWER(rcols.column_name)
  208. FROM
  209. user_constraints cons
  210. INNER JOIN
  211. user_constraints rcons ON cons.r_constraint_name = rcons.constraint_name
  212. INNER JOIN
  213. user_cons_columns rcols ON rcols.constraint_name = rcons.constraint_name
  214. LEFT OUTER JOIN
  215. user_cons_columns cols ON cons.constraint_name = cols.constraint_name
  216. WHERE
  217. cons.constraint_type = 'R' AND
  218. cons.table_name = UPPER(%s)
  219. ORDER BY cols.position
  220. """, [table_name])
  221. for constraint, column, other_table, other_column in cursor.fetchall():
  222. # If we're the first column, make the record
  223. if constraint not in constraints:
  224. constraints[constraint] = {
  225. "columns": [],
  226. "primary_key": False,
  227. "unique": False,
  228. "foreign_key": (other_table, other_column),
  229. "check": False,
  230. "index": False,
  231. }
  232. # Record the details
  233. constraints[constraint]['columns'].append(column)
  234. # Now get indexes
  235. cursor.execute("""
  236. SELECT
  237. index_name,
  238. LOWER(column_name)
  239. FROM
  240. user_ind_columns cols
  241. WHERE
  242. table_name = UPPER(%s) AND
  243. NOT EXISTS (
  244. SELECT 1
  245. FROM user_constraints cons
  246. WHERE cols.index_name = cons.index_name
  247. )
  248. ORDER BY cols.column_position
  249. """, [table_name])
  250. for constraint, column in cursor.fetchall():
  251. # If we're the first column, make the record
  252. if constraint not in constraints:
  253. constraints[constraint] = {
  254. "columns": [],
  255. "primary_key": False,
  256. "unique": False,
  257. "foreign_key": None,
  258. "check": False,
  259. "index": True,
  260. }
  261. # Record the details
  262. constraints[constraint]['columns'].append(column)
  263. return constraints