DBIx::Class::InflateColumn bug (was Re: [Dbix-class] RFC: Inflating user-supplied values)

Jason Kohles email at jasonkohles.com
Mon May 7 22:27:42 GMT 2007


On May 7, 2007, at 1:16 PM, Christopher H. Laco wrote:

>>>> and while it is all working now (thanks Claco!) I have to admit I
>>>> still find the behavior counter-intuitive at best.  I guess when I
>>>> was just inflating DateTime objects it made more sense, since the
>>>> inflated values were so much more complex than the representation
>>>> that was stored in the database, but when I started using
>>>> DBIx::Class::InflateColumn::Currency, it seemed odd to me that
>>>> '$10.00' would be inflated to a Data::Currency object if it came  
>>>> from=
>
>>>> the database, but not if it came from the user.  I've fixed this  
>>>> for
>>>> my immediate need by writing an HTML::FormFu::Inflator subclass  
>>>> that
>>>> parallels DBIx::Class::InflateColumn::Currency, but going  
>>>> forward I'd=
>
>>>> like to avoid repeating so much code, so I've come up with an  
>>>> idea to=
>
>>>> extend DBIx::Class::InflateColumn to allow user-provided values  
>>>> to be=
>
>>>> inflated the same way that database values are.
>>>
>>> Well, if you call $obj->inflated_field('$10.00');
>>>
>>> then do $obj->inflated_field; the second call will return the  
>>> inflated=
>
>>> object.
>>>
>>> I'm not sure I see what the problem you're trying to solve is?
>
> [mst]
>
>>> Well, if you call $obj->inflated_field('$10.00');
>
> The problem is, that will never work. I could be wrong, but  
> whatever you
> pass to inflated columns has to either be an inflated object, or a  
> valid
> value matching EXACTLY what the current DB supports.
>
> In this case '$10.00' is neither a currency object, nor a valid value
> for a db column of type float.
>
> The same has always been true for DateTime fields (at least in my
> experience):
>
> $obj->col('1-2-2007')
>
> will never work in database that don't grok that datetime format.  
> But if
> you pass in a DateTime object, all is well...
>

The problem is that it does sometimes work, and I think I've finally  
figured out why...

If you set the value with $obj->col( '$10.00' ), and then request the  
value with $obj->col, you do indeed get back an inflated value.   
Looking through the code however, it appears that the inflation is  
done by DBIx::Column::InflateColumn::get_inflated_column, meaning the  
value is inflated when you retrieve it, not when you set it.

What this means is that this will work as expected:

$obj->col( '$10.00' );
print ref( $obj->col )."\n"; # will print Data::Currency

However, this will _not_ work as expected:

$obj->col( '$10.00' );
$obj->update;

The reason the second one doesn't work has to do with the inner  
workings of get_inflated_column and set_inflated_column.

When you do this:  $obj->get_inflated_column( $col );

The get_inflated_column method will look for a previously inflated  
value in $self->{ _inflated_column }{ $col } and return it if it  
finds it, if it doesn't already have an inflated value, it will call  
get_column to retrieve the value from the database, inflate that,  
store it in $self->{ _inflated_column }{ $col } and return the  
inflated value.

When you do this: $obj->set_inflated_column( $col, Data::Currency->new 
( 10, 'USD' ) );

The set_inflated_column method will deflate the object and call  
set_column with that value, so that other things that don't  
understand the inflated values will work correctly (things like  
DBIx::Class::Row::update for example).

When you do this: $obj->set_inflated_column( $col, '$10.00' ); is  
where things start to fall apart...

set_inflated_column will call _deflated_column with an argument of  
'$10.00', which gets returned unchanged because it isn't a reference,  
then it calls set_column with that value, thereby populating the  
object with invalid data.  Now when you call $obj->update, update  
will call get_dirty_columns and pass the results to  
SQL::Abstract::update, but since the value was not inflated before it  
was deflated, the unchanged value is passed to the database and the  
update proceeds with invalid data.


I've been trying to determine the best way to fix this, but writing  
tests for it has proven difficult, I think I'm going to have to write  
a test class to inflate objects to...

-- 
Jason Kohles
email at jasonkohles.com
http://www.jasonkohles.com/
"A witty saying proves nothing."  -- Voltaire





More information about the Dbix-class mailing list