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

Ticket #8838: duplicate_join_table_names_fix_with_tests.diff

File duplicate_join_table_names_fix_with_tests.diff, 5.0 kB (added by jcoglan, 1 year ago)

This is the latest version of the patch, not the '.2' file

  • activerecord/lib/active_record/base.rb

    old new  
    11671167          connection.add_lock!(sql, options) 
    11681168        end 
    11691169 
     1170        # Returns the names (or aliases if used) of each table used in a JOIN fragment 
     1171        def table_aliases_from_join_fragment(sql) 
     1172          return [] if sql.blank? 
     1173          return sql.scan(/JOIN\s+(`[^`]+`|"[^"]+"|\[[^\]]+\]|\S+)(?:\s+(?:AS\s+)?(`[^`]+`|"[^"]+"|\[[^\]]+\]|\S+))?/i).collect do |name| 
     1174            ((name[1] =~ /^ON$/i) ? name[0] : (name[1] || name[0])).gsub(/^[`"\[]?(.*)[`"\]]?$/, '\1') 
     1175          end 
     1176        end 
     1177 
    11701178        # The optional scope argument is for the current :find scope. 
    11711179        def add_joins!(sql, options, scope = :auto) 
    11721180          scope = scope(:find) if :auto == scope 
    11731181          join = (scope && scope[:joins]) || options[:joins] 
    1174           sql << " #{join} " if join 
     1182          return if join.blank? 
     1183          extend_sql_avoiding_table_naming_clashes!(sql, scope && scope[:joins]) 
     1184          extend_sql_avoiding_table_naming_clashes!(sql, options[:joins]) 
    11751185        end 
    11761186 
     1187        # Appends +addition+ to +sql+ by checking for table name clashes between the two 
     1188        # fragments. Table aliases in +sql+ are changed as necessary before appending +addition+. 
     1189        # It's done this way round so that manually specifying <tt>:joins</tt> retains the table name 
     1190        # you specify, reducing the potential for confusion. 
     1191        def extend_sql_avoiding_table_naming_clashes!(sql, addition) 
     1192          used_table_aliases = table_aliases_from_join_fragment(addition) 
     1193          old_table_aliases = table_aliases_from_join_fragment(sql) 
     1194          (used_table_aliases & old_table_aliases).each do |join_table_alias| 
     1195            i = 0 
     1196            begin 
     1197              i += 1 
     1198              new_alias = "renamed_join_table_#{i}" 
     1199            end until !used_table_aliases.include?(new_alias) 
     1200            convert_table_name_to_new_alias!(sql, join_table_alias, new_alias) 
     1201          end 
     1202          sql << " #{addition} " 
     1203        end 
     1204 
     1205        # Modifies the SQL fragment such that every instance of +old_table_name+ 
     1206        # is replaced by or aliased using (in JOIN ... AS blocks) +new_alias+. 
     1207        def convert_table_name_to_new_alias!(sql, old_table_name, new_alias) 
     1208          regex = Regexp.new("(?:(?:JOIN|AS)?\\s+|\\()[`\"\\[]?#{old_table_name}[`\"\\]]?(?:\\s+(?:AS\\s+)?(?:`[^`]+`|\"[^\"]+\"|\\[[^\\]]+\\]|\\S+)|\\.|\\s)", Regexp::IGNORECASE) 
     1209          sql.gsub!(regex) do |match| 
     1210            prefix = (match =~ /^\(/) ? '(' : '' 
     1211            suffix = match.gsub(/^.*?(\s+ON|.)$/i, '\1') 
     1212            if test = match.match(/^JOIN\s+(?:`[^`]+`|"[^"]+"|\[[^\]]+\]|\S+)(\s+(?:AS\s+)?(?:`[^`]+`|"[^"]+"|\[[^\]]+\]|\S+))/i) and !(test.captures.first =~ /^ ON$/i) 
     1213              # If the table name is already aliased within this match, don't replace it 
     1214              result = match 
     1215            else 
     1216              replacement = "JOIN #{old_table_name} AS #{new_alias}" if match =~ /^JOIN\s/i 
     1217              replacement = "AS #{new_alias}" if match =~ /^AS\s/i 
     1218              replacement = " #{new_alias}" unless match =~ /^(JOIN|AS)\s/i 
     1219              result = "#{prefix}#{replacement}#{suffix}" 
     1220            end 
     1221            result 
     1222          end 
     1223        end 
     1224 
    11771225        # Adds a sanitized version of +conditions+ to the +sql+ string. Note that the passed-in +sql+ string is changed. 
    11781226        # The optional scope argument is for the current :find scope. 
    11791227        def add_conditions!(sql, conditions, scope = :auto) 
  • activerecord/test/associations/eager_test.rb

    old new  
    240240    assert posts[1].categories.include?(categories(:general)) 
    241241  end 
    242242 
     243  def test_eager_association_loading_with_circular_habtm 
     244    assert_equal 4, categories(:general).posts.find(:all, :include => :categories).size 
     245    assert_equal 2, categories(:general).posts.find(:first, :include => :categories, :conditions => 'posts.id = 1').categories.size 
     246  end 
     247 
    243248  def test_eager_with_inheritance 
    244249    posts = SpecialPost.find(:all, :include => [ :comments ]) 
    245250  end 
     
    390395      assert_equal 3, authors(:david).posts_with_comments.count(:conditions => "length(comments.body) > 15") 
    391396    end 
    392397  end 
     398 
     399  def test_eager_load_with_inner_join 
     400    assert_not_nil posts(:welcome) 
     401    assert posts(:welcome).people.size > 0 
     402    assert posts(:welcome).people.find(:all).size > 0 
     403    id = posts(:welcome).id 
     404    assert_equal posts(:welcome).people.find(:all).size, posts(:welcome).people.find(:all, :include => :posts).size 
     405    assert_equal posts(:welcome).people.find(:all).size, Person.find(:all, :include => :posts, :joins => 'inner join readers on readers.person_id = people.id', :conditions => ['readers.post_id = ?',id]).size 
     406  end 
    393407end