Rails Nits — Error Messages

Update: Due to a misconfiguration some of my blog entries from the month of Feburary recently lost. This is merely a repost of the original content.

At the Ruby User Groups meeting the other day someone asked me what things I did not like about Ruby and Rails. At the time I did not have any really good answers, which bothered me because that is an important question. No technology is perfect and honest critiques are a vitally important way to improve the state of the art. In that spirit this is the first in a series to relate some issues I have with Rails (and fixes when possible).

When everything works Rails is absolutely brilliant. However, it when things do not go well it often yields ambiguous, vague or misleading error messages. I have noticed this several time but today it was driven home once again. Error messages might seem like a little thing but a single bad error message can send a developer off in the wrong direction wasting hours (or at least tens of minutes ).

For example, earlier today I was investigating moving our database schema management onto ActiveRecord::Migration.1 I found Jamis’ Getting Started With ActiveRecord Migrations article, which is excellent. I followed all the steps but when I ran the migrate rake task I got the following instead of the correct schema.

pwilliams@xps:~/projects/ramps$ rake migrate 
(in /home/pwilliams/projects/ramps) 
rake aborted!  
MysqlError: Table 'ramps_development.schema_info' doesn't exist: SELECT version FROM schema_info

My first thought was that I needed create the schema_info table that the SQL above references. However, while looking for the shape that table I find that the ActiveRecord:Migration API documentation says it does not need to be create manually.

To run migrations against the currently configured database, use rake migrate. This will update the database by running all of the pending migrations, creating the schema_info table if missing.

At that point I was totally confused, the documentation says this table will be created for you if necessary but the code is not actually creating it. After digging around in the code2 a little I finally figured that the problem was the table create code was eating the real error message. The code that creates the schema_info table looks like this

# Should not be called normally, but this operation is non-destructive.  
# The migrations module handles this automatically.  
def initialize_schema_information 
  begin 
    execute "CREATE TABLE #{ActiveRecord::Migrator.schema_info_table_name} (version #{type_to_sql(:integer)})" 
    execute "INSERT INTO #{ActiveRecord::Migrator.schema_info_table_name} (version) VALUES(0)"
  rescue ActiveRecord::StatementInvalid 
    # Schema has been intialized 
  end      
end

That code just tries to create the needed table. It catches any failures while executing the table creation and eats the error under the assumption that a failure to create the table means the table already exists. And therein lies the problem. I had not granted the user specified in my database.yml rights to added tables. The rails user did not need this privilege before because I was loading sql files as myself not as the rails user. It is appropriate that ActiveRecord::Migration failed, it cannot add tables if the RDBMS does not allow it to, but that error message is totally unacceptable. If the actual problem had be reported it would have taken be about 30 seconds to fix it, rather than 30 minutes.

In the spirit of improving this problem here is a patch that make ActiveRecord::ConnectionAdapters::SchemaStatements#initialize_schema_information return a better error message in this case, and possibly others. As it turns out it was actually quite easy to improve this error message. Rather than rescuing the attempt to create the table from all failures. Try to select from the tables first, if that fails then attempt to create the table but let any failure be raise up so that the user sees them. Here is a file that when added to RAILS-PROJECT-DIR/lib/tasks does the trick. Below are the entire contents of that file.

module ActiveRecord
  module ConnectionAdapters
    module SchemaStatements

      # Creates the schema_info table in preparation for using 
      # ActiveRecord::Migration. Obviously, this should not be 
      # called normally, but this operation is non-destructive.
      # The migrations module handles this automatically.
      def initialize_schema_information
        begin
          execute "SELECT COUNT(*) from #{ActiveRecord::Migrator.schema_info_table_name}"
        rescue ActiveRecord::StatementInvalid
          execute "CREATE TABLE #{ActiveRecord::Migrator.schema_info_table_name} (version #{type_to_sql(:integer)})"
          execute "INSERT INTO #{ActiveRecord::Migrator.schema_info_table_name} (version) VALUES(0)"
        end
      end
    end
  end
end

I tried implementing that as a plugin but apparently plugins do not get loaded when executing the migrate rake task. That makes sense since plugins are really a Rails thing and not a Active record thing. Implementing it as a rake file meant that it gets loaded automagically before the migrate task is actually executed so the code changes are in place a the appropriate time.

This is yet another example of the power of open classes. The ability to fix bugs in the framework and libraries without having to physically patch the shipped source code of the framework or library is priceless.

  1. ActiveRecord::Migration appears to be a fairly excellent way to deal with managing a rapidly changing database schema. We are going to use it.

  2. Digging around in the Rails code is complicated by a) the fact that everything is dynamically loaded in Rails which means that where code comes from is not always obvious and b) Rails core developers a weird fascination with :nodoc: which causes rdoc to not generate any documentation a class. I will probably cover those issues in more detail sometime.

Comments 11

  1. Omar Colon wrote:

    This saved me alot of headaches. Thanks!

    Posted 09 Aug 2006 at 6:46 pm
  2. Ryan wrote:

    Well written article, you also saved me a lot of time…and as I was reading, I thought to myself, Gee my rails user doesn’t have access to create tables in this database…sure enough that’s the issue. Thanks a ton.

    Posted 10 Aug 2006 at 4:29 pm
  3. Phil Crosby wrote:

    Clear description of the problem. For reference, here’s a line to grant the needed privileges

    grant create,drop on mydb_test.* to ‘myuser’@'localhost’ identified by ‘pass’;

    Posted 18 Sep 2006 at 10:31 pm
  4. phil wrote:

    Thank you for your enlightenig articel. Tracing the same error message I applied your patch. But what would have saved me 30 minutes, is a check if the mysql-gem ist installed on the machine I’m working on, instead I got a misleading Lost connection to MySQL server during query: CREATE TABLE schema_info (version int(11))

    Posted 21 Sep 2006 at 1:52 pm
  5. Gareth wrote:

    Thanks - this had me really confused, until I discovered that someone had set the database up with the wrong permissions…

    A nicer error message would definitely have helped

    Posted 30 Oct 2006 at 5:54 am
  6. Ron Chaplin wrote:

    ||on 21 Sep 2006 at 1:52 pm phil
    ||
    ||Thank you for your enlightenig articel. Tracing the same error
    ||message I applied your patch. But what would have saved me 30
    ||minutes, is a check if the mysql-gem ist installed on the machine
    ||I’m working on, instead I got a misleading Lost connection to
    ||MySQL server during query: CREATE TABLE schema_info (version
    ||int(11))

    I also recieved the error message same as phil.

    Upon trying to install the mysql-gem upon my Ubuntu Machine, I did not realize that it was a win32 build. I used gem install mysql –debug.

    The Key being –debug

    After a bit more searching for Ubuntu Rails Mysql, I found that there is a lib for ruby to handle mysql.

    rake db:migrate worked flawlessly after that.
    A minor victory for me in the RonR Learning adventure!

    HTH Some1

    Ron Chaplin

    Posted 08 Nov 2006 at 12:11 am
  7. Vikas wrote:

    thanks a lot for posting this. i should have realized my rails user didn’t have create privileges! this saved me so much wasted time!

    Posted 02 Dec 2006 at 11:48 am
  8. Frank Lamontagne wrote:

    Thanks! That was my problem also.

    Posted 13 Jun 2007 at 12:42 pm
  9. Ciaran wrote:

    This post is still helping folks like me - I had this exact problem and sure enough my user didn’t have correct DB permissions. Cheers!

    Posted 24 Feb 2008 at 8:48 am
  10. Vlad wrote:

    Today I encountered the exact same issue while bootstrapping a Rails app using rails 2.0 .

    Posted 20 May 2008 at 9:05 am
  11. mtkd wrote:

    many thanks, fixed my issue

    Posted 31 Aug 2008 at 2:09 pm

Post a Comment

Your email is never published nor shared. Required fields are marked *