Ruby on Rails | Screencasts | Download | Documentation | Weblog | Community | Source

Changeset 4601

Show
Ignore:
Timestamp:
07/10/06 18:24:35 (2 years ago)
Author:
bitsweat
Message:

SQLServer: added tests to ensure all database statements are closed, refactored identity_insert management code to use blocks, removed update/delete rowcount code out of execute and into update/delete, changed insert to go through execute method, removed unused quoting methods, disabled pessimistic locking tests as feature is currently unsupported, fixed RakeFile to load sqlserver specific tests whether running in ado or odbc mode, fixed support for recently added decimal types, added support for limits on integer types. Closes #5670.

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • trunk/activerecord/CHANGELOG

    r4600 r4601  
    11*SVN* 
     2 
     3* SQLServer: added tests to ensure all database statements are closed, refactored identity_insert management code to use blocks, removed update/delete rowcount code out of execute and into update/delete, changed insert to go through execute method, removed unused quoting methods, disabled pessimistic locking tests as feature is currently unsupported, fixed RakeFile to load sqlserver specific tests whether running in ado or odbc mode, fixed support for recently added decimal types, added support for limits on integer types. #5670 [Tom Ward] 
     4 
     5* SQLServer: fix db:schema:dump case-sensitivity. #4684 [Will Rogers] 
    26 
    37* Oracle: BigDecimal support. #5667 [schoenm@earthlink.net] 
  • trunk/activerecord/lib/active_record/connection_adapters/sqlserver_adapter.rb

    r4596 r4601  
    1414# Modifications (ODBC): Mark Imbriaco <mark.imbriaco@pobox.com> 
    1515# Date: 6/26/2005 
    16 
    17 # Current maintainer: Ryan Tomayko <rtomayko@gmail.com> 
    18 
     16 
    1917# Modifications (Migrations): Tom Ward <tom@popdog.net> 
    2018# Date: 27/10/2005 
    2119# 
     20# Modifications (Numerous fixes as maintainer): Ryan Tomayko <rtomayko@gmail.com> 
     21# Date: Up to July 2006 
     22 
     23# Current maintainer: Tom Ward <tom@popdog.net> 
    2224 
    2325module ActiveRecord 
     
    8284        end 
    8385      end 
    84  
     86       
    8587      def cast_to_time(value) 
    8688        return value if value.is_a?(Time) 
     
    9092 
    9193      def cast_to_datetime(value) 
     94        return value.to_time if value.is_a?(DBI::Timestamp) 
     95         
    9296        if value.is_a?(Time) 
    9397          if value.year != 0 and value.month != 0 and value.day != 0 
     
    97101          end 
    98102        end 
     103    
     104        if value.is_a?(DateTime) 
     105          return Time.mktime(value.year, value.mon, value.day, value.hour, value.min, value.sec) 
     106        end 
     107         
    99108        return cast_to_time(value) if value.is_a?(Date) or value.is_a?(String) rescue nil 
    100109        value 
     110      end 
     111       
     112      # TODO: Find less hack way to convert DateTime objects into Times 
     113       
     114      def self.string_to_time(value) 
     115        if value.is_a?(DateTime) 
     116          return Time.mktime(value.year, value.mon, value.day, value.hour, value.min, value.sec) 
     117        else 
     118          super 
     119        end 
    101120      end 
    102121 
     
    185204          :time        => { :name => "datetime" }, 
    186205          :date        => { :name => "datetime" }, 
    187           :binary      => { :name => "image" }, 
    188           :boolean     => { :name => "bit"
     206          :binary      => { :name => "image"}, 
     207          :boolean     => { :name => "bit"
    189208        } 
    190209      end 
     
    196215      def supports_migrations? #:nodoc: 
    197216        true 
     217      end 
     218 
     219      def type_to_sql(type, limit = nil, precision = nil, scale = nil) #:nodoc: 
     220        return super unless type.to_s == 'integer' 
     221 
     222        if limit.nil? || limit == 4 
     223          'integer' 
     224        elsif limit < 4 
     225          'smallint' 
     226        else 
     227          'bigint' 
     228        end 
    198229      end 
    199230 
     
    237268        table_name = table_name.to_s if table_name.is_a?(Symbol) 
    238269        table_name = table_name.split('.')[-1] unless table_name.nil? 
    239         sql = "SELECT COLUMN_NAME as ColName, 
    240                       COLUMN_DEFAULT as DefaultValue, 
    241                        DATA_TYPE as ColType, 
    242                        IS_NULLABLE As IsNullable, 
    243                        COL_LENGTH('#{table_name}', COLUMN_NAME) as Length, 
    244                        COLUMNPROPERTY(OBJECT_ID('#{table_name}'), COLUMN_NAME, 'IsIdentity') as IsIdentity, 
    245                        NUMERIC_PRECISION as [Precision], 
    246                        NUMERIC_SCALE as Scale 
    247                FROM INFORMATION_SCHEMA.COLUMNS 
    248                WHERE TABLE_NAME = '#{table_name}'" 
     270        sql = %Q{ 
     271          SELECT  
     272            cols.COLUMN_NAME as ColName,   
     273            cols.COLUMN_DEFAULT as DefaultValue, 
     274            cols.NUMERIC_SCALE as numeric_scale, 
     275            cols.NUMERIC_PRECISION as numeric_precision,  
     276            cols.DATA_TYPE as ColType,  
     277            cols.IS_NULLABLE As IsNullable,   
     278            COL_LENGTH(cols.TABLE_NAME, cols.COLUMN_NAME) as Length,   
     279            COLUMNPROPERTY(OBJECT_ID(cols.TABLE_NAME), cols.COLUMN_NAME, 'IsIdentity') as IsIdentity,   
     280            cols.NUMERIC_SCALE as Scale  
     281          FROM INFORMATION_SCHEMA.COLUMNS cols  
     282          WHERE cols.TABLE_NAME = '#{table_name}'    
     283        } 
    249284        # Comment out if you want to have the Columns select statment logged. 
    250285        # Personally, I think it adds unnecessary bloat to the log.  
     
    256291          default = field[:DefaultValue].to_s.gsub!(/[()\']/,"") =~ /null/ ? nil : field[:DefaultValue] 
    257292          if field[:ColType] =~ /numeric|decimal/i 
    258             type = "#{field[:ColType]}(#{field[:Precision]},#{field[:Scale]})" 
     293            type = "#{field[:ColType]}(#{field[:numeric_precision]},#{field[:numeric_scale]})" 
    259294          else 
    260295            type = "#{field[:ColType]}(#{field[:Length]})" 
     
    268303 
    269304      def insert(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil) 
    270         begin 
    271           table_name = get_table_name(sql) 
    272           col = get_identity_column(table_name) 
    273           ii_enabled = false 
    274  
    275           if col != nil 
    276             if query_contains_identity_column(sql, col) 
    277               begin 
    278                 execute enable_identity_insert(table_name, true) 
    279                 ii_enabled = true 
    280               rescue Exception => e 
    281                 raise ActiveRecordError, "IDENTITY_INSERT could not be turned ON" 
     305        execute(sql, name) 
     306        id_value || select_one("SELECT @@IDENTITY AS Ident")["Ident"] 
     307      end 
     308 
     309      def update(sql, name = nil) 
     310        execute(sql, name) do |handle| 
     311          handle.rows 
     312        end || select_one("SELECT @@ROWCOUNT AS AffectedRows")["AffectedRows"]         
     313      end 
     314       
     315      alias_method :delete, :update 
     316 
     317      def execute(sql, name = nil) 
     318        if sql =~ /^\s*INSERT/i && (table_name = query_requires_identity_insert?(sql)) 
     319          log(sql, name) do 
     320            with_identity_insert_enabled(table_name) do  
     321              @connection.execute(sql) do |handle| 
     322                yield(handle) if block_given? 
    282323              end 
    283324            end 
    284325          end 
    285           log(sql, name) do 
    286             @connection.execute(sql).finish 
    287             id_value || select_one("SELECT @@IDENTITY AS Ident")["Ident"] 
    288           end 
    289         ensure 
    290           if ii_enabled 
    291             begin 
    292               execute enable_identity_insert(table_name, false) 
    293             rescue Exception => e 
    294               raise ActiveRecordError, "IDENTITY_INSERT could not be turned OFF" 
    295             end 
    296           end 
    297         end 
    298       end 
    299  
    300       def execute(sql, name = nil) 
    301         if sql =~ /^\s*INSERT/i 
    302           insert(sql, name) 
    303         elsif sql =~ /^\s*UPDATE|^\s*DELETE/i 
    304           log(sql, name) do 
    305             ret = @connection.execute(sql).finish 
    306             retVal = select_one("SELECT @@ROWCOUNT AS AffectedRows")["AffectedRows"] 
    307           end 
    308326        else 
    309327          log(sql, name) do 
    310             if block_given? 
    311               @connection.execute(sql) do |sth| 
    312                 yield(sth) 
    313               end 
    314             else 
    315               @connection.execute(sql).finish 
     328            @connection.execute(sql) do |handle| 
     329              yield(handle) if block_given? 
    316330            end 
    317331          end 
    318332        end 
    319333      end 
    320  
    321       def update(sql, name = nil) 
    322         execute(sql, name) 
    323       end 
    324       alias_method :delete, :update 
    325  
     334       
    326335      def begin_db_transaction 
    327336        @connection["AutoCommit"] = false 
     
    355364      def quote_string(string) 
    356365        string.gsub(/\'/, "''") 
    357       end 
    358  
    359       def quoted_true 
    360         "1" 
    361       end 
    362        
    363       def quoted_false 
    364         "0" 
    365366      end 
    366367 
     
    421422 
    422423      def tables(name = nil) 
    423         execute("SELECT table_name from information_schema.tables WHERE table_type = 'BASE TABLE'", name) do |sth| 
     424        execute("SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_TYPE = 'BASE TABLE'", name) do |sth| 
    424425          sth.inject([]) do |tables, field| 
    425426            table_name = field[0] 
     
    451452      end 
    452453       
    453       def remove_column(table_name, column_name) 
    454         execute "ALTER TABLE #{table_name} DROP COLUMN #{column_name}" 
    455       end  
    456        
     454      # Adds a new column to the named table. 
     455      # See TableDefinition#column for details of the options you can use. 
     456      def add_column(table_name, column_name, type, options = {}) 
     457        add_column_sql = "ALTER TABLE #{table_name} ADD #{quote_column_name(column_name)} #{type_to_sql(type, options[:limit], options[:precision], options[:scale])}" 
     458        add_column_options!(add_column_sql, options) 
     459        # TODO: Add support to mimic date columns, using constraints to mark them as such in the database 
     460        # add_column_sql << " CONSTRAINT ck__#{table_name}__#{column_name}__date_only CHECK ( CONVERT(CHAR(12), #{quote_column_name(column_name)}, 14)='00:00:00:000' )" if type == :date        
     461        execute(add_column_sql) 
     462      end 
     463        
    457464      def rename_column(table, column, new_column_name) 
    458465        execute "EXEC sp_rename '#{table}.#{column}', '#{new_column_name}'" 
     
    461468      def change_column(table_name, column_name, type, options = {}) #:nodoc: 
    462469        sql_commands = ["ALTER TABLE #{table_name} ALTER COLUMN #{column_name} #{type_to_sql(type, options[:limit], options[:precision], options[:scale])}"] 
    463         if options[:default] 
     470        unless options[:default].nil? 
    464471          remove_default_constraint(table_name, column_name) 
    465           sql_commands << "ALTER TABLE #{table_name} ADD CONSTRAINT DF_#{table_name}_#{column_name} DEFAULT #{options[:default]} FOR #{column_name}" 
     472          sql_commands << "ALTER TABLE #{table_name} ADD CONSTRAINT DF_#{table_name}_#{column_name} DEFAULT #{quote(options[:default])} FOR #{column_name}" 
    466473        end 
    467474        sql_commands.each {|c| 
     
    471478       
    472479      def remove_column(table_name, column_name) 
     480        remove_check_constraints(table_name, column_name) 
    473481        remove_default_constraint(table_name, column_name) 
    474         execute "ALTER TABLE #{table_name} DROP COLUMN #{column_name}
     482        execute "ALTER TABLE [#{table_name}] DROP COLUMN [#{column_name}]
    475483      end 
    476484       
    477485      def remove_default_constraint(table_name, column_name) 
    478         defaults = select "select def.name from sysobjects def, syscolumns col, sysobjects tab where col.cdefault = def.id and col.name = '#{column_name}' and tab.name = '#{table_name}' and col.id = tab.id" 
    479         defaults.each {|constraint| 
     486        constraints = select "select def.name from sysobjects def, syscolumns col, sysobjects tab where col.cdefault = def.id and col.name = '#{column_name}' and tab.name = '#{table_name}' and col.id = tab.id" 
     487         
     488        constraints.each do |constraint| 
    480489          execute "ALTER TABLE #{table_name} DROP CONSTRAINT #{constraint["name"]}" 
    481         } 
     490        end 
     491      end 
     492       
     493      def remove_check_constraints(table_name, column_name) 
     494        # TODO remove all constraints in single method 
     495        constraints = select "SELECT CONSTRAINT_NAME FROM INFORMATION_SCHEMA.CONSTRAINT_COLUMN_USAGE where TABLE_NAME = '#{table_name}' and COLUMN_NAME = '#{column_name}'" 
     496        constraints.each do |constraint| 
     497          execute "ALTER TABLE #{table_name} DROP CONSTRAINT #{constraint["CONSTRAINT_NAME"]}" 
     498        end 
    482499      end 
    483500       
     
    486503      end 
    487504 
    488       private 
     505      private  
    489506        def select(sql, name = nil) 
    490507          rows = [] 
     
    495512              row.column_names.each do |col| 
    496513                record[col] = row[col] 
    497                 record[col] = record[col].to_time if record[col].is_a? DBI::Timestamp 
     514                if record[col].is_a? DBI::Timestamp 
     515                  ts = record[col] 
     516                  record[col] = DateTime.new(ts.year, ts.month, ts.day, ts.hour, ts.minute, ts.sec) 
     517                end 
    498518              end 
    499519              rows << record 
     
    503523        end 
    504524 
    505         def enable_identity_insert(table_name, enable = true) 
    506           if has_identity_column(table_name) 
    507             "SET IDENTITY_INSERT #{table_name} #{enable ? 'ON' : 'OFF'}" 
    508           end 
     525        # Turns IDENTITY_INSERT ON for table during execution of the block 
     526        # N.B. This sets the state of IDENTITY_INSERT to OFF after the 
     527        # block has been executed without regard to its previous state 
     528 
     529        def with_identity_insert_enabled(table_name, &block) 
     530          set_identity_insert(table_name, true) 
     531          yield 
     532        ensure 
     533          set_identity_insert(table_name, false)   
     534        end 
     535         
     536        def set_identity_insert(table_name, enable = true) 
     537          execute "SET IDENTITY_INSERT #{table_name} #{enable ? 'ON' : 'OFF'}" 
     538        rescue Exception => e 
     539          raise ActiveRecordError, "IDENTITY_INSERT could not be turned #{enable ? 'ON' : 'OFF'} for table #{table_name}"   
    509540        end 
    510541 
     
    519550        end 
    520551 
    521         def has_identity_column(table_name) 
    522           !get_identity_column(table_name).nil? 
    523         end 
    524  
    525         def get_identity_column(table_name) 
     552        def identity_column(table_name) 
    526553          @table_columns = {} unless @table_columns 
    527554          @table_columns[table_name] = columns(table_name) if @table_columns[table_name] == nil 
     
    533560        end 
    534561 
    535         def query_contains_identity_column(sql, col) 
    536           sql =~ /\[#{col}\]/ 
     562        def query_requires_identity_insert?(sql) 
     563          table_name = get_table_name(sql) 
     564          id_column = identity_column(table_name) 
     565          sql =~ /\[#{id_column}\]/ ? table_name : nil 
    537566        end 
    538567 
  • trunk/activerecord/Rakefile

    r4588 r4601  
    3131  Rake::TestTask.new("test_#{adapter}") { |t| 
    3232    t.libs << "test" << "test/connections/native_#{adapter}" 
    33     t.pattern = "test/*_test{,_#{adapter}}.rb" 
     33    if adapter =~ /^sqlserver/ 
     34      t.pattern = "test/*_test{,_sqlserver}.rb" 
     35    else 
     36      t.pattern = "test/*_test{,_#{adapter}}.rb" 
     37    end 
    3438    t.verbose = true 
    3539  } 
  • trunk/activerecord/test/locking_test.rb

    r4596 r4601  
    6464# blocks, so separate script called by Kernel#system is needed. 
    6565# (See exec vs. async_exec in the PostgreSQL adapter.) 
    66 class PessimisticLockingTest < Test::Unit::TestCase 
    67   self.use_transactional_fixtures = false 
    68   fixtures :people 
    6966 
    70   def setup 
    71     @allow_concurrency = ActiveRecord::Base.allow_concurrency 
    72     ActiveRecord::Base.allow_concurrency = true 
    73   end 
     67# TODO: The SQL Server adapter currently has no support for pessimistic locking 
    7468 
    75   def teardown 
    76     ActiveRecord::Base.allow_concurrency = @allow_concurrency 
    77   end 
    78  
    79   # Test that the adapter doesn't blow up on add_lock! 
    80   def test_sane_find_with_lock 
    81     assert_nothing_raised do 
    82       Person.transaction do 
    83         Person.find 1, :lock => true 
    84       end 
     69unless current_adapter?(:SQLServerAdapter) 
     70  class PessimisticLockingTest < Test::Unit::TestCase 
     71    self.use_transactional_fixtures = false 
     72    fixtures :people 
     73   
     74    def setup 
     75      @allow_concurrency = ActiveRecord::Base.allow_concurrency 
     76      ActiveRecord::Base.allow_concurrency = true 
    8577    end 
    86   end 
    87  
    88   # Test no-blowup for scoped lock. 
    89   def test_sane_find_with_lock 
    90     assert_nothing_raised do 
    91       Person.transaction do 
    92         Person.with_scope(:find => { :lock => true }) do 
    93           Person.find 1 
     78   
     79    def teardown 
     80      ActiveRecord::Base.allow_concurrency = @allow_concurrency 
     81    end 
     82   
     83    # Test that the adapter doesn't blow up on add_lock! 
     84    def test_sane_find_with_lock 
     85      assert_nothing_raised do 
     86        Person.transaction do 
     87          Person.find 1, :lock => true 
    9488        end 
    9589      end 
    9690    end 
    97   end 
    98  
    99   # Locking a record reloads it. 
    100   def test_sane_lock_method 
    101     assert_nothing_raised do 
    102       Person.transaction do 
    103         person = Person.find 1 
    104         old, person.first_name = person.first_name, 'fooman' 
    105         person.lock! 
    106         assert_equal old, person.first_name 
     91   
     92    # Test no-blowup for scoped lock. 
     93    def test_sane_find_with_lock 
     94      assert_nothing_raised do 
     95        Person.transaction do 
     96          Person.with_scope(:find => { :lock => true }) do 
     97            Person.find 1 
     98          end 
     99        end 
    107100      end 
    108101    end 
    109   end 
    110  
    111   if current_adapter?(:PostgreSQLAdapter, :OracleAdapter) 
    112     def test_no_locks_no_wait 
    113       first, second = duel { Person.find 1 } 
    114       assert first.end > second.end 
     102   
     103    # Locking a record reloads it. 
     104    def test_sane_lock_method 
     105      assert_nothing_raised do 
     106        Person.transaction do 
     107          person = Person.find 1 
     108          old, person.first_name = person.first_name, 'fooman' 
     109          person.lock! 
     110          assert_equal old, person.first_name 
     111        end 
     112      end 
    115113    end 
    116  
    117     def test_second_lock_waits 
    118       assert [0.2, 1, 5].any? { |zzz| 
    119         first, second = duel(zzz) { Person.find 1, :lock => true } 
    120         second.end > first.end 
    121       } 
     114   
     115    if current_adapter?(:PostgreSQLAdapter, :OracleAdapter) 
     116      def test_no_locks_no_wait 
     117        first, second = duel { Person.find 1 } 
     118        assert first.end > second.end 
     119      end 
     120   
     121      def test_second_lock_waits 
     122        assert [0.2, 1, 5].any? { |zzz| 
     123          first, second = duel(zzz) { Person.find 1, :lock => true } 
     124          second.end > first.end 
     125        } 
     126      end 
     127   
     128      protected 
     129        def duel(zzz = 5) 
     130          t0, t1, t2, t3 = nil, nil, nil, nil 
     131   
     132          a = Thread.new do 
     133            t0 = Time.now 
     134            Person.transaction do 
     135              yield 
     136              sleep zzz       # block thread 2 for zzz seconds 
     137            end 
     138            t1 = Time.now 
     139          end 
     140   
     141          b = Thread.new do 
     142            sleep zzz / 2.0   # ensure thread 1 tx starts first 
     143            t2 = Time.now 
     144            Person.transaction { yield } 
     145            t3 = Time.now 
     146          end 
     147   
     148          a.join 
     149          b.join 
     150   
     151          assert t1 > t0 + zzz 
     152          assert t2 > t0 
     153          assert t3 > t2 
     154          [t0.to_f..t1.to_f, t2.to_f..t3.to_f] 
     155        end 
    122156    end 
    123  
    124     protected 
    125       def duel(zzz = 5) 
    126         t0, t1, t2, t3 = nil, nil, nil, nil 
    127  
    128         a = Thread.new do 
    129           t0 = Time.now 
    130           Person.transaction do 
    131             yield 
    132             sleep zzz       # block thread 2 for zzz seconds 
    133           end 
    134           t1 = Time.now 
    135         end 
    136  
    137         b = Thread.new do 
    138           sleep zzz / 2.0   # ensure thread 1 tx starts first 
    139           t2 = Time.now 
    140           Person.transaction { yield } 
    141           t3 = Time.now 
    142         end 
    143  
    144         a.join 
    145         b.join 
    146  
    147         assert t1 > t0 + zzz 
    148         assert t2 > t0 
    149         assert t3 > t2 
    150         [t0.to_f..t1.to_f, t2.to_f..t3.to_f] 
    151       end 
    152157  end 
    153158end 
  • trunk/activerecord/test/migration_test.rb

    r4600 r4601  
    4040      Reminder.reset_column_information 
    4141 
    42       %w(last_name key bio age height wealth birthday favorite_day male 
    43          mail administrator).each do |column| 
     42      %w(last_name key bio age height wealth birthday favorite_day 
     43         male administrator).each do |column| 
    4444        Person.connection.remove_column('people', column) rescue nil 
    4545      end 
     
    178178        t.column :foo, :string 
    179179      end 
    180       Person.connection.execute "insert into testings values (1, 'hello')" 
     180       
     181      con = Person.connection      
     182      Person.connection.execute "insert into testings (#{con.quote_column_name('id')}, #{con.quote_column_name('foo')}) values (1, 'hello')" 
    181183      assert_nothing_raised {Person.connection.add_column :testings, :bar, :string, :null => false, :default => "default" } 
    182184 
    183185      assert_raises(ActiveRecord::StatementInvalid) do 
    184         Person.connection.execute "insert into testings values (2, 'hello', NULL)" 
     186        Person.connection.execute "insert into testings (#{con.quote_column_name('id')}, #{con.quote_column_name('foo')}, #{con.quote_column_name('bar')}) values (2, 'hello', NULL)" 
    185187      end 
    186188    ensure 
     
    268270 
    269271      if current_adapter?(:SQLServerAdapter, :OracleAdapter, :SybaseAdapter) 
    270         # SQL Server, Sybase, and Oracle don't differentiate between date/time 
     272        # Sybase, and Oracle don't differentiate between date/time 
    271273        assert_equal Time, bob.favorite_day.class 
    272274      else 
     
    356358 
    357359        # Using explicit id in insert for compatibility across all databases 
    358         assert_nothing_raised { ActiveRecord::Base.connection.execute "INSERT INTO octopi VALUES (1, 'http://www.foreverflying.com/octopus-black7.jpg')" } 
     360        con = ActiveRecord::Base.connection      
     361        assert_nothing_raised { con.execute "INSERT INTO octopi (#{con.quote_column_name('id')}, #{con.quote_column_name('url')}) VALUES (1, 'http://www.foreverflying.com/octopus-black7.jpg')" } 
    359362 
    360363        assert_equal 'http://www.foreverflying.com/octopus-black7.jpg', ActiveRecord::Base.connection.select_value("SELECT url FROM octopi WHERE id=1") 
     
    380383      assert old_columns.find { |c| c.name == 'approved' and c.type == :boolean and c.default == true } 
    381384      assert_nothing_raised { Topic.connection.change_column :topics, :approved, :boolean, :default => false } 
    382       new_columns = Topic.connection.columns(Topic.table_name, "#{name} Columns") 
     385      new_columns = Topic.connection.columns(Topic.table_name, "#{name} Columns")      
    383386      assert_nil new_columns.find { |c| c.name == 'approved' and c.type == :boolean and c.default == true } 
    384387      assert new_columns.find { |c| c.name == 'approved' and c.type == :boolean and c.default == false }