south.db.sql_server.pyodbc: 199 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/sql_server/pyodbc.py

Stats: 0 executed, 190 missed, 9 excluded, 235 ignored

  1. from datetime import date, datetime, time
  2. from warnings import warn
  3. from django.db import models
  4. from django.db.models import fields
  5. from south.db import generic
  6. from south.db.generic import delete_column_constraints, invalidate_table_constraints, copy_column_constraints
  7. from south.exceptions import ConstraintDropped
  8. from django.utils.encoding import smart_unicode
  9. from django.core.management.color import no_style
  10. class DatabaseOperations(generic.DatabaseOperations):
  11. """
  12. django-pyodbc (sql_server.pyodbc) implementation of database operations.
  13. """
  14. backend_name = "pyodbc"
  15. add_column_string = 'ALTER TABLE %s ADD %s;'
  16. alter_string_set_type = 'ALTER COLUMN %(column)s %(type)s'
  17. alter_string_set_null = 'ALTER COLUMN %(column)s %(type)s NULL'
  18. alter_string_drop_null = 'ALTER COLUMN %(column)s %(type)s NOT NULL'
  19. allows_combined_alters = False
  20. drop_index_string = 'DROP INDEX %(index_name)s ON %(table_name)s'
  21. drop_constraint_string = 'ALTER TABLE %(table_name)s DROP CONSTRAINT %(constraint_name)s'
  22. delete_column_string = 'ALTER TABLE %s DROP COLUMN %s'
  23. #create_check_constraint_sql = "ALTER TABLE %(table)s " + \
  24. # generic.DatabaseOperations.add_check_constraint_fragment
  25. create_foreign_key_sql = "ALTER TABLE %(table)s ADD CONSTRAINT %(constraint)s " + \
  26. "FOREIGN KEY (%(column)s) REFERENCES %(target)s"
  27. create_unique_sql = "ALTER TABLE %(table)s ADD CONSTRAINT %(constraint)s UNIQUE (%(columns)s)"
  28. default_schema_name = "dbo"
  29. has_booleans = False
  30. @delete_column_constraints
  31. def delete_column(self, table_name, name):
  32. q_table_name, q_name = (self.quote_name(table_name), self.quote_name(name))
  33. # Zap the indexes
  34. for ind in self._find_indexes_for_column(table_name,name):
  35. params = {'table_name':q_table_name, 'index_name': ind}
  36. sql = self.drop_index_string % params
  37. self.execute(sql, [])
  38. # Zap the constraints
  39. for const in self._find_constraints_for_column(table_name,name):
  40. params = {'table_name':q_table_name, 'constraint_name': const}
  41. sql = self.drop_constraint_string % params
  42. self.execute(sql, [])
  43. # Zap default if exists
  44. drop_default = self.drop_column_default_sql(table_name, name)
  45. if drop_default:
  46. sql = "ALTER TABLE [%s] %s" % (table_name, drop_default)
  47. self.execute(sql, [])
  48. # Finally zap the column itself
  49. self.execute(self.delete_column_string % (q_table_name, q_name), [])
  50. def _find_indexes_for_column(self, table_name, name):
  51. "Find the indexes that apply to a column, needed when deleting"
  52. sql = """
  53. SELECT si.name, si.id, sik.colid, sc.name
  54. FROM dbo.sysindexes SI WITH (NOLOCK)
  55. INNER JOIN dbo.sysindexkeys SIK WITH (NOLOCK)
  56. ON SIK.id = Si.id
  57. AND SIK.indid = SI.indid
  58. INNER JOIN dbo.syscolumns SC WITH (NOLOCK)
  59. ON SI.id = SC.id
  60. AND SIK.colid = SC.colid
  61. WHERE SI.indid !=0
  62. AND Si.id = OBJECT_ID('%s')
  63. AND SC.name = '%s'
  64. """
  65. idx = self.execute(sql % (table_name, name), [])
  66. return [i[0] for i in idx]
  67. def _find_constraints_for_column(self, table_name, name, just_names=True):
  68. """
  69. Find the constraints that apply to a column, needed when deleting. Defaults not included.
  70. This is more general than the parent _constraints_affecting_columns, as on MSSQL this
  71. includes PK and FK constraints.
  72. """
  73. sql = """
  74. SELECT CC.[CONSTRAINT_NAME]
  75. ,TC.[CONSTRAINT_TYPE]
  76. ,CHK.[CHECK_CLAUSE]
  77. ,RFD.TABLE_SCHEMA
  78. ,RFD.TABLE_NAME
  79. ,RFD.COLUMN_NAME
  80. -- used for normalized names
  81. ,CC.TABLE_NAME
  82. ,CC.COLUMN_NAME
  83. FROM [INFORMATION_SCHEMA].[TABLE_CONSTRAINTS] TC
  84. JOIN INFORMATION_SCHEMA.CONSTRAINT_COLUMN_USAGE CC
  85. ON TC.CONSTRAINT_CATALOG = CC.CONSTRAINT_CATALOG
  86. AND TC.CONSTRAINT_SCHEMA = CC.CONSTRAINT_SCHEMA
  87. AND TC.CONSTRAINT_NAME = CC.CONSTRAINT_NAME
  88. LEFT JOIN INFORMATION_SCHEMA.CHECK_CONSTRAINTS CHK
  89. ON CHK.CONSTRAINT_CATALOG = CC.CONSTRAINT_CATALOG
  90. AND CHK.CONSTRAINT_SCHEMA = CC.CONSTRAINT_SCHEMA
  91. AND CHK.CONSTRAINT_NAME = CC.CONSTRAINT_NAME
  92. AND 'CHECK' = TC.CONSTRAINT_TYPE
  93. LEFT JOIN INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS REF
  94. ON REF.CONSTRAINT_CATALOG = CC.CONSTRAINT_CATALOG
  95. AND REF.CONSTRAINT_SCHEMA = CC.CONSTRAINT_SCHEMA
  96. AND REF.CONSTRAINT_NAME = CC.CONSTRAINT_NAME
  97. AND 'FOREIGN KEY' = TC.CONSTRAINT_TYPE
  98. LEFT JOIN INFORMATION_SCHEMA.CONSTRAINT_COLUMN_USAGE RFD
  99. ON RFD.CONSTRAINT_CATALOG = REF.UNIQUE_CONSTRAINT_CATALOG
  100. AND RFD.CONSTRAINT_SCHEMA = REF.UNIQUE_CONSTRAINT_SCHEMA
  101. AND RFD.CONSTRAINT_NAME = REF.UNIQUE_CONSTRAINT_NAME
  102. WHERE CC.CONSTRAINT_CATALOG = CC.TABLE_CATALOG
  103. AND CC.CONSTRAINT_SCHEMA = CC.TABLE_SCHEMA
  104. AND CC.TABLE_CATALOG = %s
  105. AND CC.TABLE_SCHEMA = %s
  106. AND CC.TABLE_NAME = %s
  107. AND CC.COLUMN_NAME = %s
  108. """
  109. db_name = self._get_setting('name')
  110. schema_name = self._get_schema_name()
  111. table = self.execute(sql, [db_name, schema_name, table_name, name])
  112. if just_names:
  113. return [r[0] for r in table]
  114. all = {}
  115. for r in table:
  116. cons_name, type = r[:2]
  117. if type=='PRIMARY KEY' or type=='UNIQUE':
  118. cons = all.setdefault(cons_name, (type,[]))
  119. cons[1].append(r[7])
  120. elif type=='CHECK':
  121. cons = (type, r[2])
  122. elif type=='FOREIGN KEY':
  123. if cons_name in all:
  124. raise NotImplementedError("Multiple-column foreign keys are not supported")
  125. else:
  126. cons = (type, r[3:6])
  127. else:
  128. raise NotImplementedError("Don't know how to handle constraints of type "+ type)
  129. all[cons_name] = cons
  130. return all
  131. @invalidate_table_constraints
  132. def alter_column(self, table_name, name, field, explicit_name=True, ignore_constraints=False):
  133. """
  134. Alters the given column name so it will match the given field.
  135. Note that conversion between the two by the database must be possible.
  136. Will not automatically add _id by default; to have this behavour, pass
  137. explicit_name=False.
  138. @param table_name: The name of the table to add the column to
  139. @param name: The name of the column to alter
  140. @param field: The new field definition to use
  141. """
  142. self._fix_field_definition(field)
  143. if not ignore_constraints:
  144. qn = self.quote_name
  145. sch = qn(self._get_schema_name())
  146. tab = qn(table_name)
  147. table = ".".join([sch, tab])
  148. try:
  149. self.delete_foreign_key(table_name, name)
  150. except ValueError:
  151. # no FK constraint on this field. That's OK.
  152. pass
  153. constraints = self._find_constraints_for_column(table_name, name, False)
  154. for constraint in constraints.keys():
  155. params = dict(table_name = table,
  156. constraint_name = qn(constraint))
  157. sql = self.drop_constraint_string % params
  158. self.execute(sql, [])
  159. ret_val = super(DatabaseOperations, self).alter_column(table_name, name, field, explicit_name, ignore_constraints=True)
  160. if not ignore_constraints:
  161. for cname, (ctype,args) in constraints.items():
  162. params = dict(table = table,
  163. constraint = qn(cname))
  164. if ctype=='UNIQUE':
  165. params['columns'] = ", ".join(map(qn,args))
  166. sql = self.create_unique_sql % params
  167. elif ctype=='PRIMARY KEY':
  168. params['columns'] = ", ".join(map(qn,args))
  169. sql = self.create_primary_key_string % params
  170. elif ctype=='FOREIGN KEY':
  171. continue
  172. # Foreign keys taken care of below
  173. #target = "%s.%s(%s)" % tuple(map(qn,args))
  174. #params.update(column = qn(name), target = target)
  175. #sql = self.create_foreign_key_sql % params
  176. elif ctype=='CHECK':
  177. warn(ConstraintDropped("CHECK "+ args, table_name, name))
  178. continue
  179. #TODO: Some check constraints should be restored; but not before the generic
  180. # backend restores them.
  181. #params['check'] = args
  182. #sql = self.create_check_constraint_sql % params
  183. else:
  184. raise NotImplementedError("Don't know how to handle constraints of type "+ type)
  185. self.execute(sql, [])
  186. # Create foreign key if necessary
  187. if field.rel and self.supports_foreign_keys:
  188. self.execute(
  189. self.foreign_key_sql(
  190. table_name,
  191. field.column,
  192. field.rel.to._meta.db_table,
  193. field.rel.to._meta.get_field(field.rel.field_name).column
  194. )
  195. )
  196. model = self.mock_model("FakeModelForIndexCreation", table_name)
  197. for stmt in self._get_connection().creation.sql_indexes_for_field(model, field, no_style()):
  198. self.execute(stmt)
  199. return ret_val
  200. def _alter_set_defaults(self, field, name, params, sqls):
  201. "Subcommand of alter_column that sets default values (overrideable)"
  202. # First drop the current default if one exists
  203. table_name = self.quote_name(params['table_name'])
  204. drop_default = self.drop_column_default_sql(table_name, name)
  205. if drop_default:
  206. sqls.append((drop_default, []))
  207. # Next, set any default
  208. if field.has_default():
  209. default = field.get_default()
  210. literal = self._value_to_unquoted_literal(field, default)
  211. sqls.append(('ADD DEFAULT %s for %s' % (self._quote_string(literal), self.quote_name(name),), []))
  212. def _value_to_unquoted_literal(self, field, value):
  213. # Start with the field's own translation
  214. conn = self._get_connection()
  215. value = field.get_db_prep_save(value, connection=conn)
  216. # This is still a Python object -- nobody expects to need a literal.
  217. if isinstance(value, basestring):
  218. return smart_unicode(value)
  219. elif isinstance(value, (date,time,datetime)):
  220. return value.isoformat()
  221. else:
  222. #TODO: Anybody else needs special translations?
  223. return str(value)
  224. def _default_value_workaround(self, value):
  225. if isinstance(value, (date,time,datetime)):
  226. return value.isoformat()
  227. else:
  228. return super(DatabaseOperations, self)._default_value_workaround(value)
  229. def _quote_string(self, s):
  230. return "'" + s.replace("'","''") + "'"
  231. def drop_column_default_sql(self, table_name, name, q_name=None):
  232. "MSSQL specific drop default, which is a pain"
  233. sql = """
  234. SELECT object_name(cdefault)
  235. FROM syscolumns
  236. WHERE id = object_id('%s')
  237. AND name = '%s'
  238. """
  239. cons = self.execute(sql % (table_name, name), [])
  240. if cons and cons[0] and cons[0][0]:
  241. return "DROP CONSTRAINT %s" % cons[0][0]
  242. return None
  243. def _fix_field_definition(self, field):
  244. if isinstance(field, (fields.BooleanField, fields.NullBooleanField)):
  245. if field.default == True:
  246. field.default = 1
  247. if field.default == False:
  248. field.default = 0
  249. # This is copied from South's generic add_column, with two modifications:
  250. # 1) The sql-server-specific call to _fix_field_definition
  251. # 2) Removing a default, when needed, by calling drop_default and not the more general alter_column
  252. @invalidate_table_constraints
  253. def add_column(self, table_name, name, field, keep_default=True):
  254. """
  255. Adds the column 'name' to the table 'table_name'.
  256. Uses the 'field' paramater, a django.db.models.fields.Field instance,
  257. to generate the necessary sql
  258. @param table_name: The name of the table to add the column to
  259. @param name: The name of the column to add
  260. @param field: The field to use
  261. """
  262. self._fix_field_definition(field)
  263. sql = self.column_sql(table_name, name, field)
  264. if sql:
  265. params = (
  266. self.quote_name(table_name),
  267. sql,
  268. )
  269. sql = self.add_column_string % params
  270. self.execute(sql)
  271. # Now, drop the default if we need to
  272. if not keep_default and field.default is not None:
  273. field.default = fields.NOT_PROVIDED
  274. #self.alter_column(table_name, name, field, explicit_name=False, ignore_constraints=True)
  275. self.drop_default(table_name, name, field)
  276. @invalidate_table_constraints
  277. def drop_default(self, table_name, name, field):
  278. fragment = self.drop_column_default_sql(table_name, name)
  279. if fragment:
  280. table_name = self.quote_name(table_name)
  281. sql = " ".join(["ALTER TABLE", table_name, fragment])
  282. self.execute(sql)
  283. @invalidate_table_constraints
  284. def create_table(self, table_name, field_defs):
  285. # Tweak stuff as needed
  286. for _, f in field_defs:
  287. self._fix_field_definition(f)
  288. # Run
  289. generic.DatabaseOperations.create_table(self, table_name, field_defs)
  290. def _find_referencing_fks(self, table_name):
  291. "MSSQL does not support cascading FKs when dropping tables, we need to implement."
  292. # FK -- Foreign Keys
  293. # UCTU -- Unique Constraints Table Usage
  294. # FKTU -- Foreign Key Table Usage
  295. # (last two are both really CONSTRAINT_TABLE_USAGE, different join conditions)
  296. sql = """
  297. SELECT FKTU.TABLE_SCHEMA as REFING_TABLE_SCHEMA,
  298. FKTU.TABLE_NAME as REFING_TABLE_NAME,
  299. FK.[CONSTRAINT_NAME] as FK_NAME
  300. FROM [INFORMATION_SCHEMA].[REFERENTIAL_CONSTRAINTS] FK
  301. JOIN [INFORMATION_SCHEMA].[CONSTRAINT_TABLE_USAGE] UCTU
  302. ON FK.UNIQUE_CONSTRAINT_CATALOG = UCTU.CONSTRAINT_CATALOG and
  303. FK.UNIQUE_CONSTRAINT_NAME = UCTU.CONSTRAINT_NAME and
  304. FK.UNIQUE_CONSTRAINT_SCHEMA = UCTU.CONSTRAINT_SCHEMA
  305. JOIN [INFORMATION_SCHEMA].[CONSTRAINT_TABLE_USAGE] FKTU
  306. ON FK.CONSTRAINT_CATALOG = FKTU.CONSTRAINT_CATALOG and
  307. FK.CONSTRAINT_NAME = FKTU.CONSTRAINT_NAME and
  308. FK.CONSTRAINT_SCHEMA = FKTU.CONSTRAINT_SCHEMA
  309. WHERE FK.CONSTRAINT_CATALOG = %s
  310. AND UCTU.TABLE_SCHEMA = %s -- REFD_TABLE_SCHEMA
  311. AND UCTU.TABLE_NAME = %s -- REFD_TABLE_NAME
  312. """
  313. db_name = self._get_setting('name')
  314. schema_name = self._get_schema_name()
  315. return self.execute(sql, [db_name, schema_name, table_name])
  316. @invalidate_table_constraints
  317. def delete_table(self, table_name, cascade=True):
  318. """
  319. Deletes the table 'table_name'.
  320. """
  321. if cascade:
  322. refing = self._find_referencing_fks(table_name)
  323. for schmea, table, constraint in refing:
  324. table = ".".join(map (self.quote_name, [schmea, table]))
  325. params = dict(table_name = table,
  326. constraint_name = self.quote_name(constraint))
  327. sql = self.drop_constraint_string % params
  328. self.execute(sql, [])
  329. cascade = False
  330. super(DatabaseOperations, self).delete_table(table_name, cascade)
  331. @copy_column_constraints
  332. @delete_column_constraints
  333. def rename_column(self, table_name, old, new):
  334. """
  335. Renames the column of 'table_name' from 'old' to 'new'.
  336. WARNING - This isn't transactional on MSSQL!
  337. """
  338. if old == new:
  339. # No Operation
  340. return
  341. # Examples on the MS site show the table name not being quoted...
  342. params = (table_name, self.quote_name(old), self.quote_name(new))
  343. self.execute("EXEC sp_rename '%s.%s', %s, 'COLUMN'" % params)
  344. @invalidate_table_constraints
  345. def rename_table(self, old_table_name, table_name):
  346. """
  347. Renames the table 'old_table_name' to 'table_name'.
  348. WARNING - This isn't transactional on MSSQL!
  349. """
  350. if old_table_name == table_name:
  351. # No Operation
  352. return
  353. params = (self.quote_name(old_table_name), self.quote_name(table_name))
  354. self.execute('EXEC sp_rename %s, %s' % params)
  355. def _db_type_for_alter_column(self, field):
  356. return self._db_positive_type_for_alter_column(DatabaseOperations, field)
  357. def _alter_add_column_mods(self, field, name, params, sqls):
  358. return self._alter_add_positive_check(DatabaseOperations, field, name, params, sqls)
  359. @invalidate_table_constraints
  360. def delete_foreign_key(self, table_name, column):
  361. super(DatabaseOperations, self).delete_foreign_key(table_name, column)
  362. # A FK also implies a non-unique index
  363. find_index_sql = """
  364. SELECT i.name -- s.name, t.name, c.name
  365. FROM sys.tables t
  366. INNER JOIN sys.schemas s ON t.schema_id = s.schema_id
  367. INNER JOIN sys.indexes i ON i.object_id = t.object_id
  368. INNER JOIN sys.index_columns ic ON ic.object_id = t.object_id
  369. INNER JOIN sys.columns c ON c.object_id = t.object_id
  370. AND ic.column_id = c.column_id
  371. WHERE i.is_unique=0 AND i.is_primary_key=0 AND i.is_unique_constraint=0
  372. AND s.name = %s
  373. AND t.name = %s
  374. AND c.name = %s
  375. """
  376. schema = self._get_schema_name()
  377. indexes = self.execute(find_index_sql, [schema, table_name, column])
  378. qn = self.quote_name
  379. for index in (i[0] for i in indexes if i[0]): # "if i[0]" added because an empty name may return
  380. self.execute("DROP INDEX %s on %s.%s" % (qn(index), qn(schema), qn(table_name) ))