commit:25a5e8dee57be7db1fa4734dffafcb2d8f2f9010
author:Chip Black
committer:Chip Black
date:Mon Feb 9 13:47:38 2009 -0600
parents:46c936173c5b03efa38d26598a08a258cde9636b
Changed random and limit functionality to be fucking awesome

Created a db_select subroutine that returns a closure that returns
filenames until there are no more, then returns undef.  Random
functionality has been moved into this routine, so 'random' and
'limit==foo' are now query keywords instead of a special case.  Also,
random is now done in the DB layer for even greater speed.
diff --git a/udb.pl b/udb.pl
line changes: +82/-89
index 262cb5f..3f0fbbd
--- a/udb.pl
+++ b/udb.pl
@@ -194,6 +194,84 @@ sub db_info {
 	print "\tsize\t\t", -s $file, "\n";
 }
 
+# db_select returns an anonymous function that returns a filename on
+# every call, then undef when there are no more filenames
+sub db_select {
+	my (@where, $order, $ltype, $limit);
+
+	$order = 'ORDER BY artist,album,year,track';
+	foreach (@_) {
+		if (/^(artist|title|album|genre|comment|filename)(==|=)(.*?)$/) {
+			my ($k, $op, $v) = ($1, $2, $3);
+			$v =~ s/"/""/g;
+			if ($op eq '==') {
+				push @where, qq{$k LIKE "$v"};
+			} else {
+				push @where, qq{$k LIKE "\%$v\%"};
+			}
+		} elsif (/^(track|year|length|bitrate|samplerate|channels)(>|<|==|=)(.*)$/) {
+			my ($k, $op, $v) = ($1, $2, int($3));
+			if ($op eq '=' or $op eq '==') {
+				push @where, qq{$k=$v};
+			} elsif ($op eq '>' or $op eq '<') {
+				push @where, qq{$k$op$v};
+			}
+		} elsif ($_ eq 'random') {
+			$order = 'ORDER BY random()';
+		} elsif (/^limit==?(.*)$/) {
+			my $v = $1;
+			if ($v =~ /^\d+$/) {
+				$order .= " LIMIT $v";
+			} elsif ($v =~ /:/) {
+				$ltype = 'time';
+				$limit = time_expand($v);
+			} elsif ($v =~ /[bkmgt]$/) {
+				$ltype = 'size';
+				$limit = size_expand($v);
+			} else {
+				print "Unknown type of limit: $v\n";
+				exit 1;
+			}
+		} else {
+			s/"/""/g;
+			push @where, qq{(artist LIKE "\%$_\%" OR title LIKE "\%$_\%" OR album LIKE "\%$_\%" OR comment LIKE "\%$_\%" OR filename LIKE "\%$_\%")};
+		}
+	}
+	my $query = "SELECT filename,length FROM tunes ";
+	if (@where) {
+		$query .= "WHERE " . join(' AND ', @where) . ' ';
+	}
+	$query .= $order;
+	#print $query,"\n";
+	my $sth = $dbh->prepare($query) or die $dbh->errstr;
+	$sth->execute;
+
+	# /me kicks lisp in the teeth
+	if (not defined $ltype) {
+		return sub {
+			return ($sth->fetchrow_array)[0];
+		}
+	} elsif ($ltype eq 'time') {
+		my $length = 0;
+		return sub {
+			if ($length < $limit) {
+				my $fi = $sth->fetchrow_hashref;
+				$length += $fi->{length};
+				return $fi->{filename};
+			}
+		}
+	} elsif ($ltype eq 'size') {
+		my $size = 0;
+		return sub {
+			if ($size < $limit) {
+				my $file = ($sth->fetchrow_array)[0];
+				$size += -s $file;
+				return $file;
+			}
+		}
+	}
+}
+
 if ($ARGV[0] eq 'add') {
 	shift;
 	$dbh->begin_work;
@@ -229,7 +307,7 @@ if ($ARGV[0] eq 'add') {
 	while ($r = $sth->fetchrow_hashref()) {
 		if (blacklisted($r->{filename}) ||
 		    ! (-e $r->{filename} || -e decode('utf8', $r->{filename}))) {
-			print "Purging |$r->{filename}|\n";
+			print "Purging $r->{filename}\n";
 			my $hash = $r->{hash};
 			$dbh->do(qq{DELETE FROM tunes WHERE hash="$hash"});
 		}
@@ -246,65 +324,6 @@ if ($ARGV[0] eq 'add') {
 			db_info($_);
 		}
 	}
-} elsif ($ARGV[0] eq 'random') {
-	shift;
-	my $method = shift;
-	my $quantity = shift;
-	if ($method eq 'size') {
-		$quantity = size_expand($quantity);
-	} elsif ($method eq 'length') {
-		$quantity = time_expand($quantity);
-	} elsif ($method eq 'all') {
-		($quantity) = $dbh->selectrow_array(qq{SELECT count(*) FROM tunes});
-		$method = 'count';
-	} elsif ($method eq 'count') {
-		# Do nothing
-	} elsif ($method =~ /^\d+$/) {
-		$quantity = int($method);
-		$method = 'count';
-	} else {
-		print "Unknown kind of random: $method $quantity\n";
-		exit 1;
-	}
-	my $n_files;
-
-	if ($method eq 'count') {
-		# This could take a very large amount of memory...
-		my @files = @{$dbh->selectcol_arrayref(qq{SELECT filename FROM tunes})};
-
-		for my $i (1..$quantity) {
-			my $n = int(rand(@files));
-			print scalar splice(@files, $n, 1), "\n";
-			last unless @files;
-		}
-	} elsif ($method eq 'size') {
-		my @files = @{$dbh->selectcol_arrayref(qq{SELECT filename FROM tunes})};
-
-		my $size = 0;
-		while ($size < $quantity) {
-			my $n = int(rand(@files));
-			my $file = splice(@files, $n, 1);
-			$size += -s $file;
-			print "$file\n";
-			last unless @files;
-		}
-	} elsif ($method eq 'length') {
-		my @files;
-		my $sth = $dbh->prepare(qq{SELECT filename,length FROM tunes});
-		$sth->execute;
-		while (my $row = $sth->fetchrow_arrayref) {
-			push @files, [@$row];
-		}
-
-		my $length = 0;
-		while ($length < $quantity) {
-			my $n = int(rand(@files));
-			my $file = splice(@files, $n, 1);
-			$length += $file->[1];
-			print "$file->[0]\n";
-			last unless @files;
-		}
-	}
 } elsif ($ARGV[0] eq 'scan') {
 	shift;
 	unless (@ARGV) {
@@ -349,35 +368,9 @@ if ($ARGV[0] eq 'add') {
 	$import_dbh->disconnect;
 	$dbh->commit;
 } else {
-	my @where;
-	foreach (@ARGV) {
-		if (/^(artist|title|album|genre|comment|filename)(==|=)(.*?)$/) {
-			my ($k, $op, $v) = ($1, $2, $3);
-			$v =~ s/"/""/g;
-			if ($op eq '==') {
-				push @where, qq{$k LIKE "$v"};
-			} else {
-				push @where, qq{$k LIKE "\%$v\%"};
-			}
-		} elsif (/^(track|year|length|bitrate|samplerate|channels)(>|<|==|=)(.*)$/) {
-			my ($k, $op, $v) = ($1, $2, int($3));
-			if ($op eq '=' or $op eq '==') {
-				push @where, qq{$k=$v};
-			} elsif ($op eq '>' or $op eq '<') {
-				push @where, qq{$k$op$v};
-			}
-		} else {
-			s/"/""/g;
-			push @where, qq{(artist LIKE "\%$_\%" OR title LIKE "\%$_\%" OR album LIKE "\%$_\%" OR comment LIKE "\%$_\%" OR filename LIKE "\%$_\%")};
-		}
-	}
-	my $query = "SELECT filename FROM tunes WHERE " . join(' AND ', @where);
-	#print $query,"\n";
-	my $sth = $dbh->prepare($query);
-	$sth->execute;
-	while (my $file = ($sth->fetchrow_array)[0]) {
-		print "$file\n";
-	}
+	my $results = db_select(@ARGV);
+	my $file;
+	print "$file\n" while $file = $results->();
 }
 
 $dbh->disconnect();