Ariel Pontes
2013-Aug-15 13:44 UTC
Rails is not compatible with databases that support custom data types (e.g. Postgres'' ENUM)
(This is adapted from this Stackoverflow
question<http://stackoverflow.com/questions/18034888/rails-column-hash-type-different-from-actual-database-type>I
asked some days ago)
I''m building my first Rails app and I ran into a weird problem.
I''m using
Postgres, which supports the ENUM type that is very useful for me. Rails
doesn''t support this, however, so I''m using many
"executes" in my
migrations. They all run successfully and work correctly. My database is
just the way I want when I examine it through pgAdmin. I''m using 3 ENUM
types in the users table at the moment:
gender (''female'', ''male'')
interested_in (''men'', ''women'',
''men_and_women'')
relationship_status (''single'', ...,
''divorced'')
For the application side I''m using the
classy_enum<https://github.com/beerlington/classy_enum> gem.
Then I tried updating the user profile to check if everything was working.
At first I erased the interested_in from the edit profile form because it
had some different logic for it. Everything worked fine. Then I added
interested_in, updated the logic for the new type, and submitted the form
again. I got this error:
PG::InvalidTextRepresentation: ERROR: invalid input value for enum
interested_in: "0" :
UPDATE "users" SET "interested_in" = $1,
"remember_token" = $2, "updated_at" = $3 WHERE
"users"."id" = 1
It''s trying to save 0 to an enum field that doesn''t include
this option and
then it obviously fails. So, the first time I saw a similar error I thought
"where on earth is this 0 coming from??" but it turned out Rails was
converting the value to an integer before saving it, and whenever you
convert a string with non-numeric characters the result is 0. That''s
what I
supposed was happening again. So I went to the console and typed:
2.0.0p247 :001 > User.columns_hash[''interested_in''].type
=> :integer
When I run the same command for gender and relationship_status I get nil,
which makes sense cause Rails doesn''t understand the ENUM type:
2.0.0p247 :007 > User.columns_hash[''gender''].type
=> nil
In pgAdmin all field types are correct, none is an integer.
I then had a hunch it could have something to do with the name *int*
erested_in, which starts with "int". I then changed the name to
is_interested_in but no luck, so I gave up on the hypothesis. After
spending some hours puzzled by the problem, dropping my whole database,
migrating it again, dropping again, loading it from structure.sql all with
no success, I decided to give it a shot again and changed the name of the
type to user_interested_in (because that doesn''t even start with
"i").
Again, no luck. More hours passed by and since I couldn''t find anything
online or come up with any other hypothesis, I checked carefully the names
of the other enum types and saw that they didn''t include
"int" at all in
any position of the string. I then changed the type name from interested_in
to attracted_to and guess what? It worked.
User.columns_hash[''interested_in''].type
=> nil
My guess was that there was some bug in Rails. It seemed like it was trying
to be smart and guessing the column type with some logic like:
model_column_type = :integer if db_column_type.include?("int")
That would be kind of crazy. Why wouldn''t it check for a full match?
Didn''t
make any sense to me.
But then I checked Rails
source<https://github.com/rails/rails/blob/master/activerecord/lib/active_record/connection_adapters/column.rb>
and that was it. That''s exactly what it does:
# rails / activerecord / lib / active_record / connection_adapters / column.rb
require ''set''
module ActiveRecord
# :stopdoc:
module ConnectionAdapters
# An abstract definition of a column in a table.
class Column
TRUE_VALUES = [true, 1, ''1'', ''t'',
''T'', ''true'', ''TRUE'',
''on'', ''ON''].to_set
FALSE_VALUES = [false, 0, ''0'', ''f'',
''F'', ''false'', ''FALSE'',
''off'', ''OFF''].to_set
...
def simplified_type(field_type)
case field_type
*when /int/**i # <-------------- HERE *
:integer
when /float|double/i
:float
.
.
.
when /boolean/i
:boolean
end
end
end
end
That''s a serious problem for databases that support the creation of
types.
Does the regex really have to be so inclusive? One thing is not offering
support, not offering an interface to creating custom typed fields, but
this is not just that. This is being incompatible. Couldn''t the regex
be
more restrictive?
--
You received this message because you are subscribed to the Google Groups
"Ruby on Rails: Core" group.
To unsubscribe from this group and stop receiving emails from it, send an email
to rubyonrails-core+unsubscribe@googlegroups.com.
To post to this group, send email to rubyonrails-core@googlegroups.com.
Visit this group at http://groups.google.com/group/rubyonrails-core.
For more options, visit https://groups.google.com/groups/opt_out.
Dennis Krupenik
2013-Aug-16 15:11 UTC
Re: Rails is not compatible with databases that support custom data types (e.g. Postgres'' ENUM)
could you please provide a stack trace of simplified_type being called with attribute *name*? -- You received this message because you are subscribed to the Google Groups "Ruby on Rails: Core" group. To unsubscribe from this group and stop receiving emails from it, send an email to rubyonrails-core+unsubscribe@googlegroups.com. To post to this group, send email to rubyonrails-core@googlegroups.com. Visit this group at http://groups.google.com/group/rubyonrails-core. For more options, visit https://groups.google.com/groups/opt_out.