Skip to content

Instantly share code, notes, and snippets.

@thinkerbot
Created July 26, 2010 16:45
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save thinkerbot/490817 to your computer and use it in GitHub Desktop.
Save thinkerbot/490817 to your computer and use it in GitHub Desktop.
tables_in_string patch
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
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment