public
Description: Ruby on Rails
Homepage: http://rubyonrails.org
Clone URL: git://github.com/rails/rails.git
Deal with MySQL's quirky handling of defaults and blob/text columns

[#1043 state:committed]

Signed-off-by: Jeremy Kemper <jeremy@bitsweat.net>
fcheung (author)
Sun Sep 14 04:06:10 -0700 2008
jeremy (committer)
Sun Sep 14 17:11:22 -0700 2008
commit  d51a39ff500d94ea4a81fbc22f0d1c540e83f4e1
tree    94f26fe8ed2fd8680a6e6336d1f0b70b17bc5390
parent  d95943b276d52c5bc4f033e532376667badbad9f
...
1
2
 
 
3
4
5
...
1
2
3
4
5
6
7
0
@@ -1,5 +1,7 @@
0
 *Edge*
0
 
0
+* MySQL: cope with quirky default values for not-null text columns.  #1043 [Frederick Cheung]
0
+
0
 * Multiparameter attributes skip time zone conversion for time-only columns [#1030 state:resolved] [Geoff Buesing]
0
 
0
 * Base.skip_time_zone_conversion_for_attributes uses class_inheritable_accessor, so that subclasses don't overwrite Base [#346 state:resolved] [miloops]
...
40
41
42
 
 
 
 
43
44
45
...
40
41
42
43
44
45
46
47
48
49
0
@@ -40,6 +40,10 @@ module ActiveRecord
0
         type == :integer || type == :float || type == :decimal
0
       end
0
 
0
+      def has_default?
0
+        !default.nil?
0
+      end
0
+
0
       # Returns the Ruby class that corresponds to the abstract data type.
0
       def klass
0
         case type
...
80
81
82
83
 
84
85
86
...
91
92
93
 
 
 
 
 
94
95
96
...
80
81
82
 
83
84
85
86
...
91
92
93
94
95
96
97
98
99
100
101
0
@@ -80,7 +80,7 @@ module ActiveRecord
0
       def extract_default(default)
0
         if type == :binary || type == :text
0
           if default.blank?
0
-            nil
0
+            return null ? nil : ''
0
           else
0
             raise ArgumentError, "#{type} columns cannot have a default value: #{default.inspect}"
0
           end
0
@@ -91,6 +91,11 @@ module ActiveRecord
0
         end
0
       end
0
 
0
+      def has_default?
0
+        return false if type == :binary || type == :text #mysql forbids defaults on blob and text columns
0
+        super
0
+      end
0
+
0
       private
0
         def simplified_type(field_type)
0
           return :boolean if MysqlAdapter.emulate_booleans && field_type.downcase.index("tinyint(1)")
...
102
103
104
105
 
106
107
108
...
102
103
104
 
105
106
107
108
0
@@ -102,7 +102,7 @@ HEADER
0
             spec[:precision] = column.precision.inspect if !column.precision.nil?
0
             spec[:scale]     = column.scale.inspect if !column.scale.nil?
0
             spec[:null]      = 'false' if !column.null
0
-            spec[:default]   = default_string(column.default) if !column.default.nil?
0
+            spec[:default]   = default_string(column.default) if column.has_default?
0
             (spec.keys - [:name, :type]).each{ |k| spec[k].insert(0, "#{k.inspect} => ")}
0
             spec
0
           end.compact
...
19
20
21
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
22
23
24
...
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
0
@@ -19,6 +19,37 @@ class DefaultTest < ActiveRecord::TestCase
0
   end
0
 
0
   if current_adapter?(:MysqlAdapter)
0
+
0
+    #MySQL 5 and higher is quirky with not null text/blob columns.
0
+    #With MySQL Text/blob columns cannot have defaults. If the column is not null MySQL will report that the column has a null default
0
+    #but it behaves as though the column had a default of ''
0
+    def test_mysql_text_not_null_defaults
0
+      klass = Class.new(ActiveRecord::Base)
0
+      klass.table_name = 'test_mysql_text_not_null_defaults'
0
+      klass.connection.create_table klass.table_name do |t|
0
+        t.column :non_null_text, :text, :null => false
0
+        t.column :non_null_blob, :blob, :null => false
0
+        t.column :null_text, :text, :null => true
0
+        t.column :null_blob, :blob, :null => true
0
+      end
0
+      assert_equal '', klass.columns_hash['non_null_blob'].default
0
+      assert_equal '', klass.columns_hash['non_null_text'].default
0
+
0
+      assert_equal nil, klass.columns_hash['null_blob'].default
0
+      assert_equal nil, klass.columns_hash['null_text'].default
0
+
0
+      assert_nothing_raised do
0
+        instance = klass.create!
0
+        assert_equal '', instance.non_null_text
0
+        assert_equal '', instance.non_null_blob
0
+        assert_nil instance.null_text
0
+        assert_nil instance.null_blob
0
+      end
0
+    ensure
0
+      klass.connection.drop_table(klass.table_name) rescue nil
0
+    end
0
+
0
+
0
     # MySQL uses an implicit default 0 rather than NULL unless in strict mode.
0
     # We use an implicit NULL so schema.rb is compatible with other databases.
0
     def test_mysql_integer_not_null_defaults
...
1133
1134
1135
1136
 
 
 
 
 
1137
1138
1139
...
1133
1134
1135
 
1136
1137
1138
1139
1140
1141
1142
1143
0
@@ -1133,7 +1133,11 @@ if ActiveRecord::Base.connection.supports_migrations?
0
       columns = Person.connection.columns(:binary_testings)
0
       data_column = columns.detect { |c| c.name == "data" }
0
 
0
-      assert_nil data_column.default
0
+      if current_adapter?(:MysqlAdapter)
0
+        assert_equal '', data_column.default
0
+      else
0
+        assert_nil data_column.default
0
+      end
0
 
0
       Person.connection.drop_table :binary_testings rescue nil
0
     end

Comments