Implementation for PostgreSQL UUID Primary Key Default

This commit adds support for using primary keys of type UUID in PostgreSQL: https://github.com/rails/rails/commit/bc8ebefe9825dbff2cffa29ff015a1e7a31f9812

This is awesome, however, I’m not sure I understand the intention with the default setting. Right now, it’s doing:

def primary_key(name, type = :primary_key, options = {})

return super unless type == :uuid

options[:default] ||= ‘uuid_generate_v4()’

options[:primary_key] = true

column name, type, options

end

I see two issues with this implementation:

  1. The uuid_generate_v4()function depends on a module (uuid-ossp), which is not always included in PostgreSQL distributions and which itself requires an external library (which seems to often have issues compiling, at least from some initial searching). The module also must be applied to each database individually (unless the user has already added it to their system template). So, unless a user has a) installed a pg distribution which includes this module, b) installed external library, c) set up their system template to always include this module in new databases, running rake db:migrate in a fresh checkout will always fail. For those reasons, it seems like an inappropriate default setting.
  2. So far as I can tell, there is no possible way to override this value, short a) specifying a different uuid function from the uuid-ossp module, or b) having installed another UUID library and specifying a function from that. You cannot specify default: 0 or default: ‘NULL’, of any value other than a valid UUID, because pg does not consider them valid entries for the type. default: nil will fail, because the method above will override it. You can pass an actual uuid as a string (default: “eddad8c1-43ad-4851-944a-a6205d266e5d”), however this seems pretty non-standard.
    To me, it seems that the ideal is actually to default to generating the UUID in Rails (via SecureRandom.uuid), however I’m not entirely sure how to do this while still allowing for database-level defaults.

Also, I wasn’t able to find any documentation on this anywhere, so either I’m missing it, or we need to add it.

I was going to file a github issue, but I wanted to check on here first to see if I was missing something or if anyone else had ideas/thoughts/insights.

Thanks,

Chad

This commit adds support for using primary keys of type UUID in
PostgreSQL: https://github.com/rails/rails/commit/bc8ebefe9825dbff2cffa29ff015a1e7a31f9812

This is *awesome*, however, I'm not sure I understand the intention with
the default setting. Right now, it's doing:

def primary_key(name, type = :primary_key, options = {})
  return super unless type == :uuid
  options[:default] ||= 'uuid_generate_v4()'
  options[:primary_key] = true
  column name, type, options
end

I see two issues with this implementation:

   1. The uuid_generate_v4()function depends on a module (uuid-ossp), which
   is not always included in PostgreSQL distributions and which itself
   requires an external library (which seems to often have issues compiling,
   at least from some initial searching). The module also must be applied to
   each database individually (unless the user has already added it to their
   system template). So, unless a user has a) installed a pg distribution
   which includes this module, b) installed external library, c) set up their
   system template to always include this module in new databases, running
   rake db:migrate in a fresh checkout will always fail. For those reasons,
   it seems like an inappropriate default setting.

You need to add "enable_extension 'uuid-ossp'" to your migrations so
that when people migrate, the extension is loaded. Same with HStore.

   2. So far as I can tell, there is no possible way to override this
   value, short a) specifying a different uuid function from the uuid-ossp
   module, or b) having installed another UUID library and specifying a
   function from that. You cannot specify default: 0 or default: 'NULL',
   of any value other than a valid UUID, because pg does not consider them
   valid entries for the type. default: nil will fail, because the method
   above will override it. You can pass an actual uuid as a string (
   default: "eddad8c1-43ad-4851-944a-a6205d266e5d"), however this seems
   pretty non-standard.

We can fix it so that "nil" is a valid setting. Can you write a patch?

To me, it seems that the ideal is actually to default to generating the
UUID in Rails (via SecureRandom.uuid), however I'm not entirely sure how to
do this while still allowing for database-level defaults.

Bingo. Using "uuid-ossp" is the cleanest solution I've found. If we
fix the "nil" case, you could add a "before_save" hook that sets the id
using `SecureRandom.uuid`, but it seems hacky, and you'd have to add the
code to every model that wanted it.

Also, I wasn't able to find any documentation on this anywhere, so either
I'm missing it, or we need to add it.

It needs to be added. :wink:

Thanks for the feedback!

WRT uuid-ossp, I was originally using Postgres.app, which it seems doesn’t ship with this module by default. I was able to get it working by switching to the homebrew recipe. I think Postgres.app is fairly popular for development/testing (Heroku recommends it), so this may be a common issue. That said, I think they should be able to fix it for OS X 10.8 if homebrew can make it work, and if we can at least allow nil, that will make it possible until then.

And, yeah, I just set up a UUID concern with a before_create hook. In the end, though, I just switched to homebrew pg with uuid-ossp instead :stuck_out_tongue:

I hadn’t seen enable_extension yet, thanks for the tip.

I think I pretty much have the patch to accept nil as a valid setting, but I will have to double-check the tests and try to add some documentation before submitting a pull request.

–Chad

Aaron—you’ll probably see this since I tagged you in it, but I got a patch submitted here: https://github.com/rails/rails/pull/10404

Just let me know if you see anything that I should change.

–Chad