Development of an internal social media platform with personalised dashboards for students
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

introspection.py 9.4KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215
  1. from django.db.backends.base.introspection import (
  2. BaseDatabaseIntrospection, FieldInfo, TableInfo,
  3. )
  4. from django.db.models.indexes import Index
  5. class DatabaseIntrospection(BaseDatabaseIntrospection):
  6. # Maps type codes to Django Field types.
  7. data_types_reverse = {
  8. 16: 'BooleanField',
  9. 17: 'BinaryField',
  10. 20: 'BigIntegerField',
  11. 21: 'SmallIntegerField',
  12. 23: 'IntegerField',
  13. 25: 'TextField',
  14. 700: 'FloatField',
  15. 701: 'FloatField',
  16. 869: 'GenericIPAddressField',
  17. 1042: 'CharField', # blank-padded
  18. 1043: 'CharField',
  19. 1082: 'DateField',
  20. 1083: 'TimeField',
  21. 1114: 'DateTimeField',
  22. 1184: 'DateTimeField',
  23. 1266: 'TimeField',
  24. 1700: 'DecimalField',
  25. 2950: 'UUIDField',
  26. }
  27. ignored_tables = []
  28. def get_field_type(self, data_type, description):
  29. field_type = super().get_field_type(data_type, description)
  30. if description.default and 'nextval' in description.default:
  31. if field_type == 'IntegerField':
  32. return 'AutoField'
  33. elif field_type == 'BigIntegerField':
  34. return 'BigAutoField'
  35. return field_type
  36. def get_table_list(self, cursor):
  37. """Return a list of table and view names in the current database."""
  38. cursor.execute("""
  39. SELECT c.relname, c.relkind
  40. FROM pg_catalog.pg_class c
  41. LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
  42. WHERE c.relkind IN ('r', 'v')
  43. AND n.nspname NOT IN ('pg_catalog', 'pg_toast')
  44. AND pg_catalog.pg_table_is_visible(c.oid)""")
  45. return [TableInfo(row[0], {'r': 't', 'v': 'v'}.get(row[1]))
  46. for row in cursor.fetchall()
  47. if row[0] not in self.ignored_tables]
  48. def get_table_description(self, cursor, table_name):
  49. """
  50. Return a description of the table with the DB-API cursor.description
  51. interface.
  52. """
  53. # As cursor.description does not return reliably the nullable property,
  54. # we have to query the information_schema (#7783)
  55. cursor.execute("""
  56. SELECT column_name, is_nullable, column_default
  57. FROM information_schema.columns
  58. WHERE table_name = %s""", [table_name])
  59. field_map = {line[0]: line[1:] for line in cursor.fetchall()}
  60. cursor.execute("SELECT * FROM %s LIMIT 1" % self.connection.ops.quote_name(table_name))
  61. return [
  62. FieldInfo(*line[0:6], field_map[line.name][0] == 'YES', field_map[line.name][1])
  63. for line in cursor.description
  64. ]
  65. def get_sequences(self, cursor, table_name, table_fields=()):
  66. sequences = []
  67. cursor.execute("""
  68. SELECT s.relname as sequence_name, col.attname
  69. FROM pg_class s
  70. JOIN pg_namespace sn ON sn.oid = s.relnamespace
  71. JOIN pg_depend d ON d.refobjid = s.oid AND d.refclassid='pg_class'::regclass
  72. JOIN pg_attrdef ad ON ad.oid = d.objid AND d.classid = 'pg_attrdef'::regclass
  73. JOIN pg_attribute col ON col.attrelid = ad.adrelid AND col.attnum = ad.adnum
  74. JOIN pg_class tbl ON tbl.oid = ad.adrelid
  75. JOIN pg_namespace n ON n.oid = tbl.relnamespace
  76. WHERE s.relkind = 'S'
  77. AND d.deptype in ('a', 'n')
  78. AND n.nspname = 'public'
  79. AND tbl.relname = %s
  80. """, [table_name])
  81. for row in cursor.fetchall():
  82. sequences.append({'name': row[0], 'table': table_name, 'column': row[1]})
  83. return sequences
  84. def get_relations(self, cursor, table_name):
  85. """
  86. Return a dictionary of {field_name: (field_name_other_table, other_table)}
  87. representing all relationships to the given table.
  88. """
  89. cursor.execute("""
  90. SELECT c2.relname, a1.attname, a2.attname
  91. FROM pg_constraint con
  92. LEFT JOIN pg_class c1 ON con.conrelid = c1.oid
  93. LEFT JOIN pg_class c2 ON con.confrelid = c2.oid
  94. LEFT JOIN pg_attribute a1 ON c1.oid = a1.attrelid AND a1.attnum = con.conkey[1]
  95. LEFT JOIN pg_attribute a2 ON c2.oid = a2.attrelid AND a2.attnum = con.confkey[1]
  96. WHERE c1.relname = %s
  97. AND con.contype = 'f'""", [table_name])
  98. relations = {}
  99. for row in cursor.fetchall():
  100. relations[row[1]] = (row[2], row[0])
  101. return relations
  102. def get_key_columns(self, cursor, table_name):
  103. key_columns = []
  104. cursor.execute("""
  105. SELECT kcu.column_name, ccu.table_name AS referenced_table, ccu.column_name AS referenced_column
  106. FROM information_schema.constraint_column_usage ccu
  107. LEFT JOIN information_schema.key_column_usage kcu
  108. ON ccu.constraint_catalog = kcu.constraint_catalog
  109. AND ccu.constraint_schema = kcu.constraint_schema
  110. AND ccu.constraint_name = kcu.constraint_name
  111. LEFT JOIN information_schema.table_constraints tc
  112. ON ccu.constraint_catalog = tc.constraint_catalog
  113. AND ccu.constraint_schema = tc.constraint_schema
  114. AND ccu.constraint_name = tc.constraint_name
  115. WHERE kcu.table_name = %s AND tc.constraint_type = 'FOREIGN KEY'""", [table_name])
  116. key_columns.extend(cursor.fetchall())
  117. return key_columns
  118. def get_constraints(self, cursor, table_name):
  119. """
  120. Retrieve any constraints or keys (unique, pk, fk, check, index) across
  121. one or more columns. Also retrieve the definition of expression-based
  122. indexes.
  123. """
  124. constraints = {}
  125. # Loop over the key table, collecting things as constraints. The column
  126. # array must return column names in the same order in which they were
  127. # created.
  128. cursor.execute("""
  129. SELECT
  130. c.conname,
  131. array(
  132. SELECT attname
  133. FROM unnest(c.conkey) WITH ORDINALITY cols(colid, arridx)
  134. JOIN pg_attribute AS ca ON cols.colid = ca.attnum
  135. WHERE ca.attrelid = c.conrelid
  136. ORDER BY cols.arridx
  137. ),
  138. c.contype,
  139. (SELECT fkc.relname || '.' || fka.attname
  140. FROM pg_attribute AS fka
  141. JOIN pg_class AS fkc ON fka.attrelid = fkc.oid
  142. WHERE fka.attrelid = c.confrelid AND fka.attnum = c.confkey[1]),
  143. cl.reloptions
  144. FROM pg_constraint AS c
  145. JOIN pg_class AS cl ON c.conrelid = cl.oid
  146. JOIN pg_namespace AS ns ON cl.relnamespace = ns.oid
  147. WHERE ns.nspname = %s AND cl.relname = %s
  148. """, ["public", table_name])
  149. for constraint, columns, kind, used_cols, options in cursor.fetchall():
  150. constraints[constraint] = {
  151. "columns": columns,
  152. "primary_key": kind == "p",
  153. "unique": kind in ["p", "u"],
  154. "foreign_key": tuple(used_cols.split(".", 1)) if kind == "f" else None,
  155. "check": kind == "c",
  156. "index": False,
  157. "definition": None,
  158. "options": options,
  159. }
  160. # Now get indexes
  161. cursor.execute("""
  162. SELECT
  163. indexname, array_agg(attname ORDER BY arridx), indisunique, indisprimary,
  164. array_agg(ordering ORDER BY arridx), amname, exprdef, s2.attoptions
  165. FROM (
  166. SELECT
  167. c2.relname as indexname, idx.*, attr.attname, am.amname,
  168. CASE
  169. WHEN idx.indexprs IS NOT NULL THEN
  170. pg_get_indexdef(idx.indexrelid)
  171. END AS exprdef,
  172. CASE am.amname
  173. WHEN 'btree' THEN
  174. CASE (option & 1)
  175. WHEN 1 THEN 'DESC' ELSE 'ASC'
  176. END
  177. END as ordering,
  178. c2.reloptions as attoptions
  179. FROM (
  180. SELECT *
  181. FROM pg_index i, unnest(i.indkey, i.indoption) WITH ORDINALITY koi(key, option, arridx)
  182. ) idx
  183. LEFT JOIN pg_class c ON idx.indrelid = c.oid
  184. LEFT JOIN pg_class c2 ON idx.indexrelid = c2.oid
  185. LEFT JOIN pg_am am ON c2.relam = am.oid
  186. LEFT JOIN pg_attribute attr ON attr.attrelid = c.oid AND attr.attnum = idx.key
  187. WHERE c.relname = %s
  188. ) s2
  189. GROUP BY indexname, indisunique, indisprimary, amname, exprdef, attoptions;
  190. """, [table_name])
  191. for index, columns, unique, primary, orders, type_, definition, options in cursor.fetchall():
  192. if index not in constraints:
  193. constraints[index] = {
  194. "columns": columns if columns != [None] else [],
  195. "orders": orders if orders != [None] else [],
  196. "primary_key": primary,
  197. "unique": unique,
  198. "foreign_key": None,
  199. "check": False,
  200. "index": True,
  201. "type": Index.suffix if type_ == 'btree' else type_,
  202. "definition": definition,
  203. "options": options,
  204. }
  205. return constraints