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) |
|---|
-
activerecord/lib/active_record/base.rb
old new 1167 1167 connection.add_lock!(sql, options) 1168 1168 end 1169 1169 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 1170 1178 # The optional scope argument is for the current :find scope. 1171 1179 def add_joins!(sql, options, scope = :auto) 1172 1180 scope = scope(:find) if :auto == scope 1173 1181 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]) 1175 1185 end 1176 1186 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 1177 1225 # Adds a sanitized version of +conditions+ to the +sql+ string. Note that the passed-in +sql+ string is changed. 1178 1226 # The optional scope argument is for the current :find scope. 1179 1227 def add_conditions!(sql, conditions, scope = :auto) -
activerecord/test/associations/eager_test.rb
old new 240 240 assert posts[1].categories.include?(categories(:general)) 241 241 end 242 242 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 243 248 def test_eager_with_inheritance 244 249 posts = SpecialPost.find(:all, :include => [ :comments ]) 245 250 end … … 390 395 assert_equal 3, authors(:david).posts_with_comments.count(:conditions => "length(comments.body) > 15") 391 396 end 392 397 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 393 407 end