south.db.sqlite3: 132 total statements, 0.0% covered

Generated: Wed 2013-03-13 10:33 CET

Source file: /media/Envs/Envs/filer-gallery/lib/python2.7/site-packages/south/db/sqlite3.py

Stats: 0 executed, 131 missed, 1 excluded, 120 ignored

  1. from south.db import generic
  2. class DatabaseOperations(generic.DatabaseOperations):
  3. """
  4. SQLite3 implementation of database operations.
  5. """
  6. backend_name = "sqlite3"
  7. # SQLite ignores several constraints. I wish I could.
  8. supports_foreign_keys = False
  9. has_check_constraints = False
  10. has_booleans = False
  11. def add_column(self, table_name, name, field, *args, **kwds):
  12. """
  13. Adds a column.
  14. """
  15. # If it's not nullable, and has no default, raise an error (SQLite is picky)
  16. if (not field.null and
  17. (not field.has_default() or field.get_default() is None) and
  18. not field.empty_strings_allowed):
  19. raise ValueError("You cannot add a null=False column without a default value.")
  20. # Initialise the field.
  21. field.set_attributes_from_name(name)
  22. # We add columns by remaking the table; even though SQLite supports
  23. # adding columns, it doesn't support adding PRIMARY KEY or UNIQUE cols.
  24. self._remake_table(table_name, added={
  25. field.column: self._column_sql_for_create(table_name, name, field, False),
  26. })
  27. def _get_full_table_description(self, connection, cursor, table_name):
  28. cursor.execute('PRAGMA table_info(%s)' % connection.ops.quote_name(table_name))
  29. # cid, name, type, notnull, dflt_value, pk
  30. return [{'name': field[1],
  31. 'type': field[2],
  32. 'null_ok': not field[3],
  33. 'dflt_value': field[4],
  34. 'pk': field[5] # undocumented
  35. } for field in cursor.fetchall()]
  36. @generic.invalidate_table_constraints
  37. def _remake_table(self, table_name, added={}, renames={}, deleted=[], altered={}, primary_key_override=None, uniques_deleted=[]):
  38. """
  39. Given a table and three sets of changes (renames, deletes, alters),
  40. recreates it with the modified schema.
  41. """
  42. # Dry runs get skipped completely
  43. if self.dry_run:
  44. return
  45. # Temporary table's name
  46. temp_name = "_south_new_" + table_name
  47. # Work out the (possibly new) definitions of each column
  48. definitions = {}
  49. cursor = self._get_connection().cursor()
  50. # Get the index descriptions
  51. indexes = self._get_connection().introspection.get_indexes(cursor, table_name)
  52. multi_indexes = self._get_multi_indexes(table_name)
  53. # Work out new column defs.
  54. for column_info in self._get_full_table_description(self._get_connection(), cursor, table_name):
  55. name = column_info['name']
  56. if name in deleted:
  57. continue
  58. # Get the type, ignoring PRIMARY KEY (we need to be consistent)
  59. type = column_info['type'].replace("PRIMARY KEY", "")
  60. # Add on primary key, not null or unique if needed.
  61. if (primary_key_override and primary_key_override == name) or \
  62. (not primary_key_override and name in indexes and
  63. indexes[name]['primary_key']):
  64. type += " PRIMARY KEY"
  65. elif not column_info['null_ok']:
  66. type += " NOT NULL"
  67. if (name in indexes and indexes[name]['unique'] and
  68. name not in uniques_deleted):
  69. type += " UNIQUE"
  70. if column_info['dflt_value'] is not None:
  71. type += " DEFAULT " + column_info['dflt_value']
  72. # Deal with a rename
  73. if name in renames:
  74. name = renames[name]
  75. # Add to the defs
  76. definitions[name] = type
  77. # Add on altered columns
  78. for name, type in altered.items():
  79. if (primary_key_override and primary_key_override == name) or \
  80. (not primary_key_override and name in indexes and
  81. indexes[name]['primary_key']):
  82. type += " PRIMARY KEY"
  83. if (name in indexes and indexes[name]['unique'] and
  84. name not in uniques_deleted):
  85. type += " UNIQUE"
  86. definitions[name] = type
  87. # Add on the new columns
  88. for name, type in added.items():
  89. if (primary_key_override and primary_key_override == name):
  90. type += " PRIMARY KEY"
  91. definitions[name] = type
  92. # Alright, Make the table
  93. self.execute("CREATE TABLE %s (%s)" % (
  94. self.quote_name(temp_name),
  95. ", ".join(["%s %s" % (self.quote_name(cname), ctype) for cname, ctype in definitions.items()]),
  96. ))
  97. # Copy over the data
  98. self._copy_data(table_name, temp_name, renames)
  99. # Delete the old table, move our new one over it
  100. self.delete_table(table_name)
  101. self.rename_table(temp_name, table_name)
  102. # Recreate multi-valued indexes
  103. # We can't do that before since it's impossible to rename indexes
  104. # and index name scope is global
  105. self._make_multi_indexes(table_name, multi_indexes, renames=renames, deleted=deleted, uniques_deleted=uniques_deleted)
  106. def _copy_data(self, src, dst, field_renames={}):
  107. "Used to copy data into a new table"
  108. # Make a list of all the fields to select
  109. cursor = self._get_connection().cursor()
  110. src_fields = [column_info[0] for column_info in self._get_connection().introspection.get_table_description(cursor, src)]
  111. dst_fields = [column_info[0] for column_info in self._get_connection().introspection.get_table_description(cursor, dst)]
  112. src_fields_new = []
  113. dst_fields_new = []
  114. for field in src_fields:
  115. if field in field_renames:
  116. dst_fields_new.append(self.quote_name(field_renames[field]))
  117. elif field in dst_fields:
  118. dst_fields_new.append(self.quote_name(field))
  119. else:
  120. continue
  121. src_fields_new.append(self.quote_name(field))
  122. # Copy over the data
  123. self.execute("INSERT INTO %s (%s) SELECT %s FROM %s;" % (
  124. self.quote_name(dst),
  125. ', '.join(dst_fields_new),
  126. ', '.join(src_fields_new),
  127. self.quote_name(src),
  128. ))
  129. def _create_unique(self, table_name, columns):
  130. self.execute("CREATE UNIQUE INDEX %s ON %s(%s);" % (
  131. self.quote_name('%s_%s' % (table_name, '__'.join(columns))),
  132. self.quote_name(table_name),
  133. ', '.join(self.quote_name(c) for c in columns),
  134. ))
  135. def _get_multi_indexes(self, table_name):
  136. indexes = []
  137. cursor = self._get_connection().cursor()
  138. cursor.execute('PRAGMA index_list(%s)' % self.quote_name(table_name))
  139. # seq, name, unique
  140. for index, unique in [(field[1], field[2]) for field in cursor.fetchall()]:
  141. if not unique:
  142. continue
  143. cursor.execute('PRAGMA index_info(%s)' % self.quote_name(index))
  144. info = cursor.fetchall()
  145. if len(info) == 1:
  146. continue
  147. columns = []
  148. for field in info:
  149. columns.append(field[2])
  150. indexes.append(columns)
  151. return indexes
  152. def _make_multi_indexes(self, table_name, indexes, deleted=[], renames={}, uniques_deleted=[]):
  153. for index in indexes:
  154. columns = []
  155. for name in index:
  156. # Handle deletion
  157. if name in deleted:
  158. columns = []
  159. break
  160. # Handle renames
  161. if name in renames:
  162. name = renames[name]
  163. columns.append(name)
  164. if columns and columns != uniques_deleted:
  165. self._create_unique(table_name, columns)
  166. def _column_sql_for_create(self, table_name, name, field, explicit_name=True):
  167. "Given a field and its name, returns the full type for the CREATE TABLE (without unique/pk)"
  168. field.set_attributes_from_name(name)
  169. if not explicit_name:
  170. name = field.db_column
  171. else:
  172. field.column = name
  173. sql = self.column_sql(table_name, name, field, with_name=False, field_prepared=True)
  174. # Remove keywords we don't want (this should be type only, not constraint)
  175. if sql:
  176. sql = sql.replace("PRIMARY KEY", "")
  177. return sql
  178. def alter_column(self, table_name, name, field, explicit_name=True, ignore_constraints=False):
  179. """
  180. Changes a column's SQL definition.
  181. Note that this sqlite3 implementation ignores the ignore_constraints argument.
  182. The argument is accepted for API compatibility with the generic
  183. DatabaseOperations.alter_column() method.
  184. """
  185. # Remake the table correctly
  186. self._remake_table(table_name, altered={
  187. name: self._column_sql_for_create(table_name, name, field, explicit_name),
  188. })
  189. def delete_column(self, table_name, column_name):
  190. """
  191. Deletes a column.
  192. """
  193. self._remake_table(table_name, deleted=[column_name])
  194. def rename_column(self, table_name, old, new):
  195. """
  196. Renames a column from one name to another.
  197. """
  198. self._remake_table(table_name, renames={old: new})
  199. def create_unique(self, table_name, columns):
  200. """
  201. Create an unique index on columns
  202. """
  203. self._create_unique(table_name, columns)
  204. def delete_unique(self, table_name, columns):
  205. """
  206. Delete an unique index
  207. """
  208. self._remake_table(table_name, uniques_deleted=columns)
  209. def create_primary_key(self, table_name, columns):
  210. if not isinstance(columns, (list, tuple)):
  211. columns = [columns]
  212. assert len(columns) == 1, "SQLite backend does not support multi-column primary keys"
  213. self._remake_table(table_name, primary_key_override=columns[0])
  214. # Not implemented this yet.
  215. def delete_primary_key(self, table_name):
  216. # By passing True in, we make sure we wipe all existing PKs.
  217. self._remake_table(table_name, primary_key_override=True)
  218. # No cascades on deletes
  219. def delete_table(self, table_name, cascade=True):
  220. generic.DatabaseOperations.delete_table(self, table_name, False)
  221. def _default_value_workaround(self, default):
  222. if default == True:
  223. default = 1
  224. elif default == False:
  225. default = 0
  226. return default