more special-casing joins
authorIan Beckwith <ianb@erislabs.net>
Wed, 20 Oct 2010 21:26:25 +0000 (22:26 +0100)
committerIan Beckwith <ianb@erislabs.net>
Wed, 20 Oct 2010 21:26:25 +0000 (22:26 +0100)
lib/ID3FS/Path/Node.pm

index b71406d..3171dd4 100644 (file)
@@ -99,25 +99,26 @@ sub to_sql
     if(defined($op))
     {
        my $join=undef;
-       # if right child is a NOT, we don't need extra join/brackets
-       # NOT will do the same and we will end up with an extra one
-       unless($right && $right->name() && $right->name() eq "NOT")
+       # if we are ANDing add an inner join
+       # also if we are NOTing, but we are looking for a tag *value*
+       if($op eq "AND")
        {
-           # if we are ANDing or ORing, add an inner join
-           # also if we are NOTing, but we are looking for a tag *value*
-           if($op eq "AND")
-           {
-               $join= "INNER";
-           }
-           elsif($op eq "NOT")
-           {
-               $not=1;
-               $join = ($hasvals ? "INNER" : "LEFT");
-           }
-           elsif($op eq "OR")
-           {
-               $join="INNER" unless($left && $left->name() && $left->name() eq "NOT")
-           }
+           # if right child is a NOT, we don't need extra join/brackets
+           # NOT will do the same and we will end up with an extra one
+           $join= "INNER" unless($right && $right->name() && $right->name() eq "NOT");
+       }
+       elsif($op eq "NOT")
+       {
+           $not=1;
+           $join = ($hasvals ? "INNER" : "LEFT");
+       }
+       elsif($op eq "OR")
+       {
+           # if the rightmost part of the left sub-expression ends in
+           # NOT, then we need an extra join. This doesn't apply if
+           # (as above) the righthand expression is a NOT.
+           $join="INNER" if(($self->right_ends_in_not($left)) &&
+                            !($right && $right->name() && $right->name() eq "NOT"));
        }
        if($join)
        {
@@ -147,4 +148,19 @@ sub used_tags
     return $self->id();
 }
 
+sub right_ends_in_not
+{
+    my($self, $node)=@_;
+    return 0 unless($node);
+    my $right=$node->right();
+    if($right && $right->type() == $TYPE_BOOL)
+    {
+       return $self->right_ends_in_not($right);
+    }
+    my $op=$node->name();
+    return 0 unless($op);
+    return 1 if($op eq "NOT");
+    return 0;
+}
+
 1;