File Coverage

blib/lib/DBIx/Tree/NestedSet.pm
Criterion Covered Total %
statement 9 303 2.9
branch 0 74 0.0
condition 0 64 0.0
subroutine 3 38 7.8
pod 18 26 69.2
total 30 505 5.9


line stmt bran cond sub pod time code
1             package DBIx::Tree::NestedSet;
2              
3 1     1   1621 use strict;
  1         1  
  1         39  
4 1     1   4 use warnings;
  1         2  
  1         26  
5 1     1   6 use Carp;
  1         10  
  1         9720  
6             $DBIx::Tree::NestedSet::VERSION='0.16';
7              
8             #POD Below!!
9              
10             ################################################################################
11             sub new{
12 0     0 1   my $class=shift;
13 0   0       $class=ref($class)||$class;
14 0           my %params=@_;
15 0   0       my $self={
      0        
      0        
      0        
      0        
      0        
      0        
      0        
16             dbh => $params{dbh},
17             left_column_name => $params{left_column_name} || 'lft',
18             right_column_name => $params{right_column_name} || 'rght',
19             no_id_creation => $params{no_id_creation} || '',
20             table_name => $params{table_name} || 'nested_set',
21             id_name => $params{id_name} || 'id',
22             no_alter_table => $params{no_alter_table} || undef,
23             db_type => $params{db_type} || 'MySQL',
24             no_locking => $params{no_locking} || undef
25             };
26 0           bless $self, $class;
27 0 0         croak("Not a DBI connection")
28             unless($params{dbh}->isa('DBI::db'));
29              
30 0           foreach('left_column_name','right_column_name','table_name','id_name'){
31 0 0         croak('"'.$self->{$_}."\" doesn't look like a valid SQL table or column name to me")
32             unless ($self->{$_} =~ m/^[_A-Za-z\d]+$/);
33             }
34              
35 0           my $db_type=$self->{db_type};
36            
37 0           my $driver = "DBIx::Tree::NestedSet::$db_type";
38 0 0         eval "require $driver;" or
39             croak("That DBD source doesn't have a driver implemented yet");
40              
41 0           my $db_obj=$driver->new(
42             dbh => $self->{dbh},
43             left_column_name => $self->{left_column_name},
44             right_column_name => $self->{right_column_name},
45             table_name => $self->{table_name},
46             no_alter_table => $self->{no_alter_table},
47             id_name => $self->{id_name},
48             no_locking => $self->{no_locking}
49             );
50 0           $self->{_db_obj}=$db_obj;
51             #$self->{start_id}=$params{start_id}|| scalar $self->{dbh}->selectrow_array('select min('.$self->{left_column_name}.') from '.$self->{table_name} );
52 0 0         $params{dbh}->{RaiseError} = 1 if(not defined $params{No_RaiseError});
53 0 0         $params{dbh}->trace($params{trace}) if($params{trace});
54 0           return $self;
55             }
56             ########################################
57              
58              
59             ################################################################################
60             sub get_table_name{
61 0     0 0   return $_[0]->{table_name};
62             }
63             ########################################
64              
65              
66             ################################################################################
67             sub get_left_column_name{
68 0     0 0   return $_[0]->{left_column_name};
69             }
70             ########################################
71              
72              
73             ################################################################################
74             sub get_right_column_name{
75 0     0 0   return $_[0]->{right_column_name};
76             }
77             ########################################
78              
79              
80             ################################################################################
81             sub get_id_name{
82 0     0 0   return $_[0]->{id_name};
83             }
84             ########################################
85              
86              
87             ################################################################################
88             sub get_dbh{
89 0     0 0   return $_[0]->{dbh};
90             }
91             ########################################
92              
93              
94             ################################################################################
95             sub get_db_type{
96 0     0 0   return $_[0]->{db_type};
97             }
98             ########################################
99              
100              
101             ################################################################################
102             sub get_no_alter_table{
103 0     0 0   return $_[0]->{no_alter_table};
104             }
105             ########################################
106              
107              
108             ################################################################################
109             sub get_no_locking{
110 0     0 0   return $_[0]->{no_locking};
111             }
112             ########################################
113              
114              
115             ################################################################################
116             sub get_root{
117 0     0 1   my $self=shift;
118 0           my $left=$self->{left_column_name};
119 0           my $table=$self->{table_name};
120 0           my $id_name=$self->{id_name};
121 0           my ($min_left)=$self->{dbh}->selectrow_array("select min($left) from $table");
122 0           return scalar $self->{dbh}->selectrow_array("select $id_name from ".$self->{table_name}." where $left=?",undef,($min_left));
123             }
124             ########################################
125              
126              
127             ################################################################################
128             sub _lock_tables{
129 0     0     my $self=shift;
130 0           $self->{_db_obj}->_lock_tables;
131             }
132             ########################################
133              
134              
135             ################################################################################
136             sub _unlock_tables{
137 0     0     my $self=shift;
138 0           $self->{_db_obj}->_unlock_tables;
139             }
140             ########################################
141              
142              
143             ################################################################################
144             sub add_child_to_right{
145 0     0 1   my($self,%params)=@_;
146 0           my $dbh=$self->{dbh};
147 0           my $left=$self->{left_column_name};
148 0           my $right=$self->{right_column_name};
149 0           my $table=$self->{table_name};
150 0           my $id_name=$self->{id_name};
151 0 0 0       if((defined $params{id}) and ($id_name ne 'id')){
152             #If they have changed the id_name but still pass in the id to act upon via the "id" parameter,
153             #fix it here. This is for backwards compatibility
154 0           $params{$id_name}=$params{id}
155             }
156 0           $self->_lock_tables();
157 0 0 0       if(!$params{$id_name} && scalar $dbh->selectrow_array("select count(*) from $table")){
158             #They haven't given us an id for this child. Assume they want to add a child DIRECTLY
159             #under the parent, as they can't have more than one root.
160 0           $params{$id_name}=$self->get_root();
161             }
162 0           my $prepared_rightmost_SQL_statement=
163             $dbh->prepare_cached("SELECT $right FROM $table WHERE $id_name=?",
164             {dbi_dummy=>__FILE__.__LINE__}
165             );
166              
167 0           my ($rightmost)=$dbh->selectrow_array($prepared_rightmost_SQL_statement,undef,($params{$id_name}));
168 0           my $prepared_rightmost_SQL_tree_fix_statement=
169             $dbh->prepare_cached(
170             "UPDATE $table SET $left = CASE WHEN $left > ? THEN $left + 2 ELSE $left END,
171             $right = CASE WHEN $right >= ? THEN $right + 2 ELSE $right END WHERE $right >= ?",
172             {dbi_dummy=>__FILE__.__LINE__}
173             );
174 0           $prepared_rightmost_SQL_tree_fix_statement->execute($rightmost,$rightmost,$rightmost);
175 0           $prepared_rightmost_SQL_tree_fix_statement->finish();
176 0           my ($params,$values)=$self->_get_params_and_values(\%params,$left,$right,$id_name);
177 0           my ($columns,$placeholders)=_prepare_columns_and_placeholders_for_adding_child_to_right($params,$left,$right);
178 0           $self->_alter_table_if_needed($params);
179 0 0         if ($self->{no_id_creation}) {
180             #We are manually passing in IDs. This is kinda a kludge to make the WebGUI folks happy.
181 0           $self->_alter_sql_for_provided_primary_key_edits(\$columns,\$placeholders,$values,\%params);
182             }
183 0           my $insert=$dbh->prepare_cached("INSERT INTO $table ($columns) VALUES($placeholders)",{dbi_dummy=>__FILE__.__LINE__});
184 0   0       $insert->execute($rightmost||1,$rightmost||1,@$values);
      0        
185 0           $insert->finish();
186 0           my $new_id;
187 0 0         if ($self->{no_id_creation}) {
188 0           $new_id=$params{provided_primary_key};
189             } else {
190 0           ($new_id)=$dbh->selectrow_array("select max($id_name) from $table");
191             }
192 0           $self->_unlock_tables();
193 0           return $new_id;
194             }
195             ########################################
196              
197              
198             ################################################################################
199             sub add_child_to_left{
200 0     0 1   my($self,%params)=@_;
201 0           my $dbh=$self->{dbh};
202            
203 0           my $left=$self->{left_column_name};
204 0           my $right=$self->{right_column_name};
205 0           my $table=$self->{table_name};
206 0           my $id_name=$self->{id_name};
207 0 0 0       if((defined $params{id}) and ($id_name ne 'id')){
208             #If they have changed the id_name but still pass in the id to act upon via the "id" parameter,
209             #fix it here. This is for backwards compatibility
210 0           $params{$id_name}=$params{id}
211             }
212 0           $self->_lock_tables();
213 0 0 0       if(!$params{$id_name} && scalar $dbh->selectrow_array("select count(*) from $table")){
214             #They haven't given us an id for this child. Assume they want to add a child DIRECTLY
215             #under the parent, as they can't have more than one root.
216 0           $params{$id_name}=$self->get_root();
217             }
218            
219 0           my $prepared_leftmost_SQL_statement=$dbh->prepare_cached("SELECT $left FROM $table WHERE $id_name=?",{dbi_dummy=>__FILE__.__LINE__});
220            
221 0           my ($leftmost)=$dbh->selectrow_array($prepared_leftmost_SQL_statement,undef,($params{$id_name}));
222 0           $prepared_leftmost_SQL_statement->finish();
223 0           my $prepared_leftmost_SQL_tree_fix_statement=
224             $dbh->prepare_cached(
225             qq|UPDATE $table
226             SET $right =
227             CASE WHEN $right > ?
228             THEN $right + 2
229             ELSE $right END,
230             $left =
231             CASE WHEN $left > ?
232             THEN $left + 2
233             ELSE $left
234             END
235             |,
236             {dbi_dummy=>__FILE__.__LINE__}
237             );
238            
239 0           $prepared_leftmost_SQL_tree_fix_statement->execute($leftmost,$leftmost);
240 0           $prepared_leftmost_SQL_tree_fix_statement->finish();
241 0           my ($params,$values)=$self->_get_params_and_values(\%params,$left,$right,$id_name);
242 0           my ($columns,$placeholders)=_prepare_columns_and_placeholders_for_adding_child_to_left($params,$left,$right);
243 0           $self->_alter_table_if_needed($params);
244 0 0         if ($self->{no_id_creation}) {
245             #We are manually passing in IDs. This is kinda a kludge to make the WebGUI folks happy.
246 0           $self->_alter_sql_for_provided_primary_key_edits(\$columns,\$placeholders,$values,\%params);
247             }
248 0           my $insert=$dbh->prepare_cached("INSERT INTO $table ($columns) VALUES($placeholders)",{dbi_dummy=>__FILE__.__LINE__});
249 0   0       $insert->execute($leftmost||1,$leftmost||1,@$values);
      0        
250 0           $insert->finish();
251 0           my $new_id;
252 0 0         if ($self->{no_id_creation}) {
253 0           $new_id=$params{provided_primary_key};
254             } else {
255 0           ($new_id)=$dbh->selectrow_array("select max($id_name) from $table");
256             }
257 0           $self->_unlock_tables();
258 0           return $new_id;
259             }
260             ########################################
261              
262              
263             ################################################################################
264             sub _alter_sql_for_provided_primary_key_edits{
265 0     0     my($self,$columns,$placeholders,$values,$params)=@_;
266 0           my $id_name=$self->{id_name};
267 0           $$columns.=",$id_name";
268 0           $$placeholders.=',?';
269 0           push @$values, $params->{provided_primary_key};
270             }
271             ########################################
272              
273              
274             ################################################################################
275             sub _alter_table_if_needed{
276             #$params is an arrayref with all the proper columns in order.
277 0     0     my ($self,$params)=@_;
278              
279             #We don't want to invoke the "automagical" table altering behavior
280 0 0         return if(defined $self->{no_alter_table});
281              
282 0           my $left=$self->{left_column_name};
283 0           my $right=$self->{right_column_name};
284 0           my $table=$self->{table_name};
285 0           my $dbh=$self->{dbh};
286 0           my $id_name=$self->{id_name};
287              
288             #my %columns_we_are_requesting=map{$_=>1} @$params;
289 0           my @columns_we_need_to_create;
290             #With MySQL I could use "Explain $table" but I'd like this to be a bit more cross-RDBMS
291 0           my $get_columns=$dbh->prepare_cached("select *,count(*) as _ignore_me_sdfas from $table group by $id_name",
292             {dbi_dummy=>__FILE__.__LINE__});
293 0           $get_columns->execute();
294 0           my %columns_that_we_have=();
295 0           foreach(@{$get_columns->{NAME}}){
  0            
296 0 0         $columns_that_we_have{$_}=1 if($_ ne '_ignore_me_sdfas');
297             }
298 0           $get_columns->finish();
299 0           foreach(@$params){
300 0 0         push @columns_we_need_to_create, $_ if(not defined $columns_that_we_have{$_});
301             }
302 0           my $db_obj=$self->{_db_obj};
303 0           foreach(@columns_we_need_to_create){
304 0 0         croak('"'.$_."\" doesn't look like a valid SQL table or column name to me")
305             unless ($_ =~ m/^[_A-Za-z\d]+$/);
306 0           $db_obj->_alter_table($_);
307 0           $dbh->do("create index $_ on $table($_)");
308             }
309             }
310             ########################################
311              
312              
313             ################################################################################
314             sub _get_params_and_values{
315 0     0     my ($self,$params,$left,$right,$id_name,$no_left_or_right)=@_;
316 0           my %ignore=(
317             $left=>1,
318             $right=>1,
319             $id_name=>1,
320             provided_primary_key=>1
321             );
322 0 0         my @params=($no_left_or_right) ? () :($left,$right); #Keep in order. . .
323 0           my @values;
324 0           foreach my $column (keys %$params){
325 0 0         if (not defined $ignore{$column}){
326 0           push @params, $column;
327 0   0       push @values, $params->{$column}||''
328             }
329             }
330 0           return (\@params,\@values);
331             }
332             ########################################
333              
334              
335             ################################################################################
336             sub edit_node{
337 0     0 1   my ($self,%params)=@_;
338 0           my $dbh=$self->{dbh};
339            
340 0           my $left=$self->{left_column_name};
341 0           my $right=$self->{right_column_name};
342 0           my $table=$self->{table_name};
343 0           my $id_name=$self->{id_name};
344 0 0 0       if((defined $params{id}) and ($id_name ne 'id')){
345             #If they have changed the id_name but still pass in the id to act upon via the "id" parameter,
346             #fix it here. This is for backwards compatibility
347 0           $params{$id_name}=$params{id};
348             }
349 0           $self->_lock_tables();
350 0           my ($params,$values)=$self->_get_params_and_values(\%params,$left,$right,$id_name,1);
351 0           my ($columns)=_prepare_columns_and_placeholders_for_edit($params);
352 0           $self->_alter_table_if_needed($params);
353 0           my $update=$dbh->prepare_cached(
354             "update $table set $columns where $id_name=?",
355             {dbi_dummy=>__FILE__.__LINE__}
356             );
357 0           my $id_value=$params{$id_name};
358 0           $update->execute(@$values,$id_value);
359 0           $update->finish();
360 0           $self->_unlock_tables();
361             }
362             ########################################
363              
364              
365             ################################################################################
366             sub _prepare_columns_and_placeholders_for_edit{
367 0     0     my ($params)=@_;
368 0           my $columns=join('=? ,',(@$params)).'=?';
369 0           return ($columns);
370             }
371             ########################################
372              
373              
374             ################################################################################
375             sub _prepare_columns_and_placeholders_for_adding_child_to_right{
376 0     0     my ($params,$left,$right)=@_;
377 0           my $columns=join(',',(@$params));
378 0 0         my $placeholders=join(',',('?','? + 1')).
379             ((scalar @$params -2 > 0) ? ',':''). #If there isn't more than 2 params, don't put in a comma
380             substr(('?,' x (scalar @$params -2 )),0,-1);
381 0           return ($columns,$placeholders);
382             }
383             ########################################
384              
385              
386             ################################################################################
387             sub _prepare_columns_and_placeholders_for_adding_child_to_left{
388 0     0     my ($params,$left,$right)=@_;
389 0           my $columns=join(',',(@$params));
390 0 0         my $placeholders=join(',',('? + 1','? + 2')).
391             ((scalar @$params -2 > 0) ? ',':''). #If there isn't more than 2 params, don't put in a comma
392             substr(('?,' x (scalar @$params -2 )),0,-1);
393 0           return ($columns,$placeholders);
394             }
395             ########################################
396              
397              
398             ################################################################################
399             sub get_id_by_key{
400 0     0 1   my($self,%params)=@_;
401 0           my $left=$self->{left_column_name};
402 0           my $right=$self->{right_column_name};
403 0           my $table=$self->{table_name};
404 0           my $key_name=$params{key_name};
405 0           my $id_name=$self->{id_name};
406 0 0 0       if((defined $params{id}) and ($id_name ne 'id')){
407             #If they have changed the id_name but still pass in the id to act upon via the "id" parameter,
408             #fix it here. This is for backwards compatibility
409 0           $params{$id_name}=$params{id};
410             }
411 0           my $ids=$self->{dbh}->selectcol_arrayref("select $id_name from $table where $key_name = ?",undef,($params{key_value}));
412 0 0         return (@$ids > 1) ? $ids : $ids->[0] ;
413             }
414             ########################################
415              
416              
417             ################################################################################
418             sub get_self_and_parents_flat{
419 0     0 1   my($self,%params)=@_;
420 0           my $dbh=$self->{dbh};
421 0           my $left=$self->{left_column_name};
422 0           my $right=$self->{right_column_name};
423 0           my $table=$self->{table_name};
424 0           my $id_name=$self->{id_name};
425 0 0 0       if((defined $params{id}) and ($id_name ne 'id')){
426             #If they have changed the id_name but still pass in the id to act upon via the "id" parameter,
427             #fix it here. This is for backwards compatibility
428 0           $params{$id_name}=$params{id};
429             }
430 0           my $prepared_get_self_and_parents_flat_SQL_statement=
431             $dbh->prepare_cached(
432             "select n2.* from $table as n1, $table as n2 where (n1.$left between n2.$left and n2.$right) and (n1.$id_name=?) order by n2.$left",
433             {dbi_dummy=>__FILE__.__LINE__}
434             );
435            
436 0   0       my $tree_structure=$dbh->selectall_arrayref($prepared_get_self_and_parents_flat_SQL_statement,
437             {Columns=>{}},
438             ($params{$id_name} || 1)
439             );
440 0           my $level=1;
441 0           foreach(@$tree_structure){
442 0           $_->{level}=$level;
443 0           $level++;
444             }
445 0           return $tree_structure;
446             }
447             ########################################
448              
449              
450             ################################################################################
451             sub get_parents_flat{
452 0     0 1   my $self=shift;
453 0           my $tree=$self->get_self_and_parents_flat(@_);
454 0 0         my $poo=pop @$tree if(@$tree);
455 0           return $tree;
456             }
457             ########################################
458              
459              
460             ################################################################################
461             sub delete_self_and_children{
462 0     0 1   my ($self,%params)=@_;
463 0           my $dbh=$self->{dbh};
464 0           my $left=$self->{left_column_name};
465 0           my $right=$self->{right_column_name};
466 0           my $table=$self->{table_name};
467 0           my $id_name=$self->{id_name};
468 0 0 0       if((defined $params{id}) and ($id_name ne 'id')){
469             # If they have changed the name of the id field
470             # but still pass in IDs with the old "id" moniker, fix it here.
471              
472 0           $params{$id_name}=$params{id};
473             }
474 0 0         if(!$params{$id_name}){
475 0           carp("You didn't give us an ID that we could start the delete from");
476 0           return [];
477             } else {
478 0           $self->_lock_tables();
479 0           my $ids;
480 0 0         if($params{not_self}){
481             #We don't want to delete the starting node.
482             #Start with the next level and go through them.
483              
484 0           my $outer_tree=$self->get_children_flat(id=>$params{$id_name},depth=>1);
485 0           foreach my $outer_node(@$outer_tree){
486 0           my $temp_tree=$self->get_self_and_children_flat(id=>$outer_node->{$id_name});
487 0           $self->_delete_node(id=>$outer_node->{$id_name});
488 0           foreach my $inner_node (@$temp_tree){
489 0           push @$ids,$inner_node->{$id_name};
490             }
491             }
492            
493             } else {
494             #Delete it all. Hasta la bye-bye!
495 0           my $tree=$self->get_self_and_children_flat(id=>$params{$id_name});
496 0           $self->_delete_node(%params);
497 0           foreach my $node (@$tree){
498 0           push @$ids,$node->{$id_name};
499             }
500             }
501 0           $self->_unlock_tables();
502 0           return $ids;
503             }
504             }
505             ########################################
506              
507              
508             ################################################################################
509             sub delete_children{
510 0     0 1   my $self=shift;
511 0           $self->delete_self_and_children(@_,not_self=>1);
512             }
513             ########################################
514              
515              
516             ################################################################################
517             sub _delete_node{
518 0     0     my($self,%params)=@_;
519 0           my $left=$self->{left_column_name};
520 0           my $right=$self->{right_column_name};
521 0           my $table=$self->{table_name};
522 0           my $id_name=$self->{id_name};
523 0           my $dbh=$self->{dbh};
524 0           my $node_info=$self->get_hashref_of_info_by_id($params{$id_name});
525            
526 0           my $prepared_delete_node_delete_statement=
527             $dbh->prepare_cached(
528             "delete from $table where $left between ? and ?",
529             {dbi_dummy=>__FILE__.__LINE__}
530             );
531            
532 0           $prepared_delete_node_delete_statement->execute($node_info->{$left},$node_info->{$right});
533 0           $prepared_delete_node_delete_statement->finish();
534              
535 0           my $prepared_delete_node_fix_nodes=
536             $dbh->prepare_cached(
537             "UPDATE $table
538             SET $left = CASE
539             WHEN $left > ? THEN $left - (? - ? + 1)
540             ELSE $left
541             END,
542             $right = CASE
543             WHEN $right > ? THEN $right - (? - ? + 1)
544             ELSE $right
545             END
546             WHERE $right > ?",
547             {dbi_dummy=>__FILE__.__LINE__}
548             );
549            
550 0           $prepared_delete_node_fix_nodes->execute(
551             $node_info->{$left},
552             $node_info->{$right},
553             $node_info->{$left},
554             $node_info->{$right},
555             $node_info->{$right},
556             $node_info->{$left},
557             $node_info->{$left},
558             );
559 0           $prepared_delete_node_fix_nodes->finish();
560             }
561             ########################################
562              
563              
564             ################################################################################
565             sub get_self_and_children_flat{
566 0     0 1   my($self,%params)=@_;
567 0           my $dbh=$self->{dbh};
568 0           my $left=$self->{left_column_name};
569 0           my $right=$self->{right_column_name};
570 0           my $table=$self->{table_name};
571 0           my $id_name=$self->{id_name};
572 0 0 0       if((defined $params{id}) and ($id_name ne 'id')){
573             # If they have changed the id name but still pass in the ID value in the id parameter, fix it.
574 0           $params{$id_name}=$params{id}
575             }
576 0           my $id_SQL='';
577 0           my $start_node_info={};
578 0 0         if (defined $params{$id_name}) {
579 0           $start_node_info=$self->get_hashref_of_info_by_id_with_level($params{$id_name});
580 0           $id_SQL="and (n1.$left between " . $dbh->quote($start_node_info->{$left})." and ".$dbh->quote($start_node_info->{$right}).") ";
581             }
582 0           my $depth_having_SQL='';
583 0 0         if($params{depth}){
584 0           $depth_having_SQL='having level <= '.$dbh->quote(($params{depth} + $start_node_info->{level}));
585             }
586 0           my $tree_structure=$dbh->selectall_arrayref("select count(n2.${id_name}) as level,n1.* from $table as n1, $table as n2 where (n1.$left between n2.$left and n2.$right) $id_SQL group by n1.${id_name} $depth_having_SQL order by n1.$left",{Columns=>{}});
587 0           return $tree_structure;
588             }
589             ########################################
590              
591              
592             ################################################################################
593             sub get_children_flat{
594 0     0 1   my $self=shift;
595 0           my $tree=$self->get_self_and_children_flat(@_);
596 0 0         my $poo=shift @$tree if(@$tree);
597 0           return $tree;
598             }
599             ########################################
600              
601              
602             ################################################################################
603             sub swap_nodes{
604 0     0 1   my($self,%params)=@_;
605 0           my $dbh=$self->{dbh};
606 0           my $left=$self->{left_column_name};
607 0           my $right=$self->{right_column_name};
608 0           my $table=$self->{table_name};
609              
610 0           my $first_id=$params{first_id};
611 0           my $second_id=$params{second_id};
612 0 0 0       croak("You didn't give me valid IDs to swap!\n") if(! $first_id || ! $second_id);
613             #This is an "eq" below because we can now have non-numeric IDs.
614 0 0         croak("You can't switch a node with itself!\n") if($first_id eq $second_id);
615              
616 0           $self->_lock_tables();
617 0           my $first_id_info=$self->get_hashref_of_info_by_id($first_id);
618 0           my $second_id_info=$self->get_hashref_of_info_by_id($second_id);
619              
620 0           my ($left_node,$right_node);
621 0 0         if($first_id_info->{$left} < $second_id_info->{$left}){
622 0           $left_node=$first_id_info;
623 0           $right_node=$second_id_info;
624             } else {
625 0           $left_node=$second_id_info;
626 0           $right_node=$first_id_info;
627             }
628 0           $dbh->do(qq|update $table set
629             $left =
630             CASE WHEN $left between $left_node->{$left} and $left_node->{$right}
631             THEN $right_node->{$right} + $left - $left_node->{$right}
632             WHEN $left between $right_node->{$left} and $right_node->{$right}
633             THEN $left_node->{$left} + $left - $right_node->{$left}
634             ELSE $left_node->{$left} + $right_node->{$right} + $left - $left_node->{$right} - $right_node->{$left} END,
635             $right =
636             CASE WHEN $right between $left_node->{$left} and $left_node->{$right}
637             THEN $right_node->{$right} + $right - $left_node->{$right}
638             WHEN $right between $right_node->{$left} and $right_node->{$right}
639             THEN $left_node->{$left} + $right - $right_node->{$left}
640             ELSE $left_node->{$left} + $right_node->{$right} + $right - $left_node->{$right} - $right_node->{$left} END
641             WHERE ($left between $left_node->{$left} and $right_node->{$right})
642             AND $left_node->{$left} < $left_node->{$right}
643             AND $left_node->{$right} < $right_node->{$left}
644             AND $right_node->{$left} < $right_node->{$right}|);
645            
646 0           $self->_unlock_tables();
647             }
648             ########################################
649              
650              
651             ################################################################################
652             sub get_hashref_of_info_by_id{
653 0     0 1   my ($self,$value)=@_;
654 0           my $dbh=$self->{dbh};
655 0           my $id_name=$self->{id_name};
656             #This excessively explicit quoting is to work around the buggy Mandrake 10.0 version of DBD::mysql
657 0           return $dbh->selectrow_hashref("select * from ".$self->{table_name}." where $id_name = ".$dbh->quote($value));
658             }
659             ########################################
660              
661              
662             ################################################################################
663             sub get_hashref_of_info_by_id_with_level{
664 0     0 1   my $self=shift;
665 0           my $wanted_id=shift;
666 0           my $left=$self->{left_column_name};
667 0           my $right=$self->{right_column_name};
668 0           my $table=$self->{table_name};
669 0           my $id_name=$self->{id_name};
670 0           return $self->{dbh}->selectrow_hashref("select count(n2.$id_name) as level,n1.* from $table as n1, $table as n2 where (n1.$left between n2.$left and n2.$right) and n1.$id_name=? group by n1.$id_name",undef,($wanted_id));
671             }
672             ########################################
673              
674              
675             ################################################################################
676             sub create_report{
677 0     0 1   my ($self,%params)=@_;
678 0           my $id_name=$self->{id_name};
679 0   0       my $ancestors=$self->get_self_and_children_flat(id => $params{$id_name}||$self->get_root);
680 0           my $report;
681 0           foreach (@$ancestors) {
682 0 0 0       $report.= (($_->{level} > 1) ? ((" " x ($params{indent_level} || 2)) x ($_->{level} - 1)) :'');
683 0           $report.= $_->{name}." (".$_->{$id_name}.")(".$_->{level}.")\n";
684             }
685 0           return $report;
686             }
687             ########################################
688              
689              
690             ################################################################################
691             sub create_default_table{
692 0     0 1   my $self=shift;
693 0           $self->{_db_obj}->_create_default_table();
694             }
695             ########################################
696              
697              
698             ################################################################################
699             sub get_default_create_table_statement{
700 0     0 1   my $self=shift;
701 0           $self->{_db_obj}->_get_default_create_table_statement();
702             }
703             ########################################
704              
705              
706             1;
707              
708             __END__