I have a legacy table with a default expression in a Rails project. This expression picks a random four-digit number and converts it to a string. During my tests I have been typing this exact statement into psql to set the default expression:
alter table owners alter column signing_code set default
substring(to_char(((random() * ((10000 - 1))::double precision) +
(1)::double precision), '0000'::text), '....$'::text);
Then when I do \d owners this is what Postgres has decided is my actual default expression:
default "substring"(to_char(((random() * ((10000 - 1))::double
precision) + (1)::double precision), '0000'::text), '....$'::text)
Notice the double quotes around the first function identifier substring. This causes two problems with Rails schema dumps/loads:
- When dumping the schema to db/schema.rb, invalid Ruby is produced because the double quotes are not escaped
- Even if you correctly escape the quotes by hand, when loading the schema back into the database, Rails incorrectly sets the entire expression as a default string value, not an expression (i.e. it surrounds the expression with single quotes)
Is there a way to get Postgres to not double quote the first function in the nested function call in my case? That would be a good workaround, otherwise I'll submit a bug with the Rails project.
Why the
substringfunction gets double quoted doesn't really matter, the two expressions are functionally equivalent. PostgreSQL will parse the default expression and then save the parsed version and during this process, thesubstringidentifier gets double quoted. You can see similar things happen if you add a CHECK constraint like this:Doing a
\don a table with that constraint will give you:PostgreSQL translates the
inexpression to an equivalent= ANYexpression because that's what it wants to work with internally. The two expressions are functionally equivalent.The real problem is that ActiveRecord doesn't know what to do with a default expression that is more complex than a single literal value. AR makes an invalid assumption about what defaults look like and then blindly plods along making a mess.
When you're dealing with anything beyond the simplest DDL SQL your best bet is to use
db/structure.sqlinstead ofdb/schema.rb.structure.sqluses the database's native dump/restore tools so it will understand and preserve everything the database knows about.Switching to
structure.sqlis pretty simple:Update
config/application.rbto use the right format:Dump your
structure.sqlusing thedb:structure:dumprake task.Remove
db/schema.rbfrom your source tree and revision control.Add
db/structure.sqlto revision control.Retrain your fingers to use different rake tasks for dumping and restoring the schema:
db:structure:dumpinstead ofdb:schema:dump.db:structure:loadinstead ofdb:schema:load.I always start with
structure.sqlasschema.rbis too limited for how I want to work with my databases.