Skip to content

Commit

Permalink
Fix ORDER BY semantics for CDT types.
Browse files Browse the repository at this point in the history
  • Loading branch information
kasei committed May 31, 2024
1 parent f2c6c3b commit 9109a19
Show file tree
Hide file tree
Showing 5 changed files with 83 additions and 21 deletions.
13 changes: 12 additions & 1 deletion lib/Attean/API/Binding.pm
Original file line number Diff line number Diff line change
Expand Up @@ -579,7 +579,18 @@ package Attean::API::Triple 0.033 {
return join(' ', '<<', (map { $_->ntriples_string } @values), '>>');
}

sub order {
my $self = shift;
return _compare('order', $self, @_);
}

sub compare {
my $self = shift;
return _compare('compare', $self, @_);
}

sub _compare {
my $cmp_method = shift;
my ($a, $b) = @_;
return 1 unless blessed($b);
if (not $b->does('Attean::API::Triple')) {
Expand All @@ -590,7 +601,7 @@ package Attean::API::Triple 0.033 {
foreach my $pos ($a->variables) {
my $at = $a->$pos();
my $bt = $b->$pos();
my $c = $at->compare($bt);
my $c = $at->$cmp_method($bt);

# If they are equal, continue. otherwise check if either term is an IRI.
# This is because term equality is defined for IRIs, but < and > isn't.
Expand Down
11 changes: 11 additions & 0 deletions lib/Attean/API/Term.pm
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,17 @@ Returns true if the term has a true SPARQL "effective boolean value", false othe
requires 'compare';
requires 'sameTerms';

=item C<< order ( $other ) >>
Similar to C<< compare >>, but provides the ordering semantics of ORDER BY.
=cut

sub order {
my $self = shift;
return $self->compare(@_);
}

sub __ntriples_string {
my $self = shift;
my $value = $self->value;
Expand Down
2 changes: 1 addition & 1 deletion lib/Attean/SimpleQueryEvaluator.pm
Original file line number Diff line number Diff line change
Expand Up @@ -268,7 +268,7 @@ supplied C<< $active_graph >>.
} elsif (blessed($bv) and $bv->does('Attean::API::Binding') and (not(defined($av)) or not($av->does('Attean::API::Binding')))) {
$c = -1;
} else {
$c = eval { $av ? $av->compare($bv) : 1 };
$c = eval { $av ? $av->order($bv) : 1 };
if ($@) {
$c = 1;
}
Expand Down
45 changes: 32 additions & 13 deletions lib/AtteanX/Functions/CompositeLists.pm
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ package AtteanX::Functions::CompositeLists 0.033 {
=cut
sub lex_to_list {
my $l = shift;
die 'TypeError' unless ($l->does('Attean::API::Literal'));
die 'TypeError: Cannot parse non-literal to cdt:List' unless ($l->does('Attean::API::Literal'));
my $dt = $l->datatype;
die 'TypeError: not a datatype literal' unless ($dt);
die 'TypeError: Expecting a List but found ' . $dt->value unless ($dt->value eq $LIST_TYPE_IRI);
Expand Down Expand Up @@ -192,14 +192,14 @@ package AtteanX::Functions::CompositeLists 0.033 {
my $active_graph = shift;
my $ct = shift;
my $pos = shift;
die 'TypeError' unless ($ct->does('Attean::API::Literal'));
die 'TypeError: Cannot interpret non-literal as CDT type' unless ($ct->does('Attean::API::Literal'));
my $dt = $ct->datatype;
if ($dt->value eq $LIST_TYPE_IRI) {
return listGet($model, $active_graph, $ct, $pos);
} elsif ($dt->value eq $AtteanX::Functions::CompositeMaps::MAP_TYPE_IRI) {
return AtteanX::Functions::CompositeMaps::mapGet($model, $active_graph, $ct, $pos);
} else {
die 'TypeError';
die 'TypeError: Unexpected non-CDT type: ' . $dt->value;
}
}

Expand All @@ -211,9 +211,9 @@ package AtteanX::Functions::CompositeLists 0.033 {
my $active_graph = shift;
my $l = shift;
my $pos = shift;
die 'TypeError' unless ($l->does('Attean::API::Literal'));
die 'TypeError: Cannot interpret non-literal as cdt:List' unless ($l->does('Attean::API::Literal'));
my $dt = $l->datatype;
die 'TypeError' unless ($dt->value eq $LIST_TYPE_IRI);
die 'TypeError: Unexpected non-List type: ' . $dt->value unless ($dt->value eq $LIST_TYPE_IRI);
my @nodes = lex_to_list($l);
die 'TypeError' unless ($pos->does('Attean::API::NumericLiteral') and $pos->datatype->value eq 'http://www.w3.org/2001/XMLSchema#integer');
my $i = int($pos->value) - 1; # cdt:get is 1-based, while the array index below is 0-based
Expand Down Expand Up @@ -645,12 +645,23 @@ package AtteanX::Functions::CompositeLists::ListLiteral {
return 0 unless ($li->equals($ri));
}
if ($seen_error) {
die 'TypeError';
die 'TypeError: Cannot compare cdt:List values with blank nodes';
}
return 1;
}

sub order {
my $self = shift;
return _compare('order', $self, @_);
}

sub compare {
my $self = shift;
return _compare('compare', $self, @_);
}

sub _compare {
my $cmp_method = shift;
my $lhs = shift;
my $rhs = shift;
# warn "LIST-LESS-THAN?";
Expand All @@ -674,13 +685,21 @@ package AtteanX::Functions::CompositeLists::ListLiteral {
# both null
next;
} elsif (not blessed($li)) {
die 'TypeError';
$seen_error++;
next;
if ($cmp_method eq 'order') {
return -1;
} else {
die 'TypeError';
$seen_error++;
next;
}
} elsif (not blessed($ri)) {
die 'TypeError';
$seen_error++;
next;
if ($cmp_method eq 'order') {
return 1;
} else {
die 'TypeError';
$seen_error++;
next;
}
}

if ($li->does('Attean::API::Blank') and $ri->does('Attean::API::Blank')) {
Expand All @@ -689,7 +708,7 @@ package AtteanX::Functions::CompositeLists::ListLiteral {
next;
}

my $icmp = $li->compare($ri);
my $icmp = $li->$cmp_method($ri);
next if ($icmp == 0);
return $icmp;
}
Expand Down
33 changes: 27 additions & 6 deletions lib/AtteanX/Functions/CompositeMaps.pm
Original file line number Diff line number Diff line change
Expand Up @@ -562,7 +562,18 @@ package AtteanX::Functions::CompositeMaps::MapLiteral {
return 1;
}

sub order {
my $self = shift;
return _compare('order', $self, @_);
}

sub compare {
my $self = shift;
return _compare('compare', $self, @_);
}

sub _compare {
my $cmp_method = shift;
my $lhs = shift;
my $rhs = shift;
# warn "MAP-LESS-THAN?";
Expand Down Expand Up @@ -611,18 +622,28 @@ package AtteanX::Functions::CompositeMaps::MapLiteral {
# both null
next;
} elsif (not blessed($v1)) {
die 'TypeError';
if ($cmp_method eq 'order') {
return -1;
} else {
die 'TypeError';
}
} elsif (not blessed($v2)) {
die 'TypeError';
if ($cmp_method eq 'order') {
return 1;
} else {
die 'TypeError';
}
}

foreach my $v ($v1, $v2) {
if ($v->does('Attean::API::IRI')) {
die 'TypeError'; # IRIs as map values cannot be compared
if ($cmp_method eq 'compare') {
foreach my $v ($v1, $v2) {
if ($v->does('Attean::API::IRI')) {
die 'TypeError'; # IRIs as map values cannot be compared
}
}
}

my $v_cmp = $v1->compare($v2); # may throw an error
my $v_cmp = $v1->$cmp_method($v2); # may throw an error
if ($v_cmp) {
return $v_cmp;
}
Expand Down

0 comments on commit 9109a19

Please sign in to comment.