]> git.decadent.org.uk Git - dak.git/blob - daklib/cruft.py
Add by-hash support
[dak.git] / daklib / cruft.py
1 """
2 helper functions for cruft-report
3
4 @contact: Debian FTPMaster <ftpmaster@debian.org>
5 @copyright 2011 Torsten Werner <twerner@debian.org>
6 """
7
8 # This program is free software; you can redistribute it and/or modify
9 # it under the terms of the GNU General Public License as published by
10 # the Free Software Foundation; either version 2 of the License, or
11 # (at your option) any later version.
12
13 # This program is distributed in the hope that it will be useful,
14 # but WITHOUT ANY WARRANTY; without even the implied warranty of
15 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 # GNU General Public License for more details.
17
18 # You should have received a copy of the GNU General Public License
19 # along with this program; if not, write to the Free Software
20 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
21
22 ################################################################################
23
24 from daklib.dbconn import *
25
26 from sqlalchemy import func
27 from sqlalchemy.orm import object_session
28
29 def newer_version(lowersuite_name, highersuite_name, session):
30     '''
31     Finds newer versions in lowersuite_name than in highersuite_name. Returns a
32     list of tuples (source, higherversion, lowerversion) where higherversion is
33     the newest version from highersuite_name and lowerversion is the newest
34     version from lowersuite_name.
35     '''
36
37     lowersuite = get_suite(lowersuite_name, session)
38     highersuite = get_suite(highersuite_name, session)
39
40     query = session.query(DBSource.source, func.max(DBSource.version)). \
41         with_parent(highersuite).group_by(DBSource.source)
42
43     list = []
44     for (source, higherversion) in query:
45         lowerversion = session.query(func.max(DBSource.version)). \
46             filter_by(source = source).filter(DBSource.version > higherversion). \
47             with_parent(lowersuite).group_by(DBSource.source).scalar()
48         if lowerversion is not None:
49             list.append((source, higherversion, lowerversion))
50
51     list.sort()
52     return list
53
54 def get_package_names(suite):
55     '''
56     Returns a query that selects all distinct package names from suite ordered
57     by package name.
58     '''
59
60     session = object_session(suite)
61     return session.query(DBBinary.package).with_parent(suite). \
62         group_by(DBBinary.package).order_by(DBBinary.package)
63
64 class NamedSource(object):
65     '''
66     A source package identified by its name with all of its versions in a
67     suite.
68     '''
69     def __init__(self, suite, source):
70         self.source = source
71         query = suite.sources.filter_by(source = source). \
72             order_by(DBSource.version)
73         self.versions = [src.version for src in query]
74
75     def __str__(self):
76         return "%s(%s)" % (self.source, ", ".join(self.versions))
77
78 class DejavuBinary(object):
79     '''
80     A binary package identified by its name which gets built by multiple source
81     packages in a suite. The architecture is ignored which leads to the
82     following corner case, e.g.:
83
84     If a source package 'foo-mips' that builds a binary package 'foo' on mips
85     and another source package 'foo-mipsel' builds a binary package with the
86     same name 'foo' on mipsel then the binary package 'foo' will be reported as
87     built from multiple source packages.
88     '''
89
90     def __init__(self, suite, package):
91         self.package = package
92         session = object_session(suite)
93         # We need a subquery to make sure that both binary and source packages
94         # are in the right suite.
95         bin_query = suite.binaries.filter_by(package = package).subquery()
96         src_query = session.query(DBSource.source).with_parent(suite). \
97             join(bin_query).order_by(DBSource.source).group_by(DBSource.source)
98         self.sources = []
99         if src_query.count() > 1:
100             for source, in src_query:
101                 self.sources.append(str(NamedSource(suite, source)))
102
103     def has_multiple_sources(self):
104         'Has the package been built by multiple sources?'
105         return len(self.sources) > 1
106
107     def __str__(self):
108         return "%s built by: %s" % (self.package, ", ".join(self.sources))
109
110 def report_multiple_source(suite):
111     '''
112     Reports binary packages built from multiple source package with different
113     names.
114     '''
115
116     print "Built from multiple source packages"
117     print "-----------------------------------"
118     print
119     for package, in get_package_names(suite):
120         binary = DejavuBinary(suite, package)
121         if binary.has_multiple_sources():
122             print binary
123     print
124
125
126 def query_without_source(suite_id, session):
127     """searches for arch: all packages from suite that do no longer
128     reference a source package in the same suite
129
130     subquery unique_binaries: selects all packages with only 1 version
131     in suite since 'dak rm' does not allow to specify version numbers"""
132
133     query = """
134     with unique_binaries as
135         (select package, max(version) as version, max(source) as source
136             from bin_associations_binaries
137         where architecture = 2 and suite = :suite_id
138             group by package having count(*) = 1)
139     select ub.package, ub.version
140         from unique_binaries ub
141         left join src_associations_src sas
142         on ub.source = sas.src and sas.suite = :suite_id
143         where sas.id is null
144         order by ub.package"""
145     return session.execute(query, {'suite_id': suite_id})
146
147
148 def queryNBS(suite_id, session):
149     """This one is really complex. It searches arch != all packages that
150     are no longer built from current source packages in suite.
151
152     temp table unique_binaries: will be populated with packages that
153     have only one version in suite because 'dak rm' does not allow
154     specifying version numbers
155
156     temp table newest_binaries: will be populated with packages that are
157     built from current sources
158
159     subquery uptodate_arch: returns all architectures built from current
160     sources
161
162     subquery unique_binaries_uptodate_arch: returns all packages in
163     architectures from uptodate_arch
164
165     subquery unique_binaries_uptodate_arch_agg: same as
166     unique_binaries_uptodate_arch but with column architecture
167     aggregated to array
168
169     subquery uptodate_packages: similar to uptodate_arch but returns all
170     packages built from current sources
171
172     subquery outdated_packages: returns all packages with architectures
173     no longer built from current source
174     """
175
176     query = """
177 create temp table unique_binaries (
178     package text not null,
179     architecture integer not null,
180     source integer not null);
181
182 insert into unique_binaries
183     select bab.package, bab.architecture, max(bab.source)
184         from bin_associations_binaries bab
185         where bab.suite = :suite_id and bab.architecture > 2
186         group by package, architecture having count(*) = 1;
187
188 create temp table newest_binaries (
189     package text not null,
190     architecture integer not null,
191     source text not null,
192     version debversion not null);
193
194 insert into newest_binaries
195     select ub.package, ub.architecture, nsa.source, nsa.version
196         from unique_binaries ub
197         join newest_src_association nsa
198             on ub.source = nsa.src and nsa.suite = :suite_id;
199
200 with uptodate_arch as
201     (select architecture, source, version
202         from newest_binaries
203         group by architecture, source, version),
204     unique_binaries_uptodate_arch as
205     (select ub.package, ub.architecture, ua.source, ua.version
206         from unique_binaries ub
207         join source s
208             on ub.source = s.id
209         join uptodate_arch ua
210             on ub.architecture = ua.architecture and s.source = ua.source),
211     unique_binaries_uptodate_arch_agg as
212     (select ubua.package,
213         array(select unnest(array_agg(a.arch_string)) order by 1) as arch_list,
214         ubua.source, ubua.version
215         from unique_binaries_uptodate_arch ubua
216         join architecture a
217             on ubua.architecture = a.id
218         group by ubua.source, ubua.version, ubua.package),
219     uptodate_packages as
220     (select package, source, version
221         from newest_binaries
222         group by package, source, version),
223     outdated_packages as
224     (select array(select unnest(array_agg(package)) order by 1) as pkg_list,
225         arch_list, source, version
226         from unique_binaries_uptodate_arch_agg
227         where package not in
228             (select package from uptodate_packages)
229         group by arch_list, source, version)
230     select * from outdated_packages order by source"""
231     return session.execute(query, {'suite_id': suite_id})