1""" 2 Oracle database specific implementations of changeset classes. 3""" 4import sqlalchemy as sa 5from sqlalchemy.databases import oracle as sa_base 6 7from migrate import exceptions 8from migrate.changeset import ansisql 9 10 11OracleSchemaGenerator = sa_base.OracleDDLCompiler 12 13 14class OracleColumnGenerator(OracleSchemaGenerator, ansisql.ANSIColumnGenerator): 15 pass 16 17 18class OracleColumnDropper(ansisql.ANSIColumnDropper): 19 pass 20 21 22class OracleSchemaChanger(OracleSchemaGenerator, ansisql.ANSISchemaChanger): 23 24 def get_column_specification(self, column, **kwargs): 25 # Ignore the NOT NULL generated 26 override_nullable = kwargs.pop('override_nullable', None) 27 if override_nullable: 28 orig = column.nullable 29 column.nullable = True 30 ret = super(OracleSchemaChanger, self).get_column_specification( 31 column, **kwargs) 32 if override_nullable: 33 column.nullable = orig 34 return ret 35 36 def visit_column(self, delta): 37 keys = delta.keys() 38 39 if 'name' in keys: 40 self._run_subvisit(delta, 41 self._visit_column_name, 42 start_alter=False) 43 44 if len(set(('type', 'nullable', 'server_default')).intersection(keys)): 45 self._run_subvisit(delta, 46 self._visit_column_change, 47 start_alter=False) 48 49 def _visit_column_change(self, table, column, delta): 50 # Oracle cannot drop a default once created, but it can set it 51 # to null. We'll do that if default=None 52 # http://forums.oracle.com/forums/message.jspa?messageID=1273234#1273234 53 dropdefault_hack = (column.server_default is None \ 54 and 'server_default' in delta.keys()) 55 # Oracle apparently doesn't like it when we say "not null" if 56 # the column's already not null. Fudge it, so we don't need a 57 # new function 58 notnull_hack = ((not column.nullable) \ 59 and ('nullable' not in delta.keys())) 60 # We need to specify NULL if we're removing a NOT NULL 61 # constraint 62 null_hack = (column.nullable and ('nullable' in delta.keys())) 63 64 if dropdefault_hack: 65 column.server_default = sa.PassiveDefault(sa.sql.null()) 66 if notnull_hack: 67 column.nullable = True 68 colspec = self.get_column_specification(column, 69 override_nullable=null_hack) 70 if null_hack: 71 colspec += ' NULL' 72 if notnull_hack: 73 column.nullable = False 74 if dropdefault_hack: 75 column.server_default = None 76 77 self.start_alter_table(table) 78 self.append("MODIFY (") 79 self.append(colspec) 80 self.append(")") 81 82 83class OracleConstraintCommon(object): 84 85 def get_constraint_name(self, cons): 86 # Oracle constraints can't guess their name like other DBs 87 if not cons.name: 88 raise exceptions.NotSupportedError( 89 "Oracle constraint names must be explicitly stated") 90 return cons.name 91 92 93class OracleConstraintGenerator(OracleConstraintCommon, 94 ansisql.ANSIConstraintGenerator): 95 pass 96 97 98class OracleConstraintDropper(OracleConstraintCommon, 99 ansisql.ANSIConstraintDropper): 100 pass 101 102 103class OracleDialect(ansisql.ANSIDialect): 104 columngenerator = OracleColumnGenerator 105 columndropper = OracleColumnDropper 106 schemachanger = OracleSchemaChanger 107 constraintgenerator = OracleConstraintGenerator 108 constraintdropper = OracleConstraintDropper 109