File Coverage

blib/lib/RDF/Query/Plan/Sort.pm
Criterion Covered Total %
statement 89 127 70.0
branch 13 24 54.1
condition 3 4 75.0
subroutine 16 21 76.1
pod 12 12 100.0
total 133 188 70.7


line stmt bran cond sub pod time code
1             # RDF::Query::Plan::Sort
2             # -----------------------------------------------------------------------------
3              
4             =head1 NAME
5              
6             RDF::Query::Plan::Sort - Executable query plan for Sorts.
7              
8             =head1 VERSION
9              
10             This document describes RDF::Query::Plan::Sort version 2.916.
11              
12             =head1 METHODS
13              
14             Beyond the methods documented below, this class inherits methods from the
15             L<RDF::Query::Plan> class.
16              
17             =over 4
18              
19             =cut
20              
21             package RDF::Query::Plan::Sort;
22              
23 35     35   185 use strict;
  35         81  
  35         932  
24 35     35   183 use warnings;
  35         72  
  35         1180  
25 35     35   192 use Scalar::Util qw(refaddr);
  35         70  
  35         1682  
26 35     35   195 use base qw(RDF::Query::Plan);
  35         82  
  35         3288  
27              
28             ######################################################################
29              
30             our ($VERSION);
31             BEGIN {
32 35     35   10016 $VERSION = '2.916';
33             }
34              
35             ######################################################################
36              
37              
38             =item C<< new ( $pattern, [ $expr1, $rev1 ], ... ) >>
39              
40             =cut
41              
42             sub new {
43 12     12 1 26 my $class = shift;
44 12         23 my $plan = shift;
45 12         29 my @exprs = @_;
46 12         28 foreach my $e (@exprs) {
47 12   100     69 $e->[1] ||= 0;
48             }
49 12         83 my $self = $class->SUPER::new( $plan, \@exprs );
50 12         95 $self->[0]{referenced_variables} = [ $plan->referenced_variables ];
51 12         58 return $self;
52             }
53              
54             =item C<< execute ( $execution_context ) >>
55              
56             =cut
57              
58             sub execute ($) {
59 12     12 1 24 my $self = shift;
60 12         24 my $context = shift;
61 12         46 $self->[0]{delegate} = $context->delegate;
62 12 50       67 if ($self->state == $self->OPEN) {
63 0         0 throw RDF::Query::Error::ExecutionError -text => "SORT plan can't be executed while already open";
64             }
65 12         25 my $plan = $self->[1];
66 12         62 $plan->execute( $context );
67            
68 12         55 my $l = Log::Log4perl->get_logger("rdf.query.plan.sort");
69 12         2793 $l->trace("executing sort");
70 12 50       118 if ($plan->state == $self->OPEN) {
71 12         29 my $exprs = $self->[2];
72 12         93 my @rows = $plan->get_all;
73 12 50       53 if ($l->is_trace) {
74 0         0 $l->trace("sorting result list:");
75 0         0 $l->trace("- $_") foreach (@rows);
76             }
77 12         124 my $query = $context->query;
78            
79 35     35   28366 use sort 'stable';
  35         20175  
  35         219  
80 12         60 my @sorted = sort { _cmp_rows( $context, $exprs, $a, $b ) } @rows;
  32         87  
81 12 50       52 if ($l->is_trace) {
82 0         0 $l->trace("sorted list:");
83 0         0 $l->trace("- $_") foreach (@sorted);
84             }
85 12         101 $self->[0]{rows} = \@sorted;
86 12         74 $self->state( $self->OPEN );
87             } else {
88 0         0 warn "could not execute plan in distinct";
89             }
90 12         45 $self;
91             }
92              
93             =item C<< next >>
94              
95             =cut
96              
97             sub next {
98 32     32 1 55 my $self = shift;
99 32 50       103 unless ($self->state == $self->OPEN) {
100 0         0 throw RDF::Query::Error::ExecutionError -text => "next() cannot be called on an un-open SORT";
101             }
102 32         47 my $bindings = shift(@{ $self->[0]{rows} });
  32         91  
103 32 50       133 if (my $d = $self->delegate) {
104 0         0 $d->log_result( $self, $bindings );
105             }
106 32         85 return $bindings;
107             }
108              
109             =item C<< close >>
110              
111             =cut
112              
113             sub close {
114 12     12 1 25 my $self = shift;
115 12 50       50 unless ($self->state == $self->OPEN) {
116 0         0 throw RDF::Query::Error::ExecutionError -text => "close() cannot be called on an un-open SORT";
117             }
118 12         81 delete $self->[0]{rows};
119 12         78 $self->[1]->close();
120 12         64 $self->SUPER::close();
121             }
122              
123             sub _cmp_rows {
124 32     32   49 my $context = shift;
125 32         45 my $exprs = shift;
126 32         44 my $a = shift;
127 32         45 my $b = shift;
128            
129 32         111 my $l = Log::Log4perl->get_logger("rdf.query.plan.sort");
130 32   50     706 my $query = $context->query || 'RDF::Query';
131 32         110 my $bridge = $context->model;
132            
133 35     35   16048 no warnings 'numeric';
  35         91  
  35         1340  
134 35     35   210 no warnings 'uninitialized';
  35         66  
  35         25366  
135 32         74 foreach my $data (@$exprs) {
136 32         70 my ($expr, $rev) = @$data;
137 32         123 my $a_val = $query->var_or_expr_value( $a, $expr, $context );
138 32         184 my $b_val = $query->var_or_expr_value( $b, $expr, $context );
139 32         130 local($RDF::Query::Node::Literal::LAZY_COMPARISONS) = 1;
140 32         134 $l->trace("comparing $a_val <=> $b_val");
141 32         961 my $cmp = $a_val <=> $b_val;
142 32 100       372 if ($cmp != 0) {
143 23 100       85 if ($rev) {
144 6         13 $cmp *= -1;
145             }
146 23         97 $l->trace("==> $cmp");
147 23         218 return $cmp;
148             } else {
149             }
150             }
151 9         70 $l->trace("==> 0");
152 9         74 return 0;
153             }
154              
155             =item C<< pattern >>
156              
157             Returns the query plan that will be used to produce the data to be sorted.
158              
159             =cut
160              
161             sub pattern {
162 7     7 1 12 my $self = shift;
163 7         36 return $self->[1];
164             }
165              
166             =item C<< distinct >>
167              
168             Returns true if the pattern is guaranteed to return distinct results.
169              
170             =cut
171              
172             sub distinct {
173 7     7 1 16 my $self = shift;
174 7         27 return $self->pattern->distinct;
175             }
176              
177             =item C<< ordered >>
178              
179             Returns true if the pattern is guaranteed to return ordered results.
180              
181             =cut
182              
183             sub ordered {
184 12     12 1 24 my $self = shift;
185 12         24 my $sort = $self->[2];
186            
187 12 100       32 return [ map { [ $_->[0], ($_->[1] ? 'DESC' : 'ASC') ] } @$sort ];
  12         112  
188             }
189              
190             =item C<< plan_node_name >>
191              
192             Returns the string name of this plan node, suitable for use in serialization.
193              
194             =cut
195              
196             sub plan_node_name {
197 0     0 1   return 'order';
198             }
199              
200             =item C<< plan_prototype >>
201              
202             Returns a list of scalar identifiers for the type of the content (children)
203             nodes of this plan node. See L<RDF::Query::Plan> for a list of the allowable
204             identifiers.
205              
206             =cut
207              
208             sub plan_prototype {
209 0     0 1   my $self = shift;
210 0           return qw(P *\wE);
211             }
212              
213             =item C<< plan_node_data >>
214              
215             Returns the data for this plan node that corresponds to the values described by
216             the signature returned by C<< plan_prototype >>.
217              
218             =cut
219              
220             sub plan_node_data {
221 0     0 1   my $self = shift;
222 0           my $exprs = $self->[2];
223 0 0         return ($self->pattern, map { [ ($_->[1] == 0 ? 'asc' : 'desc'), $_->[0] ] } @$exprs);
  0            
224             }
225              
226             =item C<< graph ( $g ) >>
227              
228             =cut
229              
230             sub graph {
231 0     0 1   my $self = shift;
232 0           my $g = shift;
233 0           my $c = $self->pattern->graph( $g );
234 0           my $expr = join(' ', map { $_->sse( {}, "" ) } @{ $self->[2] });
  0            
  0            
235 0           $g->add_node( "$self", label => "Sort ($expr)" . $self->graph_labels );
236 0           $g->add_edge( "$self", $c );
237 0           return "$self";
238             }
239              
240             =item C<< explain >>
241              
242             Returns a string serialization of the plan appropriate for display on the
243             command line.
244              
245             =cut
246              
247             sub explain {
248 0     0 1   my $self = shift;
249 0           my $s = shift;
250 0           my $count = shift;
251 0           my $indent = $s x $count;
252 0           my $type = $self->plan_node_name;
253 0           my $string = sprintf("%s%s (0x%x)\n", $indent, $type, refaddr($self));
254 0           $string .= "${indent}${s}sory by:\n";
255 0           my $exprs = $self->[2];
256 0           foreach my $e (@$exprs) {
257 0 0         my $dir = ($e->[1] == 0 ? 'asc ' : 'desc ');
258 0           $string .= "${indent}${s}${s}${dir}" . $e->[0] . "\n";
259             }
260 0           $string .= $self->pattern->explain( $s, $count+1 );
261 0           return $string;
262             }
263              
264              
265             1;
266              
267             __END__
268              
269             =back
270              
271             =head1 AUTHOR
272              
273             Gregory Todd Williams <gwilliams@cpan.org>
274              
275             =cut