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 18KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434
  1. import re
  2. from collections import namedtuple
  3. import sqlparse
  4. from django.db.backends.base.introspection import (
  5. BaseDatabaseIntrospection, FieldInfo as BaseFieldInfo, TableInfo,
  6. )
  7. from django.db.models.indexes import Index
  8. FieldInfo = namedtuple('FieldInfo', BaseFieldInfo._fields + ('pk',))
  9. field_size_re = re.compile(r'^\s*(?:var)?char\s*\(\s*(\d+)\s*\)\s*$')
  10. def get_field_size(name):
  11. """ Extract the size number from a "varchar(11)" type name """
  12. m = field_size_re.search(name)
  13. return int(m.group(1)) if m else None
  14. # This light wrapper "fakes" a dictionary interface, because some SQLite data
  15. # types include variables in them -- e.g. "varchar(30)" -- and can't be matched
  16. # as a simple dictionary lookup.
  17. class FlexibleFieldLookupDict:
  18. # Maps SQL types to Django Field types. Some of the SQL types have multiple
  19. # entries here because SQLite allows for anything and doesn't normalize the
  20. # field type; it uses whatever was given.
  21. base_data_types_reverse = {
  22. 'bool': 'BooleanField',
  23. 'boolean': 'BooleanField',
  24. 'smallint': 'SmallIntegerField',
  25. 'smallint unsigned': 'PositiveSmallIntegerField',
  26. 'smallinteger': 'SmallIntegerField',
  27. 'int': 'IntegerField',
  28. 'integer': 'IntegerField',
  29. 'bigint': 'BigIntegerField',
  30. 'integer unsigned': 'PositiveIntegerField',
  31. 'decimal': 'DecimalField',
  32. 'real': 'FloatField',
  33. 'text': 'TextField',
  34. 'char': 'CharField',
  35. 'varchar': 'CharField',
  36. 'blob': 'BinaryField',
  37. 'date': 'DateField',
  38. 'datetime': 'DateTimeField',
  39. 'time': 'TimeField',
  40. }
  41. def __getitem__(self, key):
  42. key = key.lower().split('(', 1)[0].strip()
  43. return self.base_data_types_reverse[key]
  44. class DatabaseIntrospection(BaseDatabaseIntrospection):
  45. data_types_reverse = FlexibleFieldLookupDict()
  46. def get_field_type(self, data_type, description):
  47. field_type = super().get_field_type(data_type, description)
  48. if description.pk and field_type in {'BigIntegerField', 'IntegerField'}:
  49. # No support for BigAutoField as SQLite treats all integer primary
  50. # keys as signed 64-bit integers.
  51. return 'AutoField'
  52. return field_type
  53. def get_table_list(self, cursor):
  54. """Return a list of table and view names in the current database."""
  55. # Skip the sqlite_sequence system table used for autoincrement key
  56. # generation.
  57. cursor.execute("""
  58. SELECT name, type FROM sqlite_master
  59. WHERE type in ('table', 'view') AND NOT name='sqlite_sequence'
  60. ORDER BY name""")
  61. return [TableInfo(row[0], row[1][0]) for row in cursor.fetchall()]
  62. def get_table_description(self, cursor, table_name):
  63. """
  64. Return a description of the table with the DB-API cursor.description
  65. interface.
  66. """
  67. return [
  68. FieldInfo(
  69. info['name'],
  70. info['type'],
  71. None,
  72. info['size'],
  73. None,
  74. None,
  75. info['null_ok'],
  76. info['default'],
  77. info['pk'] == 1,
  78. ) for info in self._table_info(cursor, table_name)
  79. ]
  80. def get_sequences(self, cursor, table_name, table_fields=()):
  81. pk_col = self.get_primary_key_column(cursor, table_name)
  82. return [{'table': table_name, 'column': pk_col}]
  83. def get_relations(self, cursor, table_name):
  84. """
  85. Return a dictionary of {field_name: (field_name_other_table, other_table)}
  86. representing all relationships to the given table.
  87. """
  88. # Dictionary of relations to return
  89. relations = {}
  90. # Schema for this table
  91. cursor.execute(
  92. "SELECT sql, type FROM sqlite_master "
  93. "WHERE tbl_name = %s AND type IN ('table', 'view')",
  94. [table_name]
  95. )
  96. create_sql, table_type = cursor.fetchone()
  97. if table_type == 'view':
  98. # It might be a view, then no results will be returned
  99. return relations
  100. results = create_sql[create_sql.index('(') + 1:create_sql.rindex(')')]
  101. # Walk through and look for references to other tables. SQLite doesn't
  102. # really have enforced references, but since it echoes out the SQL used
  103. # to create the table we can look for REFERENCES statements used there.
  104. for field_desc in results.split(','):
  105. field_desc = field_desc.strip()
  106. if field_desc.startswith("UNIQUE"):
  107. continue
  108. m = re.search(r'references (\S*) ?\(["|]?(.*)["|]?\)', field_desc, re.I)
  109. if not m:
  110. continue
  111. table, column = [s.strip('"') for s in m.groups()]
  112. if field_desc.startswith("FOREIGN KEY"):
  113. # Find name of the target FK field
  114. m = re.match(r'FOREIGN KEY\s*\(([^\)]*)\).*', field_desc, re.I)
  115. field_name = m.groups()[0].strip('"')
  116. else:
  117. field_name = field_desc.split()[0].strip('"')
  118. cursor.execute("SELECT sql FROM sqlite_master WHERE tbl_name = %s", [table])
  119. result = cursor.fetchall()[0]
  120. other_table_results = result[0].strip()
  121. li, ri = other_table_results.index('('), other_table_results.rindex(')')
  122. other_table_results = other_table_results[li + 1:ri]
  123. for other_desc in other_table_results.split(','):
  124. other_desc = other_desc.strip()
  125. if other_desc.startswith('UNIQUE'):
  126. continue
  127. other_name = other_desc.split(' ', 1)[0].strip('"')
  128. if other_name == column:
  129. relations[field_name] = (other_name, table)
  130. break
  131. return relations
  132. def get_key_columns(self, cursor, table_name):
  133. """
  134. Return a list of (column_name, referenced_table_name, referenced_column_name)
  135. for all key columns in given table.
  136. """
  137. key_columns = []
  138. # Schema for this table
  139. cursor.execute("SELECT sql FROM sqlite_master WHERE tbl_name = %s AND type = %s", [table_name, "table"])
  140. results = cursor.fetchone()[0].strip()
  141. results = results[results.index('(') + 1:results.rindex(')')]
  142. # Walk through and look for references to other tables. SQLite doesn't
  143. # really have enforced references, but since it echoes out the SQL used
  144. # to create the table we can look for REFERENCES statements used there.
  145. for field_index, field_desc in enumerate(results.split(',')):
  146. field_desc = field_desc.strip()
  147. if field_desc.startswith("UNIQUE"):
  148. continue
  149. m = re.search(r'"(.*)".*references (.*) \(["|](.*)["|]\)', field_desc, re.I)
  150. if not m:
  151. continue
  152. # This will append (column_name, referenced_table_name, referenced_column_name) to key_columns
  153. key_columns.append(tuple(s.strip('"') for s in m.groups()))
  154. return key_columns
  155. def get_primary_key_column(self, cursor, table_name):
  156. """Return the column name of the primary key for the given table."""
  157. # Don't use PRAGMA because that causes issues with some transactions
  158. cursor.execute(
  159. "SELECT sql, type FROM sqlite_master "
  160. "WHERE tbl_name = %s AND type IN ('table', 'view')",
  161. [table_name]
  162. )
  163. row = cursor.fetchone()
  164. if row is None:
  165. raise ValueError("Table %s does not exist" % table_name)
  166. create_sql, table_type = row
  167. if table_type == 'view':
  168. # Views don't have a primary key.
  169. return None
  170. fields_sql = create_sql[create_sql.index('(') + 1:create_sql.rindex(')')]
  171. for field_desc in fields_sql.split(','):
  172. field_desc = field_desc.strip()
  173. m = re.match(r'(?:(?:["`\[])(.*)(?:["`\]])|(\w+)).*PRIMARY KEY.*', field_desc)
  174. if m:
  175. return m.group(1) if m.group(1) else m.group(2)
  176. return None
  177. def _table_info(self, cursor, name):
  178. cursor.execute('PRAGMA table_info(%s)' % self.connection.ops.quote_name(name))
  179. # cid, name, type, notnull, default_value, pk
  180. return [{
  181. 'name': field[1],
  182. 'type': field[2],
  183. 'size': get_field_size(field[2]),
  184. 'null_ok': not field[3],
  185. 'default': field[4],
  186. 'pk': field[5], # undocumented
  187. } for field in cursor.fetchall()]
  188. def _get_foreign_key_constraints(self, cursor, table_name):
  189. constraints = {}
  190. cursor.execute('PRAGMA foreign_key_list(%s)' % self.connection.ops.quote_name(table_name))
  191. for row in cursor.fetchall():
  192. # Remaining on_update/on_delete/match values are of no interest.
  193. id_, _, table, from_, to = row[:5]
  194. constraints['fk_%d' % id_] = {
  195. 'columns': [from_],
  196. 'primary_key': False,
  197. 'unique': False,
  198. 'foreign_key': (table, to),
  199. 'check': False,
  200. 'index': False,
  201. }
  202. return constraints
  203. def _parse_column_or_constraint_definition(self, tokens, columns):
  204. token = None
  205. is_constraint_definition = None
  206. field_name = None
  207. constraint_name = None
  208. unique = False
  209. unique_columns = []
  210. check = False
  211. check_columns = []
  212. braces_deep = 0
  213. for token in tokens:
  214. if token.match(sqlparse.tokens.Punctuation, '('):
  215. braces_deep += 1
  216. elif token.match(sqlparse.tokens.Punctuation, ')'):
  217. braces_deep -= 1
  218. if braces_deep < 0:
  219. # End of columns and constraints for table definition.
  220. break
  221. elif braces_deep == 0 and token.match(sqlparse.tokens.Punctuation, ','):
  222. # End of current column or constraint definition.
  223. break
  224. # Detect column or constraint definition by first token.
  225. if is_constraint_definition is None:
  226. is_constraint_definition = token.match(sqlparse.tokens.Keyword, 'CONSTRAINT')
  227. if is_constraint_definition:
  228. continue
  229. if is_constraint_definition:
  230. # Detect constraint name by second token.
  231. if constraint_name is None:
  232. if token.ttype in (sqlparse.tokens.Name, sqlparse.tokens.Keyword):
  233. constraint_name = token.value
  234. elif token.ttype == sqlparse.tokens.Literal.String.Symbol:
  235. constraint_name = token.value[1:-1]
  236. # Start constraint columns parsing after UNIQUE keyword.
  237. if token.match(sqlparse.tokens.Keyword, 'UNIQUE'):
  238. unique = True
  239. unique_braces_deep = braces_deep
  240. elif unique:
  241. if unique_braces_deep == braces_deep:
  242. if unique_columns:
  243. # Stop constraint parsing.
  244. unique = False
  245. continue
  246. if token.ttype in (sqlparse.tokens.Name, sqlparse.tokens.Keyword):
  247. unique_columns.append(token.value)
  248. elif token.ttype == sqlparse.tokens.Literal.String.Symbol:
  249. unique_columns.append(token.value[1:-1])
  250. else:
  251. # Detect field name by first token.
  252. if field_name is None:
  253. if token.ttype in (sqlparse.tokens.Name, sqlparse.tokens.Keyword):
  254. field_name = token.value
  255. elif token.ttype == sqlparse.tokens.Literal.String.Symbol:
  256. field_name = token.value[1:-1]
  257. if token.match(sqlparse.tokens.Keyword, 'UNIQUE'):
  258. unique_columns = [field_name]
  259. # Start constraint columns parsing after CHECK keyword.
  260. if token.match(sqlparse.tokens.Keyword, 'CHECK'):
  261. check = True
  262. check_braces_deep = braces_deep
  263. elif check:
  264. if check_braces_deep == braces_deep:
  265. if check_columns:
  266. # Stop constraint parsing.
  267. check = False
  268. continue
  269. if token.ttype in (sqlparse.tokens.Name, sqlparse.tokens.Keyword):
  270. if token.value in columns:
  271. check_columns.append(token.value)
  272. elif token.ttype == sqlparse.tokens.Literal.String.Symbol:
  273. if token.value[1:-1] in columns:
  274. check_columns.append(token.value[1:-1])
  275. unique_constraint = {
  276. 'unique': True,
  277. 'columns': unique_columns,
  278. 'primary_key': False,
  279. 'foreign_key': None,
  280. 'check': False,
  281. 'index': False,
  282. } if unique_columns else None
  283. check_constraint = {
  284. 'check': True,
  285. 'columns': check_columns,
  286. 'primary_key': False,
  287. 'unique': False,
  288. 'foreign_key': None,
  289. 'index': False,
  290. } if check_columns else None
  291. return constraint_name, unique_constraint, check_constraint, token
  292. def _parse_table_constraints(self, sql, columns):
  293. # Check constraint parsing is based of SQLite syntax diagram.
  294. # https://www.sqlite.org/syntaxdiagrams.html#table-constraint
  295. statement = sqlparse.parse(sql)[0]
  296. constraints = {}
  297. unnamed_constrains_index = 0
  298. tokens = (token for token in statement.flatten() if not token.is_whitespace)
  299. # Go to columns and constraint definition
  300. for token in tokens:
  301. if token.match(sqlparse.tokens.Punctuation, '('):
  302. break
  303. # Parse columns and constraint definition
  304. while True:
  305. constraint_name, unique, check, end_token = self._parse_column_or_constraint_definition(tokens, columns)
  306. if unique:
  307. if constraint_name:
  308. constraints[constraint_name] = unique
  309. else:
  310. unnamed_constrains_index += 1
  311. constraints['__unnamed_constraint_%s__' % unnamed_constrains_index] = unique
  312. if check:
  313. if constraint_name:
  314. constraints[constraint_name] = check
  315. else:
  316. unnamed_constrains_index += 1
  317. constraints['__unnamed_constraint_%s__' % unnamed_constrains_index] = check
  318. if end_token.match(sqlparse.tokens.Punctuation, ')'):
  319. break
  320. return constraints
  321. def get_constraints(self, cursor, table_name):
  322. """
  323. Retrieve any constraints or keys (unique, pk, fk, check, index) across
  324. one or more columns.
  325. """
  326. constraints = {}
  327. # Find inline check constraints.
  328. try:
  329. table_schema = cursor.execute(
  330. "SELECT sql FROM sqlite_master WHERE type='table' and name=%s" % (
  331. self.connection.ops.quote_name(table_name),
  332. )
  333. ).fetchone()[0]
  334. except TypeError:
  335. # table_name is a view.
  336. pass
  337. else:
  338. columns = {info.name for info in self.get_table_description(cursor, table_name)}
  339. constraints.update(self._parse_table_constraints(table_schema, columns))
  340. # Get the index info
  341. cursor.execute("PRAGMA index_list(%s)" % self.connection.ops.quote_name(table_name))
  342. for row in cursor.fetchall():
  343. # SQLite 3.8.9+ has 5 columns, however older versions only give 3
  344. # columns. Discard last 2 columns if there.
  345. number, index, unique = row[:3]
  346. cursor.execute(
  347. "SELECT sql FROM sqlite_master "
  348. "WHERE type='index' AND name=%s" % self.connection.ops.quote_name(index)
  349. )
  350. # There's at most one row.
  351. sql, = cursor.fetchone() or (None,)
  352. # Inline constraints are already detected in
  353. # _parse_table_constraints(). The reasons to avoid fetching inline
  354. # constraints from `PRAGMA index_list` are:
  355. # - Inline constraints can have a different name and information
  356. # than what `PRAGMA index_list` gives.
  357. # - Not all inline constraints may appear in `PRAGMA index_list`.
  358. if not sql:
  359. # An inline constraint
  360. continue
  361. # Get the index info for that index
  362. cursor.execute('PRAGMA index_info(%s)' % self.connection.ops.quote_name(index))
  363. for index_rank, column_rank, column in cursor.fetchall():
  364. if index not in constraints:
  365. constraints[index] = {
  366. "columns": [],
  367. "primary_key": False,
  368. "unique": bool(unique),
  369. "foreign_key": None,
  370. "check": False,
  371. "index": True,
  372. }
  373. constraints[index]['columns'].append(column)
  374. # Add type and column orders for indexes
  375. if constraints[index]['index'] and not constraints[index]['unique']:
  376. # SQLite doesn't support any index type other than b-tree
  377. constraints[index]['type'] = Index.suffix
  378. order_info = sql.split('(')[-1].split(')')[0].split(',')
  379. orders = ['DESC' if info.endswith('DESC') else 'ASC' for info in order_info]
  380. constraints[index]['orders'] = orders
  381. # Get the PK
  382. pk_column = self.get_primary_key_column(cursor, table_name)
  383. if pk_column:
  384. # SQLite doesn't actually give a name to the PK constraint,
  385. # so we invent one. This is fine, as the SQLite backend never
  386. # deletes PK constraints by name, as you can't delete constraints
  387. # in SQLite; we remake the table with a new PK instead.
  388. constraints["__primary__"] = {
  389. "columns": [pk_column],
  390. "primary_key": True,
  391. "unique": False, # It's not actually a unique constraint.
  392. "foreign_key": None,
  393. "check": False,
  394. "index": False,
  395. }
  396. constraints.update(self._get_foreign_key_constraints(cursor, table_name))
  397. return constraints