]> git.decadent.org.uk Git - dak.git/blob - daklib/dbconn.py
updates
[dak.git] / daklib / dbconn.py
1 #!/usr/bin/python
2
3 """ DB access class
4
5 @contact: Debian FTPMaster <ftpmaster@debian.org>
6 @copyright: 2000, 2001, 2002, 2003, 2004, 2006  James Troup <james@nocrew.org>
7 @copyright: 2008-2009  Mark Hymers <mhy@debian.org>
8 @copyright: 2009  Joerg Jaspert <joerg@debian.org>
9 @copyright: 2009  Mike O'Connor <stew@debian.org>
10 @license: GNU General Public License version 2 or later
11 """
12
13 # This program is free software; you can redistribute it and/or modify
14 # it under the terms of the GNU General Public License as published by
15 # the Free Software Foundation; either version 2 of the License, or
16 # (at your option) any later version.
17
18 # This program is distributed in the hope that it will be useful,
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21 # GNU General Public License for more details.
22
23 # You should have received a copy of the GNU General Public License
24 # along with this program; if not, write to the Free Software
25 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
26
27 ################################################################################
28
29 # < mhy> I need a funny comment
30 # < sgran> two peanuts were walking down a dark street
31 # < sgran> one was a-salted
32 #  * mhy looks up the definition of "funny"
33
34 ################################################################################
35
36 import os
37 import psycopg2
38 import traceback
39
40 from sqlalchemy import create_engine, Table, MetaData, select
41 from sqlalchemy.orm import sessionmaker, mapper, relation
42
43 from singleton import Singleton
44
45 ################################################################################
46
47 class Architecture(object):
48     def __init__(self, *args, **kwargs):
49         pass
50
51     def __repr__(self):
52         return '<Architecture %s>' % self.arch_string
53
54 def get_architecture(architecture, session=None):
55     """
56     Returns database id for given C{architecture}.
57
58     @type architecture: string
59     @param architecture: The name of the architecture
60
61     @type session: Session
62     @param session: Optional SQLA session object (a temporary one will be
63     generated if not supplied)
64
65     @rtype: Architecture
66     @return: Architecture object for the given arch (None if not present)
67
68     """
69     if session is None:
70         session = DBConn().session()
71     q = session.query(Architecture).filter_by(arch_string=architecture)
72     if q.count() == 0:
73         return None
74     return q.one()
75
76 class Archive(object):
77     def __init__(self, *args, **kwargs):
78         pass
79
80     def __repr__(self):
81         return '<Archive %s>' % self.name
82
83 def get_archive(archive, session=None):
84     """
85     returns database id for given c{archive}.
86
87     @type archive: string
88     @param archive: the name of the arhive
89
90     @type session: Session
91     @param session: Optional SQLA session object (a temporary one will be
92     generated if not supplied)
93
94     @rtype: Archive
95     @return: Archive object for the given name (None if not present)
96
97     """
98     archive = archive.lower()
99     if session is None:
100         session = DBConn().session()
101     q = session.query(Archive).filter_by(archive_name=archive)
102     if q.count() == 0:
103         return None
104     return q.one()
105
106
107 class BinAssociation(object):
108     def __init__(self, *args, **kwargs):
109         pass
110
111     def __repr__(self):
112         return '<BinAssociation %s (%s, %s)>' % (self.ba_id, self.binary, self.suite)
113
114 class Binary(object):
115     def __init__(self, *args, **kwargs):
116         pass
117
118     def __repr__(self):
119         return '<Binary %s (%s, %s)>' % (self.package, self.version, self.architecture)
120
121 def get_binary_from_id(id, session=None):
122     """
123     Returns Binary object for given C{id}
124
125     @type id: int
126     @param id: Id of the required binary
127
128     @type session: Session
129     @param session: Optional SQLA session object (a temporary one will be
130     generated if not supplied)
131
132     @rtype: Binary
133     @return: Binary object for the given binary (None if not present)
134     """
135     if session is None:
136         session = DBConn().session()
137     q = session.query(Binary).filter_by(binary_id=id)
138     if q.count() == 0:
139         return None
140     return q.one()
141
142 class Component(object):
143     def __init__(self, *args, **kwargs):
144         pass
145
146     def __repr__(self):
147         return '<Component %s>' % self.component_name
148
149 def get_component(component, session=None):
150     """
151     Returns database id for given C{component}.
152
153     @type component: string
154     @param component: The name of the override type
155
156     @rtype: int
157     @return: the database id for the given component
158
159     """
160     component = component.lower()
161     if session is None:
162         session = DBConn().session()
163     q = session.query(Component).filter_by(component_name=component)
164     if q.count() == 0:
165         return None
166     return q.one()
167
168 class DBConfig(object):
169     def __init__(self, *args, **kwargs):
170         pass
171
172     def __repr__(self):
173         return '<DBConfig %s>' % self.name
174
175 class ContentFilename(object):
176     def __init__(self, *args, **kwargs):
177         pass
178
179     def __repr__(self):
180         return '<ContentFilename %s>' % self.filename
181
182 class ContentFilepath(object):
183     def __init__(self, *args, **kwargs):
184         pass
185
186     def __repr__(self):
187         return '<ContentFilepath %s>' % self.filepath
188
189 class ContentAssociations(object):
190     def __init__(self, *args, **kwargs):
191         pass
192
193     def __repr__(self):
194         return '<ContentAssociation %s>' % self.ca_id
195
196 class DSCFile(object):
197     def __init__(self, *args, **kwargs):
198         pass
199
200     def __repr__(self):
201         return '<DSCFile %s>' % self.dscfile_id
202
203 class PoolFile(object):
204     def __init__(self, *args, **kwargs):
205         pass
206
207     def __repr__(self):
208         return '<PoolFile %s>' % self.filename
209
210 class Fingerprint(object):
211     def __init__(self, *args, **kwargs):
212         pass
213
214     def __repr__(self):
215         return '<Fingerprint %s>' % self.fingerprint
216
217 class Keyring(object):
218     def __init__(self, *args, **kwargs):
219         pass
220
221     def __repr__(self):
222         return '<Keyring %s>' % self.keyring_name
223
224 class Location(object):
225     def __init__(self, *args, **kwargs):
226         pass
227
228     def __repr__(self):
229         return '<Location %s (%s)>' % (self.path, self.location_id)
230
231 class Maintainer(object):
232     def __init__(self, *args, **kwargs):
233         pass
234
235     def __repr__(self):
236         return '''<Maintainer '%s' (%s)>''' % (self.name, self.maintainer_id)
237
238 class Override(object):
239     def __init__(self, *args, **kwargs):
240         pass
241
242     def __repr__(self):
243         return '<Override %s (%s)>' % (self.package, self.suite_id)
244
245 class OverrideType(object):
246     def __init__(self, *args, **kwargs):
247         pass
248
249     def __repr__(self):
250         return '<OverrideType %s>' % self.overridetype
251
252 def get_override_type(override_type, session=None):
253     """
254     Returns OverrideType object for given C{override type}.
255
256     @type override_type: string
257     @param override_type: The name of the override type
258
259     @type session: Session
260     @param session: Optional SQLA session object (a temporary one will be
261     generated if not supplied)
262
263     @rtype: int
264     @return: the database id for the given override type
265
266     """
267     if session is None:
268         session = DBConn().session()
269     q = session.query(Priority).filter_by(priority=priority)
270     if q.count() == 0:
271         return None
272     return q.one()
273
274 class PendingContentAssociation(object):
275     def __init__(self, *args, **kwargs):
276         pass
277
278     def __repr__(self):
279         return '<PendingContentAssociation %s>' % self.pca_id
280
281 class Priority(object):
282     def __init__(self, *args, **kwargs):
283         pass
284
285     def __repr__(self):
286         return '<Priority %s (%s)>' % (self.priority, self.priority_id)
287
288 def get_priority(priority, session=None):
289     """
290     Returns Priority object for given C{priority name}.
291
292     @type priority: string
293     @param priority: The name of the priority
294
295     @type session: Session
296     @param session: Optional SQLA session object (a temporary one will be
297     generated if not supplied)
298
299     @rtype: Priority
300     @return: Priority object for the given priority
301
302     """
303     if session is None:
304         session = DBConn().session()
305     q = session.query(Priority).filter_by(priority=priority)
306     if q.count() == 0:
307         return None
308     return q.one()
309
310 class Queue(object):
311     def __init__(self, *args, **kwargs):
312         pass
313
314     def __repr__(self):
315         return '<Queue %s>' % self.queue_name
316
317 class QueueBuild(object):
318     def __init__(self, *args, **kwargs):
319         pass
320
321     def __repr__(self):
322         return '<QueueBuild %s (%s)>' % (self.filename, self.queue_id)
323
324 class Section(object):
325     def __init__(self, *args, **kwargs):
326         pass
327
328     def __repr__(self):
329         return '<Section %s>' % self.section
330
331 def get_section(section, session=None):
332     """
333     Returns Section object for given C{section name}.
334
335     @type section: string
336     @param section: The name of the section
337
338     @type session: Session
339     @param session: Optional SQLA session object (a temporary one will be
340     generated if not supplied)
341
342     @rtype: Section
343     @return: Section object for the given section name
344
345     """
346     if session is None:
347         session = DBConn().session()
348     q = session.query(Section).filter_by(section=section)
349     if q.count() == 0:
350         return None
351     return q.one()
352
353 class Source(object):
354     def __init__(self, *args, **kwargs):
355         pass
356
357     def __repr__(self):
358         return '<Source %s (%s)>' % (self.source, self.version)
359
360 def get_source_in_suite(source, suite, session=None):
361     """
362     Returns list of Source objects for a combination of C{source} and C{suite}.
363
364       - B{source} - source package name, eg. I{mailfilter}, I{bbdb}, I{glibc}
365       - B{suite} - a suite name, eg. I{unstable}
366
367     @type source: string
368     @param source: source package name
369
370     @type suite: string
371     @param suite: the suite name
372
373     @rtype: string
374     @return: the version for I{source} in I{suite}
375
376     """
377     if session is None:
378         session = DBConn().session()
379     q = session.query(SrcAssociation)
380     q = q.join('source').filter_by(source=source)
381     q = q.join('suite').filter_by(suite_name=suite)
382     if q.count() == 0:
383         return None
384     # ???: Maybe we should just return the SrcAssociation object instead
385     return q.one().source
386
387 class SrcAssociation(object):
388     def __init__(self, *args, **kwargs):
389         pass
390
391     def __repr__(self):
392         return '<SrcAssociation %s (%s, %s)>' % (self.sa_id, self.source, self.suite)
393
394 class SrcUploader(object):
395     def __init__(self, *args, **kwargs):
396         pass
397
398     def __repr__(self):
399         return '<SrcUploader %s>' % self.uploader_id
400
401 class Suite(object):
402     def __init__(self, *args, **kwargs):
403         pass
404
405     def __repr__(self):
406         return '<Suite %s>' % self.suite_name
407
408 def get_suite(suite, session=None):
409     """
410     Returns Suite object for given C{suite name}.
411
412     @type suite: string
413     @param suite: The name of the suite
414
415     @type session: Session
416     @param session: Optional SQLA session object (a temporary one will be
417     generated if not supplied)
418
419     @rtype: Suite
420     @return: Suite object for the requested suite name (None if not presenT)
421
422     """
423     if session is None:
424         session = DBConn().session()
425     q = session.query(Suite).filter_by(suite_name=suite)
426     if q.count() == 0:
427         return None
428     return q.one()
429
430 class SuiteArchitecture(object):
431     def __init__(self, *args, **kwargs):
432         pass
433
434     def __repr__(self):
435         return '<SuiteArchitecture (%s, %s)>' % (self.suite_id, self.arch_id)
436
437 class Uid(object):
438     def __init__(self, *args, **kwargs):
439         pass
440
441     def __repr__(self):
442         return '<Uid %s (%s)>' % (self.uid, self.name)
443
444 ################################################################################
445
446 class DBConn(Singleton):
447     """
448     database module init.
449     """
450     def __init__(self, *args, **kwargs):
451         super(DBConn, self).__init__(*args, **kwargs)
452
453     def _startup(self, *args, **kwargs):
454         self.debug = False
455         if kwargs.has_key('debug'):
456             self.debug = True
457         self.__createconn()
458
459     def __setuptables(self):
460         self.tbl_architecture = Table('architecture', self.db_meta, autoload=True)
461         self.tbl_archive = Table('archive', self.db_meta, autoload=True)
462         self.tbl_bin_associations = Table('bin_associations', self.db_meta, autoload=True)
463         self.tbl_binaries = Table('binaries', self.db_meta, autoload=True)
464         self.tbl_component = Table('component', self.db_meta, autoload=True)
465         self.tbl_config = Table('config', self.db_meta, autoload=True)
466         self.tbl_content_associations = Table('content_associations', self.db_meta, autoload=True)
467         self.tbl_content_file_names = Table('content_file_names', self.db_meta, autoload=True)
468         self.tbl_content_file_paths = Table('content_file_paths', self.db_meta, autoload=True)
469         self.tbl_dsc_files = Table('dsc_files', self.db_meta, autoload=True)
470         self.tbl_files = Table('files', self.db_meta, autoload=True)
471         self.tbl_fingerprint = Table('fingerprint', self.db_meta, autoload=True)
472         self.tbl_keyrings = Table('keyrings', self.db_meta, autoload=True)
473         self.tbl_location = Table('location', self.db_meta, autoload=True)
474         self.tbl_maintainer = Table('maintainer', self.db_meta, autoload=True)
475         self.tbl_override = Table('override', self.db_meta, autoload=True)
476         self.tbl_override_type = Table('override_type', self.db_meta, autoload=True)
477         self.tbl_pending_content_associations = Table('pending_content_associations', self.db_meta, autoload=True)
478         self.tbl_priority = Table('priority', self.db_meta, autoload=True)
479         self.tbl_queue = Table('queue', self.db_meta, autoload=True)
480         self.tbl_queue_build = Table('queue_build', self.db_meta, autoload=True)
481         self.tbl_section = Table('section', self.db_meta, autoload=True)
482         self.tbl_source = Table('source', self.db_meta, autoload=True)
483         self.tbl_src_associations = Table('src_associations', self.db_meta, autoload=True)
484         self.tbl_src_uploaders = Table('src_uploaders', self.db_meta, autoload=True)
485         self.tbl_suite = Table('suite', self.db_meta, autoload=True)
486         self.tbl_suite_architectures = Table('suite_architectures', self.db_meta, autoload=True)
487         self.tbl_uid = Table('uid', self.db_meta, autoload=True)
488
489     def __setupmappers(self):
490         mapper(Architecture, self.tbl_architecture,
491                properties = dict(arch_id = self.tbl_architecture.c.id))
492
493         mapper(Archive, self.tbl_archive,
494                properties = dict(archive_id = self.tbl_archive.c.id,
495                                  archive_name = self.tbl_archive.c.name))
496
497         mapper(BinAssociation, self.tbl_bin_associations,
498                properties = dict(ba_id = self.tbl_bin_associations.c.id,
499                                  suite_id = self.tbl_bin_associations.c.suite,
500                                  suite = relation(Suite),
501                                  binary_id = self.tbl_bin_associations.c.bin,
502                                  binary = relation(Binary)))
503
504         mapper(Binary, self.tbl_binaries,
505                properties = dict(binary_id = self.tbl_binaries.c.id,
506                                  package = self.tbl_binaries.c.package,
507                                  version = self.tbl_binaries.c.version,
508                                  maintainer_id = self.tbl_binaries.c.maintainer,
509                                  maintainer = relation(Maintainer),
510                                  source_id = self.tbl_binaries.c.source,
511                                  source = relation(Source),
512                                  arch_id = self.tbl_binaries.c.architecture,
513                                  architecture = relation(Architecture),
514                                  poolfile_id = self.tbl_binaries.c.file,
515                                  poolfile = relation(PoolFile),
516                                  binarytype = self.tbl_binaries.c.type,
517                                  fingerprint_id = self.tbl_binaries.c.sig_fpr,
518                                  fingerprint = relation(Fingerprint),
519                                  install_date = self.tbl_binaries.c.install_date,
520                                  binassociations = relation(BinAssociation,
521                                                             primaryjoin=(self.tbl_binaries.c.id==self.tbl_bin_associations.c.bin))))
522
523         mapper(Component, self.tbl_component,
524                properties = dict(component_id = self.tbl_component.c.id,
525                                  component_name = self.tbl_component.c.name))
526
527         mapper(DBConfig, self.tbl_config,
528                properties = dict(config_id = self.tbl_config.c.id))
529
530         mapper(ContentAssociations, self.tbl_content_associations,
531                properties = dict(ca_id = self.tbl_content_associations.c.id,
532                                  filename_id = self.tbl_content_associations.c.filename,
533                                  filename    = relation(ContentFilename),
534                                  filepath_id = self.tbl_content_associations.c.filepath,
535                                  filepath    = relation(ContentFilepath),
536                                  binary_id   = self.tbl_content_associations.c.binary_pkg,
537                                  binary      = relation(Binary)))
538
539
540         mapper(ContentFilename, self.tbl_content_file_names,
541                properties = dict(cafilename_id = self.tbl_content_file_names.c.id,
542                                  filename = self.tbl_content_file_names.c.file))
543
544         mapper(ContentFilepath, self.tbl_content_file_paths,
545                properties = dict(cafilepath_id = self.tbl_content_file_paths.c.id,
546                                  filepath = self.tbl_content_file_paths.c.path))
547
548         mapper(DSCFile, self.tbl_dsc_files,
549                properties = dict(dscfile_id = self.tbl_dsc_files.c.id,
550                                  source_id = self.tbl_dsc_files.c.source,
551                                  source = relation(Source),
552                                  poolfile_id = self.tbl_dsc_files.c.file,
553                                  poolfile = relation(PoolFile)))
554
555         mapper(PoolFile, self.tbl_files,
556                properties = dict(file_id = self.tbl_files.c.id,
557                                  filesize = self.tbl_files.c.size,
558                                  location_id = self.tbl_files.c.location,
559                                  location = relation(Location)))
560
561         mapper(Fingerprint, self.tbl_fingerprint,
562                properties = dict(fingerprint_id = self.tbl_fingerprint.c.id,
563                                  uid_id = self.tbl_fingerprint.c.uid,
564                                  uid = relation(Uid),
565                                  keyring_id = self.tbl_fingerprint.c.keyring,
566                                  keyring = relation(Keyring)))
567
568         mapper(Keyring, self.tbl_keyrings,
569                properties = dict(keyring_name = self.tbl_keyrings.c.name,
570                                  keyring_id = self.tbl_keyrings.c.id))
571
572         mapper(Location, self.tbl_location,
573                properties = dict(location_id = self.tbl_location.c.id,
574                                  component_id = self.tbl_location.c.component,
575                                  component = relation(Component),
576                                  archive_id = self.tbl_location.c.archive,
577                                  archive = relation(Archive),
578                                  archive_type = self.tbl_location.c.type))
579
580         mapper(Maintainer, self.tbl_maintainer,
581                properties = dict(maintainer_id = self.tbl_maintainer.c.id))
582
583         mapper(Override, self.tbl_override,
584                properties = dict(suite_id = self.tbl_override.c.suite,
585                                  suite = relation(Suite),
586                                  component_id = self.tbl_override.c.component,
587                                  component = relation(Component),
588                                  priority_id = self.tbl_override.c.priority,
589                                  priority = relation(Priority),
590                                  section_id = self.tbl_override.c.section,
591                                  section = relation(Section),
592                                  overridetype_id = self.tbl_override.c.type,
593                                  overridetype = relation(OverrideType)))
594
595         mapper(OverrideType, self.tbl_override_type,
596                properties = dict(overridetype = self.tbl_override_type.c.type,
597                                  overridetype_id = self.tbl_override_type.c.id))
598
599         mapper(PendingContentAssociation, self.tbl_pending_content_associations,
600                properties = dict(pca_id = self.tbl_pending_content_associations.c.id,
601                                  filepath_id = self.tbl_pending_content_associations.c.filepath,
602                                  filepath = relation(ContentFilepath),
603                                  filename_id = self.tbl_pending_content_associations.c.filename,
604                                  filename = relation(ContentFilename)))
605
606         mapper(Priority, self.tbl_priority,
607                properties = dict(priority_id = self.tbl_priority.c.id))
608
609         mapper(Queue, self.tbl_queue,
610                properties = dict(queue_id = self.tbl_queue.c.id))
611
612         mapper(QueueBuild, self.tbl_queue_build,
613                properties = dict(suite_id = self.tbl_queue_build.c.suite,
614                                  queue_id = self.tbl_queue_build.c.queue,
615                                  queue = relation(Queue)))
616
617         mapper(Section, self.tbl_section,
618                properties = dict(section_id = self.tbl_section.c.id))
619
620         mapper(Source, self.tbl_source,
621                properties = dict(source_id = self.tbl_source.c.id,
622                                  version = self.tbl_source.c.version,
623                                  maintainer_id = self.tbl_source.c.maintainer,
624                                  maintainer = relation(Maintainer,
625                                                        primaryjoin=(self.tbl_source.c.maintainer==self.tbl_maintainer.c.id)),
626                                  poolfile_id = self.tbl_source.c.file,
627                                  poolfile = relation(PoolFile),
628                                  fingerprint_id = self.tbl_source.c.sig_fpr,
629                                  fingerprint = relation(Fingerprint),
630                                  changedby_id = self.tbl_source.c.changedby,
631                                  changedby = relation(Maintainer,
632                                                       primaryjoin=(self.tbl_source.c.changedby==self.tbl_maintainer.c.id)),
633                                  srcfiles = relation(DSCFile,
634                                                      primaryjoin=(self.tbl_source.c.id==self.tbl_dsc_files.c.source)),
635                                  srcassociations = relation(SrcAssociation,
636                                                             primaryjoin=(self.tbl_source.c.id==self.tbl_src_associations.c.source))))
637
638         mapper(SrcAssociation, self.tbl_src_associations,
639                properties = dict(sa_id = self.tbl_src_associations.c.id,
640                                  suite_id = self.tbl_src_associations.c.suite,
641                                  suite = relation(Suite),
642                                  source_id = self.tbl_src_associations.c.source,
643                                  source = relation(Source)))
644
645         mapper(SrcUploader, self.tbl_src_uploaders,
646                properties = dict(uploader_id = self.tbl_src_uploaders.c.id,
647                                  source_id = self.tbl_src_uploaders.c.source,
648                                  source = relation(Source,
649                                                    primaryjoin=(self.tbl_src_uploaders.c.source==self.tbl_source.c.id)),
650                                  maintainer_id = self.tbl_src_uploaders.c.maintainer,
651                                  maintainer = relation(Maintainer,
652                                                        primaryjoin=(self.tbl_src_uploaders.c.maintainer==self.tbl_maintainer.c.id))))
653
654         mapper(Suite, self.tbl_suite,
655                properties = dict(suite_id = self.tbl_suite.c.id))
656
657         mapper(SuiteArchitecture, self.tbl_suite_architectures,
658                properties = dict(suite_id = self.tbl_suite_architectures.c.suite,
659                                  suite = relation(Suite),
660                                  arch_id = self.tbl_suite_architectures.c.architecture,
661                                  architecture = relation(Architecture)))
662
663         mapper(Uid, self.tbl_uid,
664                properties = dict(uid_id = self.tbl_uid.c.id))
665
666     ## Connection functions
667     def __createconn(self):
668         from config import Config
669         cnf = Config()
670         if cnf["DB::Host"]:
671             # TCP/IP
672             connstr = "postgres://%s" % cnf["DB::Host"]
673             if cnf["DB::Port"] and cnf["DB::Port"] != "-1":
674                 connstr += ":%s" % cnf["DB::Port"]
675             connstr += "/%s" % cnf["DB::Name"]
676         else:
677             # Unix Socket
678             connstr = "postgres:///%s" % cnf["DB::Name"]
679             if cnf["DB::Port"] and cnf["DB::Port"] != "-1":
680                 connstr += "?port=%s" % cnf["DB::Port"]
681
682         self.db_pg   = create_engine(connstr, echo=self.debug)
683         self.db_meta = MetaData()
684         self.db_meta.bind = self.db_pg
685         self.db_smaker = sessionmaker(bind=self.db_pg,
686                                       autoflush=True,
687                                       transactional=True)
688
689         self.__setuptables()
690         self.__setupmappers()
691
692     def session(self):
693         return self.db_smaker()
694
695     def prepare(self,name,statement):
696         if not self.prepared_statements.has_key(name):
697             pgc.execute(statement)
698             self.prepared_statements[name] = statement
699
700
701     def get_location_id(self, location, component, archive):
702         """
703         Returns database id for the location behind the given combination of
704           - B{location} - the path of the location, eg. I{/srv/ftp.debian.org/ftp/pool/}
705           - B{component} - the id of the component as returned by L{get_component_id}
706           - B{archive} - the id of the archive as returned by L{get_archive_id}
707         Results are kept in a cache during runtime to minimize database queries.
708
709         @type location: string
710         @param location: the path of the location
711
712         @type component: int
713         @param component: the id of the component
714
715         @type archive: int
716         @param archive: the id of the archive
717
718         @rtype: int
719         @return: the database id for the location
720
721         """
722
723         archive_id = self.get_archive_id(archive)
724
725         if not archive_id:
726             return None
727
728         res = None
729
730         if component:
731             component_id = self.get_component_id(component)
732             if component_id:
733                 res = self.__get_single_id("SELECT id FROM location WHERE path=%(location)s AND component=%(component)s AND archive=%(archive)s",
734                         {'location': location,
735                          'archive': int(archive_id),
736                          'component': component_id}, cachename='location')
737         else:
738             res = self.__get_single_id("SELECT id FROM location WHERE path=%(location)s AND archive=%(archive)d",
739                     {'location': location, 'archive': archive_id, 'component': ''}, cachename='location')
740
741         return res
742
743
744
745 def get_files_id (self, filename, size, md5sum, location_id):
746     """
747     Returns -1, -2 or the file_id for filename, if its C{size} and C{md5sum} match an
748     existing copy.
749
750     The database is queried using the C{filename} and C{location_id}. If a file does exist
751     at that location, the existing size and md5sum are checked against the provided
752     parameters. A size or checksum mismatch returns -2. If more than one entry is
753     found within the database, a -1 is returned, no result returns None, otherwise
754     the file id.
755
756     @type filename: string
757     @param filename: the filename of the file to check against the DB
758
759     @type size: int
760     @param size: the size of the file to check against the DB
761
762     @type md5sum: string
763     @param md5sum: the md5sum of the file to check against the DB
764
765     @type location_id: int
766     @param location_id: the id of the location as returned by L{get_location_id}
767
768     @rtype: int / None
769     @return: Various return values are possible:
770                - -2: size/checksum error
771                - -1: more than one file found in database
772                - None: no file found in database
773                - int: file id
774
775     """
776     values = {'filename' : filename,
777               'location' : location_id}
778
779     if not res:
780         query = """SELECT id, size, md5sum
781                    FROM files
782                    WHERE filename = %(filename)s AND location = %(location)s"""
783
784         cursor = self.db_con.cursor()
785         cursor.execute( query, values )
786
787         if cursor.rowcount == 0:
788             res = None
789
790         elif cursor.rowcount != 1:
791             res = -1
792
793         else:
794             row = cursor.fetchone()
795
796             if row[1] != int(size) or row[2] != md5sum:
797                 res =  -2
798
799             else:
800                 res = row[0]
801
802     return res
803
804
805 def get_or_set_contents_file_id(self, filename):
806     """
807     Returns database id for given filename.
808
809     If no matching file is found, a row is inserted.
810
811     @type filename: string
812     @param filename: The filename
813
814     @rtype: int
815     @return: the database id for the given component
816     """
817     try:
818         values={'value': filename}
819         query = "SELECT id FROM content_file_names WHERE file = %(value)s"
820         if not id:
821             c = self.db_con.cursor()
822             c.execute( "INSERT INTO content_file_names VALUES (DEFAULT, %(value)s) RETURNING id",
823                        values )
824
825             id = c.fetchone()[0]
826
827         return id
828     except:
829         traceback.print_exc()
830         raise
831
832 def get_or_set_contents_path_id(self, path):
833     """
834     Returns database id for given path.
835
836     If no matching file is found, a row is inserted.
837
838     @type path: string
839     @param path: The filename
840
841     @rtype: int
842     @return: the database id for the given component
843     """
844     try:
845         values={'value': path}
846         query = "SELECT id FROM content_file_paths WHERE path = %(value)s"
847         if not id:
848             c = self.db_con.cursor()
849             c.execute( "INSERT INTO content_file_paths VALUES (DEFAULT, %(value)s) RETURNING id",
850                        values )
851
852             id = c.fetchone()[0]
853
854         return id
855     except:
856         traceback.print_exc()
857         raise
858
859 def get_suite_architectures(self, suite):
860     """
861     Returns list of architectures for C{suite}.
862
863     @type suite: string, int
864     @param suite: the suite name or the suite_id
865
866     @rtype: list
867     @return: the list of architectures for I{suite}
868     """
869
870     suite_id = None
871     if type(suite) == str:
872         suite_id = self.get_suite_id(suite)
873     elif type(suite) == int:
874         suite_id = suite
875     else:
876         return None
877
878     c = self.db_con.cursor()
879     c.execute( """SELECT a.arch_string FROM suite_architectures sa
880                   JOIN architecture a ON (a.id = sa.architecture)
881                   WHERE suite='%s'""" % suite_id )
882
883     return map(lambda x: x[0], c.fetchall())
884
885 def insert_content_paths(self, bin_id, fullpaths):
886     """
887     Make sure given path is associated with given binary id
888
889     @type bin_id: int
890     @param bin_id: the id of the binary
891     @type fullpaths: list
892     @param fullpaths: the list of paths of the file being associated with the binary
893
894     @return: True upon success
895     """
896
897     c = self.db_con.cursor()
898
899     c.execute("BEGIN WORK")
900     try:
901
902         for fullpath in fullpaths:
903             (path, file) = os.path.split(fullpath)
904
905             # Get the necessary IDs ...
906             file_id = self.get_or_set_contents_file_id(file)
907             path_id = self.get_or_set_contents_path_id(path)
908
909             c.execute("""INSERT INTO content_associations
910                            (binary_pkg, filepath, filename)
911                        VALUES ( '%d', '%d', '%d')""" % (bin_id, path_id, file_id) )
912
913         c.execute("COMMIT")
914         return True
915     except:
916         traceback.print_exc()
917         c.execute("ROLLBACK")
918         return False
919
920 def insert_pending_content_paths(self, package, fullpaths):
921     """
922     Make sure given paths are temporarily associated with given
923     package
924
925     @type package: dict
926     @param package: the package to associate with should have been read in from the binary control file
927     @type fullpaths: list
928     @param fullpaths: the list of paths of the file being associated with the binary
929
930     @return: True upon success
931     """
932
933     c = self.db_con.cursor()
934
935     c.execute("BEGIN WORK")
936     try:
937         arch_id = self.get_architecture_id(package['Architecture'])
938
939         # Remove any already existing recorded files for this package
940         c.execute("""DELETE FROM pending_content_associations
941                      WHERE package=%(Package)s
942                      AND version=%(Version)s
943                      AND architecture=%(ArchID)s""", {'Package': package['Package'],
944                                                       'Version': package['Version'],
945                                                       'ArchID':  arch_id})
946
947         for fullpath in fullpaths:
948             (path, file) = os.path.split(fullpath)
949
950             if path.startswith( "./" ):
951                 path = path[2:]
952             # Get the necessary IDs ...
953             file_id = self.get_or_set_contents_file_id(file)
954             path_id = self.get_or_set_contents_path_id(path)
955
956             c.execute("""INSERT INTO pending_content_associations
957                            (package, version, architecture, filepath, filename)
958                         VALUES (%%(Package)s, %%(Version)s, '%d', '%d', '%d')"""
959                 % (arch_id, path_id, file_id), package )
960
961         c.execute("COMMIT")
962         return True
963     except:
964         traceback.print_exc()
965         c.execute("ROLLBACK")
966         return False