Read my latest article: 8 things I look for in a Ruby on Rails app (posted Thu, 06 Jul 2017 16:59:00 GMT)

PostgreSQL sequences in Rails

Posted by Sat, 20 Aug 2005 06:05:00 GMT

Rails doesn’t support legacy or custom named sequences at the moment. (as far as I am aware). It’s kind of tricky to have it detect the SEQUENCE name automatically (every time).

In PHP, I used this big ugly query to detect this info:
$sql = $db->prepare("SELECT seq.relname::text
                        FROM pg_class src, pg_class seq, pg_namespace, pg_attribute,
                        pg_depend
                        WHERE
                            pg_depend.refobjsubid = pg_attribute.attnum AND
                            pg_depend.refobjid = src.oid AND
                            seq.oid = pg_depend.objid AND
                            src.relnamespace = pg_namespace.oid AND
                            pg_attribute.attrelid = src.oid AND
                            pg_namespace.nspname = ? AND
                            src.relname = ? AND
                            pg_attribute.attname = ?");

I used this to mimmick the mysql_insert_id function in PHP for PostgreSQL… ( pg_insert_id )

Well, with Rails, I thought that I would build a similar patch, as the current code just assumes the value would be {column}_id_seq.

After hours of playing around and thinking that I figured it all out ... I decided to run a quick test with a non standard sequence name… like this one:

testingdb=# \d legacy.foobar
                                     Table "legacy.foobar" 
  Column   |         Type          |                         Modifiers                          
-----------+-----------------------+------------------------------------------------------------
 foobar_id | integer               | not null default nextval('legacy.old_sequence_name'::text)
 name      | character varying(40) | 
Indexes:
    "foobar_pkey" PRIMARY KEY, btree (foobar_id)

testingdb=# INSERT INTO legacy.foobar (name) VALUES ('abc')
testingdb-# ;
INSERT 17514 1
testingdb=# SELECT * FROM legacy.foobar ;
 foobar_id | name 
-----------+------
       106 | abc
(1 row)

My patch wouldn’t figure that out because the sequence was not created by SERIAL. So, my patch started to feel lame and a total waste of time, because I thought that it was fixing a problem.. that works pretty much as effectively as assuming it is _seq.. but without needing to run a SQL query to determine that. We all (should) know that the field will be named like that when working with SERIAL. So, my patch didn’t buy us anything.

However, Active Record still doesn’t support those funky sequence names. So, I found this ticket #1273.

Their approach was very similar to what caused me to use my long SQL query in the first place because this was suggested to me well over a year ago and I found it to not work in the following situation.

If I have two seperate schemas with the same table name in each like so:

=# \d legacy.people
                                       Table "legacy.people" 
  Column   |         Type          |                           Modifiers                           
-----------+-----------------------+---------------------------------------------------------------
 people_id | integer               | not null default nextval('legacy.people_people_id_seq'::text)
 name      | character varying(50) | 
Indexes:
    "people_pkey" PRIMARY KEY, btree (people_id)

=# \d foo.people
                                       Table "foo.people" 
  Column   |         Type          |                         Modifiers                          
-----------+-----------------------+------------------------------------------------------------
 people_id | integer               | not null default nextval('foo.people_people_id_seq'::text)
 name      | character varying(50) | 
Indexes:
    "people_pkey" PRIMARY KEY, btree (people_id)      

That patch will not work because you can’t call the following query:

# SELECT adsrc FROM pg_attrdef WHERE adrelid = (SELECT oid FROM pg_class WHERE relname = 'people');
ERROR:  more than one row returned by a subquery used as an expression  

... because there are two tables with the same name! (fun, huh?)

Mine would work… but why bother with that huge query? So, I took my ticket out of [PATCH] and decided that I thought it would be best to just assume that sequences are generated with SERIAL ( link ) by default in AR.

Okay, so what can we do about custom SEQUENCE names?

Well, I am proposing the following (and mentioned this in the ticket #2016)...

class LegacyTable < ActiveRecord::Base
  def self.table_name() "legacy.foobar" end

  # new option for this
  set_primary_key "foobar_id", :sequence => "legacy.old_sequence_name" 
end

(or something along those lines)

With this, I can work around these legacy database scenarios with a quick option. Thoughts/opinions?

I decided to post this on my blog as well, because I do know that there are a few skeptical PostgreSQL people out there who read my blog… I want you to know that I am looking out for you. ;-)

I am sick and tired.. and going to sleep now.

Get help with your Rails project

comments powered by Disqus