Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Loading…

Allow override of PostgreSQL UUID primary key default #10404

Merged
merged 1 commit into from

2 participants

@chadmoone

bc8ebef adds the ability to define tables with primary keys which use PostgreSQL's native UUID type, which is pretty sweet :smile:.

However, because PostgreSQL does not itself provide a usable default value or UUID generator function, thePostgreSQLAdapter implementation sets the default value to uuid_generate_v4(), which is a function provided by an external extension (uuid-ossp) that is not enabled by default in PostgreSQL (it is also not supported by some distributions of PostgreSQL—notably Heroku's Postgres.app).

This default can currently only be overridden by either passing another UUID generation function (which will also not be enabled by default), or passing an actual static UUID value, which is not appropriate for a primary key.

This patch allows the :default option on a table definition to be set to nil, which will set no default value.

Setting the default value to nil will require you to always generate a UUID before saving a record (likely in a model's or concern's before_save callback), but it will at least allow you to use the UUID primary key feature without extensions or external libraries.

I also tried to add a little documentation that will hopefully help people looking into this in the future. Please let me know if you have any feedback or suggestions.

(Tagging @tenderlove, as this is the patch the he and I discussed in the rails-core mailing list.)

...tive_record/connection_adapters/postgresql_adapter.rb
((33 lines not shown))
def primary_key(name, type = :primary_key, options = {})
return super unless type == :uuid
- options[:default] ||= 'uuid_generate_v4()'
@rafaelfranca Owner

Maybe

options[:default] = options.fetch(:default, 'uuid_generate_v4()')
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
...tive_record/connection_adapters/postgresql_adapter.rb
@@ -330,9 +330,40 @@ def json(name, options = {})
class TableDefinition < ActiveRecord::ConnectionAdapters::TableDefinition
include ColumnMethods
+ # Defines the primary key field.
+ # Use of the native PostgreSQL UUID type is supported, and can be used
+ # by defining your tables as such:
+ #
+ # create_table :stuffs, id: :uuid do |t|
+ # t.string :content
+ # t.timestamps
+ # end
+ #
+ # By default, this will use the +uuid_generate_v4()+ function from the
+ # +uuid-ossp+ extension, which MUST be enabled on your databse. To enable
+ # the +uuid-ossp+ extension, you can use the +enable_extension+ method in your
+ # migrations To use a UUID primary key without +uuid-ossp+ enabled, you can
+ # set the +:default+ option to nil:
+ #
@rafaelfranca Owner

Please remove the trailing spaces after the #

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
...tive_record/connection_adapters/postgresql_adapter.rb
((9 lines not shown))
+ # t.string :content
+ # t.timestamps
+ # end
+ #
+ # By default, this will use the +uuid_generate_v4()+ function from the
+ # +uuid-ossp+ extension, which MUST be enabled on your databse. To enable
+ # the +uuid-ossp+ extension, you can use the +enable_extension+ method in your
+ # migrations To use a UUID primary key without +uuid-ossp+ enabled, you can
+ # set the +:default+ option to nil:
+ #
+ # create_table :stuffs, id: false do |t|
+ # t.primary_key :id, :uuid, default: nil
+ # t.uuid :foo_id
+ # t.timestamps
+ # end
+ #
@rafaelfranca Owner

Please remove the trailing spaces after the #

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
...tive_record/connection_adapters/postgresql_adapter.rb
((16 lines not shown))
+ # migrations To use a UUID primary key without +uuid-ossp+ enabled, you can
+ # set the +:default+ option to nil:
+ #
+ # create_table :stuffs, id: false do |t|
+ # t.primary_key :id, :uuid, default: nil
+ # t.uuid :foo_id
+ # t.timestamps
+ # end
+ #
+ # You may also pass a different UUID generation function from +uuid-ossp+
+ # or another library.
+ #
+ # Note that setting the UUID primary key default value to +nil+
+ # will require you to assure that you always provide a UUID value
+ # before saving a record (as primary keys cannot be nil). This might be
+ # done via the SecureRandom.uuid method and a +before_save+ callback,
@rafaelfranca Owner

Please remove the trailing spaces after the ,

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
activerecord/test/cases/adapters/postgresql/uuid_test.rb
((14 lines not shown))
+
+ @connection.transaction do
+ @connection.create_table('pg_uuids', id: false) do |t|
+ t.primary_key :id, :uuid, default: nil
+ t.string 'name'
+ end
+ end
+ end
+
+ def teardown
+ @connection.execute 'drop table if exists pg_uuids'
+ end
+
+ def test_id_allows_default_override_via_nil
+ col_desc = @connection.execute("SELECT pg_get_expr(d.adbin, d.adrelid) as default
+ FROM pg_attribute a
@rafaelfranca Owner

Please remove the trailing spaces after the a

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@rafaelfranca
Owner

Thank you so much. Your patch is very good.

Congratulations for the nice documentation.

I did some stylistic comments.

@chadmoone

Thanks for the feedback!

My bad on the trailing spaces, I need to get my linter to check for that...

I'll get the tweaks in and squashed shortly.

@chadmoone chadmoone allow override of uuid_generate_v4() default by passing default: nil
without this, it's not possible to use UUID primary keys without uuid-ossp installed and activated
55c40c0
@chadmoone

Ok, that should include everything mentioned above. Let me know if you see anything else.

@rafaelfranca rafaelfranca merged commit 09ac177 into from
@rafaelfranca
Owner

Thank you

@chadmoone chadmoone deleted the branch
@chadmoone chadmoone referenced this pull request in carbonfive/raygun
Closed

Use postgres UUIDs for primary keys #85

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on May 1, 2013
  1. @chadmoone

    allow override of uuid_generate_v4() default by passing default: nil

    chadmoone authored
    without this, it's not possible to use UUID primary keys without uuid-ossp installed and activated
This page is out of date. Refresh to see the latest.
View
31 activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
@@ -330,9 +330,38 @@ def json(name, options = {})
class TableDefinition < ActiveRecord::ConnectionAdapters::TableDefinition
include ColumnMethods
+ # Defines the primary key field.
+ # Use of the native PostgreSQL UUID type is supported, and can be used
+ # by defining your tables as such:
+ #
+ # create_table :stuffs, id: :uuid do |t|
+ # t.string :content
+ # t.timestamps
+ # end
+ #
+ # By default, this will use the +uuid_generate_v4()+ function from the
+ # +uuid-ossp+ extension, which MUST be enabled on your databse. To enable
+ # the +uuid-ossp+ extension, you can use the +enable_extension+ method in your
+ # migrations To use a UUID primary key without +uuid-ossp+ enabled, you can
+ # set the +:default+ option to nil:
+ #
+ # create_table :stuffs, id: false do |t|
+ # t.primary_key :id, :uuid, default: nil
+ # t.uuid :foo_id
+ # t.timestamps
+ # end
+ #
+ # You may also pass a different UUID generation function from +uuid-ossp+
+ # or another library.
+ #
+ # Note that setting the UUID primary key default value to +nil+
+ # will require you to assure that you always provide a UUID value
+ # before saving a record (as primary keys cannot be nil). This might be
+ # done via the SecureRandom.uuid method and a +before_save+ callback,
+ # for instance.
def primary_key(name, type = :primary_key, options = {})
return super unless type == :uuid
- options[:default] ||= 'uuid_generate_v4()'
+ options[:default] = options.fetch(:default, 'uuid_generate_v4()')
options[:primary_key] = true
column name, type, options
end
View
31 activerecord/test/cases/adapters/postgresql/uuid_test.rb
@@ -51,3 +51,34 @@ def test_auto_create_uuid
assert_not_nil u.other_uuid
end
end
+
+class PostgresqlUUIDTestNilDefault < ActiveRecord::TestCase
+ class UUID < ActiveRecord::Base
+ self.table_name = 'pg_uuids'
+ end
+
+ def setup
+ @connection = ActiveRecord::Base.connection
+
+ @connection.reconnect!
+
+ @connection.transaction do
+ @connection.create_table('pg_uuids', id: false) do |t|
+ t.primary_key :id, :uuid, default: nil
+ t.string 'name'
+ end
+ end
+ end
+
+ def teardown
+ @connection.execute 'drop table if exists pg_uuids'
+ end
+
+ def test_id_allows_default_override_via_nil
+ col_desc = @connection.execute("SELECT pg_get_expr(d.adbin, d.adrelid) as default
+ FROM pg_attribute a
+ LEFT JOIN pg_attrdef d ON a.attrelid = d.adrelid AND a.attnum = d.adnum
+ WHERE a.attname='id' AND a.attrelid = 'pg_uuids'::regclass").first
+ assert_nil col_desc["default"]
+ end
+end
Something went wrong with that request. Please try again.