]> git.decadent.org.uk Git - dak.git/blob - daklib/database.py
import utils
[dak.git] / daklib / database.py
1 #!/usr/bin/env python
2
3 """ DB access functions
4 @group readonly: get_suite_id, get_section_id, get_priority_id, get_override_type_id,
5                  get_architecture_id, get_archive_id, get_component_id, get_location_id,
6                  get_source_id, get_suite_version, get_files_id, get_maintainer, get_suites,
7                  get_suite_architectures
8 @group read/write: get_or_set*, set_files_id
9
10 @contact: Debian FTP Master <ftpmaster@debian.org>
11 @copyright: 2000, 2001, 2002, 2003, 2004, 2006  James Troup <james@nocrew.org>
12 @copyright: 2009  Joerg Jaspert <joerg@debian.org>
13 @license: GNU General Public License version 2 or later
14 """
15
16 # This program is free software; you can redistribute it and/or modify
17 # it under the terms of the GNU General Public License as published by
18 # the Free Software Foundation; either version 2 of the License, or
19 # (at your option) any later version.
20
21 # This program is distributed in the hope that it will be useful,
22 # but WITHOUT ANY WARRANTY; without even the implied warranty of
23 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
24 # GNU General Public License for more details.
25
26 # You should have received a copy of the GNU General Public License
27 # along with this program; if not, write to the Free Software
28 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
29
30 ################################################################################
31
32 import sys
33 import time
34 import types
35 import utils
36
37 ################################################################################
38
39 Cnf = None                    #: Configuration, apt_pkg.Configuration
40 projectB = None               #: database connection, pgobject
41 suite_id_cache = {}           #: cache for suites
42 section_id_cache = {}         #: cache for sections
43 priority_id_cache = {}        #: cache for priorities
44 override_type_id_cache = {}   #: cache for overrides
45 architecture_id_cache = {}    #: cache for architectures
46 archive_id_cache = {}         #: cache for archives
47 component_id_cache = {}       #: cache for components
48 location_id_cache = {}        #: cache for locations
49 maintainer_id_cache = {}      #: cache for maintainers
50 keyring_id_cache = {}         #: cache for keyrings
51 source_id_cache = {}          #: cache for sources
52
53 files_id_cache = {}           #: cache for files
54 maintainer_cache = {}         #: cache for maintainer names
55 fingerprint_id_cache = {}     #: cache for fingerprints
56 queue_id_cache = {}           #: cache for queues
57 uid_id_cache = {}             #: cache for uids
58 suite_version_cache = {}      #: cache for suite_versions (packages)
59 suite_bin_version_cache = {}
60 cache_preloaded = False
61
62 ################################################################################
63
64 def init (config, sql):
65     """
66     database module init.
67
68     @type config: apt_pkg.Configuration
69     @param config: apt config, see U{http://apt.alioth.debian.org/python-apt-doc/apt_pkg/cache.html#Configuration}
70
71     @type sql: pgobject
72     @param sql: database connection
73
74     """
75     global Cnf, projectB
76
77     Cnf = config
78     projectB = sql
79
80
81 def do_query(query):
82     """
83     Executes a database query. Writes statistics / timing to stderr.
84
85     @type query: string
86     @param query: database query string, passed unmodified
87
88     @return: db result
89
90     @warning: The query is passed B{unmodified}, so be careful what you use this for.
91     """
92     sys.stderr.write("query: \"%s\" ... " % (query))
93     before = time.time()
94     r = projectB.query(query)
95     time_diff = time.time()-before
96     sys.stderr.write("took %.3f seconds.\n" % (time_diff))
97     if type(r) is int:
98         sys.stderr.write("int result: %s\n" % (r))
99     elif type(r) is types.NoneType:
100         sys.stderr.write("result: None\n")
101     else:
102         sys.stderr.write("pgresult: %s\n" % (r.getresult()))
103     return r
104
105 ################################################################################
106
107 def get_suite_id (suite):
108     """
109     Returns database id for given C{suite}.
110     Results are kept in a cache during runtime to minimize database queries.
111
112     @type suite: string
113     @param suite: The name of the suite
114
115     @rtype: int
116     @return: the database id for the given suite
117
118     """
119     global suite_id_cache
120
121     if suite_id_cache.has_key(suite):
122         return suite_id_cache[suite]
123
124     q = projectB.query("SELECT id FROM suite WHERE suite_name = '%s'" % (suite))
125     ql = q.getresult()
126     if not ql:
127         return -1
128
129     suite_id = ql[0][0]
130     suite_id_cache[suite] = suite_id
131
132     return suite_id
133
134 def get_section_id (section):
135     """
136     Returns database id for given C{section}.
137     Results are kept in a cache during runtime to minimize database queries.
138
139     @type section: string
140     @param section: The name of the section
141
142     @rtype: int
143     @return: the database id for the given section
144
145     """
146     global section_id_cache
147
148     if section_id_cache.has_key(section):
149         return section_id_cache[section]
150
151     q = projectB.query("SELECT id FROM section WHERE section = '%s'" % (section))
152     ql = q.getresult()
153     if not ql:
154         return -1
155
156     section_id = ql[0][0]
157     section_id_cache[section] = section_id
158
159     return section_id
160
161 def get_priority_id (priority):
162     """
163     Returns database id for given C{priority}.
164     Results are kept in a cache during runtime to minimize database queries.
165
166     @type priority: string
167     @param priority: The name of the priority
168
169     @rtype: int
170     @return: the database id for the given priority
171
172     """
173     global priority_id_cache
174
175     if priority_id_cache.has_key(priority):
176         return priority_id_cache[priority]
177
178     q = projectB.query("SELECT id FROM priority WHERE priority = '%s'" % (priority))
179     ql = q.getresult()
180     if not ql:
181         return -1
182
183     priority_id = ql[0][0]
184     priority_id_cache[priority] = priority_id
185
186     return priority_id
187
188 def get_override_type_id (type):
189     """
190     Returns database id for given override C{type}.
191     Results are kept in a cache during runtime to minimize database queries.
192
193     @type type: string
194     @param type: The name of the override type
195
196     @rtype: int
197     @return: the database id for the given override type
198
199     """
200     global override_type_id_cache
201
202     if override_type_id_cache.has_key(type):
203         return override_type_id_cache[type]
204
205     q = projectB.query("SELECT id FROM override_type WHERE type = '%s'" % (type))
206     ql = q.getresult()
207     if not ql:
208         return -1
209
210     override_type_id = ql[0][0]
211     override_type_id_cache[type] = override_type_id
212
213     return override_type_id
214
215 def get_architecture_id (architecture):
216     """
217     Returns database id for given C{architecture}.
218     Results are kept in a cache during runtime to minimize database queries.
219
220     @type architecture: string
221     @param architecture: The name of the override type
222
223     @rtype: int
224     @return: the database id for the given architecture
225
226     """
227     global architecture_id_cache
228
229     if architecture_id_cache.has_key(architecture):
230         return architecture_id_cache[architecture]
231
232     q = projectB.query("SELECT id FROM architecture WHERE arch_string = '%s'" % (architecture))
233     ql = q.getresult()
234     if not ql:
235         return -1
236
237     architecture_id = ql[0][0]
238     architecture_id_cache[architecture] = architecture_id
239
240     return architecture_id
241
242 def get_archive_id (archive):
243     """
244     Returns database id for given C{archive}.
245     Results are kept in a cache during runtime to minimize database queries.
246
247     @type archive: string
248     @param archive: The name of the override type
249
250     @rtype: int
251     @return: the database id for the given archive
252
253     """
254     global archive_id_cache
255
256     archive = archive.lower()
257
258     if archive_id_cache.has_key(archive):
259         return archive_id_cache[archive]
260
261     q = projectB.query("SELECT id FROM archive WHERE lower(name) = '%s'" % (archive))
262     ql = q.getresult()
263     if not ql:
264         return -1
265
266     archive_id = ql[0][0]
267     archive_id_cache[archive] = archive_id
268
269     return archive_id
270
271 def get_component_id (component):
272     """
273     Returns database id for given C{component}.
274     Results are kept in a cache during runtime to minimize database queries.
275
276     @type component: string
277     @param component: The name of the component
278
279     @rtype: int
280     @return: the database id for the given component
281
282     """
283     global component_id_cache
284
285     component = component.lower()
286
287     if component_id_cache.has_key(component):
288         return component_id_cache[component]
289
290     q = projectB.query("SELECT id FROM component WHERE lower(name) = '%s'" % (component))
291     ql = q.getresult()
292     if not ql:
293         return -1
294
295     component_id = ql[0][0]
296     component_id_cache[component] = component_id
297
298     return component_id
299
300 def get_location_id (location, component, archive):
301     """
302     Returns database id for the location behind the given combination of
303       - B{location} - the path of the location, eg. I{/srv/ftp.debian.org/ftp/pool/}
304       - B{component} - the id of the component as returned by L{get_component_id}
305       - B{archive} - the id of the archive as returned by L{get_archive_id}
306     Results are kept in a cache during runtime to minimize database queries.
307
308     @type location: string
309     @param location: the path of the location
310
311     @type component: int
312     @param component: the id of the component
313
314     @type archive: int
315     @param archive: the id of the archive
316
317     @rtype: int
318     @return: the database id for the location
319
320     """
321     global location_id_cache
322
323     cache_key = location + '_' + component + '_' + location
324     if location_id_cache.has_key(cache_key):
325         return location_id_cache[cache_key]
326
327     archive_id = get_archive_id (archive)
328     if component != "":
329         component_id = get_component_id (component)
330         if component_id != -1:
331             q = projectB.query("SELECT id FROM location WHERE path = '%s' AND component = %d AND archive = %d" % (location, component_id, archive_id))
332     else:
333         q = projectB.query("SELECT id FROM location WHERE path = '%s' AND archive = %d" % (location, archive_id))
334     ql = q.getresult()
335     if not ql:
336         return -1
337
338     location_id = ql[0][0]
339     location_id_cache[cache_key] = location_id
340
341     return location_id
342
343 def get_source_id (source, version):
344     """
345     Returns database id for the combination of C{source} and C{version}
346       - B{source} - source package name, eg. I{mailfilter}, I{bbdb}, I{glibc}
347       - B{version}
348     Results are kept in a cache during runtime to minimize database queries.
349
350     @type source: string
351     @param source: source package name
352
353     @type version: string
354     @param version: the source version
355
356     @rtype: int
357     @return: the database id for the source
358
359     """
360     global source_id_cache
361
362     cache_key = source + '_' + version + '_'
363     if source_id_cache.has_key(cache_key):
364         return source_id_cache[cache_key]
365
366     q = projectB.query("SELECT id FROM source s WHERE s.source = '%s' AND s.version = '%s'" % (source, version))
367
368     if not q.getresult():
369         return None
370
371     source_id = q.getresult()[0][0]
372     source_id_cache[cache_key] = source_id
373
374     return source_id
375
376 def get_suite_version(source, suite):
377     """
378     Returns database id for a combination of C{source} and C{suite}.
379
380       - B{source} - source package name, eg. I{mailfilter}, I{bbdb}, I{glibc}
381       - B{suite} - a suite name, eg. I{unstable}
382
383     Results are kept in a cache during runtime to minimize database queries.
384
385     @type source: string
386     @param source: source package name
387
388     @type suite: string
389     @param suite: the suite name
390
391     @rtype: string
392     @return: the version for I{source} in I{suite}
393
394     """
395
396     global suite_version_cache
397     cache_key = "%s_%s" % (source, suite)
398
399     if suite_version_cache.has_key(cache_key):
400         return suite_version_cache[cache_key]
401
402     q = projectB.query("""
403     SELECT s.version FROM source s, suite su, src_associations sa
404     WHERE sa.source=s.id
405       AND sa.suite=su.id
406       AND su.suite_name='%s'
407       AND s.source='%s'"""
408                               % (suite, source))
409
410     if not q.getresult():
411         return None
412
413     version = q.getresult()[0][0]
414     suite_version_cache[cache_key] = version
415
416     return version
417
418 def get_latest_binary_version_id(binary, section, suite, arch):
419     global suite_bin_version_cache
420     cache_key = "%s_%s_%s_%s" % (binary, section, suite, arch)
421     cache_key_all = "%s_%s_%s_%s" % (binary, section, suite, get_architecture_id("all"))
422
423     # Check for the cache hit for its arch, then arch all
424     if suite_bin_version_cache.has_key(cache_key):
425         return suite_bin_version_cache[cache_key]
426     if suite_bin_version_cache.has_key(cache_key_all):
427         return suite_bin_version_cache[cache_key_all]
428     if cache_preloaded == True:
429         return # package does not exist
430
431     q = projectB.query("SELECT DISTINCT b.id FROM binaries b JOIN bin_associations ba ON (b.id = ba.bin) JOIN override o ON (o.package=b.package) WHERE b.package = '%s' AND b.architecture = '%d' AND ba.suite = '%d' AND o.section = '%d'" % (binary, int(arch), int(suite), int(section)))
432
433     if not q.getresult():
434         return False
435
436     highest_bid = q.getresult()[0][0]
437
438     suite_bin_version_cache[cache_key] = highest_bid
439     return highest_bid
440
441 def preload_binary_id_cache():
442     global suite_bin_version_cache, cache_preloaded
443
444     # Get suite info
445     q = projectB.query("SELECT id FROM suite")
446     suites = q.getresult()
447
448     # Get arch mappings
449     q = projectB.query("SELECT id FROM architecture")
450     arches = q.getresult()
451
452     for suite in suites:
453         for arch in arches:
454             q = projectB.query("SELECT DISTINCT b.id, b.package, o.section FROM binaries b JOIN bin_associations ba ON (b.id = ba.bin) JOIN override o ON (o.package=b.package) WHERE b.architecture = '%d' AND ba.suite = '%d'" % (int(arch[0]), int(suite[0])))
455
456             for bi in q.getresult():
457                 cache_key = "%s_%s_%s_%s" % (bi[1], bi[2], suite[0], arch[0])
458                 suite_bin_version_cache[cache_key] = int(bi[0])
459
460     cache_preloaded = True
461
462 def get_suite_architectures(suite):
463     """
464     Returns list of architectures for C{suite}.
465
466     @type suite: string, int
467     @param suite: the suite name or the suite_id
468
469     @rtype: list
470     @return: the list of architectures for I{suite}
471     """
472
473     suite_id = None
474     if type(suite) == str:
475         suite_id = get_suite_id(suite)
476     elif type(suite) == int:
477         suite_id = suite
478     else:
479         return None
480
481     sql = """ SELECT a.arch_string FROM suite_architectures sa
482               JOIN architecture a ON (a.id = sa.architecture)
483               WHERE suite='%s' """ % (suite_id)
484
485     q = projectB.query(sql)
486     return map(lambda x: x[0], q.getresult())
487
488 ################################################################################
489
490 def get_or_set_maintainer_id (maintainer):
491     """
492     If C{maintainer} does not have an entry in the maintainer table yet, create one
493     and return the new id.
494     If C{maintainer} already has an entry, simply return the existing id.
495
496     Results are kept in a cache during runtime to minimize database queries.
497
498     @type maintainer: string
499     @param maintainer: the maintainer name
500
501     @rtype: int
502     @return: the database id for the maintainer
503
504     """
505     global maintainer_id_cache
506
507     if maintainer_id_cache.has_key(maintainer):
508         return maintainer_id_cache[maintainer]
509
510     q = projectB.query("SELECT id FROM maintainer WHERE name = '%s'" % (maintainer))
511     if not q.getresult():
512         projectB.query("INSERT INTO maintainer (name) VALUES ('%s')" % (maintainer))
513         q = projectB.query("SELECT id FROM maintainer WHERE name = '%s'" % (maintainer))
514     maintainer_id = q.getresult()[0][0]
515     maintainer_id_cache[maintainer] = maintainer_id
516
517     return maintainer_id
518
519 ################################################################################
520
521 def get_or_set_keyring_id (keyring):
522     """
523     If C{keyring} does not have an entry in the C{keyrings} table yet, create one
524     and return the new id.
525     If C{keyring} already has an entry, simply return the existing id.
526
527     Results are kept in a cache during runtime to minimize database queries.
528
529     @type keyring: string
530     @param keyring: the keyring name
531
532     @rtype: int
533     @return: the database id for the keyring
534
535     """
536     global keyring_id_cache
537
538     if keyring_id_cache.has_key(keyring):
539         return keyring_id_cache[keyring]
540
541     q = projectB.query("SELECT id FROM keyrings WHERE name = '%s'" % (keyring))
542     if not q.getresult():
543         projectB.query("INSERT INTO keyrings (name) VALUES ('%s')" % (keyring))
544         q = projectB.query("SELECT id FROM keyrings WHERE name = '%s'" % (keyring))
545     keyring_id = q.getresult()[0][0]
546     keyring_id_cache[keyring] = keyring_id
547
548     return keyring_id
549
550 ################################################################################
551
552 def get_or_set_uid_id (uid):
553     """
554     If C{uid} does not have an entry in the uid table yet, create one
555     and return the new id.
556     If C{uid} already has an entry, simply return the existing id.
557
558     Results are kept in a cache during runtime to minimize database queries.
559
560     @type uid: string
561     @param uid: the uid.
562
563     @rtype: int
564     @return: the database id for the uid
565
566     """
567
568     global uid_id_cache
569
570     if uid_id_cache.has_key(uid):
571         return uid_id_cache[uid]
572
573     q = projectB.query("SELECT id FROM uid WHERE uid = '%s'" % (uid))
574     if not q.getresult():
575         projectB.query("INSERT INTO uid (uid) VALUES ('%s')" % (uid))
576         q = projectB.query("SELECT id FROM uid WHERE uid = '%s'" % (uid))
577     uid_id = q.getresult()[0][0]
578     uid_id_cache[uid] = uid_id
579
580     return uid_id
581
582 ################################################################################
583
584 def get_or_set_fingerprint_id (fingerprint):
585     """
586     If C{fingerprint} does not have an entry in the fingerprint table yet, create one
587     and return the new id.
588     If C{fingerprint} already has an entry, simply return the existing id.
589
590     Results are kept in a cache during runtime to minimize database queries.
591
592     @type fingerprint: string
593     @param fingerprint: the fingerprint
594
595     @rtype: int
596     @return: the database id for the fingerprint
597
598     """
599     global fingerprint_id_cache
600
601     if fingerprint_id_cache.has_key(fingerprint):
602         return fingerprint_id_cache[fingerprint]
603
604     q = projectB.query("SELECT id FROM fingerprint WHERE fingerprint = '%s'" % (fingerprint))
605     if not q.getresult():
606         projectB.query("INSERT INTO fingerprint (fingerprint) VALUES ('%s')" % (fingerprint))
607         q = projectB.query("SELECT id FROM fingerprint WHERE fingerprint = '%s'" % (fingerprint))
608     fingerprint_id = q.getresult()[0][0]
609     fingerprint_id_cache[fingerprint] = fingerprint_id
610
611     return fingerprint_id
612
613 ################################################################################
614
615 def get_files_id (filename, size, md5sum, location_id):
616     """
617     Returns -1, -2 or the file_id for filename, if its C{size} and C{md5sum} match an
618     existing copy.
619
620     The database is queried using the C{filename} and C{location_id}. If a file does exist
621     at that location, the existing size and md5sum are checked against the provided
622     parameters. A size or checksum mismatch returns -2. If more than one entry is
623     found within the database, a -1 is returned, no result returns None, otherwise
624     the file id.
625
626     Results are kept in a cache during runtime to minimize database queries.
627
628     @type filename: string
629     @param filename: the filename of the file to check against the DB
630
631     @type size: int
632     @param size: the size of the file to check against the DB
633
634     @type md5sum: string
635     @param md5sum: the md5sum of the file to check against the DB
636
637     @type location_id: int
638     @param location_id: the id of the location as returned by L{get_location_id}
639
640     @rtype: int / None
641     @return: Various return values are possible:
642                - -2: size/checksum error
643                - -1: more than one file found in database
644                - None: no file found in database
645                - int: file id
646
647     """
648     global files_id_cache
649
650     cache_key = "%s_%d" % (filename, location_id)
651
652     if files_id_cache.has_key(cache_key):
653         return files_id_cache[cache_key]
654
655     size = int(size)
656     q = projectB.query("SELECT id, size, md5sum FROM files WHERE filename = '%s' AND location = %d" % (filename, location_id))
657     ql = q.getresult()
658     if ql:
659         if len(ql) != 1:
660             return -1
661         ql = ql[0]
662         orig_size = int(ql[1])
663         orig_md5sum = ql[2]
664         if orig_size != size or orig_md5sum != md5sum:
665             return -2
666         files_id_cache[cache_key] = ql[0]
667         return files_id_cache[cache_key]
668     else:
669         return None
670
671 ################################################################################
672
673 def get_or_set_queue_id (queue):
674     """
675     If C{queue} does not have an entry in the queue table yet, create one
676     and return the new id.
677     If C{queue} already has an entry, simply return the existing id.
678
679     Results are kept in a cache during runtime to minimize database queries.
680
681     @type queue: string
682     @param queue: the queue name (no full path)
683
684     @rtype: int
685     @return: the database id for the queue
686
687     """
688     global queue_id_cache
689
690     if queue_id_cache.has_key(queue):
691         return queue_id_cache[queue]
692
693     q = projectB.query("SELECT id FROM queue WHERE queue_name = '%s'" % (queue))
694     if not q.getresult():
695         projectB.query("INSERT INTO queue (queue_name) VALUES ('%s')" % (queue))
696         q = projectB.query("SELECT id FROM queue WHERE queue_name = '%s'" % (queue))
697     queue_id = q.getresult()[0][0]
698     queue_id_cache[queue] = queue_id
699
700     return queue_id
701
702 ################################################################################
703
704 def set_files_id (filename, size, md5sum, sha1sum, sha256sum, location_id):
705     """
706     Insert a new entry into the files table and return its id.
707
708     @type filename: string
709     @param filename: the filename
710
711     @type size: int
712     @param size: the size in bytes
713
714     @type md5sum: string
715     @param md5sum: md5sum of the file
716
717     @type sha1sum: string
718     @param sha1sum: sha1sum of the file
719
720     @type sha256sum: string
721     @param sha256sum: sha256sum of the file
722
723     @type location_id: int
724     @param location_id: the id of the location as returned by L{get_location_id}
725
726     @rtype: int
727     @return: the database id for the new file
728
729     """
730     global files_id_cache
731
732     projectB.query("INSERT INTO files (filename, size, md5sum, sha1sum, sha256sum, location) VALUES ('%s', %d, '%s', '%s', '%s', %d)" % (filename, long(size), md5sum, sha1sum, sha256sum, location_id))
733
734     return get_files_id (filename, size, md5sum, location_id)
735
736     ### currval has issues with postgresql 7.1.3 when the table is big
737     ### it was taking ~3 seconds to return on auric which is very Not
738     ### Cool(tm).
739     ##
740     ##q = projectB.query("SELECT id FROM files WHERE id = currval('files_id_seq')")
741     ##ql = q.getresult()[0]
742     ##cache_key = "%s_%d" % (filename, location_id)
743     ##files_id_cache[cache_key] = ql[0]
744     ##return files_id_cache[cache_key]
745
746 ################################################################################
747
748 def get_maintainer (maintainer_id):
749     """
750     Return the name of the maintainer behind C{maintainer_id}.
751
752     Results are kept in a cache during runtime to minimize database queries.
753
754     @type maintainer_id: int
755     @param maintainer_id: the id of the maintainer, eg. from L{get_or_set_maintainer_id}
756
757     @rtype: string
758     @return: the name of the maintainer
759
760     """
761     global maintainer_cache
762
763     if not maintainer_cache.has_key(maintainer_id):
764         q = projectB.query("SELECT name FROM maintainer WHERE id = %s" % (maintainer_id))
765         maintainer_cache[maintainer_id] = q.getresult()[0][0]
766
767     return maintainer_cache[maintainer_id]
768
769 ################################################################################
770
771 def get_suites(pkgname, src=False):
772     """
773     Return the suites in which C{pkgname} can be found. If C{src} is True query for source
774     package, else binary package.
775
776     @type pkgname: string
777     @param pkgname: name of the package
778
779     @type src: bool
780     @param src: if True look for source packages, false (default) looks for binary.
781
782     @rtype: list
783     @return: list of suites, or empty list if no match
784
785     """
786     if src:
787         sql = """
788         SELECT suite_name
789         FROM source,
790              src_associations,
791              suite
792         WHERE source.id = src_associations.source
793         AND   source.source = '%s'
794         AND   src_associations.suite = suite.id
795         """ % (pkgname)
796     else:
797         sql = """
798         SELECT suite_name
799         FROM binaries,
800              bin_associations,
801              suite
802         WHERE binaries.id = bin_associations.bin
803         AND   package = '%s'
804         AND   bin_associations.suite = suite.id
805         """ % (pkgname)
806
807     q = projectB.query(sql)
808     return map(lambda x: x[0], q.getresult())
809
810
811 ################################################################################
812
813 def copy_temporary_contents(package, version, deb):
814     """
815     copy the previously stored contents from the temp table to the permanant one
816
817     during process-unchecked, the deb should have been scanned and the
818     contents stored in pending_content_associations
819     """
820
821     # first see if contents exist:
822
823     exists = projectB.query("""SELECT 1 FROM pending_content_associations
824                                WHERE package='%s' LIMIT 1""" % package ).getresult()
825
826     if not exists:
827         # This should NOT happen.  We should have added contents
828         # during process-unchecked.  if it did, log an error, and send
829         # an email.
830         subst = {
831             "__PACKAGE__": package,
832             "__VERSION__": version,
833             "__TO_ADDRESS__": Cnf["Dinstall::MyAdminAddress"],
834             "__DAK_ADDRESS__": Cnf["Dinstall::MyEmailAddress"] }
835
836         message = utils.TemplateSubst(Subst, Cnf["Dir::Templates"]+"/missing-contents")
837         utils.send_mail( message )
838
839         exists = DBConn().insert_content_path(package, version, deb)
840
841     if exists:
842         sql = """INSERT INTO content_associations(binary_pkg,filepath,filename)
843                  SELECT currval('binaries_id_seq'), filepath, filename FROM pending_content_associations
844                  WHERE package='%s'
845                      AND version='%s'""" % (package, version)
846         projectB.query(sql)
847         projectB.query("""DELETE from pending_content_associations
848                           WHERE package='%s'
849                             AND version='%s'""" % (package, version))
850
851     return exists