]> git.donarmstrong.com Git - wannabuild.git/blobdiff - bin/wanna-build
Also parse the second packages file to clear dep-waits.
[wannabuild.git] / bin / wanna-build
index cd04f18376e27af936085924204a5536e2d6adf9..636305ebf3e8dc59025291b4393a5c5d2a3d5176 100755 (executable)
@@ -24,9 +24,8 @@ package conf;
 $basedir ||= "/var/lib/debbuild";
 $dbbase ||= "build-db";
 $transactlog ||= "transactions.log";
-#$mailprog ||= "/usr/sbin/sendmail";
-$mailprog = "/bin/true";
-require "/etc/wanna-build.conf";
+$mailprog ||= "/usr/sbin/sendmail";
+require "/org/wanna-build/etc/wanna-build.conf";
 die "$conf::basedir is not a directory\n" if ! -d $conf::basedir;
 die "dbbase is empty\n" if ! $dbbase;
 die "transactlog is empty\n" if ! $transactlog;
@@ -40,11 +39,12 @@ use POSIX;
 use FileHandle;
 use File::Copy;
 use DBI;
+use lib '/org/wanna-build/bin';
 use WannaBuild;
 
 our ($verbose, $mail_logs, $list_order, $list_state,
     $curr_date, $op_mode, $user, $real_user, $distribution,
-    $fail_reason, $opt_override, $import_from, $opt_create_db,
+    $fail_reason, $opt_override, $import_from, $export_to, $opt_create_db,
     %prioval, %sectval,
     $info_all_dists, $arch,
     $category, %catval, %short_category,
@@ -118,6 +118,7 @@ my %options =
         "merge-sources" => { mode => "merge-sources" },
         "pretend-avail" => { short => "p", mode => "pretend-avail" },
         "merge-all"     => { mode => "merge-all" },
+        "merge-all-overlay" => { mode => "merge-all-overlay" },
         info                   => { short => "i", mode => "info" },
         'binNMU' => { mode => 'set-binary-nmu', arg => \$binNMUver, 
                             code => sub { die "Invalid binNMU version: $binNMUver\n"
@@ -173,15 +174,15 @@ my %options =
                                                 code => sub {
                                                         die "Argument of --min-age must be a non-zero number\n"
                                                                 if $list_min_age == 0;
-                                                        $list_min_age *= 24*60*60;
                                                 } },
         "max-age"      => { arg => \$list_min_age,
                                                 code => sub {
                                                         die "Argument of --max-age must be a non-zero number\n"
                                                                 if $list_min_age == 0;
-                                                        $list_min_age *= -24*60*60;
+                                                        $list_min_age *= -1;
                                                 } },
         # special actions
+        export         => { arg => \$export_to, mode => "export" },
         import         => { arg => \$import_from, mode => "import" },
         "manual-edit"  => { mode => "manual-edit" },
         );
@@ -251,7 +252,7 @@ if ($verbose) {
        print "wanna-build $version for $distribution on $arch\n";
 }
 
-if (!@ARGV && !isin( $op_mode, qw(list merge-quinn merge-partial-quinn import 
+if (!@ARGV && !isin( $op_mode, qw(list merge-quinn merge-partial-quinn import export
                                  merge-packages manual-edit
                                  merge-sources))) {
        warn "No packages given.\n";
@@ -302,8 +303,17 @@ END {
        }
 }
 
-$dbh = DBI->connect("DBI:Pg:database=wanna-build") || 
-       die "FATAL: Cannot open database: $DBI::errstr\n";
+my $schema_suffix = '';
+if (isin( $op_mode, qw(list info)) && $distribution !~ /security/ && !(not -t and $user =~ /-/)) {
+       $dbh = DBI->connect("DBI:Pg:service=wanna-build") || 
+               die "FATAL: Cannot open database: $DBI::errstr\n";
+       $schema_suffix = '_public';
+}
+else
+{
+       $dbh = DBI->connect("DBI:Pg:service=wanna-build-privileged") || 
+               die "FATAL: Cannot open database: $DBI::errstr\n";
+}
 
 # TODO: This shouldn't be needed, file a bug.
 $dbh->{pg_server_prepare} = 0;
@@ -355,6 +365,7 @@ sub process {
                        die "This operation is restricted to admin users\n"
                                if (defined @conf::admin_users and
                                    !isin( $real_user, @conf::admin_users));
+                       lock_table();
                        parse_quinn_diff(1);
                        last SWITCH;
                };
@@ -362,6 +373,7 @@ sub process {
                        die "This operation is restricted to admin users\n"
                                if (defined @conf::admin_users and
                                    !isin( $real_user, @conf::admin_users));
+                       lock_table();
                        parse_quinn_diff(0);
                        last SWITCH;
                };
@@ -369,13 +381,15 @@ sub process {
                        die "This operation is restricted to admin users\n"
                                if (defined @conf::admin_users and
                                    !isin( $real_user, @conf::admin_users));
-                       parse_packages();
+                       lock_table();
+                       parse_packages(0);
                        last SWITCH;
                };
                /^merge-sources/ && do {
                        die "This operation is restricted to admin users\n"
                                if (defined @conf::admin_users and
                                    !isin( $real_user, @conf::admin_users));
+                       lock_table();
                        parse_sources(0);
                        last SWITCH;
                };
@@ -383,13 +397,14 @@ sub process {
                        pretend_avail( @ARGV );
                        last SWITCH;
                };
-               /^merge-all/ && do {
+               /^merge-all$/ && do {
                        die "This operation is restricted to admin users\n"
                                if (defined @conf::admin_users and
                                    !isin( $real_user, @conf::admin_users));
+                       lock_table();
                        my @ARGS = @ARGV;
                        @ARGV = ( $ARGS[0] );
-                       my $pkgs = parse_packages();
+                       my $pkgs = parse_packages(0);
                        @ARGV = ( $ARGS[1] );
                        parse_quinn_diff(0);
                        @ARGV = ( $ARGS[2] );
@@ -397,16 +412,39 @@ sub process {
                        call_edos_depcheck( $ARGS[0], $srcs );
                        last SWITCH;
                };
+               /^merge-all-overlay/ && do {
+                       die "This operation is restricted to admin users\n"
+                               if (defined @conf::admin_users and
+                                   !isin( $real_user, @conf::admin_users));
+                       lock_table();
+                       my @ARGS = @ARGV;
+                       @ARGV = ( $ARGS[0] );
+                       my $pkgs = parse_packages(0);
+                       @ARGV = ( $ARGS[3] );
+                       my $pkgs = parse_packages(1);
+                       @ARGV = ( $ARGS[1] );
+                       parse_quinn_diff(0);
+                       @ARGV = ( $ARGS[2] );
+                       my $srcs = parse_sources(1);
+                       call_edos_depcheck( $ARGS[3], $srcs );
+                       last SWITCH;
+               };
                /^import/ && do {
                        die "This operation is restricted to admin users\n"
                                if (defined @conf::admin_users and
                                    !isin( $real_user, @conf::admin_users));
-                       $dbh->do("DELETE from " . table_name())
+                       $dbh->do("DELETE from " . table_name() . 
+                               " WHERE distribution = ?", undef,
+                               $distribution)
                                or die $dbh->errstr;
                        forget_users();
                        read_db( $import_from );
                        last SWITCH;
                };
+               /^export/ && do {
+                       export_db( $export_to );
+                       last SWITCH;
+               };
 
                die "Unexpected operation mode $op_mode\n";
        }
@@ -598,7 +636,7 @@ sub add_one_building {
                my $ok = 'ok';
                if ($pkg->{'binary_nmu_version'}) {
                        print "$name: Warning: needs binary NMU $pkg->{'binary_nmu_version'}\n" .
-                             "$pkg->{'Binary-NMU-Changelog'}\n";
+                             "$pkg->{'binary_nmu_changelog'}\n";
                        $ok = 'aok';
                } else {
                        print "$name: Warning: Previous version failed!\n"
@@ -857,7 +895,7 @@ sub add_one_notforus {
                delete $pkg->{'depends'};
                delete $pkg->{'buildpri'};
                delete $pkg->{'binary_nmu_version'};
-               delete $pkg->{'Binary-NMU-Changelog'};
+               delete $pkg->{'binary_nmu_changelog'};
                log_ta( $pkg, "--no-build" );
                print "$name: registered as unsuitable\n" if $verbose;
 
@@ -926,7 +964,8 @@ sub add_one_needsbuild {
                }
        }
        if (defined ($pkg->{'builder'}) && $user ne $pkg->{'builder'} &&
-                   !($pkg->{'builder'} =~ /^(\w+)-\w+/ && $1 eq $user)) {
+               !($pkg->{'builder'} =~ /^(\w+)-\w+/ && $1 eq $user) &&
+               !$opt_override) {
                print "$name: not taken by you, but by ".
                          "$pkg->{'builder'}. Skipping.\n";
                return;
@@ -936,10 +975,14 @@ sub add_one_needsbuild {
                          "Skipping.\n";
                return;
        }
-       change_state( \$pkg, 'BD-Uninstallable' );
-       $pkg->{'bd_problem'} = "Installability of build dependencies not tested yet";
-       delete $pkg->{'builder'};
-       delete $pkg->{'depends'};
+       if ($distribution eq "unstable") {
+               change_state( \$pkg, 'BD-Uninstallable' );
+               $pkg->{'bd_problem'} = "Installability of build dependencies not tested yet";
+       } else {
+               change_state( \$pkg, 'Needs-Build' );
+       }
+       $pkg->{'builder'} = undef;
+       $pkg->{'depends'} = undef;
        log_ta( $pkg, "--give-back" );
        update_source_info($pkg);
        print "$name: given back\n" if $verbose;
@@ -970,13 +1013,13 @@ sub set_one_binnmu {
                        delete $pkg->{'builder'};
                        delete $pkg->{'depends'};
                        delete $pkg->{'binary_nmu_version'};
-                       delete $pkg->{'Binary-NMU-Changelog'};
+                       delete $pkg->{'binary_nmu_changelog'};
                } elsif ($binNMUver <= $pkg->{'binary_nmu_version'}) {
                        print "$name: already building binNMU $pkg->{'binary_nmu_version'}\n";
                        return;
                } else {
                        $pkg->{'binary_nmu_version'} = $binNMUver;
-                       $pkg->{'Binary-NMU-Changelog'} = $fail_reason;
+                       $pkg->{'binary_nmu_changelog'} = $fail_reason;
                        $pkg->{'notes'} = 'out-of-date';
                        $pkg->{'buildpri'} = $pkg->{'permbuildpri'}
                                if (defined $pkg->{'permbuildpri'});
@@ -1001,12 +1044,18 @@ sub set_one_binnmu {
                return;
        }
 
-       change_state( \$pkg, 'BD-Uninstallable' );
-       $pkg->{'bd_problem'} = "Installability of build dependencies not tested yet";
+       if ($distribution eq "unstable") {
+               change_state( \$pkg, 'BD-Uninstallable' );
+               $pkg->{'bd_problem'} = "Installability of build dependencies not tested yet";
+       }
+       else
+       {
+               change_state( \$pkg, 'Needs-Build' );
+       }
        delete $pkg->{'builder'};
        delete $pkg->{'depends'};
        $pkg->{'binary_nmu_version'} = $binNMUver;
-       $pkg->{'Binary-NMU-Changelog'} = $fail_reason;
+       $pkg->{'binary_nmu_changelog'} = $fail_reason;
        $pkg->{'notes'} = 'out-of-date';
        log_ta( $pkg, "--binNMU" );
        update_source_info($pkg);
@@ -1131,6 +1180,8 @@ sub parse_sources {
        my $name;
        my $full = shift;
 
+       my $db = get_all_source_info();
+
        local($/) = ""; # read in paragraph mode
        while( <> ) {
                my( $version, $arch, $section, $priority, $builddep, $buildconf, $binaries );
@@ -1151,7 +1202,7 @@ sub parse_sources {
                $pkgs{$name}{'bin'} = $binaries;
                $pkgs{$name}{'dep'} = $builddep;
                $pkgs{$name}{'conf'} = $buildconf;
-               my $pkg = get_source_info($name);
+               my $pkg = $db->{$name};
 
                if (defined $pkg) {
                        my $change = 0;
@@ -1164,6 +1215,7 @@ sub parse_sources {
                                          "from database, because now Arch: all\n"
                                                  if $verbose;
                                del_source_info($name);
+                               delete $db->{$name};
                                next;
                        }
 
@@ -1191,7 +1243,6 @@ sub parse_sources {
         }
        # remove installed packages that no longer have source available
        # or binaries installed
-        my $db = get_all_source_info();
         foreach $name (keys %$db) {
                next if $name =~ /^_/;
                my $pkg = $db->{$name};
@@ -1203,6 +1254,7 @@ sub parse_sources {
                                  "not in Sources anymore\n"
                                          if $verbose;
                        del_source_info($name);
+                       delete $db->{$name};
                } else {
                        next if !isin( $pkg->{'state'}, qw(Installed) );
                        if ($full && not defined $merge_srcvers{$name}) {
@@ -1213,6 +1265,7 @@ sub parse_sources {
                                       "binaries don't exist anymore\n"
                                               if $verbose;
                            del_source_info($name);
+                           delete $db->{$name};
                         } elsif ($full && version_less( $merge_srcvers{$name}, $pkg->{'version'})) {
                             print "$name ($pkg->{'version'}): ".
                                       "package is Installed but binaries are from ".
@@ -1227,8 +1280,10 @@ sub parse_sources {
 # This function looks through a Packages file and sets the state of
 # packages to 'Installed'
 sub parse_packages {
+       my depwait_only = shift;
        my $installed;
 
+       my $pkgs = get_all_source_info();
        local($/) = ""; # read in paragraph mode
        while( <> ) {
                my( $name, $version, $depends, $source, $sourcev, $architecture, $provides, $binaryv, $binnmu );
@@ -1244,6 +1299,7 @@ sub parse_packages {
                next if (defined ($installed->{$name}) and $installed->{$name}{'version'} ne "" and
                        version_lesseq( $version, $installed->{$name}{'version'} ));
                $installed->{$name}{'version'} = $version;
+               next if $depwait_only;
                $installed->{$name}{'depends'} = $depends;
                $installed->{$name}{'all'} = 1 if $architecture eq "all";
                undef $installed->{$name}{'Provider'};
@@ -1270,7 +1326,7 @@ sub parse_packages {
                next if defined($merge_srcvers{$name}) and $merge_srcvers{$name} eq $version;
                $merge_srcvers{$name} = $version;
 
-               my $pkg = get_source_info($name);
+               my $pkg = $pkgs->{$name};
 
                if (defined $pkg) {
                        if (isin( $pkg->{'state'}, qw(Not-For-Us)) ||
@@ -1324,7 +1380,7 @@ sub parse_packages {
                $pkg->{'version'} = $version
                        if version_less( $pkg->{'version'}, $version);
                delete $pkg->{'binary_nmu_version'};
-               delete $pkg->{'Binary-NMU-Changelog'};
+               delete $pkg->{'binary_nmu_changelog'};
                log_ta( $pkg, "--merge-packages" );
                update_source_info($pkg);
                print "$name ($version) is up-to-date now.\n" if $verbose;
@@ -1419,6 +1475,8 @@ sub parse_quinn_diff {
        my %quinn_pkgs;
        my $dubious = "";
        
+       my $pkgs = get_all_source_info();
+
        while( <> ) {
                my $change = 0;
                next if !m,^([-\w\d/]*)/                        # section
@@ -1433,7 +1491,7 @@ sub parse_quinn_diff {
                $priority = "unknown" if $priority eq "-";
                $priority = "standard" if ($name eq "debian-installer");
 
-               my $pkg = get_source_info($name);
+               my $pkg = $pkgs->{$name};
 
                # Always update section and priority.
                if (defined($pkg)) {
@@ -1450,7 +1508,7 @@ sub parse_quinn_diff {
                        change_state( \$pkg, 'Dep-Wait' );
                        $pkg->{'version'}  = $version;
                        delete $pkg->{'binary_nmu_version'};
-                       delete $pkg->{'Binary-NMU-Changelog'};
+                       delete $pkg->{'binary_nmu_changelog'};
                        log_ta( $pkg, "--merge-quinn" );
                        $change++;
                        print "$name ($version) still waiting for dependencies.\n"
@@ -1515,7 +1573,7 @@ sub parse_quinn_diff {
                        $pkg->{'notes'}    = $notes;
                        delete $pkg->{'builder'};
                        delete $pkg->{'binary_nmu_version'};
-                       delete $pkg->{'Binary-NMU-Changelog'};
+                       delete $pkg->{'binary_nmu_changelog'};
                        log_ta( $pkg, "--merge-quinn" );
                        $new_vers{$name}++;
                        $change++;
@@ -1549,7 +1607,8 @@ sub parse_quinn_diff {
 
        if ($dubious) {
                send_mail( $conf::db_maint,
-                                  "Dubious versions in " . table_name() . " table",
+                                  "Dubious versions in " . table_name() . " "
+                                  . $distribution . " table",
                                   "The following packages have a newer version in the ".
                                   "wanna-build database\n".
                                   "than what quinn-diff says, and this is strange for ".
@@ -1743,19 +1802,10 @@ sub list_packages {
        my %scnt;
        my $ctime = time;
 
-       my $db = get_all_source_info();
+       my $db = get_all_source_info(state => $state, user => $user, category => $category, list_min_age => $list_min_age);
        foreach $name (keys %$db) {
                next if $name =~ /^_/;
-               $pkg = $db->{$name};
-               next if $state ne "all" && $pkg->{'state'} !~ /^\Q$state\E$/i;
-               next if $user && (lc($state) ne 'needs-build' and $pkg->{'builder'} ne $user);
-               next if $category && $pkg->{'state'} eq "Failed" &&
-                               $pkg->{'failed_category'} ne $category;
-               next if ($list_min_age > 0 &&
-                                ($ctime-parse_date($pkg->{'State-Change'})) < $list_min_age)||
-                               ($list_min_age < 0 &&
-                                ($ctime-parse_date($pkg->{'State-Change'})) > -$list_min_age);
-               push( @list, $pkg );
+               push @list, $db->{$name};
        }
 
        foreach $pkg (sort sort_list_func @list) {
@@ -1784,7 +1834,7 @@ sub list_packages {
                          join("\n    ",split("\n",$pkg->{'bd_problem'})), "\n"
                        if $pkg->{'state'} eq "BD-Uninstallable";
                print "  Previous state was $pkg->{'previous_state'} until ",
-                         "$pkg->{'State-Change'}\n"
+                         "$pkg->{'state_change'}\n"
                        if $verbose && $pkg->{'previous_state'};
                print "  No previous state recorded\n"
                        if $verbose && !$pkg->{'previous_state'};
@@ -1814,7 +1864,7 @@ sub info_packages {
                foreach $dist (@dists) {
                        my $pname = "$name" . ($info_all_dists ? "($dist)" : "");
                        
-                       $pkg = get_source_info($name);
+                       $pkg = get_readonly_source_info($name);
                        if (!defined( $pkg )) {
                                print "$pname: not registered\n";
                                next;
@@ -1827,15 +1877,39 @@ sub info_packages {
                                chomp( $val );
                                $val = "\n$val" if isin( $key, qw(Failed Old-Failed));
                                $val =~ s/\n/\n    /g;
-                               printf "  %-20s: %s\n", $key, $val;
+                               my $print_key = $key;
+                               $print_key = 'Package' if ($key eq 'package');
+                               $print_key = 'Version' if ($key eq 'version');
+                               $print_key = 'Builder' if ($key eq 'builder');
+                               $print_key = 'State' if ($key eq 'state');
+                               $print_key = 'Section' if ($key eq 'section');
+                               $print_key = 'Priority' if ($key eq 'priority');
+                               $print_key = 'Installed-Version' if ($key eq 'installed_version');
+                               $print_key = 'Previous-State' if ($key eq 'previous_state');
+                               $print_key = 'State-Change' if ($key eq 'state_change');
+                               printf "  %-20s: %s\n", $print_key, $val;
                        }
                        foreach $key (sort keys %$pkg) {
                                next if isin( $key, @firstkeys );
                                my $val = $pkg->{$key};
+                               next if !defined($val);
                                chomp( $val );
                                $val = "\n$val" if isin( $key, qw(Failed Old-Failed));
                                $val =~ s/\n/\n    /g;
-                               printf "  %-20s: %s\n", $key, $val;
+                               my $print_key = $key;
+                               $print_key = 'BD-Problem' if ($key eq 'bd_problem');
+                               $print_key = 'Binary-NMU-Changelog' if ($key eq 'binary_nmu_changelog');
+                               $print_key = 'Binary-NMU-Version' if ($key eq 'binary_nmu_version');
+                               $print_key = 'BuildPri' if ($key eq 'buildpri');
+                               $print_key = 'Depends' if ($key eq 'depends');
+                               $print_key = 'Failed' if ($key eq 'failed');
+                               $print_key = 'Failed-Category' if ($key eq 'failed_category');
+                               $print_key = 'Notes' if ($key eq 'notes');
+                               $print_key = 'Distribution' if ($key eq 'distribution');
+                               $print_key = 'Old-Failed' if ($key eq 'old_failed');
+                               $print_key = 'PermBuildPri' if ($key eq 'permbuildpri');
+                               $print_key = 'Rel' if ($key eq 'rel');
+                               printf "  %-20s: %s\n", $print_key, $val;
                        }
                }
        }
@@ -1860,7 +1934,7 @@ sub forget_packages {
                        $data .= sprintf "  %-20s: %s\n", $key, $val;
                }
                send_mail( $conf::db_maint,
-                                  "$name deleted from DB " . table_name(),
+                                  "$name deleted from DB " . table_name() . " " . $distribution,
                                   "The package '$name' has been deleted from the database ".
                                   "by $user.\n\n".
                                   "Data registered about the deleted package:\n".
@@ -1873,7 +1947,8 @@ sub forget_packages {
 }
 
 sub forget_users {
-       $dbh->do("DELETE from " . user_table_name()) or die $dbh->errstr;
+       $dbh->do("DELETE from " . user_table_name() . 
+               " WHERE distribution = ?", undef, $distribution) or die $dbh->errstr;
 }
 
 sub read_db {
@@ -1905,8 +1980,10 @@ sub read_db {
                elsif(exists($thispkg{'user'})) {
                        # user in import, username in database.
                        $dbh->do('INSERT INTO ' . user_table_name() .
-                                       ' (username, last_seen) values (?, ?)',
-                               undef, $thispkg{'user'}, $thispkg{'last_seen'})
+                                       ' (username, distribution, last_seen)' .
+                                       ' values (?, ?, ?)',
+                               undef, $thispkg{'user'}, $distribution,
+                               $thispkg{'last_seen'})
                                or die $dbh->errstr;
                 }
        }
@@ -1929,22 +2006,48 @@ sub check_entry {
                          join( "\n", map { "$_: $pkg->{$_}" } keys %$pkg ), "\n";
                die "Database entry lacks package or username field\n";
        }
-       if (!exists $pkg->{'version'}) {
-               die "Database entry for $pkg->{'package'} lacks Version: field\n";
-       }
        # if no State: field, generate one (for old db compat)
        if (!exists($pkg->{'state'})) {
                $pkg->{'state'} =
                        exists $pkg->{'failed'} ? 'Failed' : 'Building';
        }
+       if (!exists $pkg->{'version'} and $pkg->{'state'} ne 'Not-For-Us') {
+               die "Database entry for $pkg->{'package'} lacks Version: field\n";
+       }
        # check state field
        die "Bad state $pkg->{'state'} of package $pkg->{Package}\n"
                if !isin( $pkg->{'state'},
-                                 qw(Needs-Build Building Built Build-Attempted Uploaded Installed Dep-Wait
+                                 qw(Needs-Build Building Built Build-Attempted Uploaded Installed Dep-Wait Dep-Wait-Removed
                                         Failed Failed-Removed Not-For-Us BD-Uninstallable
                                         ) );
 }
 
+sub export_db {
+       my $file = shift;
+       my($name,$pkg,$key);
+
+       print "Writing ASCII database to $file..." if $verbose >= 1;
+       open( F, ">$file" ) or
+               die "Can't open export $file: $!\n";
+
+        my $db = get_all_source_info();
+        foreach $name (keys %$db) {
+               next if $name =~ /^_/;
+               my $pkg = $db->{$name};
+               foreach $key (keys %{$pkg}) {
+                       my $val = $pkg->{$key};
+                       next if !defined($val);
+                       $val =~ s/\n*$//;
+                       $val =~ s/^/ /mg;
+                       $val =~ s/^ +$/ ./mg;
+                       print F "$key: $val\n";
+               }
+               print F "\n";
+       }
+       close( F );
+       print "done\n" if $verbose >= 1;
+}
+
 sub change_state {
        my $pkgr = shift;
        my $pkg = $$pkgr;
@@ -1953,7 +2056,7 @@ sub change_state {
        
        return if defined($$state) and $$state eq $newstate;
         $pkg->{'previous_state'} = $$state if defined($$state);
-       $pkg->{'State-Change'} = $curr_date;
+       $pkg->{'state_change'} = $curr_date;
 
        if (defined($$state) and $$state eq 'Failed') {
                $pkg->{'old_failed'} =
@@ -1981,13 +2084,13 @@ sub log_ta {
                   "changed from $prevstate to $pkg->{'state'} ".
                   "by $real_user as $user";
        
-       my $transactlog = db_transactlog();
-       if (!open( LOG, ">>$transactlog" )) {
-               warn "Can't open log file $transactlog: $!\n";
-               return;
-       }
-       print LOG "$curr_date: $str\n";
-       close( LOG );
+       $dbh->do('INSERT INTO ' . transactions_table_name() .
+                       ' (package, distribution, version, action, ' .
+                       ' prevstate, state, real_user, set_user, time) ' .
+                       ' values (?, ?, ?, ?, ?, ?, ?, ?, ?)',
+               undef, $pkg->{'package'}, $distribution,
+               $pkg->{'version'}, $action, $prevstate, $pkg->{'state'},
+               $real_user, $user, 'now()') or die $dbh->errstr;
 
        if (!($prevstate eq 'Failed' && $pkg->{'state'} eq 'Failed')) {
                $str .= " (with --override)"
@@ -2019,10 +2122,6 @@ sub send_mail {
        close( PIPE );
 }
 
-sub db_transactlog {
-       return "$conf::basedir/$arch/$conf::transactlog";
-}
-
 # for parsing input to dep-wait
 sub parse_deplist {
     my $deps = shift;
@@ -2059,59 +2158,6 @@ sub parse_deplist {
     return \%result;
 }
 
-# for parsing Build-Depends from Sources
-sub parse_srcdeplist {
-    my $pkg = shift;
-    my $deps = shift;
-    my $arch = shift;
-    my $dep;
-    my @results;
-    
-    foreach $dep (split( /\s*,\s*/, $deps )) {
-       my @alts = split( /\s*\|\s*/, $dep );
-        # Anything with an | is ignored, as it can be configured on a
-        # per-buildd basis what will be installed
-        next if $#alts != 0;
-       $_ = shift @alts;
-        if (!/^([^\s([]+)\s*(\(\s*([<=>]+)\s*(\S+)\s*\))?(\s*\[([^]]+)\])?/) {
-            warn( "parse_srcdeplist: bad dependency $_\n" );
-            next;
-        }
-        my($dep, $rel, $relv, $archlist) = ($1, $3, $4, $6);
-        if ($archlist) {
-            $archlist =~ s/^\s*(.*)\s*$/$1/;
-            my @archs = split( /\s+/, $archlist );
-            my ($use_it, $ignore_it, $include) = (0, 0, 0);
-            foreach (@archs) {
-                if (/^!/) {
-                    $ignore_it = 1 if substr($_, 1) eq $arch;
-                } else {
-                    $use_it = 1 if $_ eq $arch;
-                    $include = 1;
-                }
-            }
-            warn "Warning: inconsistent arch restriction on ",
-                 "$pkg: $dep depedency\n"
-                 if $ignore_it && $use_it;
-            next if $ignore_it || ($include && !$use_it);
-        }
-        my $neg = 0;
-        if ($dep =~ /^!/) {
-            $dep =~ s/^!\s*//;
-            $neg = 1;
-        }
-        my $result;
-        $result->{'package'} = $dep;
-        $result->{'Neg'} = $neg;
-        if ($rel && $relv) {
-            $result->{'rel'} = $rel;
-            $result->{'version'} = $relv;
-        }
-        push @results, $result;
-    }
-    return \@results;
-}
-
 sub build_deplist {
        my $list = shift;
        my($key, $result);
@@ -2125,83 +2171,6 @@ sub build_deplist {
        return $result;
 }
 
-sub get_unsatisfied_dep {
-    my $bd  = shift;
-    my $pkgs = shift;
-    my $dep = shift;
-    my $savedep = shift;
-
-    my $pkgname = $dep->{'package'};
-
-    if (defined $pkgs->{$pkgname}{'Provider'}) {
-        # provides.  leave them for buildd/sbuild.
-        return "";
-    }
-
-    # check cache
-    return $pkgs->{$pkgname}{'Unsatisfied'} if $savedep and defined($pkgs->{$pkgname}{'Unsatisfied'});
-
-    # Return unsatisfied deps to a higher caller to process
-    if ((!defined($pkgs->{$pkgname})) or
-        (defined($dep->{'rel'}) and !version_compare( $pkgs->{$pkgname}{'version'}, $dep->{'rel'}, $dep->{'Version'} ) ) ) {
-        my %deplist;
-        $deplist{$pkgname} = $dep;
-        my $deps = build_deplist(\%deplist);
-        $pkgs->{$pkgname}{'Unsatisfied'} = $deps if $savedep;
-        return $deps;
-    }
-
-    # set cache to "" to avoid infinite recursion
-    $pkgs->{$pkgname}{'Unsatisfied'} = "" if $savedep;
-
-    if (defined $pkgs->{$dep->{'package'}}{'depends'}) {
-        my $deps = parse_deplist( $pkgs->{$dep->{'package'}}{'depends'} );
-        foreach (keys %$deps) {
-            $dep = $$deps{$_};
-            # recur on dep.
-            my $ret = get_unsatisfied_dep($bd,$pkgs,$dep,1);
-            if ($ret ne "") {
-                my $retdep = parse_deplist( $ret );
-                foreach (keys %$retdep) {
-                    $dep = $$retdep{$_};
-
-                    $dep->{'rel'} = '>=' if defined($dep->{'rel'}) and $dep->{'rel'} =~ '^=';
-
-                    if (defined($dep->{'rel'}) and $dep->{'rel'} =~ '^>' and defined ($pkgs->{$dep->{'package'}}) and
-                        version_compare($bd->{$pkgs->{$dep->{'package'}}{'Source'}}{'ver'},'>>',$pkgs->{$dep->{'package'}}{'Sourcev'})) {
-                        if (not defined($merge_binsrc{$dep->{'package'}})) {
-                            # the uninstallable package doesn't exist in the new source; look for something else that does.
-                            delete $$retdep{$dep->{'package'}};
-                            foreach (sort (split( /\s*,\s*/, $bd->{$pkgs->{$dep->{'package'}}{'Source'}}{'bin'}))) {
-                                next if ($pkgs->{$_}{'all'} or not defined $pkgs->{$_}{'version'});
-                                $dep->{'package'} = $_;
-                                $dep->{'rel'} = '>>';
-                                $dep->{'version'} = $pkgs->{$_}{'Version'};
-                                $$retdep{$_} = $dep;
-                                last;
-                            }
-                        }
-                    } else {
-                        # sanity check to make sure the depending binary still exists, and the depended binary exists and dep-wait on a new version of it
-                        if ( defined($merge_binsrc{$pkgname}) and defined($pkgs->{$dep->{'package'}}{'version'}) ) {
-                            delete $$retdep{$dep->{'package'}};
-                            $dep->{'package'} = $pkgname;
-                            $dep->{'rel'} = '>>';
-                            $dep->{'version'} = $pkgs->{$pkgname}{'Version'};
-                            $$retdep{$pkgname} = $dep;
-                        }
-                        delete $$retdep{$dep->{'package'}} if (defined ($dep->{'rel'}) and $dep->{'rel'} =~ '^>');
-                    }
-                }
-                $ret = build_deplist($retdep);
-                $pkgs->{$pkgname}{'Unsatisfied'} = $ret if $savedep;
-                return $ret;
-            }
-        }
-    }
-    return "";
-}
-
 sub call_edos_depcheck {
     my $packagesfile = shift;
     my $srcs = shift;
@@ -2234,7 +2203,7 @@ sub call_edos_depcheck {
 
     open SOURCES, '>', $tmpfile or die "Could not open temporary file $tmpfile\n";
     for my $key (keys %interesting_packages) {
-       my $pkg = get_source_info($key);
+       my $pkg = $db->{$key};
        print SOURCES "Package: $key\n";
        print SOURCES "Version: $pkg->{'version'}\n";
        print SOURCES "Build-Depends: $srcs->{$key}{'dep'}\n" if $srcs->{$key}{'dep'};
@@ -2270,7 +2239,7 @@ sub call_edos_depcheck {
     unlink( $tmpfile );
 
     for my $key (keys %interesting_packages) {
-       my $pkg = get_source_info($key);
+       my $pkg = $db->{$key};
        my $change = 
            (defined $interesting_packages{$key} and $pkg->{'state'} eq 'Needs-Build') ||
            (not defined $interesting_packages{$key} and $pkg->{'state'} eq 'BD-Uninstallable');
@@ -2305,6 +2274,7 @@ sub usage {
 Usage: $prgname <options...> <package_version...>
 Options:
     -v, --verbose: Verbose execution.
+    -A arch: Architecture this operation is for.
     --take: Take package for building [default operation]
     -f, --failed: Record in database that a build failed due to
         deficiencies in the package (that aren't fixable without a new
@@ -2342,6 +2312,7 @@ Options:
         if -l is missing, set user name to be entered in db; usually
         automatically choosen
     --import FILE: Import database from a ASCII file FILE
+    --export FILE: Export database to a ASCII file FILE
 
 The remaining arguments (depending on operation) usually start with
 "name_version", the trailer is ignored. This allows to pass the names
@@ -2365,24 +2336,69 @@ sub pkg_version_eq {
 }
 
 sub table_name {
-       return $arch . '_' . $distribution;
+       return '"' . $arch . $schema_suffix . '".packages';
 }
 
 sub user_table_name {
-       return $arch . '_' . $distribution . '_users';
+       return '"' . $arch . $schema_suffix . '".users';
+}
+
+sub transactions_table_name {
+       return '"' . $arch . $schema_suffix . '".transactions';
+}
+
+sub get_readonly_source_info {
+       my $name = shift;
+       my $pkg = $dbh->selectrow_hashref('SELECT * FROM ' . 
+               table_name() . ' WHERE package = ? AND distribution = ?',
+               undef, $name, $distribution);
+       return $pkg;
 }
 
 sub get_source_info {
        my $name = shift;
        my $pkg = $dbh->selectrow_hashref('SELECT * FROM ' . 
-               table_name() . ' WHERE package = ?',
-               undef, $name);
+               table_name() . ' WHERE package = ? AND distribution = ?' .
+               ' FOR UPDATE',
+               undef, $name, $distribution);
        return $pkg;
 }
 
 sub get_all_source_info {
-       my $db = $dbh->selectall_hashref('SELECT * FROM ' . table_name(),
-               'package');
+       my %options = @_;
+
+       my $q = 'SELECT * FROM ' . table_name()
+               . ' WHERE distribution = ? ';
+       my @args = ($distribution);
+       if ($options{state} && uc($options{state}) ne "ALL") {
+               $q .= ' AND upper(state) = ? ';
+               push @args, uc($options{state});
+       }
+
+       if ($options{user}) {
+               #this basically means "this user, or no user at all":
+               $q .= ' AND (builder = ? OR upper(state) = ?)';
+               push @args, $options{user};
+               push @args, "NEEDS-BUILD";
+       }
+
+       if ($options{category}) {
+               $q .= ' AND failed_category <> ? AND upper(state) = ? ';
+               push @args, $options{category};
+               push @args, "FAILED";
+       }
+
+       if ($options{list_min_age} > 0) {
+               $q .= ' AND age(state_change::timestamp) > ? ';
+               push @args, $options{list_min_age} . " days";
+       }
+
+       if ($options{list_min_age} < 0) {
+               $q .= ' AND age(state_change::timestamp) < ? ';
+               push @args, -$options{list_min_age} . " days";
+       }
+
+       my $db = $dbh->selectall_hashref($q, 'package', undef, @args);
        return $db;
 }
 
@@ -2415,7 +2431,7 @@ sub update_source_info {
                        'depends = ?, ' .
                        'rel = ?, ' .
                        'bd_problem = ? ' .
-                       'WHERE package = ?',
+                       'WHERE package = ? AND distribution = ?',
                undef,
                $pkg->{'version'},
                $pkg->{'state'},
@@ -2436,36 +2452,38 @@ sub update_source_info {
                $pkg->{'depends'},
                $pkg->{'rel'},
                $pkg->{'bd_problem'},
-               $pkg->{'package'}) or die $dbh->errstr;
+               $pkg->{'package'},
+               $distribution) or die $dbh->errstr;
 }
 
 sub add_source_info {
        my $pkg = shift;
        $dbh->do('INSERT INTO ' . table_name() .
-                       ' (package) values (?)',
-               undef, $pkg->{'package'}) or die $dbh->errstr;
+                       ' (package, distribution) values (?, ?)',
+               undef, $pkg->{'package'}, $distribution) or die $dbh->errstr;
 }
 
 sub del_source_info {
        my $name = shift;
        $dbh->do('DELETE FROM ' . table_name() .
-                       ' WHERE package = ?',
-               undef, $name) or die $dbh->errstr;
+                       ' WHERE package = ? AND distribution = ?',
+               undef, $name, $distribution) or die $dbh->errstr;
 }
 
 sub get_user_info {
        my $name = shift;
        my $user = $dbh->selectrow_hashref('SELECT * FROM ' . 
-               user_table_name() . ' WHERE username = ?',
-               undef, $name);
+               user_table_name() . ' WHERE username = ? AND distribution = ?',
+               undef, $name, $distribution);
        return $user;
 }
 
 sub update_user_info {
        my $user = shift;
        $dbh->do('UPDATE ' . user_table_name() .
-                       ' SET last_seen = now() WHERE username = ?',
-               undef, $user)
+                       ' SET last_seen = now() WHERE username = ?' .
+                       ' AND distribution = ?',
+               undef, $user, $distribution)
                or die $dbh->errstr;
 }
 
@@ -2473,8 +2491,15 @@ sub update_user_info {
 sub add_user_info {
        my $user = shift;
        $dbh->do('INSERT INTO ' . user_table_name() .
-                       ' (username, last_seen) values (?, now())',
-               undef, $user)
+                       ' (username, distribution, last_seen)' .
+                       ' values (?, ?, now())',
+               undef, $user, $distribution)
                or die $dbh->errstr;
 }
 
+sub lock_table()
+{
+       $dbh->do('LOCK TABLE ' . table_name() .
+               ' IN EXCLUSIVE MODE', undef) or die $dbh->errstr;
+}
+