Commits

Andrew Godwin committed 6494af8 Merge

Merged in shaib/south (pull request #134)

Fixes for defaults and Oracle

  • Participants
  • Parent commits 4adc9e1, 247ecc3

Comments (0)

Files changed (5)

File south/db/firebird.py

 
     def _alter_set_defaults(self, field, name, params, sqls):
         "Subcommand of alter_column that sets default values (overrideable)"
-        # Next, set any default
-        if not field.null and field.has_default():
-            default = field.get_default()
-            sqls.append(('ALTER COLUMN %s SET DEFAULT %%s ' % (self.quote_name(name),), [default]))
-        elif self._column_has_default(params):
+        # 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.
+        if self._column_has_default(params):
             sqls.append(('ALTER COLUMN %s DROP DEFAULT' % (self.quote_name(name),), []))
 
 

File south/db/generic.py

 
     def _alter_set_defaults(self, field, name, params, sqls):
         "Subcommand of alter_column that sets default values (overrideable)"
-        # Next, set any default
-        if not field.null and field.has_default():
-            default = field.get_db_prep_save(field.get_default(), connection=self._get_connection())
-            sqls.append(('ALTER COLUMN %s SET DEFAULT %%s ' % (self.quote_name(name),), [default]))
-        else:
-            sqls.append(('ALTER COLUMN %s DROP DEFAULT' % (self.quote_name(name),), []))
+        # 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.
+        sqls.append(('ALTER COLUMN %s DROP DEFAULT' % (self.quote_name(name),), []))
 
     def _update_nulls_to_default(self, params, field):
         "Subcommand of alter_column that updates nulls to default value (overrideable)"
             sqls.append((self.alter_string_drop_null % params, []))
 
         # Do defaults
-        #self._alter_set_defaults(field, name, params, sqls)
+        self._alter_set_defaults(field, name, params, sqls)
 
         # Actually change the column (step 1 -- Nullity may need to be fixed)
         if self.allows_combined_alters:

File south/db/oracle.py

 
         for field_name, field in fields:
             
+            field = self._field_sanity(field)
+
             # avoid default values in CREATE TABLE statements (#925)
             field._suppress_default = True
 
+
             col = self.column_sql(table_name, field_name, field)
             if not col:
                 continue
             'nullity': 'NOT NULL',
             'default': 'NULL'
         }
-        if field.null:
+        if field.null:
             params['nullity'] = 'NULL'
 
         sql_templates = [
-            (self.alter_string_set_type, params),
-            (self.alter_string_set_default, params),
+            (self.alter_string_set_type, params, []),
+            (self.alter_string_set_default, params, []),
         ]
         if not field.null and field.has_default():
             # Use default for rows that had nulls. To support the case where
                 p.update(kw)
                 return p
             sql_templates[:0] = [
-                (self.alter_string_set_type, change_params(nullity='NULL')),
-                (self.alter_string_update_nulls_to_default, change_params(default=self._default_value_workaround(field.get_default()))),
+                (self.alter_string_set_type, change_params(nullity='NULL'),[]),
+                (self.alter_string_update_nulls_to_default, change_params(default="%s"), [field.get_default()]),
             ]
 
 
                 'constraint': self.quote_name(constraint),
             })
 
-        for sql_template, params in sql_templates:
+        for sql_template, params, args in sql_templates:
             try:
-                self.execute(sql_template % params, print_all_errors=False)
+                self.execute(sql_template % params, args, print_all_errors=False)
             except DatabaseError as exc:
                 description = str(exc)
                 # Oracle complains if a column is already NULL/NOT NULL
 
     @generic.invalidate_table_constraints
     def add_column(self, table_name, name, field, keep_default=False):
+        field = self._field_sanity(field)
         sql = self.column_sql(table_name, name, field)
         sql = self.adj_column_sql(sql)
 
         """
         if isinstance(field, models.BooleanField) and field.has_default():
             field.default = int(field.to_python(field.get_default()))
+        # On Oracle, empty strings are null
+        if isinstance(field, (models.CharField, models.TextField)):
+            field.null = field.empty_strings_allowed
         return field
+
 
     def _default_value_workaround(self, value):
         from datetime import date,time,datetime

File south/db/sql_server/pyodbc.py

     
     def _alter_set_defaults(self, field, name, params, sqls): 
         "Subcommand of alter_column that sets default values (overrideable)"
-        # First drop the current default if one exists
+        # 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, []))
             
-        # Next, set any default
-        
-        if field.has_default():
-            default = field.get_default()
-            literal = self._value_to_unquoted_literal(field, default)
-            sqls.append(('ADD DEFAULT %s for %s' % (self._quote_string(literal), self.quote_name(name),), []))
-
     def _value_to_unquoted_literal(self, field, value):
         # Start with the field's own translation
         conn = self._get_connection()

File south/tests/db.py

         null = db.execute("SELECT spam FROM test_altercd")[0][0]
         self.assertFalse(null, "Default for char field was installed into database")
 
+        # Change again to a column with default and not null
+        db.alter_column("test_altercd", "spam", models.CharField(max_length=30, default="loof", null=False))
+        # Assert the default is not in the database
+        if 'oracle' in db.backend_name:
+            # Oracle special treatment -- nulls are always allowed in char columns, so 
+            # inserting doesn't raise an integrity error; so we check again as above
+            db.execute("DELETE FROM test_altercd")
+            db.execute("INSERT INTO test_altercd (eggs) values (12)")
+            null = db.execute("SELECT spam FROM test_altercd")[0][0]
+            self.assertFalse(null, "Default for char field was installed into database")
+        else:
+            # For other backends, insert should now just fail
+            self.assertRaises(IntegrityError,
+                              db.execute, "INSERT INTO test_altercd (eggs) values (12)")
+
     @skipIf('oracle' in db.backend_name, "Oracle does not differentiate empty trings from null")
     def test_default_empty_string(self):
         """
         empty = db.execute("SELECT ham FROM test_cd_empty")[0][0]
         self.assertEquals(empty, "", "Empty Default for char field isn't empty string")
         
+    @skipUnless('oracle' in db.backend_name, "Oracle does not differentiate empty trings from null")
+    def test_oracle_strings_null(self):
+        """
+        Test that under Oracle, CherFields are created as null even when specified not-null,
+        because otherwise they would not be able to hold empty strings (which Oracle equates
+        with nulls).
+        Verify fix of #1269.
+        """
+        db.create_table("test_ora_char_nulls", [
+            ('spam', models.CharField(max_length=30, null=True)),
+            ('eggs', models.CharField(max_length=30)),
+        ])
+        db.add_column("test_ora_char_nulls", "ham", models.CharField(max_length=30))
+        db.alter_column("test_ora_char_nulls", "spam", models.CharField(max_length=30, null=False))
+        # So, by the look of it, we should now have three not-null columns
+        db.execute("INSERT INTO test_ora_char_nulls VALUES (NULL, NULL, NULL)")
+        
+
     def test_mysql_defaults(self):
         """
         Test MySQL default handling for BLOB and TEXT.