rails migration: postgresql for md5 of random stri

2019-05-11 16:27发布

Rails 3 + postgresql

I want to have a sha of a random string for a default value of a column.

So, in my migration I have:

t.string :uniqueid, default: md5(random()::text)

However i can not get this to actually produce anything, I've used backticks, quotes,etc. From examples that I've seen it seems like that pg function only works in a SELECT statement.

Is that accurate? Any ideas on how I could achieve this?

Thanks

1条回答
一纸荒年 Trace。
2楼-- · 2019-05-11 17:01

Rails will try to interpret this:

t.string :uniqueid, default: md5(random()::text)

as Ruby code and :default => md5(...) doesn't mean anything in Ruby. If you quote it, then Rails will think it is a string and make the default value for uniqueid the string 'md5(random()::text)' and that's not going to help.

Your problem is that Rails doesn't understand complicated things like using a function as a column default, Rails has the bizarre and misguided notion that you should do everything in Rails rather than inside the database. If you want to use a function call in a column default, you can do the alter table by hand:

connection.execute(%q{
    alter table your_table alter column uniqueid set default md5(random()::text)
})

That will get you the default you want in the database but you might notice that there's no mention of the new default in your schema.rb. If you want a usable schema then you'll have to, of course, push Rails out of the way and use an SQL schema instead by putting this in your application.rb:

config.active_record.schema_format = :sql

Note that SQL schema dumps were broken until 3.2 and there are schema loading problems in various Rails versions (but you can always psql < structure.sql your way around that). Then delete your schema.rb and use structure.sql instead. On the upside, SQL schema dumps will keep track of fancy things real foreign keys, check constraints, triggers, ...

BTW, if you really want SHA then you'll want to look at the digest function from pgcrypto.


Of course none of this will work because ActiveRecord is too dumb to leave things alone that it doesn't understand. ActiveRecord will see the default value when it parses the table definition it gets from the database but it won't understand it so it will ignore it. Then, during the INSERT, ActiveRecord seems to insist on supplying values for all the columns, even columns that you didn't assign values to. The result is that you'll end up with a NULL in uniqueid even though you have attached a sensible default value in the database.

So, how do you get around that? As usual with ActiveRecord, you pretend that the database is a stupid spreadsheet and do everything in Ruby using, probably, a before_create hook. Or you could find a less database-hostile ORM than ActiveRecord; I'm not sure how well the other ORMs will handle non-constant column defaults though.

Alternatively, you could try writing a BEFORE INSERT trigger that would assign the default value to the column if NEW.uniqueid IS NULL.

查看更多
登录 后发表回答