Question on SQL exceptions


#1

To prevent duplicate values in the DBMS I use a unique index on those
columns. I am testing that duplicate values cannot, in fact, be added.

This is the cucumber scenario:

Scenario: The legal name must be unique
Given I do have a user named “admin”
And the user named “admin” is authenticated
And the user named “admin” is authorized to “add” “entities”
And I do have an entity named “Myuser”
And the entity named “Myuser” has a legal name “Myuser Legal Name”
When they visit the add a new entity page
And they enter valid entity data
And they enter the entity legal name “MyUser LEGAL NAME”
And I press “Create”
Then they should see a save error message

This is the step definition that should be triggered:

When /should see a save error message/ do
response.body.should =~ /errors? prohibited this (.*) from being
saved/im
end

But what happens is that SQLite3 throws an SQL exception:
SQLite3::SQLException: column entity_legal_name is not unique:…;

that is not caught by this controller:

def create
@entity = Entity.new(params[:entity])

# need this to strip out observer attributes for datebalks plugin
# see config/initializers/hash_addins.rb

@client = @entity.build_client(params[:client].datebalk!)

respond_to do |format|
  if @entity.save
    flash[:notice] = 'Client was successfully created.'
    format.html { redirect_to(@client) }
    format.xml  { render :xml => @client,
      :status => :created, :location => @client }
  else
    format.html { render :action => "new" }
    format.xml  { render :xml => @client.errors,
      :status => :unprocessable_entity }
  end
end

I thought, probably incorrectly, that when #save is called then any
errors are returned to the controller to handle. This is evidently not
happening so can someone tell me how this is supposed to be handled?


#2

James B. wrote:

Q.

To prevent duplicate values in the DBMS I use a unique index on those
columns. I am testing that duplicate values cannot, in fact, be added.

I thought, probably incorrectly, that when #save is called then any
errors are returned to the controller to handle. This is evidently not
happening so can someone tell me how this is supposed to be handled?

A. rescue ActiveRecord::StatementInvalid


#3

ActiveRecord doesn’t know anything about db constraint errors. If one
is violated, the error propagates up in the form of an exception.

Put a validates_uniqueness_of :login_name on your User class, and
you’ll get the behavior you want. You can keep the db constraint in
as a safety net against possible race conditions at the app layer.

Pat


#4

On Mar 9, 2009, at 4:53 PM, James B. wrote:

work
end
end
rescue => my_exception
puts “Rescue clause invoked!”
puts my_exception
end

For now, all I want is to see the rescue clause invoked, but it is
not.
have I put this clause in the wrong place? Am I specifying it wrong?

You can try “rescue Exception” which rescues from all exceptions, not
just ones which inherit from StandardError. (rescue without an
explicit error class only rescues from StandardExceptions and error
classes which derive from it):

http://gist.github.com/76474

Also - I believe this only works in 1.8.6 and above, so if you’re
still on 1.8.5 I’d recommend an upgrade.

Scott


#5

at the moment. I thought that this should at least produce some out
end
rescue => my_exception
puts “Rescue clause invoked!”
puts my_exception
end

For now, all I want is to see the rescue clause invoked, but it is
not.
have I put this clause in the wrong place? Am I specifying it wrong?

Just a stab in the dark, but I haven’t seen any mention of calling
save vs. save!. save just puts any errors in the model, save! will
raise the exception if there are any errors. That may not be the case
for ActiveRecord:StatementInvalid exception, but I thought i’d mention
it anyway.

timg


#6

Tim G. wrote:

Just a stab in the dark, but I haven’t seen any mention of calling
save vs. save!. save just puts any errors in the model, save! will
raise the exception if there are any errors. That may not be the case
for ActiveRecord:StatementInvalid exception, but I thought i’d mention
it anyway.

timg

I wondered about this but I think that since the DBMS exception is not
being trapped at all that it makes no difference. However, I will check
if if it does or not anyway.

and

Scott T. wrote:

You can try “rescue Exception” which rescues from all exceptions, not
just ones which inherit from StandardError.

Tried that one and it did not work either.

Also - I believe this only works in 1.8.6 and above, so if you’re
still on 1.8.5 I’d recommend an upgrade.

This may be the case. The target host runs CentOS-5.3 which ships with
Ruby-1.8.5. I will see if I get different results on a 1.8.6 machine.


#7

Pat M. wrote:

ActiveRecord doesn’t know anything about db constraint errors. If one
is violated, the error propagates up in the form of an exception.

I realize that, but the exception is of the
ActiveRecord:StatementInvalid class, which I should be able to catch in
the controller with a rescue clause. But, I am not getting this to work
at the moment. I thought that this should at least produce some out put
but it never does:

def create
@entity = Entity.new(params[:entity])

respond_to do |format|

end
end
rescue => my_exception
puts “Rescue clause invoked!”
puts my_exception
end

For now, all I want is to see the rescue clause invoked, but it is not.
have I put this clause in the wrong place? Am I specifying it wrong?


#8

On Mar 9, 2009, at 1:53 PM, James B. wrote:

work
at the moment.

Not sure what your problem is. Here’s a gist that demonstrates what
you’re trying to do, and works fine. http://gist.github.com/76667

Pat


#9

Just a stab in the dark, but I haven’t seen any mention of calling
save vs. save!. save just puts any errors in the model, save! will
raise the exception if there are any errors. That may not be the case
for ActiveRecord:StatementInvalid exception, but I thought i’d
mention
it anyway.

I wondered about this but I think that since the DBMS exception is not
being trapped at all that it makes no difference. However, I will
check
if if it does or not anyway.

Right - this is from the validates_uniqueness_of documentation:

When the database catches such a duplicate insertion,

ActiveRecord::Base#save will raise an ActiveRecord::StatementInvalid

exception. You can either choose to let this error propagate (which

will result in the default Rails exception page being shown), or you

can catch it and restart the transaction (e.g. by telling the user

that the title already exists, and asking him to re-enter the

title).

This technique is also known as optimistic concurrency control:

http://en.wikipedia.org/wiki/Optimistic_concurrency_control

so it should be bubbling through. Odd that it would change in Ruby
1.8.6, but it’s worth a try.

timg


#10

Pat M. wrote:

Not sure what your problem is. Here’s a gist that demonstrates what
you’re trying to do, and works fine. http://gist.github.com/76667

Pat

I do not know what I am doing wrong either. I tried much the same thing
as you suggest last night in the console:

def my_exception(x=false)
raise ActiveRecord::StatementInvalid if x
puts “No Exception”
rescue ActiveRecord::StatementInvalid
puts “exception rescued”
end
=> nil

my_exception(false)
No Exception
=> nil

my_exception(true)
exception rescued
=> nil

So, my version of Ruby has nothing to do with whatever is happening. I
will poke at things some more and see if I can discover what my
misunderstanding is. It has to be something simple that I just do not
see at the moment.


#11

James B. wrote:

I do not know what I am doing wrong either. I tried much the same thing
as you suggest last night in the console:

Well, whatever I was doing wrong I seem to have fixed it. Things now
seem to be working as I intended.