File Coverage

blib/lib/Embedix/DB/Pg.pm
Criterion Covered Total %
statement 15 365 4.1
branch 0 130 0.0
condition 0 45 0.0
subroutine 5 31 16.1
pod 7 24 29.1
total 27 595 4.5


line stmt bran cond sub pod time code
1             package Embedix::DB::Pg;
2              
3 2     2   12 use strict;
  2         2  
  2         126  
4 2     2   13 use vars qw($AUTOLOAD);
  2         4  
  2         103  
5              
6             # for warning message from the caller's perspective
7 2     2   11 use Carp;
  2         5  
  2         150  
8              
9             # for loading data from files
10 2     2   11 use Embedix::ECD;
  2         4  
  2         52  
11              
12             # for database support
13 2     2   5218 use DBI;
  2         105966  
  2         24058  
14              
15             # constructor
16             #_______________________________________
17             sub new {
18 0     0 1   my $proto = shift;
19 0   0       my $class = ref($proto) || $proto;
20 0 0         (@_ & 1) && croak("Odd number of parameters.");
21 0           my %opt = @_;
22              
23 0   0       my $dbh = DBI->connect(@{$opt{source}}) || croak($DBI::errstr);
24 0           my $self = {
25             dbh => $dbh,
26             distro => undef, # hashref w/ info on current working distro
27             path_cache => { }, # $path_cache->{node_id} eq $path
28             };
29 0           bless($self => $class);
30              
31             #self->workOnDistro(name => $opt{name}, board => $opt{board});
32 0           return $self;
33             }
34              
35             # destructor
36             #_______________________________________
37             sub DESTROY {
38 0     0     my $self = shift;
39 0           $self->{dbh}->disconnect();
40             }
41              
42             # for when things go wrong...
43             #_______________________________________
44             sub rollbackAndCroak {
45 0     0 0   my $self = shift;
46 0           my $msg = shift;
47 0           my $dbh = $self->{dbh};
48 0           my $err = $dbh->errstr . "\n$msg";
49 0           $dbh->rollback;
50 0           croak($err);
51             }
52              
53             # $insert_statement = $hotel->buildInsertStatement (
54             # table => "table",
55             # data => \%column
56             # );
57             #_______________________________________
58             sub buildInsertStatement {
59 0     0 0   my $self = shift;
60 0           my $dbh = $self->{dbh};
61              
62 0 0         (@_ & 1) && croak "Odd number of parameters\n";
63 0           my %opt = @_;
64 0           my $column = $opt{data};
65              
66 0           my $insert = "insert into $opt{table} ( ";
67 0           $insert .= join(", ", keys %$column);
68 0           $insert =~ s/, $//;
69 0           $insert .= " ) values ( ";
70 0           $insert .= join(", ", map { $dbh->quote($_) } values %$column);
  0            
71 0           $insert =~ s/, $//;
72 0           $insert .= " );";
73              
74 0           return $insert;
75             }
76              
77             # $update_statement = $hotel->buildUpdateStatement (
78             # table => "table",
79             # data => \%column,
80             # where => "id = 'whatever'",
81             # primary_key => 'id',
82             # );
83             #
84             # note that you should use 'where' xor 'primary_key'.
85             # do not use both at the same time
86             # use at least one of them. ...xor
87             #_______________________________________
88             sub buildUpdateStatement {
89 0     0 0   my $self = shift;
90 0           my $dbh = $self->{dbh};
91              
92 0 0         (@_ & 1) && croak "Odd number of parameters\n";
93 0           my %opt = @_;
94 0           my $column = $opt{data};
95              
96 0           my $update = "update $opt{table} set ";
97 0           foreach (keys %$column) {
98 0           $update .= "$_ = " . $dbh->quote($column->{$_}) . ", ";
99             }
100 0           $update =~ s/, $//;
101              
102 0           $update .= " where ";
103 0 0         if (defined $opt{where}) {
    0          
104 0           $update .= "$opt{where};";
105             } elsif (defined $opt{primary_key}) {
106 0           my $pk = $opt{primary_key};
107 0           $update .= "$pk = '$column->{$pk}';";
108             } else {
109 0           croak "buildUpdateStatement w/o a WHERE clause\n";
110             }
111              
112 0           return $update;
113             }
114              
115             # return the current value of a sequence.
116             # This is a front end to PostgreSQL's currval() function.
117             #_______________________________________
118             sub currval {
119 0     0 0   my $self = shift;
120 0           my $seq = shift;
121 0           my $dbh = $self->{dbh};
122 0           my $sth = $dbh->prepare("select currval('$seq')");
123 0           $sth->execute;
124 0           my @val = $sth->fetchrow_array;
125 0           $sth->finish;
126 0           return $val[0];
127             }
128              
129             # return the next value of a sequence.
130             # This is a front end to PostgreSQL's currval() function.
131             #_______________________________________
132             sub nextval {
133 0     0 0   my $self = shift;
134 0           my $seq = shift;
135 0           my $dbh = $self->{dbh};
136 0           my $sth = $dbh->prepare("select nextval('$seq')");
137 0           $sth->execute;
138 0           my @val = $sth->fetchrow_array;
139 0           $sth->finish;
140 0           return $val[0];
141             }
142              
143             # Set the distribution that database opererations will work on.
144             # If the distribution is not found, this method will croak().
145             #_______________________________________
146             sub workOnDistro {
147 0 0   0 1   my $self = shift; (@_ & 1) && croak("Odd number of parameters.");
  0            
148 0           my %opt = @_;
149              
150 0 0         defined($opt{name}) || croak('name => REQUIRED!');
151 0 0         defined($opt{board}) || croak('board => REQUIRED!');
152            
153             # get distro from database
154 0           my $q = qq{ select * from distro where distro_name = ? and board = ? };
155 0           my $dbh = $self->{dbh};
156 0           my $sth = $dbh->prepare($q);
157            
158 0           $sth->execute($opt{name}, $opt{board});
159 0           my $distro = $sth->fetchrow_hashref();
160 0           $sth->finish;
161              
162 0 0         if (defined($distro)) {
163 0           $self->{distro} = $distro;
164             } else {
165 0           croak("$opt{name} for $opt{board} was not found.");
166             }
167              
168             # reinitialize caches
169 0           $self->{path_cache} = { };
170              
171 0           return $self->{distro};
172             }
173              
174             # adds an new entry into the distro table as well as an entry
175             # in the node table for the root node.
176             #_______________________________________
177             sub addDistro {
178 0 0   0 1   my $self = shift; (@_ & 1) && croak("Odd number of parameters.");
  0            
179 0           my %opt = @_;
180 0           my $dbh = $self->{dbh};
181 0           my ($sth1, $sth2, $q);
182              
183             # get root_node_id
184 0 0         my $root_node_id = defined($opt{root_node_id})
185             ? $opt{root_node_id}
186             : $self->nextval('node_node_id_seq');
187              
188             # distro table entry
189 0   0       my $distro = {
      0        
      0        
190             distro_name => $opt{name} || croak("name required"),
191             board => $opt{board} || croak("board required"),
192             description => $opt{description} || "no description available",
193             root_node_id => $root_node_id,
194             };
195 0           $q = $self->buildInsertStatement(table => "distro", data => $distro);
196 0           $sth1 = $dbh->prepare($q);
197 0 0         $sth1->execute || do { $self->rollbackAndCroak($q) };
  0            
198 0           $sth1->finish;
199              
200             # get distro_id
201 0           my $distro_id = $self->currval('distro_distro_id_seq');
202 0           $distro->{distro_id} = $distro_id;
203              
204             # root node
205 0           my $root = {
206             node_id => $root_node_id,
207             node_name => 'ecd',
208             node_class => 'Root',
209             };
210              
211             # make a root node if necessary
212 0 0         unless (defined($opt{root_node_id})) {
213 0           $q = $self->buildInsertStatement(table => "node", data => $root);
214 0           $sth2 = $dbh->prepare($q);
215 0 0         $sth2->execute || do { $self->rollbackAndCroak($q) };
  0            
216 0           $sth2->finish;
217             #rint STDERR "[edb relating: $root->{node_id} To: $distro->{distro_id}]\n";
218 0           $self->relateNode(node => $root, distro => $distro);
219             }
220 0           $dbh->commit;
221 0           return $distro;
222             }
223              
224             # associate a node with a distro by adding an entry
225             # to the node_distro table
226             #_______________________________________
227             sub relateNode {
228 0 0   0 0   my $self = shift; (@_ & 1) && croak("Odd number of parameters.");
  0            
229 0           my %opt = @_;
230 0 0         defined ($opt{node}) || croak('node => REQUIRED!');
231 0 0         defined ($opt{distro}) || croak('distro => REQUIRED!');
232 0           my $dbh = $self->{dbh};
233 0           my $s = qq/
234             insert into node_distro (node_id, distro_id)
235             values ($opt{node}{node_id}, $opt{distro}{distro_id})
236             /;
237 0 0         $dbh->do($s) || $self->rollbackAndCroak($s);
238             }
239              
240             # remove association of node from distro by deleting an entry
241             # in the node_distro table
242             #_______________________________________
243             sub unrelateNode {
244 0 0   0 0   my $self = shift; (@_ & 1) && croak("Odd number of parameters.");
  0            
245 0           my %opt = @_;
246 0 0         defined ($opt{node}) || croak('node => REQUIRED!');
247 0 0         defined ($opt{distro}) || croak('distro => REQUIRED!');
248 0           my $dbh = $self->{dbh};
249 0           my $s = qq/
250             delete from node_distro
251             where node_id = $opt{node}{node_id}
252             and distro_id = $opt{distro}{distro_id}
253             /;
254 0 0         $dbh->do($s) || $self->rollbackAndCroak($s);
255              
256             }
257              
258             # using the current working distro, make an exact
259             # clone for another architecture.
260             #_______________________________________
261             sub cloneDistro {
262 0 0   0 1   my $self = shift; (@_ & 1) && croak("Odd number of parameters.");
  0            
263 0           my %opt = @_;
264 0           my $dbh = $self->{dbh};
265              
266 0 0         defined($opt{board}) || croak('board => REQUIRED!');
267              
268             # get root_node_id
269 0           my $root_node_id = $self->{distro}{root_node_id};
270              
271             # cloned distro entry
272 0           my $distro = $self->{distro};
273 0   0       my $clone = $self->addDistro (
274             name => $distro->{distro_name},
275             board => $opt{board},
276             description => $opt{description} || $distro->{description},
277             root_node_id => $root_node_id,
278             );
279              
280             # get distro_id
281 0           my $distro_id = $self->currval('distro_distro_id_seq');
282 0           $clone->{distro_id} = $distro_id;
283              
284             # node_id collection
285 0           my $s = qq/
286             select n.node_id
287             from node n, node_distro nd
288             where n.node_id = nd.node_id
289             and nd.distro_id = $self->{distro}{distro_id}
290             /;
291 0           my $node_list = $dbh->selectall_arrayref($s);
292              
293             # node_distro manipulation
294 0           $s = qq/ insert into node_distro (node_id, distro_id) values (?, ?) /;
295 0           my $sth = $dbh->prepare_cached($s);
296 0           my $node;
297 0           foreach $node (@$node_list) {
298 0 0         $sth->execute($node->[0], $distro_id)
299             || $self->rollbackAndCroak($node->[0]);
300             }
301 0           $sth->finish;
302 0           $dbh->commit;
303 0           return $clone;
304             }
305              
306             # delete a node and all its children
307             #_______________________________________
308             sub deleteNode {
309 0 0   0 0   my $self = shift; (@_ & 1) && croak("Odd number of parameters.");
  0            
310 0           my %opt = @_;
311 0           my $dbh = $self->{dbh};
312              
313 0 0         $dbh->do("delete from node where node_id = $opt{node_id}")
314             || $self->rollbackAndCroak("failed delete");
315             }
316              
317             #_______________________________________
318             sub selectNode {
319 0 0   0 0   my $self = shift; (@_ & 1) && croak("Odd number of parameters.");
  0            
320 0           my %opt = @_;
321              
322 0           my $q = qq(
323             select n.node_id,
324             n.node_class,
325             n.node_name,
326             n.value,
327             n.value_type,
328             n.default_value,
329             n.range,
330             n.help,
331             n.prompt,
332             n.srpm,
333             n.specpatch,
334             n.static_size, n.min_dynamic_size,
335             n.storage_size, n.startup_time
336             from node n, node_parent np, node_distro nd
337             where n.node_id = np.node_id
338             and n.node_id = nd.node_id
339             and nd.distro_id = $self->{distro}{distro_id}
340             and n.node_name = ?
341             and np.parent_id = ?
342             );
343              
344 0           my $dbh = $self->{dbh};
345 0           my $sth = $dbh->prepare($q);
346 0           my ($name, $parent_id);
347 0 0         if (defined $opt{path}) {
348              
349             # XXX => implement getIdForPath()
350              
351             } else {
352 0           $name = $opt{name};
353 0           $parent_id = $opt{parent_id};
354             }
355 0           $sth->execute($name, $parent_id);
356 0           my $node = $sth->fetchrow_hashref; # there can only be one
357 0           $sth->finish;
358 0           return $node;
359             }
360              
361             # prereq => no provides entry for $node_id must exist
362             #_______________________________________
363             sub insertProvides {
364 0     0 0   my $self = shift;
365 0           my $provides = shift;
366 0 0         return unless ($provides);
367 0   0       my $node_id = shift || croak("node_id REQUIRED!");
368 0           my $dbh = $self->{dbh};
369 0           my %item;
370              
371 0           my $s = qq{ insert into provides (node_id, entry) values ( ?, ? ) };
372 0           my $sth = $dbh->prepare($s);
373 0 0         $provides = [ $provides ] unless (ref($provides));
374 0           foreach (@$provides) {
375 0 0         next if /^$/;
376 0 0         if (defined $item{$_}) {
377 0           carp("[ $node_id, $_ ] already exists");
378             } else {
379 0           $item{$_} = 1;
380 0 0         $sth->execute($node_id, $_) ||
381             croak("[ $node_id, $_ ] " . $dbh->errstr);
382             }
383             }
384 0           $sth->finish;
385             }
386              
387             # prereq => no keeplist entry for $node_id must exist
388             #_______________________________________
389             sub insertKeeplist {
390 0     0 0   my $self = shift;
391 0           my $keeplist = shift;
392 0 0         return unless ($keeplist);
393 0   0       my $node_id = shift || croak("node_id REQUIRED!");
394 0           my $dbh = $self->{dbh};
395 0           my %item;
396              
397 0           my $s = qq{ insert into keeplist (node_id, entry) values ( ?, ? ) };
398 0           my $sth = $dbh->prepare($s);
399 0 0         $keeplist = [ $keeplist ] unless (ref($keeplist));
400 0           foreach (@$keeplist) {
401 0 0         next if /^$/;
402 0 0         if (defined $item{$_}) {
403 0           carp("[ $node_id, $_ ] already exists");
404             } else {
405 0           $item{$_} = 1;
406 0 0         $sth->execute($node_id, $_) ||
407             croak("[ $node_id, $_ ] " . $dbh->errstr);
408             }
409             }
410 0           $sth->finish;
411             }
412              
413             # prereq => no build_vars entry for $node_id must exist
414             #_______________________________________
415             sub insertBuildVars {
416 0     0 0   my $self = shift;
417 0           my $build_vars = shift;
418 0 0         return unless ($build_vars);
419 0   0       my $node_id = shift || croak("node_id REQUIRED!");
420 0           my $dbh = $self->{dbh};
421 0           my %item;
422              
423 0           my $s = 'insert into build_vars (node_id, name, value) values (?, ?, ?)';
424 0           my $sth = $dbh->prepare($s);
425 0 0         $build_vars = [ $build_vars ] unless (ref($build_vars));
426 0           foreach (@$build_vars) {
427 0 0         next if /^$/;
428 0           my ($n, $v) = split(/\s*=\s*/);
429 0 0         if (defined $item{$n}) {
430 0           carp("[ $node_id, $n ] already exists");
431             } else {
432 0           $item{$n} = 1;
433 0 0         $sth->execute($node_id, $n, $v) ||
434             croak("[ $node_id, $_ ] " . $dbh->errstr);
435             }
436             }
437 0           $sth->finish;
438             }
439              
440             #_______________________________________
441             sub insertNode {
442 0 0   0 0   my $self = shift; (@_ & 1) && croak("Odd number of parameters.");
  0            
443 0           my %opt = @_;
444              
445 0   0       my $ecd = $opt{ecd} || croak('ecd => REQUIRED!');
446              
447             # insert into node table
448 0           my $node = $self->hashrefFromECD($ecd);
449 0           my $s = $self->buildInsertStatement(table => "node", data => $node);
450 0           my $dbh = $self->{dbh};
451 0           my $sth = $dbh->prepare($s);
452              
453 0 0         $sth->execute || do { $self->rollbackAndCroak($s) };
  0            
454 0           $sth->finish;
455 0           my $id = $node->{node_id} = $self->currval('node_node_id_seq');
456              
457             # insert aggregate attributes
458 0           eval {
459 0           $self->insertProvides($ecd->provides, $id);
460 0           $self->insertKeeplist($ecd->keeplist, $id);
461 0           $self->insertBuildVars($ecd->build_vars, $id);
462             };
463 0 0         if ($@) { $self->rollbackAndCroak($@) }
  0            
464              
465             # insert into node_parent table
466 0           my $np = { node_id => $id, parent_id => $opt{parent_id} };
467 0           my $s2 = $self->buildInsertStatement(table=> "node_parent", data=> $np);
468 0           my $sth2 = $dbh->prepare($s2);
469 0 0         $sth2->execute || do { $self->rollbackAndCroak($s2) };
  0            
470 0           $sth2->finish;
471              
472             # insert into node_distro_table
473 0           $self->relateNode(node => $node, distro => $self->{distro});
474              
475 0           $dbh->commit;
476 0           return $node;
477             }
478              
479             # XXX : deal w/ aggregate attributes
480             #_______________________________________
481             sub updateNode {
482 0 0   0 0   my $self = shift; (@_ & 1) && croak("Odd number of parameters.");
  0            
483 0           my %opt = @_;
484              
485 0   0       my $ecd = $opt{ecd} || croak('ecd => REQUIRED!');
486              
487 0           my $node = $self->hashrefFromECD($ecd);
488 0   0       $node->{node_id} = $opt{node_id} || croak('node_id => REQUIRED!');
489 0           my $s = $self->buildUpdateStatement(
490             table => "node",
491             data => $node,
492             primary_key => "node_id",
493             );
494 0           my $dbh = $self->{dbh};
495 0           my $sth = $dbh->prepare($s);
496              
497 0 0         $sth->execute || do { $self->rollbackAndCroak($s) };
  0            
498 0           $sth->finish;
499              
500             # nuke aggregate attributes from orbit (it's the only way to be sure)
501              
502             # insert aggregate attributes XXX
503              
504 0           $dbh->commit;
505 0           return $node;
506             }
507              
508             # Create a hashref suitable for insertion into the node table.
509             # This does NOT handle aggregates (but it does handle the range pair).
510             #_______________________________________
511             my @node_attribute = qw(
512             value type default_value range help prompt srpm specpatch
513             requires requiresexpr
514             );
515             my @node_eval_attribute = qw(
516             static_size min_dynamic_size storage_size startup_time
517             );
518             sub hashrefFromECD {
519 0     0 0   my $self = shift;
520 0           my $ecd = shift;
521 0           my %node = (
522             node_class => $ecd->getNodeClass(),
523             node_name => $ecd->name(),
524             );
525 0           my $attr;
526 0           foreach (@node_attribute) {
527 0 0         if (defined($attr = $ecd->getAttribute($_))) {
528 0 0         if (ref($attr)) {
529 0           $attr = join("\n", @$attr);
530             }
531 0 0         if ($_ eq "range") {
532 0           my ($x, $y) = split($attr, ":"); # turn it into a pg array
533 0           $attr = "{$x, $y}";
534             }
535 0           $node{$_} = $attr;
536             }
537             }
538 0           foreach (@node_eval_attribute) {
539 0 0         if (defined($attr = $ecd->getAttribute($_))) {
540 0           my $eval_method = "eval_$_";
541 0           my ($size, $give_or_take) = $ecd->$eval_method();
542 0           $attr = "{$size, $give_or_take}";
543 0           $node{$_} = $attr;
544             }
545             }
546 0 0         if (defined $node{type}) {
547 0           $node{value_type} = $node{type};
548 0           delete($node{type});
549             }
550 0 0 0       warn("$node{node_name} has a requires and requiresexpr which is bad.")
551             if (defined $node{requires} && defined($node{requiresexpr}));
552 0 0         if (defined $node{requires}) {
553 0           $node{requires_type} = 'list';
554             }
555 0 0         if (defined $node{requiresexpr}) {
556 0           $node{requires_type} = 'expr';
557 0           $node{requires} = $node{requiresexpr};
558 0           delete($node{requiresexpr});
559             };
560 0           return \%node;
561             }
562              
563             # add info in $ecd to current working distribution
564             #_______________________________________
565             sub updateDistro {
566 0 0   0 1   my $self = shift; (@_ & 1) && croak("Odd number of parameters.");
  0            
567 0           my %opt = @_;
568 0   0       my $ecd = $opt{ecd} || croak("ecd => REQUIRED!");
569 0   0       my $parent_id = $opt{parent_id} || undef;
570 0           my ($child, $node);
571              
572 0 0         unless (defined($self->{distro})) {
573 0           croak("Cannot add an ECD until a distribution to work on is selected.");
574             }
575              
576 0 0         if ($ecd->getDepth == 0) {
577             # handle root nodes (root node identification could be more robust)
578 0           $node = { };
579 0           $node->{node_id} = $self->{distro}{root_node_id};
580             } else {
581             # all other nodes
582 0           $node = $self->selectNode(
583             name => $ecd->name(),
584             parent_id => $parent_id,
585             );
586 0 0         if (defined($node)) {
587 0           $node = $self->updateNode(ecd => $ecd, node_id => $node->{node_id});
588             } else {
589 0           $node = $self->insertNode(ecd => $ecd, parent_id => $parent_id);
590             };
591             }
592              
593 0           foreach $child ($ecd->getChildren) {
594 0           $self->updateDistro(ecd => $child, parent_id => $node->{node_id});
595             }
596             }
597              
598             # get node_id for a given path
599             #_______________________________________
600             sub getIdForPath {
601 0     0 0   my $self = shift;
602 0           my $path = shift;
603              
604             }
605              
606             # return full path of a node
607             #_______________________________________
608             sub getNodePath {
609 0     0 0   my $self = shift;
610 0           my $id = shift;
611 0           my $p = $self->{path_cache};
612              
613 0           my $root_node_id = $self->{distro}{root_node_id};
614 0 0         if ($id == $root_node_id) {
615 0           return '/';
616             }
617 0           my $distro_id = $self->{distro}{distro_id};
618              
619 0 0         unless (defined $p->{$id}) {
620 0           my $q = qq{
621             select n.node_id, n.node_name, np.parent_id
622             from node n,
623             node_parent np,
624             node_distro nd
625             where n.node_id = np.node_id
626             and n.node_id = nd.node_id
627             and nd.distro_id = $distro_id
628             and n.node_id = ?
629             };
630 0           my $sth = $self->{dbh}->prepare($q);
631 0           my $i = $id;
632 0           my @path;
633             my $node;
634 0           do {
635 0           $sth->execute($i);
636 0           $node = $sth->fetchrow_hashref;
637 0           $i = $node->{parent_id};
638 0           unshift(@path, $node->{node_name});
639 0           $sth->finish;
640             } while ($i != $root_node_id);
641 0           $p->{$id} = '/' . join('/', @path);
642             }
643 0           return $p->{$id};
644             }
645              
646             # return an arrayref of component names of the form
647             # [
648             # [ "category0", [ $node, ... ] ],
649             # [ "category1", [ $node, ... ] ],
650             # ...
651             # ]
652             # where $node is [ n.node_id, n.node_name ], and it's all SORTED -- yay!
653             #_______________________________________
654             sub getComponentList {
655 0     0 1   my $self = shift;
656 0           my $dbh = $self->{dbh};
657              
658 0           my $q = qq#
659             select np.parent_id, n.node_id, n.node_name
660             from node n,
661             node_parent np,
662             node_distro nd
663             where n.node_id = np.node_id
664             and n.node_id = nd.node_id
665             and n.node_class = 'Component'
666             and nd.distro_id = $self->{distro}{distro_id}
667             #;
668              
669             # get them all categorized
670 0           my (%cat, $path, $comp, $list);
671 0           $list = $dbh->selectall_arrayref($q);
672 0           foreach $comp (@$list) {
673 0           $path = $self->getNodePath($comp->[0]);
674 0 0         if (defined $cat{$path}) {
675 0           push(@{$cat{$path}}, [$comp->[1], $comp->[2]]);
  0            
676             } else {
677 0           my $first = [ [$comp->[1], $comp->[2]] ];
678 0           $cat{$path} = $first;
679             }
680             }
681              
682             # sort each category
683 0           my @cl;
684 0           foreach (sort keys %cat) {
685 0           $list = $cat{$_};
686 0           my $sorted_list = [ sort { $a->[1] cmp $b->[1] } @$list ];
  0            
687 0           push @cl, [ $_, $sorted_list ];
688             }
689 0           return \@cl;
690             }
691              
692             #
693             #_______________________________________
694             sub getDistroList {
695 0     0 1   my $self = shift;
696 0           my $dbh = $self->{dbh};
697 0           my $q = qq/select distro_name, board, description from distro/;
698 0           my $list = $dbh->selectall_arrayref($q);
699              
700             # get them grouped by distribution
701 0           my (%board_list, $distro, $cat);
702 0           foreach $distro (@$list) {
703 0   0       $cat = $board_list{$distro->{distro_name}} ||= [ ];
704 0           push @$cat, $distro;
705             }
706              
707             # sort
708 0           my @dl;
709 0           foreach (sort keys %board_list) {
710 0           $list = $board_list{$_};
711 0           my $sorted_list = [ sort { $a->[0] cmp $b->[0] } @$list ];
  0            
712 0           push @dl, [ $_, $sorted_list ];
713             }
714 0           return \@dl;
715             }
716              
717             # need to do something clever here
718             #_______________________________________
719             sub AUTOLOAD {
720 0     0     croak('Help beppu@cpan.org think of a clever use for AUTOLOAD.');
721             }
722              
723             1;
724              
725             __END__