Skip to content

Commit

Permalink
pg, reset_pk_sequence! respects schemas. Closes #14719.
Browse files Browse the repository at this point in the history
  • Loading branch information
senny committed May 30, 2014
1 parent 7b8d95d commit 290de90
Show file tree
Hide file tree
Showing 5 changed files with 34 additions and 11 deletions.
4 changes: 4 additions & 0 deletions activerecord/CHANGELOG.md
@@ -1,3 +1,7 @@
* PostgreSQL `reset_pk_sequence!` respects schemas. Fixes #14719.

*Yves Senn*

* Keep PostgreSQL `hstore` and `json` attributes as `Hash` in `@attributes`.
Fixes duplication in combination with `store_accessor`.

Expand Down
Expand Up @@ -312,25 +312,27 @@ def pk_and_sequence_for(table) #:nodoc:
# First try looking for a sequence with a dependency on the
# given table's primary key.
result = query(<<-end_sql, 'SCHEMA')[0]
SELECT attr.attname, seq.relname
SELECT attr.attname, nsp.nspname, seq.relname
FROM pg_class seq,
pg_attribute attr,
pg_depend dep,
pg_constraint cons
pg_constraint cons,
pg_namespace nsp
WHERE seq.oid = dep.objid
AND seq.relkind = 'S'
AND attr.attrelid = dep.refobjid
AND attr.attnum = dep.refobjsubid
AND attr.attrelid = cons.conrelid
AND attr.attnum = cons.conkey[1]
AND seq.relnamespace = nsp.oid
AND cons.contype = 'p'
AND dep.classid = 'pg_class'::regclass
AND dep.refobjid = '#{quote_table_name(table)}'::regclass
end_sql

if result.nil? or result.empty?
result = query(<<-end_sql, 'SCHEMA')[0]
SELECT attr.attname,
SELECT attr.attname, nsp.nspname,
CASE
WHEN pg_get_expr(def.adbin, def.adrelid) !~* 'nextval' THEN NULL
WHEN split_part(pg_get_expr(def.adbin, def.adrelid), '''', 2) ~ '.' THEN
Expand All @@ -342,13 +344,19 @@ def pk_and_sequence_for(table) #:nodoc:
JOIN pg_attribute attr ON (t.oid = attrelid)
JOIN pg_attrdef def ON (adrelid = attrelid AND adnum = attnum)
JOIN pg_constraint cons ON (conrelid = adrelid AND adnum = conkey[1])
JOIN pg_namespace nsp ON (t.relnamespace = nsp.oid)
WHERE t.oid = '#{quote_table_name(table)}'::regclass
AND cons.contype = 'p'
AND pg_get_expr(def.adbin, def.adrelid) ~* 'nextval|uuid_generate'
end_sql
end

[result.first, result.last]
pk = result.shift
if result.last
[pk, PostgreSQL::Name.new(*result)]
else
[pk, nil]
end
rescue
nil
end
Expand Down Expand Up @@ -376,7 +384,7 @@ def rename_table(table_name, new_name)
clear_cache!
execute "ALTER TABLE #{quote_table_name(table_name)} RENAME TO #{quote_table_name(new_name)}"
pk, seq = pk_and_sequence_for(new_name)
if seq == "#{table_name}_#{pk}_seq"
if seq.identifier == "#{table_name}_#{pk}_seq"
new_seq = "#{new_name}_#{pk}_seq"
execute "ALTER TABLE #{quote_table_name(seq)} RENAME TO #{quote_table_name(new_seq)}"
end
Expand Down
Expand Up @@ -153,15 +153,17 @@ def test_pk_and_sequence_for
with_example_table do
pk, seq = @connection.pk_and_sequence_for('ex')
assert_equal 'id', pk
assert_equal @connection.default_sequence_name('ex', 'id'), seq
expected = PostgreSQL::Name.new("public", @connection.default_sequence_name('ex', 'id'))
assert_equal expected, seq
end
end

def test_pk_and_sequence_for_with_non_standard_primary_key
with_example_table 'code serial primary key' do
pk, seq = @connection.pk_and_sequence_for('ex')
assert_equal 'code', pk
assert_equal @connection.default_sequence_name('ex', 'code'), seq
expected = PostgreSQL::Name.new("public", @connection.default_sequence_name('ex', 'code'))
assert_equal expected, seq
end
end

Expand Down Expand Up @@ -216,7 +218,7 @@ def test_pk_and_sequence_for_with_collision_pg_class_oid
)

seq = @connection.pk_and_sequence_for('ex').last
assert_equal 'ex_id_seq', seq
assert_equal PostgreSQL::Name.new("public", "ex_id_seq"), seq

@connection.exec_query(
"DELETE FROM pg_depend WHERE objid = 'ex2_id_seq'::regclass AND refobjid = 'ex'::regclass AND deptype = 'a'"
Expand Down
13 changes: 11 additions & 2 deletions activerecord/test/cases/adapters/postgresql/schema_test.rb
Expand Up @@ -331,14 +331,15 @@ def test_primary_key_raises_error_if_table_not_found_on_schema_search_path
end

def test_pk_and_sequence_for_with_schema_specified
pg_name = ActiveRecord::ConnectionAdapters::PostgreSQL::Name
[
%("#{SCHEMA_NAME}"."#{PK_TABLE_NAME}"),
%("#{SCHEMA_NAME}"."#{UNMATCHED_PK_TABLE_NAME}")
].each do |given|
pk, seq = @connection.pk_and_sequence_for(given)
assert_equal 'id', pk, "primary key should be found when table referenced as #{given}"
assert_equal "#{PK_TABLE_NAME}_id_seq", seq, "sequence name should be found when table referenced as #{given}" if given == %("#{SCHEMA_NAME}"."#{PK_TABLE_NAME}")
assert_equal "#{UNMATCHED_SEQUENCE_NAME}", seq, "sequence name should be found when table referenced as #{given}" if given == %("#{SCHEMA_NAME}"."#{UNMATCHED_PK_TABLE_NAME}")
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}")
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}")
end
end

Expand Down Expand Up @@ -378,6 +379,14 @@ def test_schema_exists?
end
end

def test_reset_pk_sequence
sequence_name = "#{SCHEMA_NAME}.#{UNMATCHED_SEQUENCE_NAME}"
@connection.execute "SELECT setval('#{sequence_name}', 123)"
assert_equal "124", @connection.select_value("SELECT nextval('#{sequence_name}')")
@connection.reset_pk_sequence!("#{SCHEMA_NAME}.#{UNMATCHED_PK_TABLE_NAME}")
assert_equal "1", @connection.select_value("SELECT nextval('#{sequence_name}')")
end

private
def columns(table_name)
@connection.send(:column_definitions, table_name).map do |name, type, default|
Expand Down
2 changes: 1 addition & 1 deletion activerecord/test/cases/migration/rename_table_test.rb
Expand Up @@ -74,7 +74,7 @@ def test_rename_table_for_postgresql_should_also_rename_default_sequence

pk, seq = connection.pk_and_sequence_for('octopi')

assert_equal "octopi_#{pk}_seq", seq
assert_equal ConnectionAdapters::PostgreSQL::Name.new("public", "octopi_#{pk}_seq"), seq
end
end
end
Expand Down

0 comments on commit 290de90

Please sign in to comment.