Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
Postgres schema: Constrain sequence search classid
The pk_an_sequence_for query previously joined against pg_class's oid
for rows in pg_depend, but pg_depend's objid may point to other system
tables, such as pg_attrdef.  If a row in one of those other tables
coincidentally has the same oid as an (unrelated) sequence, that
sequence name may be returned instead of the real one.

This ensures that only the pg_depend entries pointing to pg_class are
considered.
  • Loading branch information
Josh Williams committed Mar 20, 2014
1 parent 8cea8ae commit 0110d7b
Show file tree
Hide file tree
Showing 3 changed files with 51 additions and 0 deletions.
5 changes: 5 additions & 0 deletions activerecord/CHANGELOG.md
Expand Up @@ -6,6 +6,11 @@

*Matthew Draper*

* `pk_and_sequence_for` now ensures that only the pg_depend entries
pointing to pg_class, and thus only sequence objects, are considered.

*Josh Williams*

* `where.not` adds `references` for `includes` like normal `where` calls do.

Fixes #14406.
Expand Down
Expand Up @@ -327,6 +327,7 @@ def pk_and_sequence_for(table) #:nodoc:
AND attr.attrelid = cons.conrelid
AND attr.attnum = cons.conkey[1]
AND cons.contype = 'p'
AND dep.classid = 'pg_class'::regclass
AND dep.refobjid = '#{quote_table_name(table)}'::regclass
end_sql

Expand Down
Expand Up @@ -176,6 +176,51 @@ def test_pk_and_sequence_for_returns_nil_if_table_not_found
assert_nil @connection.pk_and_sequence_for('unobtainium')
end

def test_pk_and_sequence_for_with_collision_pg_class_oid
@connection.exec_query('create table ex(id serial primary key)')
@connection.exec_query('create table ex2(id serial primary key)')

correct_depend_record = [
"'pg_class'::regclass",
"'ex_id_seq'::regclass",
'0',
"'pg_class'::regclass",
"'ex'::regclass",
'1',
"'a'"
]

collision_depend_record = [
"'pg_attrdef'::regclass",
"'ex2_id_seq'::regclass",
'0',
"'pg_class'::regclass",
"'ex'::regclass",
'1',
"'a'"
]

@connection.exec_query(
"DELETE FROM pg_depend WHERE objid = 'ex_id_seq'::regclass AND refobjid = 'ex'::regclass AND deptype = 'a'"
)
@connection.exec_query(
"INSERT INTO pg_depend VALUES(#{collision_depend_record.join(',')})"
)
@connection.exec_query(
"INSERT INTO pg_depend VALUES(#{correct_depend_record.join(',')})"
)

seq = @connection.pk_and_sequence_for('ex').last
assert_equal 'ex_id_seq', seq

@connection.exec_query(
"DELETE FROM pg_depend WHERE objid = 'ex2_id_seq'::regclass AND refobjid = 'ex'::regclass AND deptype = 'a'"
)
ensure
@connection.exec_query('DROP TABLE IF EXISTS ex')
@connection.exec_query('DROP TABLE IF EXISTS ex2')
end

def test_exec_insert_number
with_example_table do
insert(@connection, 'number' => 10)
Expand Down

0 comments on commit 0110d7b

Please sign in to comment.