schema_test.rb 22.6 KB
Newer Older
1
require "cases/helper"
2 3
require "models/default"
require "support/schema_dumping_helper"
4

5 6 7
module PGSchemaHelper
  def with_schema_search_path(schema_search_path)
    @connection.schema_search_path = schema_search_path
8
    @connection.schema_cache.clear!
9 10 11
    yield if block_given?
  ensure
    @connection.schema_search_path = "'$user', public"
12
    @connection.schema_cache.clear!
13 14 15
  end
end

16
class SchemaTest < ActiveRecord::PostgreSQLTestCase
17
  include PGSchemaHelper
18
  self.use_transactional_tests = false
19

20 21 22 23 24 25 26 27 28 29 30 31
  SCHEMA_NAME = "test_schema"
  SCHEMA2_NAME = "test_schema2"
  TABLE_NAME = "things"
  CAPITALIZED_TABLE_NAME = "Things"
  INDEX_A_NAME = "a_index_things_on_name"
  INDEX_B_NAME = "b_index_things_on_different_columns_in_each_schema"
  INDEX_C_NAME = "c_index_full_text_search"
  INDEX_D_NAME = "d_index_things_on_description_desc"
  INDEX_E_NAME = "e_index_things_on_name_vector"
  INDEX_A_COLUMN = "name"
  INDEX_B_COLUMN_S1 = "email"
  INDEX_B_COLUMN_S2 = "moment"
32
  INDEX_C_COLUMN = "(to_tsvector('english', coalesce(things.name, '')))"
33 34
  INDEX_D_COLUMN = "description"
  INDEX_E_COLUMN = "name_vector"
35
  COLUMNS = [
36 37 38 39 40 41
    "id integer",
    "name character varying(50)",
    "email character varying(50)",
    "description character varying(100)",
    "name_vector tsvector",
    "moment timestamp without time zone default now()"
42
  ]
43 44 45
  PK_TABLE_NAME = "table_with_pk"
  UNMATCHED_SEQUENCE_NAME = "unmatched_primary_key_default_value_seq"
  UNMATCHED_PK_TABLE_NAME = "table_with_unmatched_sequence_for_pk"
46

47
  class Thing1 < ActiveRecord::Base
48
    self.table_name = "test_schema.things"
49 50 51
  end

  class Thing2 < ActiveRecord::Base
52
    self.table_name = "test_schema2.things"
53 54 55
  end

  class Thing3 < ActiveRecord::Base
56
    self.table_name = 'test_schema."things.table"'
57 58
  end

59
  class Thing4 < ActiveRecord::Base
60
    self.table_name = 'test_schema."Things"'
61 62
  end

63
  class Thing5 < ActiveRecord::Base
64
    self.table_name = "things"
65 66
  end

67 68 69 70 71 72 73 74 75 76
  class Song < ActiveRecord::Base
    self.table_name = "music.songs"
    has_and_belongs_to_many :albums
  end

  class Album < ActiveRecord::Base
    self.table_name = "music.albums"
    has_and_belongs_to_many :songs
  end

77 78 79
  def setup
    @connection = ActiveRecord::Base.connection
    @connection.execute "CREATE SCHEMA #{SCHEMA_NAME} CREATE TABLE #{TABLE_NAME} (#{COLUMNS.join(',')})"
80
    @connection.execute "CREATE TABLE #{SCHEMA_NAME}.\"#{TABLE_NAME}.table\" (#{COLUMNS.join(',')})"
81
    @connection.execute "CREATE TABLE #{SCHEMA_NAME}.\"#{CAPITALIZED_TABLE_NAME}\" (#{COLUMNS.join(',')})"
82 83 84 85 86
    @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});"
87 88
    @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});"
89 90
    @connection.execute "CREATE INDEX #{INDEX_D_NAME} ON #{SCHEMA_NAME}.#{TABLE_NAME}  USING btree (#{INDEX_D_COLUMN} DESC);"
    @connection.execute "CREATE INDEX #{INDEX_D_NAME} ON #{SCHEMA2_NAME}.#{TABLE_NAME}  USING btree (#{INDEX_D_COLUMN} DESC);"
D
doabit 已提交
91 92
    @connection.execute "CREATE INDEX #{INDEX_E_NAME} ON #{SCHEMA_NAME}.#{TABLE_NAME}  USING gin (#{INDEX_E_COLUMN});"
    @connection.execute "CREATE INDEX #{INDEX_E_NAME} ON #{SCHEMA2_NAME}.#{TABLE_NAME}  USING gin (#{INDEX_E_COLUMN});"
93
    @connection.execute "CREATE TABLE #{SCHEMA_NAME}.#{PK_TABLE_NAME} (id serial primary key)"
94 95
    @connection.execute "CREATE SEQUENCE #{SCHEMA_NAME}.#{UNMATCHED_SEQUENCE_NAME}"
    @connection.execute "CREATE TABLE #{SCHEMA_NAME}.#{UNMATCHED_PK_TABLE_NAME} (id integer NOT NULL DEFAULT nextval('#{SCHEMA_NAME}.#{UNMATCHED_SEQUENCE_NAME}'::regclass), CONSTRAINT unmatched_pkey PRIMARY KEY (id))"
96 97
  end

G
Guo Xiang Tan 已提交
98
  teardown do
99 100
    @connection.drop_schema SCHEMA2_NAME, if_exists: true
    @connection.drop_schema SCHEMA_NAME, if_exists: true
101 102
  end

103
  def test_schema_names
104
    assert_equal ["public", "test_schema", "test_schema2"], @connection.schema_names
105 106
  end

107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128
  def test_create_schema
    begin
      @connection.create_schema "test_schema3"
      assert @connection.schema_names.include? "test_schema3"
    ensure
      @connection.drop_schema "test_schema3"
    end
  end

  def test_raise_create_schema_with_existing_schema
    begin
      @connection.create_schema "test_schema3"
      assert_raises(ActiveRecord::StatementInvalid) do
        @connection.create_schema "test_schema3"
      end
    ensure
      @connection.drop_schema "test_schema3"
    end
  end

  def test_drop_schema
    begin
D
dfens 已提交
129
      @connection.create_schema "test_schema3"
130 131 132
    ensure
      @connection.drop_schema "test_schema3"
    end
133
    assert_not_includes @connection.schema_names, "test_schema3"
134 135
  end

136 137 138 139 140 141 142
  def test_drop_schema_if_exists
    @connection.create_schema "some_schema"
    assert_includes @connection.schema_names, "some_schema"
    @connection.drop_schema "some_schema", if_exists: true
    assert_not_includes @connection.schema_names, "some_schema"
  end

143
  def test_habtm_table_name_with_schema
144 145
    ActiveRecord::Base.connection.drop_schema "music", if_exists: true
    ActiveRecord::Base.connection.create_schema "music"
146 147 148 149 150 151 152
    ActiveRecord::Base.connection.execute <<-SQL
      CREATE TABLE music.albums (id serial primary key);
      CREATE TABLE music.songs (id serial primary key);
      CREATE TABLE music.albums_songs (album_id integer, song_id integer);
    SQL

    song = Song.create
153
    Album.create
154 155
    assert_equal song, Song.includes(:albums).references(:albums).first
  ensure
156
    ActiveRecord::Base.connection.drop_schema "music", if_exists: true
157 158
  end

159
  def test_drop_schema_with_nonexisting_schema
160
    assert_raises(ActiveRecord::StatementInvalid) do
161 162 163 164 165
      @connection.drop_schema "idontexist"
    end

    assert_nothing_raised do
      @connection.drop_schema "idontexist", if_exists: true
166 167 168
    end
  end

A
Akira Matsuda 已提交
169
  def test_raise_wrapped_exception_on_bad_prepare
170
    assert_raises(ActiveRecord::StatementInvalid) do
171
      @connection.exec_query "select * from developers where id = ?", "sql", [bind_param(1)]
172 173 174
    end
  end

175 176 177
  if ActiveRecord::Base.connection.prepared_statements
    def test_schema_change_with_prepared_stmt
      altered = false
178 179
      @connection.exec_query "select * from developers where id = $1", "sql", [bind_param(1)]
      @connection.exec_query "alter table developers add column zomg int", "sql", []
180
      altered = true
181
      @connection.exec_query "select * from developers where id = $1", "sql", [bind_param(1)]
182 183 184
    ensure
      # We are not using DROP COLUMN IF EXISTS because that syntax is only
      # supported by pg 9.X
185
      @connection.exec_query("alter table developers drop column zomg", "sql", []) if altered
186
    end
187 188
  end

189
  def test_data_source_exists?
190 191
    [Thing1, Thing2, Thing3, Thing4].each do |klass|
      name = klass.table_name
192
      assert @connection.data_source_exists?(name), "'#{name}' data_source should exist"
193 194 195
    end
  end

196
  def test_data_source_exists_when_on_schema_search_path
197
    with_schema_search_path(SCHEMA_NAME) do
198
      assert(@connection.data_source_exists?(TABLE_NAME), "data_source should exist and be found")
199 200 201
    end
  end

202
  def test_data_source_exists_when_not_on_schema_search_path
203
    with_schema_search_path("PUBLIC") do
204
      assert(!@connection.data_source_exists?(TABLE_NAME), "data_source exists but should not be found")
205 206 207
    end
  end

208 209
  def test_data_source_exists_wrong_schema
    assert(!@connection.data_source_exists?("foo.things"), "data_source should not exist")
210 211
  end

212
  def test_data_source_exists_quoted_names
213
    [ %("#{SCHEMA_NAME}"."#{TABLE_NAME}"), %(#{SCHEMA_NAME}."#{TABLE_NAME}"), %(#{SCHEMA_NAME}."#{TABLE_NAME}")].each do |given|
214
      assert(@connection.data_source_exists?(given), "data_source should exist when specified as #{given}")
215 216 217
    end
    with_schema_search_path(SCHEMA_NAME) do
      given = %("#{TABLE_NAME}")
218
      assert(@connection.data_source_exists?(given), "data_source should exist when specified as #{given}")
219
    end
220 221
  end

222
  def test_data_source_exists_quoted_table
223
    with_schema_search_path(SCHEMA_NAME) do
224
      assert(@connection.data_source_exists?('"things.table"'), "data_source should exist")
225 226 227
    end
  end

228 229 230 231 232 233
  def test_with_schema_prefixed_table_name
    assert_nothing_raised do
      assert_equal COLUMNS, columns("#{SCHEMA_NAME}.#{TABLE_NAME}")
    end
  end

234 235 236 237 238 239
  def test_with_schema_prefixed_capitalized_table_name
    assert_nothing_raised do
      assert_equal COLUMNS, columns("#{SCHEMA_NAME}.#{CAPITALIZED_TABLE_NAME}")
    end
  end

240 241 242 243 244 245 246 247
  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

248
  def test_proper_encoding_of_table_name
249
    assert_equal '"table_name"', @connection.quote_table_name("table_name")
250
    assert_equal '"table.name"', @connection.quote_table_name('"table.name"')
251
    assert_equal '"schema_name"."table_name"', @connection.quote_table_name("schema_name.table_name")
252 253 254 255 256 257 258 259 260
    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
261
    assert_equal 0, Thing4.count
262

263
    Thing1.create(id: 1, name: "thing1", email: "thing1@localhost", moment: Time.now)
264 265 266
    assert_equal 1, Thing1.count
    assert_equal 0, Thing2.count
    assert_equal 0, Thing3.count
267
    assert_equal 0, Thing4.count
268

269
    Thing2.create(id: 1, name: "thing1", email: "thing1@localhost", moment: Time.now)
270 271 272
    assert_equal 1, Thing1.count
    assert_equal 1, Thing2.count
    assert_equal 0, Thing3.count
273
    assert_equal 0, Thing4.count
274

275
    Thing3.create(id: 1, name: "thing1", email: "thing1@localhost", moment: Time.now)
276 277 278
    assert_equal 1, Thing1.count
    assert_equal 1, Thing2.count
    assert_equal 1, Thing3.count
279 280
    assert_equal 0, Thing4.count

281
    Thing4.create(id: 1, name: "thing1", email: "thing1@localhost", moment: Time.now)
282 283 284 285
    assert_equal 1, Thing1.count
    assert_equal 1, Thing2.count
    assert_equal 1, Thing3.count
    assert_equal 1, Thing4.count
286 287
  end

288
  def test_raise_on_unquoted_schema_name
289
    assert_raises(ActiveRecord::StatementInvalid) do
290
      with_schema_search_path "$user,public"
291 292 293 294
    end
  end

  def test_without_schema_search_path
295
    assert_raises(ActiveRecord::StatementInvalid) { columns(TABLE_NAME) }
296 297 298 299 300 301
  end

  def test_ignore_nil_schema_search_path
    assert_nothing_raised { with_schema_search_path nil }
  end

302 303
  def test_index_name_exists
    with_schema_search_path(SCHEMA_NAME) do
304 305 306 307 308 309 310
      assert @connection.index_name_exists?(TABLE_NAME, INDEX_A_NAME)
      assert @connection.index_name_exists?(TABLE_NAME, INDEX_B_NAME)
      assert @connection.index_name_exists?(TABLE_NAME, INDEX_C_NAME)
      assert @connection.index_name_exists?(TABLE_NAME, INDEX_D_NAME)
      assert @connection.index_name_exists?(TABLE_NAME, INDEX_E_NAME)
      assert @connection.index_name_exists?(TABLE_NAME, INDEX_E_NAME)
      assert_not @connection.index_name_exists?(TABLE_NAME, "missing_index")
311 312 313
    end
  end

314
  def test_dump_indexes_for_schema_one
D
doabit 已提交
315
    do_dump_index_tests_for_schema(SCHEMA_NAME, INDEX_A_COLUMN, INDEX_B_COLUMN_S1, INDEX_D_COLUMN, INDEX_E_COLUMN)
316 317 318
  end

  def test_dump_indexes_for_schema_two
D
doabit 已提交
319
    do_dump_index_tests_for_schema(SCHEMA2_NAME, INDEX_A_COLUMN, INDEX_B_COLUMN_S2, INDEX_D_COLUMN, INDEX_E_COLUMN)
320 321
  end

322
  def test_dump_indexes_for_schema_multiple_schemas_in_search_path
D
doabit 已提交
323
    do_dump_index_tests_for_schema("public, #{SCHEMA_NAME}", INDEX_A_COLUMN, INDEX_B_COLUMN_S1, INDEX_D_COLUMN, INDEX_E_COLUMN)
324 325
  end

326 327
  def test_dump_indexes_for_table_with_scheme_specified_in_name
    indexes = @connection.indexes("#{SCHEMA_NAME}.#{TABLE_NAME}")
328
    assert_equal 5, indexes.size
329 330
  end

331
  def test_with_uppercase_index_name
G
Guo Xiang Tan 已提交
332
    @connection.execute "CREATE INDEX \"things_Index\" ON #{SCHEMA_NAME}.things (name)"
333

G
Guo Xiang Tan 已提交
334
    with_schema_search_path SCHEMA_NAME do
335
      assert_nothing_raised { @connection.remove_index "things", name: "things_Index" }
G
Guo Xiang Tan 已提交
336
    end
337 338
  end

339 340 341 342 343 344 345 346 347 348 349 350 351 352
  def test_remove_index_when_schema_specified
    @connection.execute "CREATE INDEX \"things_Index\" ON #{SCHEMA_NAME}.things (name)"
    assert_nothing_raised { @connection.remove_index "things", name: "#{SCHEMA_NAME}.things_Index" }

    @connection.execute "CREATE INDEX \"things_Index\" ON #{SCHEMA_NAME}.things (name)"
    assert_nothing_raised { @connection.remove_index "#{SCHEMA_NAME}.things", name: "things_Index" }

    @connection.execute "CREATE INDEX \"things_Index\" ON #{SCHEMA_NAME}.things (name)"
    assert_nothing_raised { @connection.remove_index "#{SCHEMA_NAME}.things", name: "#{SCHEMA_NAME}.things_Index" }

    @connection.execute "CREATE INDEX \"things_Index\" ON #{SCHEMA_NAME}.things (name)"
    assert_raises(ArgumentError) { @connection.remove_index "#{SCHEMA2_NAME}.things", name: "#{SCHEMA_NAME}.things_Index" }
  end

353
  def test_primary_key_with_schema_specified
354 355 356 357 358
    [
      %("#{SCHEMA_NAME}"."#{PK_TABLE_NAME}"),
      %(#{SCHEMA_NAME}."#{PK_TABLE_NAME}"),
      %(#{SCHEMA_NAME}.#{PK_TABLE_NAME})
    ].each do |given|
359
      assert_equal "id", @connection.primary_key(given), "primary key should be found when table referenced as #{given}"
360 361 362 363 364
    end
  end

  def test_primary_key_assuming_schema_search_path
    with_schema_search_path(SCHEMA_NAME) do
365
      assert_equal "id", @connection.primary_key(PK_TABLE_NAME), "primary key should be found"
366 367 368 369 370 371 372 373 374 375 376
    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

377
  def test_pk_and_sequence_for_with_schema_specified
378
    pg_name = ActiveRecord::ConnectionAdapters::PostgreSQL::Name
379 380
    [
      %("#{SCHEMA_NAME}"."#{PK_TABLE_NAME}"),
381
      %("#{SCHEMA_NAME}"."#{UNMATCHED_PK_TABLE_NAME}")
382 383
    ].each do |given|
      pk, seq = @connection.pk_and_sequence_for(given)
384
      assert_equal "id", pk, "primary key should be found when table referenced as #{given}"
385
      assert_equal pg_name.new(SCHEMA_NAME, "#{PK_TABLE_NAME}_id_seq"), seq, "sequence name should be found when table referenced as #{given}" if given == %("#{SCHEMA_NAME}"."#{PK_TABLE_NAME}")
386
      assert_equal pg_name.new(SCHEMA_NAME, UNMATCHED_SEQUENCE_NAME), seq, "sequence name should be found when table referenced as #{given}" if given == %("#{SCHEMA_NAME}"."#{UNMATCHED_PK_TABLE_NAME}")
387 388 389
    end
  end

390 391
  def test_current_schema
    {
392
      %('$user',public)                        => "public",
393
      SCHEMA_NAME                              => SCHEMA_NAME,
394
      %(#{SCHEMA2_NAME},#{SCHEMA_NAME},public) => SCHEMA2_NAME,
395
      %(public,#{SCHEMA2_NAME},#{SCHEMA_NAME}) => "public"
396
    }.each do |given, expect|
397 398 399 400
      with_schema_search_path(given) { assert_equal expect, @connection.current_schema }
    end
  end

401
  def test_prepared_statements_with_multiple_schemas
G
Guo Xiang Tan 已提交
402 403
    [SCHEMA_NAME, SCHEMA2_NAME].each do |schema_name|
      with_schema_search_path schema_name do
404
        Thing5.create(id: 1, name: "thing inside #{SCHEMA_NAME}", email: "thing1@localhost", moment: Time.now)
G
Guo Xiang Tan 已提交
405 406
      end
    end
407

G
Guo Xiang Tan 已提交
408 409 410 411 412
    [SCHEMA_NAME, SCHEMA2_NAME].each do |schema_name|
      with_schema_search_path schema_name do
        assert_equal 1, Thing5.count
      end
    end
413 414
  end

415 416
  def test_schema_exists?
    {
417
      "public"     => true,
418 419
      SCHEMA_NAME  => true,
      SCHEMA2_NAME => true,
420
      "darkside"   => false
421
    }.each do |given, expect|
422 423 424 425
      assert_equal expect, @connection.schema_exists?(given)
    end
  end

426 427 428
  def test_reset_pk_sequence
    sequence_name = "#{SCHEMA_NAME}.#{UNMATCHED_SEQUENCE_NAME}"
    @connection.execute "SELECT setval('#{sequence_name}', 123)"
429
    assert_equal 124, @connection.select_value("SELECT nextval('#{sequence_name}')")
430
    @connection.reset_pk_sequence!("#{SCHEMA_NAME}.#{UNMATCHED_PK_TABLE_NAME}")
431
    assert_equal 1, @connection.select_value("SELECT nextval('#{sequence_name}')")
432 433
  end

434 435 436 437
  def test_set_pk_sequence
    table_name = "#{SCHEMA_NAME}.#{PK_TABLE_NAME}"
    _, sequence_name = @connection.pk_and_sequence_for table_name
    @connection.set_pk_sequence! table_name, 123
438
    assert_equal 124, @connection.select_value("SELECT nextval('#{sequence_name}')")
439 440 441
    @connection.reset_pk_sequence! table_name
  end

442 443 444
  private
    def columns(table_name)
      @connection.send(:column_definitions, table_name).map do |name, type, default|
445
        "#{name} #{type}" + (default ? " default #{default}" : "")
446 447 448
      end
    end

D
doabit 已提交
449
    def do_dump_index_tests_for_schema(this_schema_name, first_index_column_name, second_index_column_name, third_index_column_name, fourth_index_column_name)
450
      with_schema_search_path(this_schema_name) do
451
        indexes = @connection.indexes(TABLE_NAME).sort_by(&:name)
452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467
        assert_equal 5, indexes.size

        index_a, index_b, index_c, index_d, index_e = indexes

        do_dump_index_assertions_for_one_index(index_a, INDEX_A_NAME, first_index_column_name)
        do_dump_index_assertions_for_one_index(index_b, INDEX_B_NAME, second_index_column_name)
        do_dump_index_assertions_for_one_index(index_d, INDEX_D_NAME, third_index_column_name)
        do_dump_index_assertions_for_one_index(index_e, INDEX_E_NAME, fourth_index_column_name)

        assert_equal :btree, index_a.using
        assert_equal :btree, index_b.using
        assert_equal :gin,   index_c.using
        assert_equal :btree, index_d.using
        assert_equal :gin,   index_e.using

        assert_equal :desc,  index_d.orders[INDEX_D_COLUMN]
468 469 470 471 472 473 474 475 476
      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
S
Sean Griffin 已提交
477 478 479 480

    def bind_param(value)
      ActiveRecord::Relation::QueryAttribute.new(nil, value, ActiveRecord::Type::Value.new)
    end
481
end
482

483
class SchemaForeignKeyTest < ActiveRecord::PostgreSQLTestCase
484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499
  include SchemaDumpingHelper

  setup do
    @connection = ActiveRecord::Base.connection
  end

  def test_dump_foreign_key_targeting_different_schema
    @connection.create_schema "my_schema"
    @connection.create_table "my_schema.trains" do |t|
      t.string :name
    end
    @connection.create_table "wagons" do |t|
      t.integer :train_id
    end
    @connection.add_foreign_key "wagons", "my_schema.trains", column: "train_id"
    output = dump_table_schema "wagons"
500
    assert_match %r{\s+add_foreign_key "wagons", "my_schema\.trains", column: "train_id"$}, output
501
  ensure
502 503
    @connection.drop_table "wagons", if_exists: true
    @connection.drop_table "my_schema.trains", if_exists: true
504
    @connection.drop_schema "my_schema", if_exists: true
505 506
  end
end
507

508
class DefaultsUsingMultipleSchemasAndDomainTest < ActiveRecord::PostgreSQLTestCase
509 510
  setup do
    @connection = ActiveRecord::Base.connection
511
    @connection.drop_schema "schema_1", if_exists: true
512 513 514 515 516 517 518 519 520 521
    @connection.execute "CREATE SCHEMA schema_1"
    @connection.execute "CREATE DOMAIN schema_1.text AS text"
    @connection.execute "CREATE DOMAIN schema_1.varchar AS varchar"
    @connection.execute "CREATE DOMAIN schema_1.bpchar AS bpchar"

    @old_search_path = @connection.schema_search_path
    @connection.schema_search_path = "schema_1, pg_catalog"
    @connection.create_table "defaults" do |t|
      t.text "text_col", default: "some value"
      t.string "string_col", default: "some value"
522
      t.decimal "decimal_col", default: "3.14159265358979323846"
523 524 525 526 527 528
    end
    Default.reset_column_information
  end

  teardown do
    @connection.schema_search_path = @old_search_path
529
    @connection.drop_schema "schema_1", if_exists: true
530 531 532 533 534 535 536 537 538 539 540
    Default.reset_column_information
  end

  def test_text_defaults_in_new_schema_when_overriding_domain
    assert_equal "some value", Default.new.text_col, "Default of text column was not correctly parsed"
  end

  def test_string_defaults_in_new_schema_when_overriding_domain
    assert_equal "some value", Default.new.string_col, "Default of string column was not correctly parsed"
  end

541 542 543 544
  def test_decimal_defaults_in_new_schema_when_overriding_domain
    assert_equal BigDecimal.new("3.14159265358979323846"), Default.new.decimal_col, "Default of decimal column was not correctly parsed"
  end

545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560
  def test_bpchar_defaults_in_new_schema_when_overriding_domain
    @connection.execute "ALTER TABLE defaults ADD bpchar_col bpchar DEFAULT 'some value'"
    Default.reset_column_information
    assert_equal "some value", Default.new.bpchar_col, "Default of bpchar column was not correctly parsed"
  end

  def test_text_defaults_after_updating_column_default
    @connection.execute "ALTER TABLE defaults ALTER COLUMN text_col SET DEFAULT 'some text'::schema_1.text"
    assert_equal "some text", Default.new.text_col, "Default of text column was not correctly parsed after updating default using '::text' since postgreSQL will add parens to the default in db"
  end

  def test_default_containing_quote_and_colons
    @connection.execute "ALTER TABLE defaults ALTER COLUMN string_col SET DEFAULT 'foo''::bar'"
    assert_equal "foo'::bar", Default.new.string_col
  end
end
561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597

class SchemaWithDotsTest < ActiveRecord::PostgreSQLTestCase
  include PGSchemaHelper
  self.use_transactional_tests = false

  setup do
    @connection = ActiveRecord::Base.connection
    @connection.create_schema "my.schema"
  end

  teardown do
    @connection.drop_schema "my.schema", if_exists: true
  end

  test "rename_table" do
    with_schema_search_path('"my.schema"') do
      @connection.create_table :posts
      @connection.rename_table :posts, :articles
      assert_equal ["articles"], @connection.tables
    end
  end

  test "Active Record basics" do
    with_schema_search_path('"my.schema"') do
      @connection.create_table :articles do |t|
        t.string :title
      end
      article_class = Class.new(ActiveRecord::Base) do
        self.table_name = '"my.schema".articles'
      end

      article_class.create!(title: "zOMG, welcome to my blorgh!")
      welcome_article = article_class.last
      assert_equal "zOMG, welcome to my blorgh!", welcome_article.title
    end
  end
end