abstract out tags_subselect
[id3fs.git] / lib / ID3FS / DB.pm
index d149b6a..0ed658c 100644 (file)
@@ -94,6 +94,8 @@ sub find_db
     return $file;
 }
 
+sub base_dir { return shift->{base}; }
+
 sub create
 {
     my($self,$name)=@_;
@@ -136,6 +138,12 @@ sub checkschema
     }
 }
 
+sub analyze
+{
+    my $self=shift;
+    $self->cmd("ANALYZE");
+}
+
 sub enable_foreign_keys
 {
     my $self=shift;
@@ -183,14 +191,15 @@ sub tags
     my @ids=();
 
     my $main_sql_start=("SELECT t2.name\n" .
-                       "\tFROM (SELECT files_id FROM tags t1\n" .
-                       "\t\tINNER JOIN files_x_tags ON t1.id=files_x_tags.tags_id\n" .
-                       "\t\tWHERE t1.id in\n\t\t\t(");
-    my $main_sql_mid=(")\n\t\t) AS subselect\n" .
-                     "\tINNER JOIN files_x_tags ON subselect.files_id=files_x_tags.files_id\n" .
-                     "\tINNER JOIN tags t2 ON files_x_tags.tags_id=t2.id\n" .
-                     "\tWHERE t2.id NOT IN (");
-    my $main_sql_end=")\n\tGROUP BY t2.name;";
+                       "\tFROM (\n" .
+                       $self->tags_subselect(@constraints) .
+                       ") AS subselect\n" .
+                     "INNER JOIN files_x_tags ON subselect.files_id=files_x_tags.files_id\n" .
+                     "INNER JOIN tags t2 ON files_x_tags.tags_id=t2.id\n" .
+                     "WHERE t2.id NOT IN (");
+    my $main_sql_end=")\nGROUP BY t2.name;";
+    # FIXME: generalise more?
+
     while(my $constraint=shift @constraints)
     {
        my $cid=$constraint->{id};
@@ -199,7 +208,6 @@ sub tags
     @ids = map( { "\"$_\""; } grep { defined; } @ids) unless($self->{postgres});
     my $tagstr=join(", ", @ids);
     my $sql = ($main_sql_start . $tagstr .
-              $main_sql_mid   . $tagstr .
               $main_sql_end);
     print "SQL: $sql\n";
     my $result=$self->cmd_rows($sql);
@@ -230,23 +238,12 @@ sub artists
        return(map { $_->[0]; } @$tags);
     }
     my @ids=();
-    my $main_sql_start=("SELECT artists.name\n" .
-                       "\tFROM (SELECT files_id FROM tags\n" .
-                       "\t\tINNER JOIN files_x_tags ON tags.id=files_x_tags.tags_id\n" .
-                       "\t\tWHERE tags.id in\n\t\t\t(");
-    my $main_sql_end=(")\n\t\t) AS subselect\n" .
-                     "\tINNER JOIN files ON subselect.files_id=files.id\n" .
-                     "\tINNER JOIN artists ON files.artists_id=artists.id\n" .
-                     "\n\tGROUP BY artists.name;");
-    while(my $constraint=shift @constraints)
-    {
-       my $cid=$constraint->{id};
-       push(@ids, $cid);
-    }
-    @ids = map( { "\"$_\""; } grep { defined; } @ids) unless($self->{postgres});
-    my $tagstr=join(", ", @ids);
-    my $sql = ($main_sql_start . $tagstr .
-              $main_sql_end);
+    my $sql=("SELECT artists.name FROM (\n" .
+            $self->tags_subselect(@constraints) .
+            ") AS subselect\n" .
+            "INNER JOIN files ON subselect.files_id=files.id\n" .
+            "INNER JOIN artists ON files.artists_id=artists.id\n" .
+            "GROUP BY artists.name;");
     print "SQL: $sql\n";
     my $result=$self->cmd_rows($sql);
     my @tagnames=map { $_->[0]; } @$result;
@@ -263,23 +260,13 @@ sub albums
     {
        return $self->artist_albums($constraints[$#constraints]->{id});
     }
-    my $main_sql_start=("SELECT albums.name\n" .
-                       "\tFROM (SELECT files_id FROM tags\n" .
-                       "\t\tINNER JOIN files_x_tags ON tags.id=files_x_tags.tags_id\n" .
-                       "\t\tWHERE tags.id in\n\t\t\t(");
-    my $main_sql_end=(")\n\t\t) AS subselect\n" .
-                     "\tINNER JOIN files ON subselect.files_id=files.id\n" .
-                     "\tINNER JOIN albums ON files.albums_id=albums.id\n" .
-                     "\n\tGROUP BY albums.name;");
-    while(my $constraint=shift @constraints)
-    {
-       my $cid=$constraint->{id};
-       push(@ids, $cid);
-    }
-    @ids = map( { "\"$_\""; } grep { defined; } @ids) unless($self->{postgres});
-    my $str=join(", ", @ids);
-    my $sql = ($main_sql_start . $str .
-              $main_sql_end);
+    my $sql=("SELECT albums.name\n" .
+            "\tFROM (\n" .
+            $self->tags_subselect(@constraints) .
+            "\t) AS subselect\n" .
+            "INNER JOIN files ON subselect.files_id=files.id\n" .
+            "INNER JOIN albums ON files.albums_id=albums.id\n" .
+            "GROUP BY albums.name;");
     my $result=$self->cmd_rows($sql);
     my @names=map { $_->[0]; } @$result;
     print "ALBUMS: ", join(', ', @names), "\n";
@@ -351,23 +338,12 @@ sub tracks
        return $self->album_tracks($artist_id, $constraints[$#constraints]->{id});
     }
 
-    my $main_sql_start=("SELECT files.name\n" .
-                       "\tFROM (SELECT files_id FROM tags\n" .
-                       "\t\tINNER JOIN files_x_tags ON tags.id=files_x_tags.tags_id\n" .
-                       "\t\tWHERE tags.id in\n\t\t\t(");
-    my $main_sql_end=(")\n\t\t) AS subselect\n" .
-                     "\tINNER JOIN files ON files.id=subselect.files_id" .
-                     "\tGROUP BY files.name;");
-    my @ids;
-    while(my $constraint=shift @constraints)
-    {
-       my $cid=$constraint->{id};
-       push(@ids, $cid);
-    }
-    @ids = map( { "\"$_\""; } grep { defined; } @ids) unless($self->{postgres});
-    my $str=join(", ", @ids);
-    my $sql = ($main_sql_start . $str .
-              $main_sql_end);
+    my $sql=("SELECT files.name\n" .
+            "\tFROM (\n" .
+            $self->tags_subselect(@constraints) .
+            "\t) AS subselect\n" .
+            "INNER JOIN files ON files.id=subselect.files_id\n" .
+            "GROUP BY files.name;");
     print "SQL: $sql\n";
     my $result=$self->cmd_rows($sql);
     my @names=map { $_->[0]; } @$result;
@@ -377,7 +353,7 @@ sub tracks
 
 sub filename
 {
-    my($self, @constraints)=@_;
+    my($self, $mountpoint, @constraints)=@_;
     if(ref($constraints[$#constraints]) eq "ID3FS::PathElement::File")
     {
        my $id=$constraints[$#constraints]->{id};
@@ -387,11 +363,59 @@ sub filename
                 "GROUP BY paths.name, files.name");
        print "FILENAME SQL: $sql\n";
        my ($path, $name)=$self->cmd_onerow($sql, $id);
-       return($self->{absbase} . "/$path/$name");
+       my $id3fs_path=join('/', map { $_->{name}; }  @constraints);
+       return($self->relativise($path, $name, $mountpoint, $id3fs_path));
     }
     die("DB::filename: unhandled case\n"); #FIXME
 }
 
+sub tags_subselect
+{
+    my($self,@constraints)=@_;
+    my @ids = grep { defined; } map { $_->{id}; } @constraints;
+    @ids=map( { "\"$_\""; } @ids) unless($self->{postgres});
+    my $sql=("\tSELECT files_id FROM tags t1\n" .
+            "\tINNER JOIN files_x_tags ON t1.id=files_x_tags.tags_id\n" .
+            "\tWHERE t1.id IN (\n\t\t" .
+            join(', ', @ids) .
+            "\n\t)\n");
+    return $sql;
+}
+
+
+sub relativise
+{
+    my($self, $path, $name, $mountpoint, $id3fs_path)=@_;
+    $id3fs_path=~s/(.*)\/.*/$1/;
+    my $rpath="$self->{absbase}/$path";
+    my $vpath="$mountpoint/$id3fs_path";
+    my @path=split(/\//,$rpath);
+    my @rel=split(/\//,$vpath);
+    #absolute paths have empty first element due to leading /
+    shift(@path) if($path[0] eq "");
+    shift(@rel)  if($rel[0]  eq "");
+    if($path[0] ne $rel[0])
+    {
+       #no path in common, return absolute
+       print "FAIL: NO PATHS IN COMMON\n";
+       return $name;
+    }
+    # f: /home/foo/bar/baz.mp3
+    # r: /home/ianb/music/albums
+    while(@path && @rel && ($path[0] eq $rel[0]))
+    {
+       shift(@path);
+       shift(@rel);
+       print "POP ";
+    }
+    print "\n";
+    my $upcount=scalar(@rel);
+    my $result="../" x $upcount;
+    $result .= join("/",@path);
+    $result .= "/$name";
+    return $result;
+}
+
 sub bare_tags
 {
     my($self)=@_;
@@ -443,7 +467,7 @@ sub add
     {
        ($pathpart, $filepart) = ($relpath =~ /(.*)\/(.*)/);
     }
-    my $file=ID3FS::AudioFile->new($path);
+    my $file=ID3FS::AudioFile->new($path, $self->{me});
     return unless(defined($file));
     my $artist=$file->artist();
     my $album=$file->album();
@@ -566,7 +590,6 @@ sub files_in
 {
     my ($self, $dir)=@_;
     $dir=~s/^$self->{base}\/?//;
-#    print "Munged dir: $dir\n";
     my $sql=("SELECT files.name FROM files\n" .
             "INNER JOIN paths ON files.paths_id=paths.id\n" .
             "WHERE paths.name=?\n");
@@ -584,7 +607,6 @@ sub prune_directories
     {
        my($path, $id)=@$pathpair;
        my $fullpath="$self->{absbase}/$path";
-#      print "PRUNING PATH $fullpath: ";
        unless(-d $fullpath)
        {
            push(@ids, $id)