+
+
+=head2 get_versions
+
+ get_versions(package=>'foopkg',
+ dist => 'unstable',
+ arch => 'i386',
+ );
+
+Returns a list of the versions of package in the distributions and
+architectures listed. This routine only returns unique values.
+
+=over
+
+=item package -- package to return list of versions
+
+=item dist -- distribution (unstable, stable, testing); can be an
+arrayref
+
+=item arch -- architecture (i386, source, ...); can be an arrayref
+
+=item time -- returns a version=>time hash at which the newest package
+matching this version was uploaded
+
+=item source -- returns source/version instead of just versions
+
+=item no_source_arch -- discards the source architecture when arch is
+not passed. [Used for finding the versions of binary packages only.]
+Defaults to 0, which does not discard the source architecture. (This
+may change in the future, so if you care, please code accordingly.)
+
+=item return_archs -- returns a version=>[archs] hash indicating which
+architectures are at which versions.
+
+=item largest_source_version_only -- if there is more than one source
+version in a particular distribution, discards all versions but the
+largest in that distribution. Defaults to 1, as this used to be the
+way that the Debian archive worked.
+
+=back
+
+When called in scalar context, this function will return hashrefs or
+arrayrefs as appropriate, in list context, it will return paired lists
+or unpaired lists as appropriate.
+
+=cut
+
+our %_versions;
+our %_versions_time;
+
+sub get_versions{
+ my %param = validate_with(params => \@_,
+ spec => {package => {type => SCALAR|ARRAYREF,
+ },
+ dist => {type => SCALAR|ARRAYREF,
+ default => 'unstable',
+ },
+ arch => {type => SCALAR|ARRAYREF,
+ optional => 1,
+ },
+ time => {type => BOOLEAN,
+ default => 0,
+ },
+ source => {type => BOOLEAN,
+ default => 0,
+ },
+ no_source_arch => {type => BOOLEAN,
+ default => 0,
+ },
+ return_archs => {type => BOOLEAN,
+ default => 0,
+ },
+ largest_source_version_only => {type => BOOLEAN,
+ default => 1,
+ },
+ schema => {type => OBJECT,
+ optional => 1,
+ },
+ },
+ );
+ if (defined $param{schema}) {
+ my @src_packages;
+ my @bin_packages;
+ for my $pkg (make_list($param{package})) {
+ if ($pkg =~ /^src:(.+)/) {
+ push @src_packages,
+ $1;
+ } else {
+ push @bin_packages,$pkg;
+ }
+ }
+
+ my $s = $param{schema};
+ my %return;
+ if (@src_packages) {
+ my $src_rs = $s->resultset('SrcVer')->
+ search({'src_pkg.pkg'=>[@src_packages],
+ -or => {'suite.codename' => [make_list($param{dist})],
+ 'suite.suite_name' => [make_list($param{dist})],
+ }
+ },
+ {join => ['src_pkg',
+ {
+ src_associations=>'suite'},
+ ],
+ '+select' => [qw(src_pkg.pkg),
+ qw(suite.codename),
+ qw(src_associations.modified),
+ q(CONCAT(src_pkg.pkg,'/',me.ver))],
+ '+as' => ['src_pkg_name','codename',
+ 'modified_time',
+ qw(src_pkg_ver)],
+ result_class => 'DBIx::Class::ResultClass::HashRefInflator',
+ order_by => {-desc => 'me.ver'},
+ },
+ );
+ my %completed_dists;
+ for my $src ($src_rs->all()) {
+ my $val = 'source';
+ if ($param{time}) {
+ $val = DateTime::Format::Pg->
+ parse_datetime($src->{modified_time})->
+ epoch();
+ }
+ if ($param{largest_source_version_only}) {
+ next if $completed_dists{$src->{codename}};
+ $completed_dists{$src->{codename}} = 1;
+ }
+ if ($param{source}) {
+ $return{$src->{src_pkg_ver}} = $val;
+ } else {
+ $return{$src->{ver}} = $val;
+ }
+ }
+ }
+ if (@bin_packages) {
+ my $bin_rs = $s->resultset('BinVer')->
+ search({'bin_pkg.pkg' => [@bin_packages],
+ -or => {'suite.codename' => [make_list($param{dist})],
+ 'suite.suite_name' => [make_list($param{dist})],
+ },
+ },
+ {join => ['bin_pkg',
+ {
+ 'src_ver'=>'src_pkg'},
+ {
+ bin_associations => 'suite'},
+ 'arch',
+ ],
+ '+select' => [qw(bin_pkg.pkg arch.arch suite.codename),
+ qw(bin_associations.modified),
+ qw(src_pkg.pkg),q(CONCAT(src_pkg.pkg,'/',me.ver)),
+ ],
+ '+as' => ['bin_pkg','arch','codename',
+ 'modified_time',
+ 'src_pkg_name','src_pkg_ver'],
+ result_class => 'DBIx::Class::ResultClass::HashRefInflator',
+ order_by => {-desc => 'src_ver.ver'},
+ });
+ if (exists $param{arch}) {
+ $bin_rs =
+ $bin_rs->search({'arch.arch' => [make_list($param{arch})]},
+ {
+ join => 'arch'}
+ );
+ }
+ my %completed_dists;
+ for my $bin ($bin_rs->all()) {
+ my $key = $bin->{ver};
+ if ($param{source}) {
+ $key = $bin->{src_pkg_ver};
+ }
+ my $val = $bin->{arch};
+ if ($param{time}) {
+ $val = DateTime::Format::Pg->
+ parse_datetime($bin->{modified_time})->
+ epoch();
+ }
+ if ($param{largest_source_version_only}) {
+ if ($completed_dists{$bin->{codename}} and not
+ exists $return{$key}) {
+ next;
+ }
+ $completed_dists{$bin->{codename}} = 1;
+ }
+ push @{$return{$key}},
+ $val;
+ }
+ }
+ if ($param{return_archs}) {
+ if ($param{time} or $param{return_archs}) {
+ return wantarray?%return :\%return;
+ }
+ return wantarray?keys %return :[keys %return];
+ }
+ }
+ my $versions;
+ if ($param{time}) {
+ return () if not defined $gVersionTimeIndex;
+ unless (tied %_versions_time) {
+ tie %_versions_time, 'MLDBM', $gVersionTimeIndex, O_RDONLY
+ or die "can't open versions index $gVersionTimeIndex: $!";
+ }
+ $versions = \%_versions_time;
+ }
+ else {
+ return () if not defined $gVersionIndex;
+ unless (tied %_versions) {
+ tie %_versions, 'MLDBM', $gVersionIndex, O_RDONLY
+ or die "can't open versions index $gVersionIndex: $!";
+ }
+ $versions = \%_versions;
+ }
+ my %versions;
+ for my $package (make_list($param{package})) {
+ my $source_only = 0;
+ if ($package =~ s/^src://) {
+ $source_only = 1;
+ }
+ my $version = $versions->{$package};
+ next unless defined $version;
+ for my $dist (make_list($param{dist})) {
+ for my $arch (exists $param{arch}?
+ make_list($param{arch}):
+ (grep {not $param{no_source_arch} or
+ $_ ne 'source'
+ } $source_only?'source':keys %{$version->{$dist}})) {
+ next unless defined $version->{$dist}{$arch};
+ my @vers = ref $version->{$dist}{$arch} eq 'HASH' ?
+ keys %{$version->{$dist}{$arch}} :
+ make_list($version->{$dist}{$arch});
+ if ($param{largest_source_version_only} and
+ $arch eq 'source' and @vers > 1) {
+ # order the versions, then pick the biggest version number
+ @vers = sort_versions(@vers);
+ @vers = $vers[-1];
+ }
+ for my $ver (@vers) {
+ my $f_ver = $ver;
+ if ($param{source}) {
+ ($f_ver) = make_source_versions(package => $package,
+ arch => $arch,
+ versions => $ver);
+ next unless defined $f_ver;
+ }
+ if ($param{time}) {
+ $versions{$f_ver} = max($versions{$f_ver}||0,$version->{$dist}{$arch}{$ver});
+ }
+ else {
+ push @{$versions{$f_ver}},$arch;
+ }
+ }
+ }
+ }
+ }
+ if ($param{time} or $param{return_archs}) {
+ return wantarray?%versions :\%versions;
+ }
+ return wantarray?keys %versions :[keys %versions];