From 91cc7c5ba55bf25572013dfa29112090003eccc2 Mon Sep 17 00:00:00 2001 From: Ian Beckwith Date: Thu, 21 Oct 2010 22:50:13 +0100 Subject: [PATCH] preprocess elements, calculating joins --- lib/ID3FS/Path.pm | 59 ++++++++++++++++++++++++++++++++++++++++++++++-- lib/ID3FS/Path/Node.pm | 61 ++++++++++++-------------------------------------- 2 files changed, 71 insertions(+), 49 deletions(-) diff --git a/lib/ID3FS/Path.pm b/lib/ID3FS/Path.pm index c683101..855ea9e 100644 --- a/lib/ID3FS/Path.pm +++ b/lib/ID3FS/Path.pm @@ -356,11 +356,65 @@ sub parse { pop @elements; } +# print "\nELEMENTS: ", join(' ', map { $_->name(); } @elements), "\n"; + my @joins=$self->number_joins(@elements); +# @joins=qw(INNER) unless(@joins); + $self->{joins}=\@joins; +# print "AFTER: ", join(' ', map { $_->name() . "(" . $_->{table} . ")"; } @elements), "\n"; +# print "JOINS: ", join(', ', @joins), "\n"; # sort elements by precedence @elements=$self->sort_elements(@elements); $self->{tagtree}=$self->elements_to_tree(\@elements); } +sub number_joins +{ + my($self, @elements)=@_; + my @joins=qw(INNER); + my $table=1; + my $lastop=undef; + return () unless(@elements); + while(my $thing=shift @elements) + { + if($thing->type() == $TYPE_BOOL) + { + my $op=$thing->name(); + if($op eq "AND") + { + $table++; + push(@joins, "INNER"); + } + elsif($op eq "NOT") + { + unless($lastop && $lastop eq "AND") + { + push(@joins, "LEFT"); + $table++; + } + } + elsif($op eq "OR") + { + if($lastop && $lastop eq "NOT") + { + $table++; + push(@joins, "INNER"); + } + } + $lastop=$op; + } + else + { + # skip tags with values + if(@elements && $elements[0]->type() == $TYPE_TAG) + { + $thing=shift(@elements); + } + $thing->table($table); + } + } + return @joins; +} + sub state { my($self, $newstate)=@_; @@ -670,12 +724,13 @@ sub tags_subselect my($self)=@_; my $hasvals=$self->expecting_values(); my $tree=$self->{tagtree}; - use Data::Dumper; - print Dumper $tree; +# use Data::Dumper; +# print Dumper $tree; my ($sqlclause, @joins)=$tree->to_sql($hasvals) if($tree); my $sql="\tSELECT fxt1.files_id FROM tags t1"; my @crosses=(); my @inners=(); + @joins=@{$self->{joins}}; for(my $i=0; $i <= $#joins; $i++) { my $cnt=$i+1; diff --git a/lib/ID3FS/Path/Node.pm b/lib/ID3FS/Path/Node.pm index a0a6b70..ee3f931 100644 --- a/lib/ID3FS/Path/Node.pm +++ b/lib/ID3FS/Path/Node.pm @@ -36,6 +36,8 @@ sub new $self->{type}=shift; $self->{name}=shift; $self->{parents_id}=shift; + $self->{table}=1; + $self->{join}=undef; if($self->{type} != $TYPE_BOOL) { my $table=''; @@ -65,74 +67,39 @@ sub name { return shift->set("name", shift); } sub type { return shift->set("type", shift); } sub id { return shift->set("id", shift); } sub parents_id { return shift->set("parents_id", shift); } +sub table { return shift->set("table", shift); } +sub join { return shift->set("join", shift); } sub to_sql { - my($self, $hasvals, $not, @joins)=@_; + my($self, $hasvals, $not)=@_; $not=0 unless(defined($not)); - my @outjoins=(); - unless(@joins) - { - @outjoins = @joins = ("INNER"); - } + $hasvals=0 unless(defined($hasvals)); my $str=''; if($self->type() != $TYPE_BOOL) { - print $self->{id}, " = ", $self->{name}, "\n"; - $str .= "t" . scalar(@joins) . ".id='" . $self->{id} . "'"; + $str .= "t" . $self->table() . ".id='" . $self->{id} . "'"; if($not && !$hasvals) { - $str = "(" . $str . " AND fxt" . scalar(@joins) . ".files_id IS NULL)"; + $str = "(" . $str . " AND fxt" . $self->table() . ".files_id IS NULL)"; } - return ($str, @outjoins); + return ($str); } my $left=$self->left(); my $right=$self->right(); - return ("", @outjoins) unless($left || $right); - - my ($leftstr, @leftjoins) = $left->to_sql($hasvals, $not, @joins) if($left); - push(@joins, @leftjoins); - push(@outjoins, @leftjoins); + return ("") unless($left || $right); + my $leftstr = $left->to_sql($hasvals, $not) if($left); my $op=$self->name(); - if(defined($op)) - { - # if we are ANDing add an inner join - if($op eq "AND") - { - # 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") -# { - push(@joins, "INNER"); - push(@outjoins, "INNER"); -# } - } - elsif($op eq "NOT") - { - $not=1; - # return an extra join, but don't pass it down to $right->to_sql - # if we are looking for a tag *value*, use INNER join rather than LEFT - push(@outjoins, ($hasvals ? "INNER" : "LEFT")); - } - } - my ($rightstr, @rightjoins) = $right->to_sql($hasvals, $not, @joins) if($right); - push(@outjoins, @rightjoins); + $not=1 if(defined($op) && ($op eq "NOT")); + my $rightstr = $right->to_sql($hasvals, $not) if($right); $str = $leftstr; $str .= " $op " if($op && !$not); $str .= $rightstr; $str=("(" . $str . ")") if($op && $left && $right); -# print "LEFT (", scalar(@$leftjoins), "): "; -# print $leftstr if $leftstr; -# print "\n"; -# print "OP: $op\n" if $op; -# print "RIGHT (", scalar(@$rightjoins), "): "; -# print $rightstr if $rightstr; -# print "\n"; - print "OUT(", scalar(@outjoins), "): $str\n"; - return($str, @outjoins); + return($str); } sub used_tags -- 2.11.0