diff options
author | ttt | 2017-05-13 00:29:47 +0530 |
---|---|---|
committer | ttt | 2017-05-13 00:29:47 +0530 |
commit | abf599be33b383a6a5baf9493093b2126a622ac8 (patch) | |
tree | 4c5ab6e0d935d5e65fabcf0258e4a00dd20a5afa /lib/python2.7/site-packages/south/db/sql_server/pyodbc.py | |
download | SBHS-2018-Rpi-abf599be33b383a6a5baf9493093b2126a622ac8.tar.gz SBHS-2018-Rpi-abf599be33b383a6a5baf9493093b2126a622ac8.tar.bz2 SBHS-2018-Rpi-abf599be33b383a6a5baf9493093b2126a622ac8.zip |
added all server files
Diffstat (limited to 'lib/python2.7/site-packages/south/db/sql_server/pyodbc.py')
-rw-r--r-- | lib/python2.7/site-packages/south/db/sql_server/pyodbc.py | 444 |
1 files changed, 444 insertions, 0 deletions
diff --git a/lib/python2.7/site-packages/south/db/sql_server/pyodbc.py b/lib/python2.7/site-packages/south/db/sql_server/pyodbc.py new file mode 100644 index 0000000..b725ec0 --- /dev/null +++ b/lib/python2.7/site-packages/south/db/sql_server/pyodbc.py @@ -0,0 +1,444 @@ +from datetime import date, datetime, time +from warnings import warn +from django.db import models +from django.db.models import fields +from south.db import generic +from south.db.generic import delete_column_constraints, invalidate_table_constraints, copy_column_constraints +from south.exceptions import ConstraintDropped +from south.utils.py3 import string_types +try: + from django.utils.encoding import smart_text # Django >= 1.5 +except ImportError: + from django.utils.encoding import smart_unicode as smart_text # Django < 1.5 +from django.core.management.color import no_style + +class DatabaseOperations(generic.DatabaseOperations): + """ + django-pyodbc (sql_server.pyodbc) implementation of database operations. + """ + + backend_name = "pyodbc" + + add_column_string = 'ALTER TABLE %s ADD %s;' + alter_string_set_type = 'ALTER COLUMN %(column)s %(type)s' + alter_string_set_null = 'ALTER COLUMN %(column)s %(type)s NULL' + alter_string_drop_null = 'ALTER COLUMN %(column)s %(type)s NOT NULL' + + allows_combined_alters = False + + drop_index_string = 'DROP INDEX %(index_name)s ON %(table_name)s' + drop_constraint_string = 'ALTER TABLE %(table_name)s DROP CONSTRAINT %(constraint_name)s' + delete_column_string = 'ALTER TABLE %s DROP COLUMN %s' + + #create_check_constraint_sql = "ALTER TABLE %(table)s " + \ + # generic.DatabaseOperations.add_check_constraint_fragment + create_foreign_key_sql = "ALTER TABLE %(table)s ADD CONSTRAINT %(constraint)s " + \ + "FOREIGN KEY (%(column)s) REFERENCES %(target)s" + create_unique_sql = "ALTER TABLE %(table)s ADD CONSTRAINT %(constraint)s UNIQUE (%(columns)s)" + + + default_schema_name = "dbo" + + has_booleans = False + + + @delete_column_constraints + def delete_column(self, table_name, name): + q_table_name, q_name = (self.quote_name(table_name), self.quote_name(name)) + + # Zap the constraints + for const in self._find_constraints_for_column(table_name,name): + params = {'table_name':q_table_name, 'constraint_name': const} + sql = self.drop_constraint_string % params + self.execute(sql, []) + + # Zap the indexes + for ind in self._find_indexes_for_column(table_name,name): + params = {'table_name':q_table_name, 'index_name': ind} + sql = self.drop_index_string % params + self.execute(sql, []) + + # Zap default if exists + drop_default = self.drop_column_default_sql(table_name, name) + if drop_default: + sql = "ALTER TABLE [%s] %s" % (table_name, drop_default) + self.execute(sql, []) + + # Finally zap the column itself + self.execute(self.delete_column_string % (q_table_name, q_name), []) + + def _find_indexes_for_column(self, table_name, name): + "Find the indexes that apply to a column, needed when deleting" + + sql = """ + SELECT si.name, si.id, sik.colid, sc.name + FROM dbo.sysindexes si WITH (NOLOCK) + INNER JOIN dbo.sysindexkeys sik WITH (NOLOCK) + ON sik.id = si.id + AND sik.indid = si.indid + INNER JOIN dbo.syscolumns sc WITH (NOLOCK) + ON si.id = sc.id + AND sik.colid = sc.colid + WHERE si.indid !=0 + AND si.id = OBJECT_ID('%s') + AND sc.name = '%s' + """ + idx = self.execute(sql % (table_name, name), []) + return [i[0] for i in idx] + + + def _find_constraints_for_column(self, table_name, name, just_names=True): + """ + Find the constraints that apply to a column, needed when deleting. Defaults not included. + This is more general than the parent _constraints_affecting_columns, as on MSSQL this + includes PK and FK constraints. + """ + + sql = """ + SELECT CC.[CONSTRAINT_NAME] + ,TC.[CONSTRAINT_TYPE] + ,CHK.[CHECK_CLAUSE] + ,RFD.TABLE_SCHEMA + ,RFD.TABLE_NAME + ,RFD.COLUMN_NAME + -- used for normalized names + ,CC.TABLE_NAME + ,CC.COLUMN_NAME + FROM [INFORMATION_SCHEMA].[TABLE_CONSTRAINTS] TC + JOIN INFORMATION_SCHEMA.CONSTRAINT_COLUMN_USAGE CC + ON TC.CONSTRAINT_CATALOG = CC.CONSTRAINT_CATALOG + AND TC.CONSTRAINT_SCHEMA = CC.CONSTRAINT_SCHEMA + AND TC.CONSTRAINT_NAME = CC.CONSTRAINT_NAME + LEFT JOIN INFORMATION_SCHEMA.CHECK_CONSTRAINTS CHK + ON CHK.CONSTRAINT_CATALOG = CC.CONSTRAINT_CATALOG + AND CHK.CONSTRAINT_SCHEMA = CC.CONSTRAINT_SCHEMA + AND CHK.CONSTRAINT_NAME = CC.CONSTRAINT_NAME + AND 'CHECK' = TC.CONSTRAINT_TYPE + LEFT JOIN INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS REF + ON REF.CONSTRAINT_CATALOG = CC.CONSTRAINT_CATALOG + AND REF.CONSTRAINT_SCHEMA = CC.CONSTRAINT_SCHEMA + AND REF.CONSTRAINT_NAME = CC.CONSTRAINT_NAME + AND 'FOREIGN KEY' = TC.CONSTRAINT_TYPE + LEFT JOIN INFORMATION_SCHEMA.CONSTRAINT_COLUMN_USAGE RFD + ON RFD.CONSTRAINT_CATALOG = REF.UNIQUE_CONSTRAINT_CATALOG + AND RFD.CONSTRAINT_SCHEMA = REF.UNIQUE_CONSTRAINT_SCHEMA + AND RFD.CONSTRAINT_NAME = REF.UNIQUE_CONSTRAINT_NAME + WHERE CC.CONSTRAINT_CATALOG = CC.TABLE_CATALOG + AND CC.CONSTRAINT_SCHEMA = CC.TABLE_SCHEMA + AND CC.TABLE_CATALOG = %s + AND CC.TABLE_SCHEMA = %s + AND CC.TABLE_NAME = %s + AND CC.COLUMN_NAME = %s + """ + db_name = self._get_setting('name') + schema_name = self._get_schema_name() + table = self.execute(sql, [db_name, schema_name, table_name, name]) + + if just_names: + return [r[0] for r in table] + + all = {} + for r in table: + cons_name, type = r[:2] + if type=='PRIMARY KEY' or type=='UNIQUE': + cons = all.setdefault(cons_name, (type,[])) + sql = ''' + SELECT COLUMN_NAME + FROM INFORMATION_SCHEMA.CONSTRAINT_COLUMN_USAGE RFD + WHERE RFD.CONSTRAINT_CATALOG = %s + AND RFD.CONSTRAINT_SCHEMA = %s + AND RFD.TABLE_NAME = %s + AND RFD.CONSTRAINT_NAME = %s + ''' + columns = self.execute(sql, [db_name, schema_name, table_name, cons_name]) + cons[1].extend(col for col, in columns) + elif type=='CHECK': + cons = (type, r[2]) + elif type=='FOREIGN KEY': + if cons_name in all: + raise NotImplementedError("Multiple-column foreign keys are not supported") + else: + cons = (type, r[3:6]) + else: + raise NotImplementedError("Don't know how to handle constraints of type "+ type) + all[cons_name] = cons + return all + + @invalidate_table_constraints + def alter_column(self, table_name, name, field, explicit_name=True, ignore_constraints=False): + """ + Alters the given column name so it will match the given field. + Note that conversion between the two by the database must be possible. + Will not automatically add _id by default; to have this behavour, pass + explicit_name=False. + + @param table_name: The name of the table to add the column to + @param name: The name of the column to alter + @param field: The new field definition to use + """ + self._fix_field_definition(field) + + if not ignore_constraints: + qn = self.quote_name + sch = qn(self._get_schema_name()) + tab = qn(table_name) + table = ".".join([sch, tab]) + try: + self.delete_foreign_key(table_name, name) + except ValueError: + # no FK constraint on this field. That's OK. + pass + constraints = self._find_constraints_for_column(table_name, name, False) + for constraint in constraints.keys(): + params = dict(table_name = table, + constraint_name = qn(constraint)) + sql = self.drop_constraint_string % params + self.execute(sql, []) + + ret_val = super(DatabaseOperations, self).alter_column(table_name, name, field, explicit_name, ignore_constraints=True) + + if not ignore_constraints: + for cname, (ctype,args) in constraints.items(): + params = dict(table = table, + constraint = qn(cname)) + if ctype=='UNIQUE': + params['columns'] = ", ".join(map(qn,args)) + sql = self.create_unique_sql % params + elif ctype=='PRIMARY KEY': + params['columns'] = ", ".join(map(qn,args)) + sql = self.create_primary_key_string % params + elif ctype=='FOREIGN KEY': + continue + # Foreign keys taken care of below + #target = "%s.%s(%s)" % tuple(map(qn,args)) + #params.update(column = qn(name), target = target) + #sql = self.create_foreign_key_sql % params + elif ctype=='CHECK': + warn(ConstraintDropped("CHECK "+ args, table_name, name)) + continue + #TODO: Some check constraints should be restored; but not before the generic + # backend restores them. + #params['check'] = args + #sql = self.create_check_constraint_sql % params + else: + raise NotImplementedError("Don't know how to handle constraints of type "+ type) + self.execute(sql, []) + # Create foreign key if necessary + if field.rel and self.supports_foreign_keys: + self.execute( + self.foreign_key_sql( + table_name, + field.column, + field.rel.to._meta.db_table, + field.rel.to._meta.get_field(field.rel.field_name).column + ) + ) + model = self.mock_model("FakeModelForIndexCreation", table_name) + for stmt in self._get_connection().creation.sql_indexes_for_field(model, field, no_style()): + self.execute(stmt) + + + return ret_val + + def _alter_set_defaults(self, field, name, params, sqls): + "Subcommand of alter_column that sets default values (overrideable)" + # Historically, we used to set defaults here. + # But since South 0.8, we don't ever set defaults on alter-column -- we only + # use database-level defaults as scaffolding when adding columns. + # However, we still sometimes need to remove defaults in alter-column. + table_name = self.quote_name(params['table_name']) + drop_default = self.drop_column_default_sql(table_name, name) + if drop_default: + sqls.append((drop_default, [])) + + def _value_to_unquoted_literal(self, field, value): + # Start with the field's own translation + conn = self._get_connection() + value = field.get_db_prep_save(value, connection=conn) + # This is still a Python object -- nobody expects to need a literal. + if isinstance(value, string_types): + return smart_text(value) + elif isinstance(value, (date,time,datetime)): + return value.isoformat() + else: + #TODO: Anybody else needs special translations? + return str(value) + def _default_value_workaround(self, value): + if isinstance(value, (date,time,datetime)): + return value.isoformat() + else: + return super(DatabaseOperations, self)._default_value_workaround(value) + + def _quote_string(self, s): + return "'" + s.replace("'","''") + "'" + + + def drop_column_default_sql(self, table_name, name, q_name=None): + "MSSQL specific drop default, which is a pain" + + sql = """ + SELECT object_name(cdefault) + FROM syscolumns + WHERE id = object_id('%s') + AND name = '%s' + """ + cons = self.execute(sql % (table_name, name), []) + if cons and cons[0] and cons[0][0]: + return "DROP CONSTRAINT %s" % cons[0][0] + return None + + def _fix_field_definition(self, field): + if isinstance(field, (fields.BooleanField, fields.NullBooleanField)): + if field.default == True: + field.default = 1 + if field.default == False: + field.default = 0 + + # This is copied from South's generic add_column, with two modifications: + # 1) The sql-server-specific call to _fix_field_definition + # 2) Removing a default, when needed, by calling drop_default and not the more general alter_column + @invalidate_table_constraints + def add_column(self, table_name, name, field, keep_default=False): + """ + Adds the column 'name' to the table 'table_name'. + Uses the 'field' paramater, a django.db.models.fields.Field instance, + to generate the necessary sql + + @param table_name: The name of the table to add the column to + @param name: The name of the column to add + @param field: The field to use + """ + self._fix_field_definition(field) + sql = self.column_sql(table_name, name, field) + if sql: + params = ( + self.quote_name(table_name), + sql, + ) + sql = self.add_column_string % params + self.execute(sql) + + # Now, drop the default if we need to + if not keep_default and field.default is not None: + field.default = fields.NOT_PROVIDED + #self.alter_column(table_name, name, field, explicit_name=False, ignore_constraints=True) + self.drop_default(table_name, name, field) + + @invalidate_table_constraints + def drop_default(self, table_name, name, field): + fragment = self.drop_column_default_sql(table_name, name) + if fragment: + table_name = self.quote_name(table_name) + sql = " ".join(["ALTER TABLE", table_name, fragment]) + self.execute(sql) + + + @invalidate_table_constraints + def create_table(self, table_name, field_defs): + # Tweak stuff as needed + for _, f in field_defs: + self._fix_field_definition(f) + + # Run + super(DatabaseOperations, self).create_table(table_name, field_defs) + + def _find_referencing_fks(self, table_name): + "MSSQL does not support cascading FKs when dropping tables, we need to implement." + + # FK -- Foreign Keys + # UCTU -- Unique Constraints Table Usage + # FKTU -- Foreign Key Table Usage + # (last two are both really CONSTRAINT_TABLE_USAGE, different join conditions) + sql = """ + SELECT FKTU.TABLE_SCHEMA as REFING_TABLE_SCHEMA, + FKTU.TABLE_NAME as REFING_TABLE_NAME, + FK.[CONSTRAINT_NAME] as FK_NAME + FROM [INFORMATION_SCHEMA].[REFERENTIAL_CONSTRAINTS] FK + JOIN [INFORMATION_SCHEMA].[CONSTRAINT_TABLE_USAGE] UCTU + ON FK.UNIQUE_CONSTRAINT_CATALOG = UCTU.CONSTRAINT_CATALOG and + FK.UNIQUE_CONSTRAINT_NAME = UCTU.CONSTRAINT_NAME and + FK.UNIQUE_CONSTRAINT_SCHEMA = UCTU.CONSTRAINT_SCHEMA + JOIN [INFORMATION_SCHEMA].[CONSTRAINT_TABLE_USAGE] FKTU + ON FK.CONSTRAINT_CATALOG = FKTU.CONSTRAINT_CATALOG and + FK.CONSTRAINT_NAME = FKTU.CONSTRAINT_NAME and + FK.CONSTRAINT_SCHEMA = FKTU.CONSTRAINT_SCHEMA + WHERE FK.CONSTRAINT_CATALOG = %s + AND UCTU.TABLE_SCHEMA = %s -- REFD_TABLE_SCHEMA + AND UCTU.TABLE_NAME = %s -- REFD_TABLE_NAME + """ + db_name = self._get_setting('name') + schema_name = self._get_schema_name() + return self.execute(sql, [db_name, schema_name, table_name]) + + @invalidate_table_constraints + def delete_table(self, table_name, cascade=True): + """ + Deletes the table 'table_name'. + """ + if cascade: + refing = self._find_referencing_fks(table_name) + for schmea, table, constraint in refing: + table = ".".join(map (self.quote_name, [schmea, table])) + params = dict(table_name = table, + constraint_name = self.quote_name(constraint)) + sql = self.drop_constraint_string % params + self.execute(sql, []) + cascade = False + super(DatabaseOperations, self).delete_table(table_name, cascade) + + @copy_column_constraints + @delete_column_constraints + def rename_column(self, table_name, old, new): + """ + Renames the column of 'table_name' from 'old' to 'new'. + WARNING - This isn't transactional on MSSQL! + """ + if old == new: + # No Operation + return + # Examples on the MS site show the table name not being quoted... + params = (table_name, self.quote_name(old), self.quote_name(new)) + self.execute("EXEC sp_rename '%s.%s', %s, 'COLUMN'" % params) + + @invalidate_table_constraints + def rename_table(self, old_table_name, table_name): + """ + Renames the table 'old_table_name' to 'table_name'. + WARNING - This isn't transactional on MSSQL! + """ + if old_table_name == table_name: + # No Operation + return + params = (self.quote_name(old_table_name), self.quote_name(table_name)) + self.execute('EXEC sp_rename %s, %s' % params) + + def _db_type_for_alter_column(self, field): + return self._db_positive_type_for_alter_column(DatabaseOperations, field) + + def _alter_add_column_mods(self, field, name, params, sqls): + return self._alter_add_positive_check(DatabaseOperations, field, name, params, sqls) + + @invalidate_table_constraints + def delete_foreign_key(self, table_name, column): + super(DatabaseOperations, self).delete_foreign_key(table_name, column) + # A FK also implies a non-unique index + find_index_sql = """ + SELECT i.name -- s.name, t.name, c.name + FROM sys.tables t + INNER JOIN sys.schemas s ON t.schema_id = s.schema_id + INNER JOIN sys.indexes i ON i.object_id = t.object_id + INNER JOIN sys.index_columns ic ON ic.object_id = t.object_id + AND ic.index_id = i.index_id + INNER JOIN sys.columns c ON c.object_id = t.object_id + AND ic.column_id = c.column_id + WHERE i.is_unique=0 AND i.is_primary_key=0 AND i.is_unique_constraint=0 + AND s.name = %s + AND t.name = %s + AND c.name = %s + """ + schema = self._get_schema_name() + indexes = self.execute(find_index_sql, [schema, table_name, column]) + qn = self.quote_name + for index in (i[0] for i in indexes if i[0]): # "if i[0]" added because an empty name may return + self.execute("DROP INDEX %s on %s.%s" % (qn(index), qn(schema), qn(table_name) )) + |