schema_test.rb 10.3 KB
Newer Older
1
require "cases/helper"
2

3
class SchemaTest < ActiveRecord::TestCase
4 5
  self.use_transactional_fixtures = false

6
  SCHEMA_NAME = 'test_schema'
7
  SCHEMA2_NAME = 'test_schema2'
8
  TABLE_NAME = 'things'
9
  CAPITALIZED_TABLE_NAME = 'Things'
10 11
  INDEX_A_NAME = 'a_index_things_on_name'
  INDEX_B_NAME = 'b_index_things_on_different_columns_in_each_schema'
12
  INDEX_C_NAME = 'c_index_full_text_search'
13 14 15
  INDEX_A_COLUMN = 'name'
  INDEX_B_COLUMN_S1 = 'email'
  INDEX_B_COLUMN_S2 = 'moment'
16
  INDEX_C_COLUMN = %q{(to_tsvector('english', coalesce(things.name, '')))}
17 18 19
  COLUMNS = [
    'id integer',
    'name character varying(50)',
20
    'email character varying(50)',
21 22
    'moment timestamp without time zone default now()'
  ]
23
  PK_TABLE_NAME = 'table_with_pk'
24

25 26 27 28 29 30 31 32 33 34 35 36
  class Thing1 < ActiveRecord::Base
    set_table_name "test_schema.things"
  end

  class Thing2 < ActiveRecord::Base
    set_table_name "test_schema2.things"
  end

  class Thing3 < ActiveRecord::Base
    set_table_name 'test_schema."things.table"'
  end

37 38 39 40
  class Thing4 < ActiveRecord::Base
    set_table_name 'test_schema."Things"'
  end

41 42 43 44
  class PrimaryKeyTestHarness < ActiveRecord::Base
    set_table_name 'test_schema.pktest'
  end

45 46 47
  def setup
    @connection = ActiveRecord::Base.connection
    @connection.execute "CREATE SCHEMA #{SCHEMA_NAME} CREATE TABLE #{TABLE_NAME} (#{COLUMNS.join(',')})"
48
    @connection.execute "CREATE TABLE #{SCHEMA_NAME}.\"#{TABLE_NAME}.table\" (#{COLUMNS.join(',')})"
49
    @connection.execute "CREATE TABLE #{SCHEMA_NAME}.\"#{CAPITALIZED_TABLE_NAME}\" (#{COLUMNS.join(',')})"
50 51 52 53 54
    @connection.execute "CREATE SCHEMA #{SCHEMA2_NAME} CREATE TABLE #{TABLE_NAME} (#{COLUMNS.join(',')})"
    @connection.execute "CREATE INDEX #{INDEX_A_NAME} ON #{SCHEMA_NAME}.#{TABLE_NAME}  USING btree (#{INDEX_A_COLUMN});"
    @connection.execute "CREATE INDEX #{INDEX_A_NAME} ON #{SCHEMA2_NAME}.#{TABLE_NAME}  USING btree (#{INDEX_A_COLUMN});"
    @connection.execute "CREATE INDEX #{INDEX_B_NAME} ON #{SCHEMA_NAME}.#{TABLE_NAME}  USING btree (#{INDEX_B_COLUMN_S1});"
    @connection.execute "CREATE INDEX #{INDEX_B_NAME} ON #{SCHEMA2_NAME}.#{TABLE_NAME}  USING btree (#{INDEX_B_COLUMN_S2});"
55 56
    @connection.execute "CREATE INDEX #{INDEX_C_NAME} ON #{SCHEMA_NAME}.#{TABLE_NAME}  USING gin (#{INDEX_C_COLUMN});"
    @connection.execute "CREATE INDEX #{INDEX_C_NAME} ON #{SCHEMA2_NAME}.#{TABLE_NAME}  USING gin (#{INDEX_C_COLUMN});"
57
    @connection.execute "CREATE TABLE #{SCHEMA_NAME}.#{PK_TABLE_NAME} (id serial primary key)"
58 59 60
  end

  def teardown
61
    @connection.execute "DROP SCHEMA #{SCHEMA2_NAME} CASCADE"
62
    @connection.execute "DROP SCHEMA #{SCHEMA_NAME} CASCADE"
63 64
  end

65 66 67 68 69 70 71
  def test_table_exists?
    [Thing1, Thing2, Thing3, Thing4].each do |klass|
      name = klass.table_name
      assert @connection.table_exists?(name), "'#{name}' table should exist"
    end
  end

72 73 74 75 76 77 78 79 80 81 82 83
  def test_table_exists_when_on_schema_search_path
    with_schema_search_path(SCHEMA_NAME) do
      assert(@connection.table_exists?(TABLE_NAME), "table should exist and be found")
    end
  end

  def test_table_exists_when_not_on_schema_search_path
    with_schema_search_path('PUBLIC') do
      assert(!@connection.table_exists?(TABLE_NAME), "table exists but should not be found")
    end
  end

84 85 86 87
  def test_table_exists_wrong_schema
    assert(!@connection.table_exists?("foo.things"), "table should not exist")
  end

88 89 90 91 92 93 94 95
  def test_table_exists_quoted_names
    [ %("#{SCHEMA_NAME}"."#{TABLE_NAME}"), %(#{SCHEMA_NAME}."#{TABLE_NAME}"), %(#{SCHEMA_NAME}."#{TABLE_NAME}")].each do |given|
      assert(@connection.table_exists?(given), "table should exist when specified as #{given}")
    end
    with_schema_search_path(SCHEMA_NAME) do
      given = %("#{TABLE_NAME}")
      assert(@connection.table_exists?(given), "table should exist when specified as #{given}")
    end
96 97
  end

98 99 100 101 102 103
  def test_with_schema_prefixed_table_name
    assert_nothing_raised do
      assert_equal COLUMNS, columns("#{SCHEMA_NAME}.#{TABLE_NAME}")
    end
  end

104 105 106 107 108 109
  def test_with_schema_prefixed_capitalized_table_name
    assert_nothing_raised do
      assert_equal COLUMNS, columns("#{SCHEMA_NAME}.#{CAPITALIZED_TABLE_NAME}")
    end
  end

110 111 112 113 114 115 116 117
  def test_with_schema_search_path
    assert_nothing_raised do
      with_schema_search_path(SCHEMA_NAME) do
        assert_equal COLUMNS, columns(TABLE_NAME)
      end
    end
  end

118 119 120 121 122 123 124 125 126 127 128 129 130 131

  def test_proper_encoding_of_table_name
    assert_equal '"table_name"', @connection.quote_table_name('table_name')
    assert_equal '"table.name"', @connection.quote_table_name('"table.name"')
    assert_equal '"schema_name"."table_name"', @connection.quote_table_name('schema_name.table_name')
    assert_equal '"schema_name"."table.name"', @connection.quote_table_name('schema_name."table.name"')
    assert_equal '"schema.name"."table_name"', @connection.quote_table_name('"schema.name".table_name')
    assert_equal '"schema.name"."table.name"', @connection.quote_table_name('"schema.name"."table.name"')
  end

  def test_classes_with_qualified_schema_name
    assert_equal 0, Thing1.count
    assert_equal 0, Thing2.count
    assert_equal 0, Thing3.count
132
    assert_equal 0, Thing4.count
133 134 135 136 137

    Thing1.create(:id => 1, :name => "thing1", :email => "thing1@localhost", :moment => Time.now)
    assert_equal 1, Thing1.count
    assert_equal 0, Thing2.count
    assert_equal 0, Thing3.count
138
    assert_equal 0, Thing4.count
139 140 141 142 143

    Thing2.create(:id => 1, :name => "thing1", :email => "thing1@localhost", :moment => Time.now)
    assert_equal 1, Thing1.count
    assert_equal 1, Thing2.count
    assert_equal 0, Thing3.count
144
    assert_equal 0, Thing4.count
145 146 147 148 149

    Thing3.create(:id => 1, :name => "thing1", :email => "thing1@localhost", :moment => Time.now)
    assert_equal 1, Thing1.count
    assert_equal 1, Thing2.count
    assert_equal 1, Thing3.count
150 151 152 153 154 155 156
    assert_equal 0, Thing4.count

    Thing4.create(:id => 1, :name => "thing1", :email => "thing1@localhost", :moment => Time.now)
    assert_equal 1, Thing1.count
    assert_equal 1, Thing2.count
    assert_equal 1, Thing3.count
    assert_equal 1, Thing4.count
157 158
  end

159 160 161 162 163 164 165 166 167 168 169 170 171 172
  def test_raise_on_unquoted_schema_name
    assert_raise(ActiveRecord::StatementInvalid) do
      with_schema_search_path '$user,public'
    end
  end

  def test_without_schema_search_path
    assert_raise(ActiveRecord::StatementInvalid) { columns(TABLE_NAME) }
  end

  def test_ignore_nil_schema_search_path
    assert_nothing_raised { with_schema_search_path nil }
  end

173 174 175 176 177 178 179 180
  def test_dump_indexes_for_schema_one
    do_dump_index_tests_for_schema(SCHEMA_NAME, INDEX_A_COLUMN, INDEX_B_COLUMN_S1)
  end

  def test_dump_indexes_for_schema_two
    do_dump_index_tests_for_schema(SCHEMA2_NAME, INDEX_A_COLUMN, INDEX_B_COLUMN_S2)
  end

181 182
  def test_with_uppercase_index_name
    ActiveRecord::Base.connection.execute "CREATE INDEX \"things_Index\" ON #{SCHEMA_NAME}.things (name)"
183
    assert_nothing_raised { ActiveRecord::Base.connection.remove_index! "things", "#{SCHEMA_NAME}.things_Index"}
184 185 186

    ActiveRecord::Base.connection.execute "CREATE INDEX \"things_Index\" ON #{SCHEMA_NAME}.things (name)"
    ActiveRecord::Base.connection.schema_search_path = SCHEMA_NAME
187
    assert_nothing_raised { ActiveRecord::Base.connection.remove_index! "things", "things_Index"}
188 189 190
    ActiveRecord::Base.connection.schema_search_path = "public"
  end

191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247
  def test_primary_key_with_schema_specified
    [ %("#{SCHEMA_NAME}"."#{PK_TABLE_NAME}"), %(#{SCHEMA_NAME}."#{PK_TABLE_NAME}"), %(#{SCHEMA_NAME}."#{PK_TABLE_NAME}")].each do |given|
      assert_equal 'id', @connection.primary_key(given), "primary key should be found when table referenced as #{given}"
    end
  end

  def test_primary_key_assuming_schema_search_path
    with_schema_search_path(SCHEMA_NAME) do
      assert_equal 'id', @connection.primary_key(PK_TABLE_NAME), "primary key should be found"
    end
  end

  def test_primary_key_raises_error_if_table_not_found_on_schema_search_path
    with_schema_search_path(SCHEMA2_NAME) do
      assert_raises(ActiveRecord::StatementInvalid) do
        @connection.primary_key(PK_TABLE_NAME)
      end
    end
  end

  def test_extract_schema_and_table
    {
      %(table_name)            => [nil,'table_name'],
      %("table.name")          => [nil,'"table.name"'],
      %(schema.table_name)     => %w{schema table_name},
      %("schema".table_name)   => %w{"schema" table_name},
      %(schema."table_name")   => %w{schema "table_name"},
      %("schema"."table_name") => %w{"schema" "table_name"},
      %("even spaces".table)   => ['"even spaces"','table'],
      %(schema."table.name")   => %w{schema "table.name"}
    }.each do |given,expect|
      assert_equal expect, @connection.extract_schema_and_table(given)
    end
  end

  def test_current_schema
    {
      %('$user',public) => 'public',
      SCHEMA_NAME => SCHEMA_NAME,
      %(#{SCHEMA2_NAME},#{SCHEMA_NAME},public) => SCHEMA2_NAME,
      %(public,#{SCHEMA2_NAME},#{SCHEMA_NAME}) => 'public'
    }.each do |given,expect|
      with_schema_search_path(given) { assert_equal expect, @connection.current_schema }
    end
  end

  def test_schema_exists?
    {
      'public'     => true,
      SCHEMA_NAME  => true,
      SCHEMA2_NAME => true,
      'darkside'   => false
    }.each do |given,expect|
      assert_equal expect, @connection.schema_exists?(given)
    end
  end

248 249 250 251 252 253 254 255 256 257 258 259 260
  private
    def columns(table_name)
      @connection.send(:column_definitions, table_name).map do |name, type, default|
        "#{name} #{type}" + (default ? " default #{default}" : '')
      end
    end

    def with_schema_search_path(schema_search_path)
      @connection.schema_search_path = schema_search_path
      yield if block_given?
    ensure
      @connection.schema_search_path = "'$user', public"
    end
261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277

    def do_dump_index_tests_for_schema(this_schema_name, first_index_column_name, second_index_column_name)
      with_schema_search_path(this_schema_name) do
        indexes = @connection.indexes(TABLE_NAME).sort_by {|i| i.name}
        assert_equal 2,indexes.size

        do_dump_index_assertions_for_one_index(indexes[0], INDEX_A_NAME, first_index_column_name)
        do_dump_index_assertions_for_one_index(indexes[1], INDEX_B_NAME, second_index_column_name)
      end
    end

    def do_dump_index_assertions_for_one_index(this_index, this_index_name, this_index_column)
      assert_equal TABLE_NAME, this_index.table
      assert_equal 1, this_index.columns.size
      assert_equal this_index_column, this_index.columns[0]
      assert_equal this_index_name, this_index.name
    end
278
end