[Dbix-class] Lazy Columns

Peter Rabbitson rabbit+dbic at rabbit.us
Tue May 11 15:02:42 GMT 2010


Bill Moseley wrote:
> 
> 
> On Tue, May 11, 2010 at 3:14 AM, Peter Rabbitson <rabbit+dbic at rabbit.us
> <mailto:rabbit%2Bdbic at rabbit.us>> wrote:
> 
> 
>     > But, case in point:  I'm using DBIx::Class::Ordered and it started to
>     > fail today with a somewhat odd error.  Turned out that "columns" had
>     > been modified such that the position column was no longer returned in
>     > the row.  Ordered was assuming it was a NULL and tried to set the
>     > position to a value that existed and the database complained that
>     it was
>     > not unique.
>     >
> 
>     I don't see the attached test case so we can fix the bug in *Ordered*
>     (by making sure we have the necessary info available before we start
>     shuffling things around).
> 
> 
> I didn't see this as a bug in Ordered.  Ordered asked for the column
> data and got an undefined value and assumed it was null.  Seemed more
> like an example of why might be good for DBIC to throw an exception if a
> column is accessed w/o being fetched.

It may or may not be - this is beyond the point. The public API from day one
has been "you get all columns by default" and "every accessor always works".
If you alter the selector, and then do not check that you are asking for
something that *you* did not bring back from the db - you get to keep the
pieces. In this case Ordered is clearly misusing the API, thus the problem
is with Ordered, and it will be fixed sooner or later (sooner if we get a
test case contribution).


> But, wrt Ordered, I am curious.  If I have these ordered tracks and then
> delete one:
> 
> Id =  30  Track =   1
> Id =  31  Track =   2
> Id =  32  Track =   3
> Id =  33  Track =   4
> Id =  34  Track =   5
> Id =  35  Track =   6
> Id =  36  Track =   7
> Id =  37  Track =   8
> Id =  38  Track =   9
> Id =  39  Track =  10
> 
> removing id 33 position 4
> 
> SELECT position FROM track me WHERE ( ( position > ? AND ( position != ?
> AND cd = ? ) ) ) ORDER BY position DESC LIMIT 1: '4', '4', '8'
> UPDATE track SET position = position - 1 WHERE ( ( ( position BETWEEN ?
> AND ? ) AND cd = ? ) ): '5', '10', '8'
> UPDATE track SET position = ? WHERE ( id = ? ): '10', '33'
> DELETE FROM track WHERE ( id = ? ): '33'
> 
> Doesn't the order of the updates look incorrect?  Wouldn't that set two
> rows with the same position before the second update moves id=33 to
> position 10 (moves it to the end)?
> 
> Would this be more correct (ignoring the race condition):

Every op is wrapped in a transaction, races are a non-issue.

> ($deleted_pos = $delete_row->position)
> DELETE FROM track WHERE ( id = ? ): '33'
> UPDATE track SET position = position - 1 WHERE position > ? AND cd = ?:
> $deleted_pos, $cd_id

That's a viable optimization, it'll go on the TODO. However see below:

> Because:
> create unique index pos_idx on track (cd,position);
> 
> ...
> 
 > package MyApp::Result::Track;
> use strict;
> use warnings;
> use base 'MyApp::Result';
> 
> __PACKAGE__->load_components(qw/ Ordered Core /);
> __PACKAGE__->table("track");
> __PACKAGE__->add_columns( qw/
>     id
>     name
>     position
>     cd
> /);
> __PACKAGE__->set_primary_key( 'id' );
> __PACKAGE__->belongs_to("cd", "MyApp::Result::Cd", { id => "cd" });
> 
> __PACKAGE__->position_column('position');
> __PACKAGE__->grouping_column('cd');
> __PACKAGE__->null_position_value(undef);  #?
> 1;

^^ nowhere do you inform DBIC that there is in fact a unique constraint
on the source/table. The moment you do the appropriate add_unique_constraint
the SQL will magically change to being correct :)

Cheers



More information about the DBIx-Class mailing list