public
Last active

tables_in_string patch

  • Download Gist
tables_in_string_test.rb
Ruby
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 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 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97
require 'test/unit'
 
# These tests illustrate that the tables_in_string will not properly handle
# oracle table names containing valid non-word characters like '$' or those
# suffixed with a db link. As a consequence, eager loading can revert to the
# left-outer-join strategy and ignore select statements on find (see
# http://gist.github.com/486750).
#
# To fix the issue, the regexp in tables_in_string needs to be updated to
# handle fully-qualified oracle names. As you can see it's kinda ugly to do
# so, and obviously the oraclisms only apply to oracle. I propose
# tables_in_string gets moved onto the adapter.
#
# To run this test:
#
# % ruby tables_in_string_test.rb # tests current, fails
# % PATCH=true ruby tables_in_string_test.rb # tests patch, passes
#
# == Example
#
# Works:
#
# class Dept < ActiveRecord::Base
# set_table_name 'dept'
# end
#
# class Emp < ActiveRecord::Base
# set_table_name 'emp'
# belongs_to :dept, :select => 'id, name nombre'
# end
#
# puts Emp.find(1, :include => :dept).dept.nombre
#
# Doesn't work:
#
# class Dept < ActiveRecord::Base
# set_table_name "schema$name.dept"
# end
#
# class Emp < ActiveRecord::Base
# set_table_name "schema$name.emp"
# belongs_to :dept, :select => "id, name nombre"
# end
#
# Emp.find(1, :include => :dept).dept.nombre # !> NoMethodError
#
class TablesInStringTest < Test::Unit::TestCase
NONQUOTED_OBJECT_NAME = /[A-Za-z][A-z0-9$#]{0,29}/
NONQUOTED_DATABASE_LINK = /[A-Za-z][A-z0-9$#\.@]{0,127}/
# Pick which regexp to use based on the PATCH ENV variable
TABLES_IN_STRING = ENV['PATCH'] == 'true' ?
/((?:#{NONQUOTED_OBJECT_NAME}\.)?#{NONQUOTED_OBJECT_NAME}(?:@#{NONQUOTED_DATABASE_LINK})?)\..?/ :
/([a-zA-Z_][\.\w]+).?\./
# The original code in 3.0 -- see active_record/relation.rb
#
# class String
# def blank?
# self !~ /\S/
# end
# end
#
# def tables_in_string(string)
# return [] if string.blank?
# # ignore raw_sql_ that is used by Oracle adapter as alias for limit/offset subqueries
# string.scan(/([a-zA-Z_][\.\w]+).?\./).flatten.map(&:downcase).uniq - ['raw_sql_']
# end
#
def tables_in_string(string)
return [] if string !~ /\S/
string.scan(TABLES_IN_STRING).flatten.map {|str| str.downcase }.uniq - ['raw_sql_']
end
#
# tables_in_string test
#
def test_tables_in_string
assert_equal %w{one two three}, tables_in_string('one.a, two.b, three.c')
end
def test_tables_in_fully_qualified_string
assert_equal %w{
tablename
schemaname.tablename
schemaname.tablename@dblink
}, tables_in_string('tablename.a, schemaname.tablename.b, schemaname.tablename@dblink.c')
end
def test_tables_in_string_with_acceptable_non_word_characters
assert_equal %w{
abc$#_123.abc$#_123@abc$#@._123
}, tables_in_string('abc$#_123.abc$#_123@abc$#@._123.columnone, abc$#_123.abc$#_123@abc$#@._123.columntwo')
end
end

Please sign in to comment on this gist.

Something went wrong with that request. Please try again.