From 03d278e4ff66a99cb4f7e05ab89b2ab8f2d9d90c Mon Sep 17 00:00:00 2001 From: Yorhel Date: Sun, 2 Oct 2016 20:07:57 +0200 Subject: [PATCH] Normalize package info tables + change browsing URLs This splits the 'package' table into 'packages' and 'package_versions', which should improve performance in some cases and simplify some future queries. Previously it wasn't very well defined whether packages were uniquely identified by (system, name) or by (system, category, name). This is now normalized to the latter form. This required changes to the package URLs to include the category. --- schema.sql => sql/schema.sql | 59 +++++--------- sql/update-2016-10-02.sql | 30 ++++++++ util/common.sh | 13 +++- util/deb.sh | 11 +-- util/freebsd.sh | 21 ++--- www/index.pl | 144 ++++++++++++++++++++++------------- www/man.js | 36 ++++----- 7 files changed, 180 insertions(+), 134 deletions(-) rename schema.sql => sql/schema.sql (80%) create mode 100644 sql/update-2016-10-02.sql diff --git a/schema.sql b/sql/schema.sql similarity index 80% rename from schema.sql rename to sql/schema.sql index f198da6..f597797 100644 --- a/schema.sql +++ b/sql/schema.sql @@ -1,9 +1,3 @@ - --- TODO: "system" -> "repository"? --- TODO: index of (reverse) man page references? --- TODO: Use some consistent naming of tables and columns - - CREATE TABLE systems ( id integer PRIMARY KEY, -- hardcoded ID. name varchar NOT NULL, @@ -12,52 +6,49 @@ CREATE TABLE systems ( short varchar NOT NULL ); - CREATE TABLE contents ( hash bytea PRIMARY KEY, content varchar NOT NULL ); - --- Note: If there are multiple arches available for the same package, then --- generally only a single one is chosen (not stored here which one). --- Also, a package may be listed here even if it has no man pages indexed, in --- order for the fetcher to determine whether it has already processed the --- package or not. This doesn't mean all packages of a repository are listed --- here. For example, the Arch fetcher checks the file list of a package before --- considering to handle it. -CREATE TABLE package ( +CREATE TABLE packages ( id SERIAL PRIMARY KEY, system integer NOT NULL REFERENCES systems(id), - category varchar, -- depends on system (e.g. "community" on Arch, "x11" on Debian) + category varchar, name varchar NOT NULL, - version varchar NOT NULL, - released date NOT NULL, - UNIQUE(system, name, version) + UNIQUE(system, name, category) -- Note the order, lookups on (system,name) are common ); +CREATE TABLE package_versions ( + id SERIAL PRIMARY KEY, + package integer NOT NULL REFERENCES packages(id), + version varchar NOT NULL, + released date NOT NULL, + UNIQUE(package, version) +); CREATE TABLE man ( - package integer NOT NULL REFERENCES package(id), - name varchar NOT NULL, -- 'fopen', 'du', etc (TODO: An index on name_from_filename(filename) may also work) - section varchar NOT NULL, -- extracted from filename (TODO: Is this column really necessary?) - filename varchar NOT NULL, -- full path + file name - locale varchar, -- parsed from the file name, NULL for the "main" man page (in the C or en_US locale) + package integer NOT NULL REFERENCES package_versions(id), + name varchar NOT NULL, + section varchar NOT NULL, + filename varchar NOT NULL, + locale varchar, hash bytea NOT NULL REFERENCES contents(hash), UNIQUE(package, filename) ); - -CREATE INDEX ON man USING hash (hash); +CREATE INDEX ON man (hash); CREATE INDEX ON man (name); + CREATE TABLE man_index AS SELECT DISTINCT name, section FROM man; CREATE INDEX ON man_index USING btree(lower(name) text_pattern_ops); CREATE TABLE stats_cache AS SELECT count(distinct hash) AS hashes, count(distinct name) AS mans, count(*) AS files, count(distinct package) AS packages FROM man; + INSERT INTO systems (id, name, release, short, relorder) VALUES (1, 'Arch Linux', NULL, 'arch', 0), (2, 'Ubuntu', '4.10', 'ubuntu-warty', 0), @@ -153,6 +144,7 @@ INSERT INTO systems (id, name, release, short, relorder) VALUES (92, 'Ubuntu', '15.10', 'ubuntu-wily', 22), (93, 'Ubuntu', '16.04', 'ubuntu-xenial', 23); + -- Removes any path components and compression extensions from the filename. CREATE OR REPLACE FUNCTION basename_from_filename(fn text) RETURNS text AS $$ DECLARE @@ -178,16 +170,3 @@ $$ LANGUAGE SQL; CREATE OR REPLACE FUNCTION name_from_filename(text) RETURNS text AS $$ SELECT regexp_replace(basename_from_filename($1), E'^(.+)\\.[^.]+$', E'\\1'); $$ LANGUAGE SQL; - - - - --- Some handy admin queries - ---BEGIN; ---DELETE FROM man WHERE package IN(SELECT id FROM package WHERE name = ''); ---DELETE FROM package WHERE name = ''; ---DELETE FROM contents c WHERE NOT EXISTS(SELECT 1 FROM man m WHERE m.hash = c.hash); ---COMMIT; - ---DELETE FROM package WHERE system = 18 AND NOT EXISTS(SELECT 1 FROM man WHERE id = package); diff --git a/sql/update-2016-10-02.sql b/sql/update-2016-10-02.sql new file mode 100644 index 0000000..a669314 --- /dev/null +++ b/sql/update-2016-10-02.sql @@ -0,0 +1,30 @@ +CREATE TABLE packages ( + id SERIAL PRIMARY KEY, + system integer NOT NULL REFERENCES systems(id), + category varchar, + name varchar NOT NULL, + UNIQUE(system, name, category) -- Note the order, lookups on (system,name) are common +); + +CREATE TABLE package_versions ( + id SERIAL PRIMARY KEY, + package integer NOT NULL REFERENCES packages(id), + version varchar NOT NULL, + released date NOT NULL, + UNIQUE(package, version) +); + +INSERT INTO packages (system, category, name) SELECT system, category, name FROM package GROUP BY system, category, name; +INSERT INTO package_versions (id, package, version, released) + SELECT p.id, pn.id, p.version, p.released FROM package p JOIN packages pn ON pn.system = p.system AND pn.category = p.category AND pn.name = p.name; + +SELECT setval('package_versions_id_seq', nextval('package_id_seq')); + +ALTER TABLE man DROP CONSTRAINT man_package_fkey; +ALTER TABLE man ADD FOREIGN KEY (package) REFERENCES package_versions(id); + +-- Use a proper b-tree index +DROP INDEX man_hash_idx; +CREATE INDEX ON man (hash); + +-- DROP TABLE package; diff --git a/util/common.sh b/util/common.sh index f30ee18..668bad4 100644 --- a/util/common.sh +++ b/util/common.sh @@ -11,13 +11,18 @@ trap "rm -rf $TMP" EXIT # Usage: add_pkginfo sysid category name version date # Returns 0 if the package is already in the database or if an error occured. -# Otherwise adds the package, sets PKGID to the new ID, and returns 1. +# Otherwise adds the package, sets PKGID to the new package_versions.id, and returns 1. PKGID= add_pkginfo() { - RES=`echo "SELECT id FROM package WHERE system = :'sysid' AND name = :'name' AND version = :'ver'"\ - | $PSQL -v "sysid=$1" -v "name=$3" -v "ver=$4"` + RES=`echo "SELECT pv.id FROM packages p JOIN package_versions pv ON pv.package = p.id + WHERE p.system = :'sysid' AND p.category = :'cat' AND p.name = :'name' AND pv.version = :'ver'"\ + | $PSQL -v "sysid=$1" -v "cat=$2" -v "name=$3" -v "ver=$4"` [ "$?" -ne 0 -o -n "$RES" ] && return 0 - RES=`echo "INSERT INTO package (system, category, name, version, released) VALUES(:'sysid',:'cat',:'name',:'ver',:'rel') RETURNING id"\ + RES=`echo " + INSERT INTO packages (system, category, name) VALUES(:'sysid', :'cat', :'name') ON CONFLICT DO NOTHING; + INSERT INTO package_versions (version, released, package) VALUES(:'ver', :'rel', + (SELECT packages.id FROM packages WHERE system = :'sysid' AND category = :'cat' AND name = :'name')) + RETURNING id"\ | $PSQL -v "sysid=$1" -v "cat=$2" -v "name=$3" -v "ver=$4" -v "rel=$5"` [ "$?" -ne 0 ] && return 0 PKGID=$RES diff --git a/util/deb.sh b/util/deb.sh index 6d918fa..6ed77ab 100755 --- a/util/deb.sh +++ b/util/deb.sh @@ -31,11 +31,10 @@ checkpkg() { DATE=`date -d "$DATE" +%F` # Insert package in the database - PKGID=`echo "INSERT INTO package (system, category, name, version, released) VALUES(:'sysid',:'cat',:'name',:'ver',:'rel') RETURNING id"\ - | $PSQL -v "sysid=$SYSID" -v "cat=$SECTION" -v "name=$NAME" -v "ver=$VERSION" -v "rel=$DATE"` + add_pkginfo $SYSID $SECTION $NAME $VERSION $DATE # Extract and handle the man pages - if [ "$?" -eq 0 -a -n "$PKGID" ]; then + if [ "$?" -eq 1 -a -n "$PKGID" ]; then # Old format if [ "`head -c8 \"$FN\"`" = "0.939000" ]; then tail -n+3 "$FN" | tail -c+"`head -n2 \"$FN\" | tail -n1`" | tail -c+2 | add_tar - $PKGID -z @@ -116,11 +115,13 @@ syncrepo() { if($p && $v && $s && $f) { $f =~ s{^(Debian-1.[12])/}{dists/$1/main/}; print "$p $v $s $f" if $pkg{$p} && $pkg{$p} == 1 - && !$db->selectrow_arrayref(q{SELECT 1 FROM package WHERE system = ? AND name = ? AND version = ?}, {}, $sysid, $p, $v); + && !$db->selectrow_arrayref(q{ + SELECT 1 FROM packages p JOIN package_versions pv ON pv.package = p.id + WHERE p.system = ? AND p.category = ? AND p.name = ? AND pv.version = ?}, {}, $sysid, $s, $p, $v); #warn "Duplicate package? $p\n" if $pkg{$p} && $pkg{$p} == 2; $pkg{$p} = 2; } - $p=$v=$f=undef + $p=$v=$f=$s=undef } } close F; diff --git a/util/freebsd.sh b/util/freebsd.sh index e4427a9..4ff7014 100755 --- a/util/freebsd.sh +++ b/util/freebsd.sh @@ -52,8 +52,7 @@ check_pkg() { # return fi - PKGID=`echo "INSERT INTO package (system, category, name, version, released) VALUES(:'sysid',:'cat',:'name',:'ver',:'rel') RETURNING id"\ - | $PSQL -v "sysid=$SYSID" -v "cat=$CAT" -v "name=$NAME" -v "ver=$VER" -v "rel=$DATE"` + add_pkginfo $SYSID $CAT $NAME $VER $DATE add_tar "$TMP/$FN" $PKGID rm -f "$TMP/$FN" } @@ -84,8 +83,8 @@ check_pkgdir() { # echo "== Error fetching package index for /$CAT/" continue fi - perl -l - "$TMP/pkgnames" "$TMP/pkglist" $SYSID <<'EOP' >"$TMP/newpkgs" - ($names, $list, $sysid) = @ARGV; + perl -l - "$TMP/pkgnames" "$TMP/pkglist" $SYSID $CAT <<'EOP' >"$TMP/newpkgs" + ($names, $list, $sysid, $cat) = @ARGV; use DBI; $db = DBI->connect('dbi:Pg:dbname=manned', 'manned', '', {RaiseError => 1}); @@ -103,7 +102,9 @@ check_pkgdir() { # if(!$n || !$names{$n} || !$v) { warn "== Unknown package: $c\n"; } else { - print "$c $n $v" if !$db->selectrow_arrayref(q{SELECT 1 FROM package WHERE system = ? AND name = ? AND version = ?}, {}, $sysid, $n, $v); + print "$c $n $v" if !$db->selectrow_arrayref(q{ + SELECT 1 FROM packages p JOIN package_versions pv ON pv.package = p.id + WHERE p.system = ? AND p.category = ? AND p.name = ? AND pv.version = ?}, {}, $sysid, $cat, $n, $v); } } close F; @@ -720,15 +721,7 @@ f9_1() { } f9_2() { - MIR="http://ftp.dk.freebsd.org/pub/FreeBSD/releases/i386/9.2-RELEASE/" - echo "============ $MIR" - check_dist 86 "$MIR/base.txz" "core-base" "2013-09-27" - check_dist 86 "$MIR/games.txz" "core-games" "2013-09-27" - check_pkgdir 86 "$MIR/packages" -} - -f9_3() { - MIR="http://ftp.dk.freebsd.org/pub/FreeBSD/releases/i386/9.2-RELEASE/" + MIR="http://ftp-archive.freebsd.org/mirror/FreeBSD-Archive/old-releases/i386/9.2-RELEASE/" echo "============ $MIR" check_dist 86 "$MIR/base.txz" "core-base" "2013-09-27" check_dist 86 "$MIR/games.txz" "core-games" "2013-09-27" diff --git a/www/index.pl b/www/index.pl index aea587f..523a6f7 100755 --- a/www/index.pl +++ b/www/index.pl @@ -46,8 +46,23 @@ TUWF::register( qr// => \&home, qr{info/about} => \&about, qr{browse/search} => \&browsesearch, - qr{browse/([^/]+)} => \&browsesys, - qr{browse/([^/]+)/([^/]+)(?:/([^/]+))?} => \&browsepkg, + + qr{pkg/([^/]+)} => \&pkg_list, + # pkg/$system/$category/$name (/$version); $category may contain a slash, too. + qr{pkg/([^/]+)/(.+)?} => \&pkg_info, + + # Redirects for old URLs. + # /browse/ has been moved to /pkg/ with the package category added to the path + qr{browse/([^/]+)} => sub { $_[0]->resRedirect("/pkg/$_[1]", 'perm'); }, + qr{browse/([^/]+)/([^/]+)(?:/([^/]+))?} => sub { + my($self, $sys, $name, $ver) = @_; + $sys = $self->{sysbyshort}{$sys}; + return $self->resNotFound if !$sys; + my $pkgs = $self->dbPackageGet(sysid => $sys->{id}, name => $name, results => 1); + return $self->resNotFound if !@$pkgs; + $self->resRedirect("/pkg/$sys->{short}/$pkgs->[0]{category}/$name".($ver ? "/$ver" :''), 'perm'); + }, + qr{xml/search\.xml} => \&xmlsearch, qr{([^/]+)/([0-9a-f]{8})} => \&man, qr{([^/]+)/([0-9a-f]{8})/src} => \&src, @@ -82,13 +97,13 @@ sub home { $sys = $sys{$sys}; (my $img = $sys->[0]{short}) =~ s/^(.+)-.+$/$1/; li; - a href => "/browse/$sys->[0]{short}" if @$sys == 1; + a href => "/pkg/$sys->[0]{short}" if @$sys == 1; span style => "background-image: url('images/$img.png')", ''; b $sys->[0]{name}; if(@$sys > 1) { my $i = 0; for(reverse @$sys) { - a href => "/browse/$_->{short}", ++$i > 3 ? (class => 'hidden') : (), $_->{release}; + a href => "/pkg/$_->{short}", ++$i > 3 ? (class => 'hidden') : (), $_->{release}; lit ' '; } a href => "#", class => 'more', 'more...' if $i > 3; @@ -272,7 +287,7 @@ sub browsesearch { } -sub browsesys { +sub pkg_list { my($self, $short) = @_; my $sys = $self->{sysbyshort}{$short}; @@ -284,7 +299,7 @@ sub browsesys { ); return $self->resNotFound if $f->{_err}; - my $pkg = $self->dbPackageList( + my $pkg = $self->dbPackageGet( hasman => 1, sysid => $sys->{id}, char => $f->{c} eq 'all' ? undef : $f->{c}, @@ -299,7 +314,7 @@ sub browsesys { use utf8; if($more) { p class => 'pagination'; - a href => "/browse/$short?c=$f->{c};s=$pkg->[199]{name}", 'next »'; + a href => "/pkg/$short?c=$f->{c};s=$pkg->[199]{name}", 'next »'; end; } }; @@ -310,7 +325,7 @@ sub browsesys { p id => 'charselect'; for('all', 0, 'a'..'z') { - a href => "/browse/$short?c=$_", $_?uc$_:'#' if $_ ne $f->{c}; + a href => "/pkg/$short?c=$_", $_?uc$_:'#' if $_ ne $f->{c}; b $_?uc$_:'#' if $_ eq $f->{c}; } end; @@ -320,7 +335,7 @@ sub browsesys { ul id => 'packages'; for(@$pkg) { li; - a href => "/browse/$short/$_->{name}", $_->{name}; + a href => "/pkg/$short/$_->{category}/$_->{name}", $_->{name}; i ' '.$_->{category}; end; } @@ -330,14 +345,44 @@ sub browsesys { } -sub browsepkg { - my($self, $short, $name, $ver) = @_; +sub pkg_frompath { + my($self, $sys, $path) = @_; + + # $path should be "$category/$name" or "$category/$name/$version", since + # $category may contain a slash, let's try both options. + + # $category/$name + # e.g. contrib/games/alien + if($path =~ m{^(.+)/([^/]+)$}) { + my($category, $name) = ($1, $2); + my $pkg = $self->dbPackageGet(sysid => $sys, category => $category, name => $name, hasman => 1)->[0]; + return ($pkg, '') if $pkg; + } + + # $category/$name/$version + # e.g. contrib/games/alien/10.2 + if($path =~ m{^(.+)/([^/]+)/([^/]+)$}) { + my($category, $name, $version) = ($1, $2, $3); + my $pkg = $self->dbPackageGet(sysid => $sys, category => $category, name => $name, hasman => 1)->[0]; + return ($pkg, $version) if $pkg; + } + + (undef, ''); +} + + +sub pkg_info { + my($self, $short, $path) = @_; my $sys = $self->{sysbyshort}{$short}; return $self->resNotFound if !$sys; - my $pkgs = $self->dbPackageGet($sys->{id}, $name); - my $sel = $ver ? (grep $_->{version} eq $ver, @$pkgs)[0] : $pkgs->[0]; + my($pkg, $ver) = pkg_frompath($self, $sys->{id}, $path); + return $self->resNotFound if !$pkg; + + my $vers = $self->dbPackageVersions($pkg->{id}); + + my $sel = $ver ? (grep $_->{version} eq $ver, @$vers)[0] : $vers->[0]; return $self->resNotFound if !$sel; my $f = $self->formValidate({ get => 's', required => 0}); @@ -346,33 +391,30 @@ sub browsepkg { my $mans = $self->dbManInfo(package => $sel->{id}, results => 201, start => $f->{s}, sort => 'name'); my $more = @$mans > 200 && pop @$mans; + # Latest version of this package determines last modification date of the page. + $self->setLastMod($vers->[0]{released}); + # TODO: A "previous" link would be nice... my $next = sub { use utf8; if($more) { p class => 'pagination'; - a href => "/browse/$sys->{short}/$name/$sel->{version}?s=$mans->[199]{name}", 'next »'; + a href => "/pkg/$sys->{short}/$pkg->{category}/$pkg->{name}/$sel->{version}?s=$mans->[199]{name}", 'next »'; end; } }; - # Latest version of this package determines last modification date of the page. - $self->setLastMod($pkgs->[0]{released}); - - my $title = "$sys->{name}".($sys->{release}?" $sys->{release}":"")." / $name $sel->{version}"; + my $title = "$sys->{name}".($sys->{release}?" $sys->{release}":"")." / $pkg->{category} / $pkg->{name} $sel->{version}"; $self->htmlHeader(title => $title); h1 $title; - # TODO: Link back to the system browsing page - h2 'Versions'; ul id => 'packages'; - for(@$pkgs) { + for(@$vers) { li; txt "$_->{released} "; - a href => "/browse/$sys->{short}/$name/$_->{version}", $_->{version} if $_ != $sel; + a href => "/pkg/$sys->{short}/$pkg->{category}/$pkg->{name}/$_->{version}", $_->{version} if $_ != $sel; b " $_->{version}" if $_ == $sel; - i " $_->{category}"; end; } end; @@ -405,13 +447,13 @@ sub manjslist { do { my %pkgs; for(@{$sys{$_}}) { - my $pn = "$_->{package}-$_->{version}"; - $pkgs{$pn} = [ $_->{package}, $_->{version}, [], $_->{released} ] if !$pkgs{$pn}; - push @{$pkgs{$pn}[2]}, [ $_->{section}, $_->{locale}, substr $_->{hash}, 0, 8 ]; + my $pn = "$_->{category}-$_->{package}-$_->{version}"; + $pkgs{$pn} = [ $_->{category}, $_->{package}, $_->{version}, [], $_->{released} ] if !$pkgs{$pn}; + push @{$pkgs{$pn}[3]}, [ $_->{section}, $_->{locale}, substr $_->{hash}, 0, 8 ]; } [ grep - delete($_->[3]) && ($_->[2] = [sort { $a->[0] cmp $b->[0] || ($a->[1]||'') cmp ($b->[1]||'') } @{$_->[2]}]), - sort { $a->[0] cmp $b->[0] || $b->[3] cmp $a->[3] } values %pkgs ]; + delete($_->[4]) && ($_->[3] = [sort { $a->[0] cmp $b->[0] || ($a->[1]||'') cmp ($b->[1]||'') } @{$_->[3]}]), + sort { $a->[0] cmp $b->[0] || $a->[1] cmp $b->[1] || $b->[4] cmp $a->[4] } values %pkgs ]; } ], sort { my $x=$self->{sysbyid}{$a}; my $y=$self->{sysbyid}{$b}; $x->{name} cmp $y->{name} or $y->{relorder} <=> $x->{relorder} } keys %sys @@ -650,7 +692,7 @@ sub dbManContent { } -# Options: name, section, shorthash, locale, package, start, results, sort +# Options: name, section, shorthash, package, start, results, sort sub dbManInfo { my $s = shift; my %o = @_; @@ -662,21 +704,21 @@ sub dbManInfo { $o{section} ? ('m.section = ?' => $o{section}) : (), $o{shorthash} ? (q{substring(m.hash from 1 for 4) = decode(?, 'hex')} => $o{shorthash}) : (), $o{hash} ? (q{m.hash = decode(?, 'hex')} => $o{hash}) : (), - $o{locale} ? ('m.locale = ?', $o{locale}) : exists $o{locale} ? ('m.locale IS NULL' => 1) : (), $o{start} ? ('m.name > ?' => $o{start}) : (), ); # TODO: Flags to indicate what to information to fetch return $s->dbAll(q{ - SELECT p.system, p.category, p.name AS package, p.version, p.released, m.name, m.section, m.filename, m.locale, encode(m.hash, 'hex') AS hash - FROM package p - JOIN man m ON m.package = p.id + SELECT p.system, p.category, p.name AS package, pv.version, pv.released, m.name, m.section, m.filename, m.locale, encode(m.hash, 'hex') AS hash + FROM packages p + JOIN package_versions pv ON p.id = pv.package + JOIN man m ON m.package = pv.id !W !s LIMIT ? }, \%where, - $o{sort} ? 'ORDER BY name, locale NULLS FIRST' : '', + $o{sort} ? 'ORDER BY name' : '', $o{results}||10000 ); } @@ -705,29 +747,25 @@ sub dbSystemGet { } -# TODO: Optimize # Options: sysid char hasman start results -sub dbPackageList { +sub dbPackageGet { my $s = shift; my %o = (results => 10, @_); my @where = ( $o{sysid} ? ('system = ?' => $o{sysid}) : (), + $o{category} ? ('category = ?' => $o{category}) : (), + $o{name} ? ('name = ?' => $o{name}) : (), $o{start} ? ('name > ?' => $o{start}) : (), - defined($o{hasman}) ? ('!s EXISTS(SELECT 1 FROM man m WHERE m.package = p.id)' => $o{hasman}?'':'NOT') : (), + # This seems slow, perhaps cache? + defined($o{hasman}) ? ('!s EXISTS(SELECT 1 FROM package_versions pv WHERE pv.package = p.id AND EXISTS(SELECT 1 FROM man m WHERE m.package = pv.id))' => $o{hasman}?'':'NOT') : (), $o{char} ? ( 'LOWER(SUBSTR(name, 1, 1)) = ?' => $o{char} ) : (), defined($o{char}) && !$o{char} ? ( '(ASCII(name) < 97 OR ASCII(name) > 122) AND (ASCII(name) < 65 OR ASCII(name) > 90)' => 1 ) : (), - # Only get the latest version - # TODO: This is more efficient than using, e.g. SELECT DISTINCT to achieve - # the same effect. The downside of this solution is that packages of which - # the latest release does not include man pages are not listed, even if an - # earlier version does have mans. - 'NOT EXISTS(SELECT 1 FROM package p2 WHERE p2.system = p.system AND p2.name = p.name AND p2.released > p.released)' ); return $s->dbAll(q{ - SELECT name, category - FROM package p + SELECT id, system, name, category + FROM packages p !W ORDER BY name LIMIT ?}, @@ -735,18 +773,16 @@ sub dbPackageList { } -# TODO: Optimize? -sub dbPackageGet { - my($s, $sysid, $name) = @_; +sub dbPackageVersions { + my($s, $id) = @_; return $s->dbAll(q{ - SELECT id, category, name, version, released - FROM package p - WHERE system = ? - AND name = ? - AND EXISTS(SELECT 1 FROM man m WHERE m.package = p.id) + SELECT id, version, released + FROM package_versions v + WHERE package = ? + AND EXISTS(SELECT 1 FROM man m WHERE m.package = v.id) ORDER BY released DESC}, - $sysid, $name) + $id) } diff --git a/www/man.js b/www/man.js index cea5e72..ad1ff68 100644 --- a/www/man.js +++ b/www/man.js @@ -346,7 +346,7 @@ function bsDecode(s) { /* Structure of VARS.mans: [ ["System", "Full name", "short", [ - [ "package", "version", [ + [ "category", "package", "version", [ [ "section", "locale"||null ], ... ], @@ -358,6 +358,8 @@ function bsDecode(s) { ], ... ] + + The godawful navigation code desperately needs a rewrite. */ navShowLocales = false; @@ -410,14 +412,14 @@ function navCreate(nav) { function navCreatePkg(nav, view, dd, sys, n) { var pkg = sys[3][n]; - var isold = n > 0 && sys[3][n-1][0] == pkg[0]; - if(isold && !pkg[3]) + var isold = n > 0 && sys[3][n-1][0] == pkg[0] && sys[3][n-1][1] == pkg[1];; + if(isold && !pkg[4]) return false; var mannum = 0; var pdd = tag('dd', null); - for(var i=0; i 0) { - dd.appendChild(tag('dt', tag('a', {href:'/browse/'+sys[2]+'/'+pkg[0]+'/'+pkg[1]}, pkg[0]), - isold || !sys[3][n+1] || sys[3][n+1][0] != pkg[0] ? null : tag('a', - {href:'#', _pkgn: pkg[0], _pkgi:n, 'class':'expand', + dd.appendChild(tag('dt', tag('a', {href:'/pkg/'+sys[2]+'/'+pkg[0]+'/'+pkg[1]+'/'+pkg[2]}, pkg[1]), + isold || !sys[3][n+1] || sys[3][n+1][0] != pkg[0] || sys[3][n+1][1] != pkg[1] ? null : tag('a', + {href:'#', _pkgn: pkg[0]+'-'+pkg[1], _pkgi:n, 'class':'expand', title: 'Show/hide historical versions of this package', onclick: function() { - for(var j=this._pkgi+1; j 1 && VARS.mans[i-2][0] == VARS.mans[i-1][0] ? VARS.mans[i-1][4] : !!a.shift(); for(var i=0; i 0 && VARS.mans[i][3][j-1][0] == VARS.mans[i][3][j][0]) - VARS.mans[i][3][j][3] = j > 1 && VARS.mans[i][3][j-2][0] == VARS.mans[i][3][j-1][0] ? VARS.mans[i][3][j-1][3] : !!a.shift(); + if(j > 0 && VARS.mans[i][3][j-1][0] == VARS.mans[i][3][j][0] && VARS.mans[i][3][j-1][1] == VARS.mans[i][3][j][1]) + VARS.mans[i][3][j][4] = j > 1 && VARS.mans[i][3][j-2][0] == VARS.mans[i][3][j-1][0] && VARS.mans[i][3][j-2][1] == VARS.mans[i][3][j-1][1] ? VARS.mans[i][3][j-1][4] : !!a.shift(); }