|  line  | 
 stmt  | 
 bran  | 
 cond  | 
 sub  | 
 pod  | 
 time  | 
 code  | 
| 
1
 | 
  
 
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # RDF::Query::Plan::Aggregate  | 
| 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # -----------------------------------------------------------------------------  | 
| 
3
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
4
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 =head1 NAME  | 
| 
5
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
6
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 RDF::Query::Plan::Aggregate - Executable query plan for Aggregates.  | 
| 
7
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
8
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 =head1 VERSION  | 
| 
9
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
10
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 This document describes RDF::Query::Plan::Aggregate version 2.918.  | 
| 
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::Aggregate;  | 
| 
22
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
23
 | 
35
 | 
 
 | 
 
 | 
  
35
  
 | 
 
 | 
140
 | 
 use strict;  | 
| 
 
 | 
35
 | 
 
 | 
 
 | 
 
 | 
 
 | 
53
 | 
    | 
| 
 
 | 
35
 | 
 
 | 
 
 | 
 
 | 
 
 | 
885
 | 
    | 
| 
24
 | 
35
 | 
 
 | 
 
 | 
  
35
  
 | 
 
 | 
127
 | 
 use warnings;  | 
| 
 
 | 
35
 | 
 
 | 
 
 | 
 
 | 
 
 | 
56
 | 
    | 
| 
 
 | 
35
 | 
 
 | 
 
 | 
 
 | 
 
 | 
805
 | 
    | 
| 
25
 | 
35
 | 
 
 | 
 
 | 
  
35
  
 | 
 
 | 
156
 | 
 use base qw(RDF::Query::Plan);  | 
| 
 
 | 
35
 | 
 
 | 
 
 | 
 
 | 
 
 | 
64
 | 
    | 
| 
 
 | 
35
 | 
 
 | 
 
 | 
 
 | 
 
 | 
2780
 | 
    | 
| 
26
 | 
35
 | 
 
 | 
 
 | 
  
35
  
 | 
 
 | 
160
 | 
 use Scalar::Util qw(blessed);  | 
| 
 
 | 
35
 | 
 
 | 
 
 | 
 
 | 
 
 | 
43
 | 
    | 
| 
 
 | 
35
 | 
 
 | 
 
 | 
 
 | 
 
 | 
1380
 | 
    | 
| 
27
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
28
 | 
35
 | 
 
 | 
 
 | 
  
35
  
 | 
 
 | 
154
 | 
 use RDF::Query::Error qw(:try);  | 
| 
 
 | 
35
 | 
 
 | 
 
 | 
 
 | 
 
 | 
56
 | 
    | 
| 
 
 | 
35
 | 
 
 | 
 
 | 
 
 | 
 
 | 
198
 | 
    | 
| 
29
 | 
35
 | 
 
 | 
 
 | 
  
35
  
 | 
 
 | 
3592
 | 
 use RDF::Query::Node qw(literal);  | 
| 
 
 | 
35
 | 
 
 | 
 
 | 
 
 | 
 
 | 
51
 | 
    | 
| 
 
 | 
35
 | 
 
 | 
 
 | 
 
 | 
 
 | 
1923
 | 
    | 
| 
30
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
31
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 ######################################################################  | 
| 
32
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
33
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 our ($VERSION);  | 
| 
34
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 BEGIN {  | 
| 
35
 | 
35
 | 
 
 | 
 
 | 
  
35
  
 | 
 
 | 
90776
 | 
 	$VERSION	= '2.918';  | 
| 
36
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
37
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
38
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 ######################################################################  | 
| 
39
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
40
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 =item C<< new ( $pattern, \@group_by, expressions => [ [ $alias, $op, \%options, @attributes ], ... ] ) >>  | 
| 
41
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
42
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 =cut  | 
| 
43
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
44
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub new {  | 
| 
45
 | 
16
 | 
 
 | 
 
 | 
  
16
  
 | 
  
1
  
 | 
25
 | 
 	my $class	= shift;  | 
| 
46
 | 
16
 | 
 
 | 
 
 | 
 
 | 
 
 | 
19
 | 
 	my $plan	= shift;  | 
| 
47
 | 
16
 | 
 
 | 
 
 | 
 
 | 
 
 | 
17
 | 
 	my $groupby	= shift;  | 
| 
48
 | 
16
 | 
 
 | 
 
 | 
 
 | 
 
 | 
35
 | 
 	my %args	= @_;  | 
| 
49
 | 
16
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
26
 | 
 	my @ops		= @{ $args{ 'expressions' } || [] };  | 
| 
 
 | 
16
 | 
 
 | 
 
 | 
 
 | 
 
 | 
59
 | 
    | 
| 
50
 | 
16
 | 
 
 | 
 
 | 
 
 | 
 
 | 
51
 | 
 	my $self	= $class->SUPER::new( $plan, $groupby, \@ops );  | 
| 
51
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	$self->[0]{referenced_variables}	= [  | 
| 
52
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 											RDF::Query::_uniq(  | 
| 
53
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 												$plan->referenced_variables,  | 
| 
54
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 												map {  | 
| 
55
 | 
16
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
75
 | 
 													($_->isa('RDF::Query::Node::Variable'))  | 
| 
 
 | 
7
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
32
 | 
    | 
| 
56
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 														? $_->name  | 
| 
57
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 														: $_->isa('RDF::Query::Node')  | 
| 
58
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 															? ()  | 
| 
59
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 															: $_->referenced_variables  | 
| 
60
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 												} @$groupby)  | 
| 
61
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 										];  | 
| 
62
 | 
16
 | 
 
 | 
 
 | 
 
 | 
 
 | 
72
 | 
 	return $self;  | 
| 
63
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
64
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
65
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 =item C<< execute ( $execution_context ) >>  | 
| 
66
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
67
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 =cut  | 
| 
68
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
69
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub execute ($) {  | 
| 
70
 | 
16
 | 
 
 | 
 
 | 
  
16
  
 | 
  
1
  
 | 
18
 | 
 	my $self	= shift;  | 
| 
71
 | 
16
 | 
 
 | 
 
 | 
 
 | 
 
 | 
18
 | 
 	my $context	= shift;  | 
| 
72
 | 
16
 | 
 
 | 
 
 | 
 
 | 
 
 | 
45
 | 
 	$self->[0]{delegate}	= $context->delegate;  | 
| 
73
 | 
16
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
40
 | 
 	if ($self->state == $self->OPEN) {  | 
| 
74
 | 
  
0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
 		throw RDF::Query::Error::ExecutionError -text => "AGGREGATE plan can't be executed while already open";  | 
| 
75
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	}  | 
| 
76
 | 
16
 | 
 
 | 
 
 | 
 
 | 
 
 | 
29
 | 
 	my $plan	= $self->[1];  | 
| 
77
 | 
16
 | 
 
 | 
 
 | 
 
 | 
 
 | 
60
 | 
 	$plan->execute( $context );  | 
| 
78
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	  | 
| 
79
 | 
16
 | 
 
 | 
 
 | 
 
 | 
 
 | 
80
 | 
 	my $l		= Log::Log4perl->get_logger("rdf.query.plan.aggregate");  | 
| 
80
 | 
16
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
599
 | 
 	if ($plan->state == $self->OPEN) {  | 
| 
81
 | 
16
 | 
 
 | 
 
 | 
 
 | 
 
 | 
69
 | 
 		my $query	= $context->query;  | 
| 
82
 | 
16
 | 
 
 | 
 
 | 
 
 | 
 
 | 
51
 | 
 		my $bridge	= $context->model;  | 
| 
83
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 		  | 
| 
84
 | 
16
 | 
 
 | 
 
 | 
 
 | 
 
 | 
27
 | 
 		my %seen;  | 
| 
85
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 		my %groups;  | 
| 
86
 | 
  
0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
 		my %group_data;  | 
| 
87
 | 
16
 | 
 
 | 
 
 | 
 
 | 
 
 | 
44
 | 
 		my @groupby	= $self->groupby;  | 
| 
88
 | 
16
 | 
 
 | 
 
 | 
 
 | 
 
 | 
20
 | 
 		my @ops		= @{ $self->[3] };  | 
| 
 
 | 
16
 | 
 
 | 
 
 | 
 
 | 
 
 | 
34
 | 
    | 
| 
89
 | 
16
 | 
 
 | 
 
 | 
 
 | 
 
 | 
35
 | 
 		local($RDF::Query::Node::Literal::LAZY_COMPARISONS)	= 1;  | 
| 
90
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 		  | 
| 
91
 | 
16
 | 
 
 | 
 
 | 
 
 | 
 
 | 
70
 | 
 		ROW: while (my $row = $plan->next) {  | 
| 
92
 | 
75
 | 
 
 | 
 
 | 
 
 | 
 
 | 
196
 | 
 			$l->debug("aggregate on $row");  | 
| 
93
 | 
75
 | 
 
 | 
 
 | 
 
 | 
 
 | 
6511
 | 
 			my @group;  | 
| 
94
 | 
75
 | 
 
 | 
 
 | 
 
 | 
 
 | 
135
 | 
 			foreach my $g (@groupby) {  | 
| 
95
 | 
27
 | 
 
 | 
 
 | 
 
 | 
 
 | 
100
 | 
 				my $v	= $query->var_or_expr_value( $row, $g, $context );  | 
| 
96
 | 
27
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
174
 | 
 				if ($g->isa('RDF::Query::Expression::Alias')) {  | 
| 
97
 | 
  
0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
 					$row->{ $g->name }	= $v;  | 
| 
98
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 				}  | 
| 
99
 | 
27
 | 
 
 | 
 
 | 
 
 | 
 
 | 
59
 | 
 				push(@group, $v);  | 
| 
100
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 			}  | 
| 
101
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 			  | 
| 
102
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # 			my @group	= map { $query->var_or_expr_value( $row, $_ ) } @groupby;  | 
| 
103
 | 
75
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
132
 | 
 			my $group	= join('<<<', map { blessed($_) ? $_->as_string : '' } map { blessed($_) ? $query->var_or_expr_value( $row, $_, $context ) : '' } @group);  | 
| 
 
 | 
27
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
114
 | 
    | 
| 
 
 | 
27
 | 
 
 | 
 
 | 
 
 | 
 
 | 
93
 | 
    | 
| 
104
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 			  | 
| 
105
 | 
75
 | 
 
 | 
 
 | 
 
 | 
 
 | 
228
 | 
 			push( @{ $group_data{ 'rows' }{ $group } }, $row );  | 
| 
 
 | 
75
 | 
 
 | 
 
 | 
 
 | 
 
 | 
213
 | 
    | 
| 
106
 | 
75
 | 
 
 | 
 
 | 
 
 | 
 
 | 
148
 | 
 			$group_data{ 'groups' }{ $group }	= \@group;  | 
| 
107
 | 
75
 | 
 
 | 
 
 | 
 
 | 
 
 | 
293
 | 
 			foreach my $i (0 .. $#groupby) {  | 
| 
108
 | 
27
 | 
 
 | 
 
 | 
 
 | 
 
 | 
27
 | 
 				my $g	= $groupby[$i];  | 
| 
109
 | 
27
 | 
 
 | 
 
 | 
 
 | 
 
 | 
127
 | 
 				$group_data{ 'groupby_sample' }{ $group }	= $row;  | 
| 
110
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 			}  | 
| 
111
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 		}  | 
| 
112
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 		  | 
| 
113
 | 
16
 | 
 
 | 
 
 | 
 
 | 
 
 | 
23
 | 
 		my @groups	= values %{ $group_data{'groups'} };  | 
| 
 
 | 
16
 | 
 
 | 
 
 | 
 
 | 
 
 | 
66
 | 
    | 
| 
114
 | 
16
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
48
 | 
 		if (scalar(@groups) == 0) {  | 
| 
115
 | 
  
0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
 			$group_data{'rows'}{''}		= [];  | 
| 
116
 | 
  
0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
 			$group_data{'groups'}{''}	= [];  | 
| 
117
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 		}  | 
| 
118
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 		  | 
| 
119
 | 
16
 | 
 
 | 
 
 | 
 
 | 
 
 | 
17
 | 
 		my @rows;  | 
| 
120
 | 
16
 | 
 
 | 
 
 | 
 
 | 
 
 | 
25
 | 
 		GROUP: foreach my $group (keys %{ $group_data{ 'rows' } }) {  | 
| 
 
 | 
16
 | 
 
 | 
 
 | 
 
 | 
 
 | 
55
 | 
    | 
| 
121
 | 
27
 | 
 
 | 
 
 | 
 
 | 
 
 | 
111
 | 
 			$l->debug( "group: $group" );  | 
| 
122
 | 
27
 | 
 
 | 
 
 | 
 
 | 
 
 | 
122
 | 
 			my %options;  | 
| 
123
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 			my %aggregates;  | 
| 
124
 | 
  
0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
 			my %passthrough_data;  | 
| 
125
 | 
27
 | 
 
 | 
 
 | 
 
 | 
 
 | 
36
 | 
 			my @group	= @{ $group_data{ 'groups' }{ $group } };  | 
| 
 
 | 
27
 | 
 
 | 
 
 | 
 
 | 
 
 | 
75
 | 
    | 
| 
126
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 			  | 
| 
127
 | 
27
 | 
 
 | 
 
 | 
 
 | 
 
 | 
60
 | 
 			my $row_sample	= $group_data{ 'groupby_sample' }{ $group };  | 
| 
128
 | 
27
 | 
 
 | 
 
 | 
 
 | 
 
 | 
47
 | 
 			foreach my $g (@groupby) {  | 
| 
129
 | 
18
 | 
  
 50
  
 | 
  
 33
  
 | 
 
 | 
 
 | 
135
 | 
 				if ($g->isa('RDF::Query::Expression::Alias') or $g->isa('RDF::Query::Node::Variable')) {  | 
| 
 
 | 
 
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
130
 | 
18
 | 
 
 | 
 
 | 
 
 | 
 
 | 
49
 | 
 					my $name	= $g->name;  | 
| 
131
 | 
18
 | 
 
 | 
 
 | 
 
 | 
 
 | 
86
 | 
 					$passthrough_data{ $name }	= $row_sample->{ $name };  | 
| 
132
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 				} elsif ($g->isa('RDF::Query::Expression')) {  | 
| 
133
 | 
  
0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
 					my @names	= $g->referenced_variables;  | 
| 
134
 | 
  
0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
 					foreach my $name (@names) {  | 
| 
135
 | 
  
0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
 						$passthrough_data{ $name }	= $row_sample->{ $name };  | 
| 
136
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 					}  | 
| 
137
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 				} else {  | 
| 
138
 | 
  
0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
 					my $name	= $g->sse;  | 
| 
139
 | 
  
0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
 					$passthrough_data{ $name }	= $row_sample->{ $name };  | 
| 
140
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 				}  | 
| 
141
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 			}  | 
| 
142
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 			  | 
| 
143
 | 
27
 | 
 
 | 
 
 | 
 
 | 
 
 | 
43
 | 
 			my @operation_data	= (map { [ @{ $_ }, \%aggregates ] } @ops);  | 
| 
 
 | 
29
 | 
 
 | 
 
 | 
 
 | 
 
 | 
31
 | 
    | 
| 
 
 | 
29
 | 
 
 | 
 
 | 
 
 | 
 
 | 
104
 | 
    | 
| 
144
 | 
27
 | 
 
 | 
 
 | 
 
 | 
 
 | 
54
 | 
 			foreach my $data (@operation_data) {  | 
| 
145
 | 
29
 | 
 
 | 
 
 | 
 
 | 
 
 | 
196
 | 
 				my $aggregate_data	= pop(@$data);  | 
| 
146
 | 
29
 | 
 
 | 
 
 | 
 
 | 
 
 | 
67
 | 
 				my ($alias, $op, $opts, @cols)	= @$data;  | 
| 
147
 | 
29
 | 
 
 | 
 
 | 
 
 | 
 
 | 
48
 | 
 				$options{ $alias }	= $opts;  | 
| 
148
 | 
29
 | 
 
 | 
 
 | 
 
 | 
 
 | 
42
 | 
 				my $distinct	= ($op =~ /^(.*)-DISTINCT$/);  | 
| 
149
 | 
29
 | 
 
 | 
 
 | 
 
 | 
 
 | 
39
 | 
 				$op				=~ s/-DISTINCT$//;  | 
| 
150
 | 
29
 | 
 
 | 
 
 | 
 
 | 
 
 | 
32
 | 
 				my $col	= $cols[0];  | 
| 
151
 | 
29
 | 
 
 | 
 
 | 
 
 | 
 
 | 
35
 | 
 				my %agg_group_seen;  | 
| 
152
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 				try {  | 
| 
153
 | 
29
 | 
 
 | 
 
 | 
  
29
  
 | 
 
 | 
879
 | 
 					foreach my $row (@{ $group_data{ 'rows' }{ $group } }) {  | 
| 
 
 | 
29
 | 
 
 | 
 
 | 
 
 | 
 
 | 
80
 | 
    | 
| 
154
 | 
88
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
214
 | 
 						my @proj_rows	= map { (blessed($col)) ? $query->var_or_expr_value( $row, $col, $context ) : '*' } @cols;  | 
| 
 
 | 
88
 | 
 
 | 
 
 | 
 
 | 
 
 | 
412
 | 
    | 
| 
155
 | 
88
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
560
 | 
 						if ($distinct) {  | 
| 
156
 | 
4
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
17
 | 
 							next if ($agg_group_seen{ join('<<<', @proj_rows) }++);  | 
| 
157
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 						}  | 
| 
158
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 						  | 
| 
159
 | 
86
 | 
 
 | 
 
 | 
 
 | 
 
 | 
334
 | 
 						$l->debug( "- row: $row" );  | 
| 
160
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	# 					$groups{ $group }	||= { map { $_ => $row->{ $_ } } @groupby };  | 
| 
161
 | 
86
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
2725
 | 
 						if ($op eq 'COUNT') {  | 
| 
 
 | 
 
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
 
 | 
 
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
 
 | 
 
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
 
 | 
 
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
 
 | 
 
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
 
 | 
 
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
162
 | 
14
 | 
 
 | 
 
 | 
 
 | 
 
 | 
34
 | 
 							$l->debug("- aggregate op: COUNT");  | 
| 
163
 | 
14
 | 
 
 | 
 
 | 
 
 | 
 
 | 
45
 | 
 							my $should_inc	= 0;  | 
| 
164
 | 
14
 | 
  
 50
  
 | 
  
 33
  
 | 
 
 | 
 
 | 
61
 | 
 							if (not(blessed($col)) and $col eq '*') {  | 
| 
165
 | 
  
0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
 								$should_inc	= 1;  | 
| 
166
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 							} else {  | 
| 
167
 | 
14
 | 
 
 | 
 
 | 
 
 | 
 
 | 
35
 | 
 								my $value	= $query->var_or_expr_value( $row, $col, $context );  | 
| 
168
 | 
14
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
64
 | 
 								$should_inc	= (defined $value) ? 1 : 0;  | 
| 
169
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 							}  | 
| 
170
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 							  | 
| 
171
 | 
14
 | 
 
 | 
 
 | 
 
 | 
 
 | 
32
 | 
 							$aggregate_data->{ $alias }{ $group }[0]	= $op;  | 
| 
172
 | 
14
 | 
 
 | 
 
 | 
 
 | 
 
 | 
36
 | 
 							$aggregate_data->{ $alias }{ $group }[1]	+= $should_inc;  | 
| 
173
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 						} elsif ($op eq 'SUM') {  | 
| 
174
 | 
8
 | 
 
 | 
 
 | 
 
 | 
 
 | 
15
 | 
 							$l->debug("- aggregate op: SUM");  | 
| 
175
 | 
8
 | 
 
 | 
 
 | 
 
 | 
 
 | 
37
 | 
 							my $value	= $query->var_or_expr_value( $row, $col, $context );  | 
| 
176
 | 
8
 | 
 
 | 
 
 | 
 
 | 
 
 | 
35
 | 
 							my $type	= _node_type( $value );  | 
| 
177
 | 
8
 | 
 
 | 
 
 | 
 
 | 
 
 | 
20
 | 
 							$aggregate_data->{ $alias }{ $group }[0]	= $op;  | 
| 
178
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 							  | 
| 
179
 | 
8
 | 
 
 | 
 
 | 
 
 | 
 
 | 
6
 | 
 							my $strict	= 1;  | 
| 
180
 | 
8
 | 
  
 50
  
 | 
  
 33
  
 | 
 
 | 
 
 | 
36
 | 
 							unless ($value->isa('RDF::Query::Node::Literal') and $value->is_numeric_type) {  | 
| 
181
 | 
  
0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
 								throw RDF::Query::Error::TypeError -text => "Cannot compute SUM aggregate with a non-numeric term: " . $value->as_ntriples;  | 
| 
182
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 							}  | 
| 
183
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 							  | 
| 
184
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	#						if ($value->isa('RDF::Query::Node::Literal')) {  | 
| 
185
 | 
8
 | 
 
 | 
 
 | 
 
 | 
 
 | 
20
 | 
 							my $v	= $value->numeric_value;  | 
| 
186
 | 
8
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
12
 | 
 							if (scalar( @{ $aggregate_data->{ $alias }{ $group } } ) > 1) {  | 
| 
 
 | 
8
 | 
 
 | 
 
 | 
 
 | 
 
 | 
25
 | 
    | 
| 
187
 | 
4
 | 
  
 50
  
 | 
  
  0
  
 | 
 
 | 
 
 | 
14
 | 
 								if ($type ne $aggregate_data->{ $alias }{ $group }[2] and not($value->isa('RDF::Query::Node::Literal') and $value->is_numeric_type and blessed($aggregate_data->{ $alias }{ $group }[1]) and $aggregate_data->{ $alias }{ $group }[1]->isa('RDF::Query::Node::Literal') and $aggregate_data->{ $alias }{ $group }[1]->is_numeric_type)) {  | 
| 
 
 | 
 
 | 
 
 | 
  
 33
  
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
188
 | 
  
0
  
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
0
 | 
 									if ($context->strict_errors) {  | 
| 
189
 | 
  
0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
 										delete $aggregate_data->{ $alias }{ $group };  | 
| 
190
 | 
  
0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
 										throw RDF::Query::Error::ComparisonError -text => "Cannot compute SUM aggregate over nodes of multiple, non-numeric types";  | 
| 
191
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 									} else {  | 
| 
192
 | 
  
0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
 										$strict	= 0;  | 
| 
193
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 									}  | 
| 
194
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 								}  | 
| 
195
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 								  | 
| 
196
 | 
4
 | 
 
 | 
 
 | 
 
 | 
 
 | 
6
 | 
 								$aggregate_data->{ $alias }{ $group }[1]	+= $v;  | 
| 
197
 | 
4
 | 
 
 | 
 
 | 
 
 | 
 
 | 
20
 | 
 								$aggregate_data->{ $alias }{ $group }[2]	= RDF::Query::Expression::Binary->promote_type('+', $type, $aggregate_data->{ $alias }{ $group }[2]);  | 
| 
198
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 							} else {  | 
| 
199
 | 
4
 | 
 
 | 
 
 | 
 
 | 
 
 | 
6
 | 
 								$aggregate_data->{ $alias }{ $group }[1]	= $v;  | 
| 
200
 | 
4
 | 
 
 | 
 
 | 
 
 | 
 
 | 
11
 | 
 								$aggregate_data->{ $alias }{ $group }[2]	= $type;  | 
| 
201
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 							}  | 
| 
202
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	#						}  | 
| 
203
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 						} elsif ($op eq 'MAX') {  | 
| 
204
 | 
25
 | 
 
 | 
 
 | 
 
 | 
 
 | 
48
 | 
 							$l->debug("- aggregate op: MAX");  | 
| 
205
 | 
25
 | 
 
 | 
 
 | 
 
 | 
 
 | 
121
 | 
 							my $value	= $query->var_or_expr_value( $row, $col, $context );  | 
| 
206
 | 
25
 | 
 
 | 
 
 | 
 
 | 
 
 | 
104
 | 
 							my $type	= _node_type( $value );  | 
| 
207
 | 
25
 | 
 
 | 
 
 | 
 
 | 
 
 | 
65
 | 
 							$aggregate_data->{ $alias }{ $group }[0]	= $op;  | 
| 
208
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 							  | 
| 
209
 | 
25
 | 
 
 | 
 
 | 
 
 | 
 
 | 
26
 | 
 							my $strict	= 1;  | 
| 
210
 | 
25
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
22
 | 
 							if (scalar( @{ $aggregate_data->{ $alias }{ $group } } ) > 1) {  | 
| 
 
 | 
25
 | 
 
 | 
 
 | 
 
 | 
 
 | 
53
 | 
    | 
| 
211
 | 
14
 | 
  
100
  
 | 
  
  0
  
 | 
 
 | 
 
 | 
55
 | 
 								if ($type ne $aggregate_data->{ $alias }{ $group }[2] and not($value->isa('RDF::Query::Node::Literal') and $value->is_numeric_type and blessed($aggregate_data->{ $alias }{ $group }[1]) and $aggregate_data->{ $alias }{ $group }[1]->isa('RDF::Query::Node::Literal') and $aggregate_data->{ $alias }{ $group }[1]->is_numeric_type)) {  | 
| 
 
 | 
 
 | 
 
 | 
  
 66
  
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
212
 | 
2
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
24
 | 
 									if ($context->strict_errors) {  | 
| 
213
 | 
  
0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
 										delete $aggregate_data->{ $alias }{ $group };  | 
| 
214
 | 
  
0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
 										throw RDF::Query::Error::ComparisonError -text => "Cannot compute MAX aggregate over nodes of multiple, non-numeric types";  | 
| 
215
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 									} else {  | 
| 
216
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
3
 | 
 										$strict	= 0;  | 
| 
217
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 									}  | 
| 
218
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 								}  | 
| 
219
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 								  | 
| 
220
 | 
14
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
25
 | 
 								if ($strict) {  | 
| 
221
 | 
12
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
38
 | 
 									if ($value > $aggregate_data->{ $alias }{ $group }[1]) {  | 
| 
222
 | 
4
 | 
 
 | 
 
 | 
 
 | 
 
 | 
10
 | 
 										$aggregate_data->{ $alias }{ $group }[1]	= $value;  | 
| 
223
 | 
4
 | 
 
 | 
 
 | 
 
 | 
 
 | 
11
 | 
 										$aggregate_data->{ $alias }{ $group }[2]	= $type;  | 
| 
224
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 									}  | 
| 
225
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 								} else {  | 
| 
226
 | 
2
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
4
 | 
 									if ("$value" gt "$aggregate_data->{ $alias }{ $group }[1]") {  | 
| 
227
 | 
1
 | 
 
 | 
 
 | 
 
 | 
 
 | 
15
 | 
 										$aggregate_data->{ $alias }{ $group }[1]	= $value;  | 
| 
228
 | 
1
 | 
 
 | 
 
 | 
 
 | 
 
 | 
3
 | 
 										$aggregate_data->{ $alias }{ $group }[2]	= $type;  | 
| 
229
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 									}  | 
| 
230
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 								}  | 
| 
231
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 							} else {  | 
| 
232
 | 
11
 | 
 
 | 
 
 | 
 
 | 
 
 | 
16
 | 
 								$aggregate_data->{ $alias }{ $group }[1]	= $value;  | 
| 
233
 | 
11
 | 
 
 | 
 
 | 
 
 | 
 
 | 
32
 | 
 								$aggregate_data->{ $alias }{ $group }[2]	= $type;  | 
| 
234
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 							}  | 
| 
235
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 						} elsif ($op eq 'MIN') {  | 
| 
236
 | 
25
 | 
 
 | 
 
 | 
 
 | 
 
 | 
45
 | 
 							$l->debug("- aggregate op: MIN");  | 
| 
237
 | 
25
 | 
 
 | 
 
 | 
 
 | 
 
 | 
127
 | 
 							my $value	= $query->var_or_expr_value( $row, $col, $context );  | 
| 
238
 | 
25
 | 
 
 | 
 
 | 
 
 | 
 
 | 
111
 | 
 							my $type	= _node_type( $value );  | 
| 
239
 | 
25
 | 
 
 | 
 
 | 
 
 | 
 
 | 
52
 | 
 							$aggregate_data->{ $alias }{ $group }[0]	= $op;  | 
| 
240
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 							  | 
| 
241
 | 
25
 | 
 
 | 
 
 | 
 
 | 
 
 | 
25
 | 
 							my $strict	= 1;  | 
| 
242
 | 
25
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
26
 | 
 							if (scalar( @{ $aggregate_data->{ $alias }{ $group } } ) > 1) {  | 
| 
 
 | 
25
 | 
 
 | 
 
 | 
 
 | 
 
 | 
56
 | 
    | 
| 
243
 | 
19
 | 
  
100
  
 | 
  
  0
  
 | 
 
 | 
 
 | 
62
 | 
 								if ($type ne $aggregate_data->{ $alias }{ $group }[2] and not($value->isa('RDF::Query::Node::Literal') and $value->is_numeric_type and blessed($aggregate_data->{ $alias }{ $group }[1]) and $aggregate_data->{ $alias }{ $group }[1]->isa('RDF::Query::Node::Literal') and $aggregate_data->{ $alias }{ $group }[1]->is_numeric_type)) {  | 
| 
 
 | 
 
 | 
 
 | 
  
 66
  
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
244
 | 
4
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
44
 | 
 									if ($context->strict_errors) {  | 
| 
245
 | 
  
0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
 										delete $aggregate_data->{ $alias }{ $group };  | 
| 
246
 | 
  
0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
 										throw RDF::Query::Error::ComparisonError -text => "Cannot compute MIN aggregate over nodes of multiple, non-numeric types";  | 
| 
247
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 									} else {  | 
| 
248
 | 
4
 | 
 
 | 
 
 | 
 
 | 
 
 | 
5
 | 
 										$strict	= 0;  | 
| 
249
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 									}  | 
| 
250
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 								}  | 
| 
251
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 								  | 
| 
252
 | 
19
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
23
 | 
 								if ($strict) {  | 
| 
253
 | 
15
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
44
 | 
 									if ($value < $aggregate_data->{ $alias }{ $group }[1]) {  | 
| 
254
 | 
3
 | 
 
 | 
 
 | 
 
 | 
 
 | 
7
 | 
 										$aggregate_data->{ $alias }{ $group }[1]	= $value;  | 
| 
255
 | 
3
 | 
 
 | 
 
 | 
 
 | 
 
 | 
12
 | 
 										$aggregate_data->{ $alias }{ $group }[2]	= $type;  | 
| 
256
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 									}  | 
| 
257
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 								} else {  | 
| 
258
 | 
4
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
11
 | 
 									if ("$value" lt "$aggregate_data->{ $alias }{ $group }[1]") {  | 
| 
259
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
30
 | 
 										$aggregate_data->{ $alias }{ $group }[1]	= $value;  | 
| 
260
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
6
 | 
 										$aggregate_data->{ $alias }{ $group }[2]	= $type;  | 
| 
261
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 									}  | 
| 
262
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 								}  | 
| 
263
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 							} else {  | 
| 
264
 | 
6
 | 
 
 | 
 
 | 
 
 | 
 
 | 
12
 | 
 								$aggregate_data->{ $alias }{ $group }[1]	= $value;  | 
| 
265
 | 
6
 | 
 
 | 
 
 | 
 
 | 
 
 | 
14
 | 
 								$aggregate_data->{ $alias }{ $group }[2]	= $type;  | 
| 
266
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 							}  | 
| 
267
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 						} elsif ($op eq 'SAMPLE') {  | 
| 
268
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 							### this is just the MIN code from above, without the strict comparison checking  | 
| 
269
 | 
5
 | 
 
 | 
 
 | 
 
 | 
 
 | 
11
 | 
 							$l->debug("- aggregate op: SAMPLE");  | 
| 
270
 | 
5
 | 
 
 | 
 
 | 
 
 | 
 
 | 
23
 | 
 							my $value	= $query->var_or_expr_value( $row, $col, $context );  | 
| 
271
 | 
5
 | 
 
 | 
 
 | 
 
 | 
 
 | 
22
 | 
 							my $type	= _node_type( $value );  | 
| 
272
 | 
5
 | 
 
 | 
 
 | 
 
 | 
 
 | 
9
 | 
 							$aggregate_data->{ $alias }{ $group }[0]	= $op;  | 
| 
273
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 							  | 
| 
274
 | 
5
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
3
 | 
 							if (scalar( @{ $aggregate_data->{ $alias }{ $group } } ) > 1) {  | 
| 
 
 | 
5
 | 
 
 | 
 
 | 
 
 | 
 
 | 
11
 | 
    | 
| 
275
 | 
4
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
10
 | 
 								if ("$value" lt "$aggregate_data->{ $alias }{ $group }[1]") {  | 
| 
276
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
30
 | 
 									$aggregate_data->{ $alias }{ $group }[1]	= $value;  | 
| 
277
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
5
 | 
 									$aggregate_data->{ $alias }{ $group }[2]	= $type;  | 
| 
278
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 								}  | 
| 
279
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 							} else {  | 
| 
280
 | 
1
 | 
 
 | 
 
 | 
 
 | 
 
 | 
3
 | 
 								$aggregate_data->{ $alias }{ $group }[1]	= $value;  | 
| 
281
 | 
1
 | 
 
 | 
 
 | 
 
 | 
 
 | 
3
 | 
 								$aggregate_data->{ $alias }{ $group }[2]	= $type;  | 
| 
282
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 							}  | 
| 
283
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 						} elsif ($op eq 'AVG') {  | 
| 
284
 | 
4
 | 
 
 | 
 
 | 
 
 | 
 
 | 
7
 | 
 							$l->debug("- aggregate op: AVG");  | 
| 
285
 | 
4
 | 
 
 | 
 
 | 
 
 | 
 
 | 
22
 | 
 							my $value	= $query->var_or_expr_value( $row, $col, $context );  | 
| 
286
 | 
4
 | 
 
 | 
 
 | 
 
 | 
 
 | 
22
 | 
 							my $type	= _node_type( $value );  | 
| 
287
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # 							warn "AVG\t$group\t" . $value->as_string . "\n";  | 
| 
288
 | 
4
 | 
 
 | 
 
 | 
 
 | 
 
 | 
9
 | 
 							$aggregate_data->{ $alias }{ $group }[0]	= $op;  | 
| 
289
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 							  | 
| 
290
 | 
4
 | 
  
 50
  
 | 
  
 33
  
 | 
 
 | 
 
 | 
30
 | 
 							unless (blessed($value) and $value->isa('RDF::Query::Node::Literal') and $value->is_numeric_type) {  | 
| 
 
 | 
 
 | 
 
 | 
  
 33
  
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
291
 | 
  
0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
 								delete $aggregate_data->{ $alias }{ $group };  | 
| 
292
 | 
  
0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
 								throw RDF::Query::Error::ComparisonError -text => "Cannot compute AVG aggregate over non-numeric nodes";  | 
| 
293
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 							}  | 
| 
294
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 							  | 
| 
295
 | 
4
 | 
  
 50
  
 | 
  
 33
  
 | 
 
 | 
 
 | 
30
 | 
 							if (blessed($value) and $value->isa('RDF::Query::Node::Literal') and $value->is_numeric_type) {  | 
| 
 
 | 
 
 | 
 
 | 
  
 33
  
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
296
 | 
4
 | 
 
 | 
 
 | 
 
 | 
 
 | 
7
 | 
 								$aggregate_data->{ $alias }{ $group }[1]++;  | 
| 
297
 | 
4
 | 
 
 | 
 
 | 
 
 | 
 
 | 
11
 | 
 								$aggregate_data->{ $alias }{ $group }[2]	+= $value->numeric_value;  | 
| 
298
 | 
4
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
8
 | 
 								if ($aggregate_data->{ $alias }{ $group }[3]) {  | 
| 
299
 | 
3
 | 
 
 | 
 
 | 
 
 | 
 
 | 
14
 | 
 									$aggregate_data->{ $alias }{ $group }[3]	= RDF::Query::Expression::Binary->promote_type('+', $type, $aggregate_data->{ $alias }{ $group }[3]);  | 
| 
300
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 								} else {  | 
| 
301
 | 
1
 | 
 
 | 
 
 | 
 
 | 
 
 | 
4
 | 
 									$aggregate_data->{ $alias }{ $group }[3]	= $type;  | 
| 
302
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 								}  | 
| 
303
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 							}  | 
| 
304
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 						} elsif ($op eq 'GROUP_CONCAT') {  | 
| 
305
 | 
5
 | 
 
 | 
 
 | 
 
 | 
 
 | 
11
 | 
 							$l->debug("- aggregate op: GROUP_CONCAT");  | 
| 
306
 | 
5
 | 
 
 | 
 
 | 
 
 | 
 
 | 
19
 | 
 							$aggregate_data->{ $alias }{ $group }[0]	= $op;  | 
| 
307
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 							  | 
| 
308
 | 
5
 | 
 
 | 
 
 | 
 
 | 
 
 | 
17
 | 
 							my $str		= RDF::Query::Node::Resource->new('sparql:str');  | 
| 
309
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	  | 
| 
310
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 							my @values	= map {  | 
| 
311
 | 
5
 | 
 
 | 
 
 | 
 
 | 
 
 | 
48
 | 
 								my $expr	= RDF::Query::Expression::Function->new( $str, $query->var_or_expr_value( $row, $_, $context ) );  | 
| 
 
 | 
5
 | 
 
 | 
 
 | 
 
 | 
 
 | 
13
 | 
    | 
| 
312
 | 
5
 | 
 
 | 
 
 | 
 
 | 
 
 | 
15
 | 
 								my $val		= $expr->evaluate( $context->query, $row );  | 
| 
313
 | 
5
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
15
 | 
 								blessed($val) ? $val->literal_value : '';  | 
| 
314
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 							} @cols;  | 
| 
315
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 							  | 
| 
316
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 		# 					warn "adding '$string' to group_concat aggregate";  | 
| 
317
 | 
5
 | 
 
 | 
 
 | 
 
 | 
 
 | 
5
 | 
 							push( @{ $aggregate_data->{ $alias }{ $group }[1] }, @values );  | 
| 
 
 | 
5
 | 
 
 | 
 
 | 
 
 | 
 
 | 
22
 | 
    | 
| 
318
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 						} else {  | 
| 
319
 | 
  
0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
 							throw RDF::Query::Error -text => "Unknown aggregate operator $op";  | 
| 
320
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 						}  | 
| 
321
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 					}  | 
| 
322
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 				} catch RDF::Query::Error::ComparisonError with {  | 
| 
323
 | 
  
0
  
 | 
 
 | 
 
 | 
  
0
  
 | 
 
 | 
0
 | 
 					delete $aggregate_data->{ $alias }{ $group };  | 
| 
324
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 				} catch RDF::Query::Error::TypeError with {  | 
| 
325
 | 
  
0
  
 | 
 
 | 
 
 | 
  
0
  
 | 
 
 | 
0
 | 
 					delete $aggregate_data->{ $alias }{ $group };  | 
| 
326
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 				}  | 
| 
327
 | 
29
 | 
 
 | 
 
 | 
 
 | 
 
 | 
638
 | 
 			}  | 
| 
328
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 			  | 
| 
329
 | 
27
 | 
 
 | 
 
 | 
 
 | 
 
 | 
1517
 | 
 			my %row	= %passthrough_data;  | 
| 
330
 | 
27
 | 
 
 | 
 
 | 
 
 | 
 
 | 
63
 | 
 			foreach my $agg (keys %aggregates) {  | 
| 
331
 | 
29
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
80
 | 
 				if (defined($aggregates{$agg}{$group})) {  | 
| 
332
 | 
29
 | 
 
 | 
 
 | 
 
 | 
 
 | 
44
 | 
 					my $op			= $aggregates{ $agg }{ $group }[0];  | 
| 
333
 | 
29
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
155
 | 
 					if ($op eq 'AVG') {  | 
| 
 
 | 
 
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
 
 | 
 
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
334
 | 
1
 | 
 
 | 
 
 | 
 
 | 
 
 | 
3
 | 
 						my $value	= ($aggregates{ $agg }{ $group }[2] / $aggregates{ $agg }{ $group }[1]);  | 
| 
335
 | 
1
 | 
 
 | 
 
 | 
 
 | 
 
 | 
3
 | 
 						my $type	= $aggregates{ $agg }{ $group }[3];  | 
| 
336
 | 
1
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
4
 | 
 						if ($type eq 'http://www.w3.org/2001/XMLSchema#integer') {  | 
| 
337
 | 
  
0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
 							$type	= 'http://www.w3.org/2001/XMLSchema#decimal';  | 
| 
338
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 						}  | 
| 
339
 | 
1
 | 
  
 50
  
 | 
  
 33
  
 | 
 
 | 
 
 | 
9
 | 
 						$row{ $agg }	= (blessed($value) and $value->isa('RDF::Trine::Node')) ? $value : RDF::Trine::Node::Literal->new( $value, undef, $type, 1 );  | 
| 
340
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 					} elsif ($op eq 'GROUP_CONCAT') {  | 
| 
341
 | 
1
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
6
 | 
 						my $j	= (exists $options{$agg}{seperator}) ? $options{$agg}{seperator} : ' ';  | 
| 
342
 | 
1
 | 
 
 | 
 
 | 
 
 | 
 
 | 
2
 | 
 						$row{ $agg }	= RDF::Query::Node::Literal->new( join($j, @{ $aggregates{ $agg }{ $group }[1] }) );  | 
| 
 
 | 
1
 | 
 
 | 
 
 | 
 
 | 
 
 | 
6
 | 
    | 
| 
343
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 					} elsif ($op =~ /COUNT/) {  | 
| 
344
 | 
5
 | 
 
 | 
 
 | 
 
 | 
 
 | 
11
 | 
 						my $value	= $aggregates{ $agg }{ $group }[1];  | 
| 
345
 | 
5
 | 
  
 50
  
 | 
  
 33
  
 | 
 
 | 
 
 | 
43
 | 
 						$row{ $agg }	= (blessed($value) and $value->isa('RDF::Trine::Node')) ? $value : RDF::Trine::Node::Literal->new( $value, undef, 'http://www.w3.org/2001/XMLSchema#integer', 1 );  | 
| 
346
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 					} else {  | 
| 
347
 | 
22
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
45
 | 
 						if (defined($aggregates{$agg}{$group})) {  | 
| 
348
 | 
22
 | 
 
 | 
 
 | 
 
 | 
 
 | 
43
 | 
 							my $value	= $aggregates{ $agg }{ $group }[1];  | 
| 
349
 | 
22
 | 
  
100
  
 | 
  
 66
  
 | 
 
 | 
 
 | 
190
 | 
 							$row{ $agg }	= (blessed($value) and $value->isa('RDF::Trine::Node')) ? $value : RDF::Trine::Node::Literal->new( $value, undef, $aggregates{ $agg }{ $group }[2], 1 );  | 
| 
350
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 						}  | 
| 
351
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 					}  | 
| 
352
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 				}  | 
| 
353
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 			}  | 
| 
354
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 			  | 
| 
355
 | 
27
 | 
 
 | 
 
 | 
 
 | 
 
 | 
527
 | 
 			my $vars	= RDF::Query::VariableBindings->new( \%row );  | 
| 
356
 | 
27
 | 
 
 | 
 
 | 
 
 | 
 
 | 
98
 | 
 			$l->debug("aggregate row: $vars");  | 
| 
357
 | 
27
 | 
 
 | 
 
 | 
 
 | 
 
 | 
872
 | 
 			push(@rows, $vars);  | 
| 
358
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 		}  | 
| 
359
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 		  | 
| 
360
 | 
16
 | 
 
 | 
 
 | 
 
 | 
 
 | 
53
 | 
 		$self->[0]{rows}	= \@rows;  | 
| 
361
 | 
16
 | 
 
 | 
 
 | 
 
 | 
 
 | 
87
 | 
 		$self->state( $self->OPEN );  | 
| 
362
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	} else {  | 
| 
363
 | 
  
0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
 		warn "could not execute plan in distinct";  | 
| 
364
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	}  | 
| 
365
 | 
16
 | 
 
 | 
 
 | 
 
 | 
 
 | 
172
 | 
 	$self;  | 
| 
366
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
367
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
368
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 =item C<< next >>  | 
| 
369
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
370
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 =cut  | 
| 
371
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
372
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub next {  | 
| 
373
 | 
43
 | 
 
 | 
 
 | 
  
43
  
 | 
  
1
  
 | 
40
 | 
 	my $self	= shift;  | 
| 
374
 | 
43
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
79
 | 
 	unless ($self->state == $self->OPEN) {  | 
| 
375
 | 
  
0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
 		throw RDF::Query::Error::ExecutionError -text => "next() cannot be called on an un-open AGGREGATE";  | 
| 
376
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	}  | 
| 
377
 | 
43
 | 
 
 | 
 
 | 
 
 | 
 
 | 
39
 | 
 	my $bindings	= shift(@{ $self->[0]{rows} });  | 
| 
 
 | 
43
 | 
 
 | 
 
 | 
 
 | 
 
 | 
94
 | 
    | 
| 
378
 | 
43
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
104
 | 
 	if (my $d = $self->delegate) {  | 
| 
379
 | 
  
0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
 		$d->log_result( $self, $bindings );  | 
| 
380
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	}  | 
| 
381
 | 
43
 | 
 
 | 
 
 | 
 
 | 
 
 | 
72
 | 
 	return $bindings;  | 
| 
382
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
383
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
384
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 =item C<< close >>  | 
| 
385
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
386
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 =cut  | 
| 
387
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
388
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub close {  | 
| 
389
 | 
16
 | 
 
 | 
 
 | 
  
16
  
 | 
  
1
  
 | 
20
 | 
 	my $self	= shift;  | 
| 
390
 | 
16
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
40
 | 
 	unless ($self->state == $self->OPEN) {  | 
| 
391
 | 
  
0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
 		throw RDF::Query::Error::ExecutionError -text => "close() cannot be called on an un-open AGGREGATE";  | 
| 
392
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	}  | 
| 
393
 | 
16
 | 
 
 | 
 
 | 
 
 | 
 
 | 
35
 | 
 	delete $self->[0]{rows};  | 
| 
394
 | 
16
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
36
 | 
 	if (defined($self->[1])) {  | 
| 
395
 | 
16
 | 
 
 | 
 
 | 
 
 | 
 
 | 
64
 | 
 		$self->[1]->close();  | 
| 
396
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	}  | 
| 
397
 | 
16
 | 
 
 | 
 
 | 
 
 | 
 
 | 
41
 | 
 	$self->SUPER::close();  | 
| 
398
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
399
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
400
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 =item C<< pattern >>  | 
| 
401
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
402
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 Returns the query plan that will be used to produce the aggregated data.  | 
| 
403
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
404
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 =cut  | 
| 
405
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
406
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub pattern {  | 
| 
407
 | 
31
 | 
 
 | 
 
 | 
  
31
  
 | 
  
1
  
 | 
34
 | 
 	my $self	= shift;  | 
| 
408
 | 
31
 | 
 
 | 
 
 | 
 
 | 
 
 | 
159
 | 
 	return $self->[1];  | 
| 
409
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
410
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
411
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 =item C<< groupby >>  | 
| 
412
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
413
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 Returns the grouping arguments that will be used to produce the aggregated data.  | 
| 
414
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
415
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 =cut  | 
| 
416
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
417
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub groupby {  | 
| 
418
 | 
45
 | 
 
 | 
 
 | 
  
45
  
 | 
  
1
  
 | 
43
 | 
 	my $self	= shift;  | 
| 
419
 | 
45
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
41
 | 
 	return @{ $self->[2] || [] };  | 
| 
 
 | 
45
 | 
 
 | 
 
 | 
 
 | 
 
 | 
159
 | 
    | 
| 
420
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
421
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
422
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 =item C<< plan_node_name >>  | 
| 
423
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
424
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 Returns the string name of this plan node, suitable for use in serialization.  | 
| 
425
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
426
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 =cut  | 
| 
427
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
428
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub plan_node_name {  | 
| 
429
 | 
  
0
  
 | 
 
 | 
 
 | 
  
0
  
 | 
  
1
  
 | 
0
 | 
 	return 'aggregate';  | 
| 
430
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
431
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
432
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 =item C<< plan_node_data >>  | 
| 
433
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
434
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 Returns the data for this plan node that corresponds to the values described by  | 
| 
435
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 the signature returned by C<< plan_prototype >>.  | 
| 
436
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
437
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 =cut  | 
| 
438
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
439
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub plan_node_data {  | 
| 
440
 | 
  
0
  
 | 
 
 | 
 
 | 
  
0
  
 | 
  
1
  
 | 
0
 | 
 	my $self	= shift;  | 
| 
441
 | 
  
0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
 	return ($self->pattern, $self->groupby);  | 
| 
442
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
443
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
444
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 =item C<< sse ( $context, $indent ) >>  | 
| 
445
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
446
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 =cut  | 
| 
447
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
448
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub sse {  | 
| 
449
 | 
15
 | 
 
 | 
 
 | 
  
15
  
 | 
  
1
  
 | 
26
 | 
 	my $self	= shift;  | 
| 
450
 | 
15
 | 
 
 | 
 
 | 
 
 | 
 
 | 
23
 | 
 	my $context	= shift;  | 
| 
451
 | 
15
 | 
 
 | 
 
 | 
 
 | 
 
 | 
16
 | 
 	my $indent	= shift;  | 
| 
452
 | 
15
 | 
 
 | 
 
 | 
 
 | 
 
 | 
22
 | 
 	my $more	= '    ';  | 
| 
453
 | 
15
 | 
 
 | 
 
 | 
 
 | 
 
 | 
50
 | 
 	my $psse	= $self->pattern->sse( $context, "${indent}${more}" );  | 
| 
454
 | 
15
 | 
 
 | 
 
 | 
 
 | 
 
 | 
60
 | 
 	my @group	= map { $_->sse($context, "${indent}${more}") } $self->groupby;  | 
| 
 
 | 
6
 | 
 
 | 
 
 | 
 
 | 
 
 | 
20
 | 
    | 
| 
455
 | 
15
 | 
 
 | 
 
 | 
 
 | 
 
 | 
63
 | 
 	my $gsse	= join(' ', @group);  | 
| 
456
 | 
15
 | 
 
 | 
 
 | 
 
 | 
 
 | 
16
 | 
 	my @ops;  | 
| 
457
 | 
15
 | 
 
 | 
 
 | 
 
 | 
 
 | 
21
 | 
 	foreach my $p (@{ $self->[3] }) {  | 
| 
 
 | 
15
 | 
 
 | 
 
 | 
 
 | 
 
 | 
37
 | 
    | 
| 
458
 | 
17
 | 
 
 | 
 
 | 
 
 | 
 
 | 
45
 | 
 		my ($alias, $op, $options, @cols)	= @$p;  | 
| 
459
 | 
17
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
31
 | 
 		my $cols	= '(' . join(' ', map { ref($_) ? $_->sse($context, "${indent}${more}") : '*' } @cols) . ')';  | 
| 
 
 | 
17
 | 
 
 | 
 
 | 
 
 | 
 
 | 
68
 | 
    | 
| 
460
 | 
17
 | 
 
 | 
 
 | 
 
 | 
 
 | 
126
 | 
 		my @opts_keys	= keys %$options;  | 
| 
461
 | 
17
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
35
 | 
 		if (@opts_keys) {  | 
| 
462
 | 
  
0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
 			my $opt_string	= '(' . join(' ', map { $_, qq["$options->{$_}"] } @opts_keys) . ')';  | 
| 
 
 | 
  
0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
    | 
| 
463
 | 
  
0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
 			push(@ops, qq[("$alias" "$op" $cols $opt_string)]);  | 
| 
464
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 		} else {  | 
| 
465
 | 
17
 | 
 
 | 
 
 | 
 
 | 
 
 | 
68
 | 
 			push(@ops, qq[("$alias" "$op" $cols)]);  | 
| 
466
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 		}  | 
| 
467
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	}  | 
| 
468
 | 
15
 | 
 
 | 
 
 | 
 
 | 
 
 | 
32
 | 
 	my $osse	= join(' ', @ops);  | 
| 
469
 | 
15
 | 
 
 | 
 
 | 
 
 | 
 
 | 
144
 | 
 	return sprintf(  | 
| 
470
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 		"(aggregate\n${indent}${more}%s\n${indent}${more}(%s)\n${indent}${more}(%s))",  | 
| 
471
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 		$psse,  | 
| 
472
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 		$gsse,  | 
| 
473
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 		$osse,  | 
| 
474
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	);  | 
| 
475
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
476
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
477
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # =item C<< plan_prototype >>  | 
| 
478
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #   | 
| 
479
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # Returns a list of scalar identifiers for the type of the content (children)  | 
| 
480
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # nodes of this plan node. See L<RDF::Query::Plan> for a list of the allowable  | 
| 
481
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # identifiers.  | 
| 
482
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #   | 
| 
483
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # =cut  | 
| 
484
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #   | 
| 
485
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # sub plan_prototype {  | 
| 
486
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # 	my $self	= shift;  | 
| 
487
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # 	return qw(P \E *\ssW);  | 
| 
488
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # }  | 
| 
489
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #   | 
| 
490
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # =item C<< plan_node_data >>  | 
| 
491
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #   | 
| 
492
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # Returns the data for this plan node that corresponds to the values described by  | 
| 
493
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # the signature returned by C<< plan_prototype >>.  | 
| 
494
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #   | 
| 
495
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # =cut  | 
| 
496
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #   | 
| 
497
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # sub plan_node_data {  | 
| 
498
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # 	my $self	= shift;  | 
| 
499
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # 	my @group	= $self->groupby;  | 
| 
500
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # 	my @ops		= @{ $self->[3] };  | 
| 
501
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # 	return ($self->pattern, \@group, map { [@$_] } @ops);  | 
| 
502
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # }  | 
| 
503
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
504
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 =item C<< distinct >>  | 
| 
505
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
506
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 Returns true if the pattern is guaranteed to return distinct results.  | 
| 
507
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
508
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 =cut  | 
| 
509
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
510
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub distinct {  | 
| 
511
 | 
16
 | 
 
 | 
 
 | 
  
16
  
 | 
  
1
  
 | 
24
 | 
 	my $self	= shift;  | 
| 
512
 | 
16
 | 
 
 | 
 
 | 
 
 | 
 
 | 
45
 | 
 	return $self->pattern->distinct;  | 
| 
513
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
514
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
515
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 =item C<< ordered >>  | 
| 
516
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
517
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 Returns true if the pattern is guaranteed to return ordered results.  | 
| 
518
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
519
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 =cut  | 
| 
520
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
521
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub ordered {  | 
| 
522
 | 
14
 | 
 
 | 
 
 | 
  
14
  
 | 
  
1
  
 | 
25
 | 
 	my $self	= shift;  | 
| 
523
 | 
14
 | 
 
 | 
 
 | 
 
 | 
 
 | 
37
 | 
 	my $sort	= [ $self->groupby ];  | 
| 
524
 | 
14
 | 
 
 | 
 
 | 
 
 | 
 
 | 
118
 | 
 	return []; # XXX aggregates are actually sorted, so figure out what should go here...  | 
| 
525
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
526
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
527
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub _node_type {  | 
| 
528
 | 
67
 | 
 
 | 
 
 | 
  
67
  
 | 
 
 | 
65
 | 
 	my $node	= shift;  | 
| 
529
 | 
67
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
133
 | 
 	if (blessed($node)) {  | 
| 
530
 | 
67
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
171
 | 
 		if ($node->isa('RDF::Query::Node::Literal')) {  | 
| 
 
 | 
 
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
 
 | 
 
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
531
 | 
67
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
118
 | 
 			if (my $type = $node->literal_datatype) {  | 
| 
532
 | 
34
 | 
 
 | 
 
 | 
 
 | 
 
 | 
125
 | 
 				return $type;  | 
| 
533
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 			} else {  | 
| 
534
 | 
33
 | 
 
 | 
 
 | 
 
 | 
 
 | 
127
 | 
 				return 'literal';  | 
| 
535
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 			}  | 
| 
536
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 		} elsif ($node->isa('RDF::Query::Node::Resource')) {  | 
| 
537
 | 
  
0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 			return 'resource';  | 
| 
538
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 		} elsif ($node->isa('RDF::Query::Node::Blank')) {  | 
| 
539
 | 
  
0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 			return 'blank';  | 
| 
540
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 		} else {  | 
| 
541
 | 
  
0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 			return '';  | 
| 
542
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 		}  | 
| 
543
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	} else {  | 
| 
544
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 		return '';  | 
| 
545
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	}  | 
| 
546
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
547
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
548
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 1;  | 
| 
549
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
550
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 __END__  | 
| 
551
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
552
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 =back  | 
| 
553
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
554
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 =head1 AUTHOR  | 
| 
555
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
556
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
  Gregory Todd Williams <gwilliams@cpan.org>  | 
| 
557
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
558
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 =cut  |