]> git.decadent.org.uk Git - dak.git/blob - daklib/dbconn.py
Fix overridetype function
[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 # Don't remove this, we re-export the exceptions to scripts which import us
44 from sqlalchemy.exc import *
45
46 from singleton import Singleton
47 from textutils import fix_maintainer
48
49 ################################################################################
50
51 __all__ = ['IntegrityError', 'SQLAlchemyError']
52
53 ################################################################################
54
55 class Architecture(object):
56     def __init__(self, *args, **kwargs):
57         pass
58
59     def __repr__(self):
60         return '<Architecture %s>' % self.arch_string
61
62 __all__.append('Architecture')
63
64 def get_architecture(architecture, session=None):
65     """
66     Returns database id for given C{architecture}.
67
68     @type architecture: string
69     @param architecture: The name of the architecture
70
71     @type session: Session
72     @param session: Optional SQLA session object (a temporary one will be
73     generated if not supplied)
74
75     @rtype: Architecture
76     @return: Architecture object for the given arch (None if not present)
77
78     """
79     if session is None:
80         session = DBConn().session()
81     q = session.query(Architecture).filter_by(arch_string=architecture)
82     if q.count() == 0:
83         return None
84     return q.one()
85
86 __all__.append('get_architecture')
87
88 def get_architecture_suites(architecture, session=None):
89     """
90     Returns list of Suite objects for given C{architecture} name
91
92     @type source: str
93     @param source: Architecture name to search for
94
95     @type session: Session
96     @param session: Optional SQL session object (a temporary one will be
97     generated if not supplied)
98
99     @rtype: list
100     @return: list of Suite objects for the given name (may be empty)
101     """
102
103     if session is None:
104         session = DBConn().session()
105
106     q = session.query(Suite)
107     q = q.join(SuiteArchitecture)
108     q = q.join(Architecture).filter_by(arch_string=architecture).order_by('suite_name')
109     return q.all()
110
111 __all__.append('get_architecture_suites')
112
113 ################################################################################
114
115 class Archive(object):
116     def __init__(self, *args, **kwargs):
117         pass
118
119     def __repr__(self):
120         return '<Archive %s>' % self.name
121
122 __all__.append('Archive')
123
124 def get_archive(archive, session=None):
125     """
126     returns database id for given c{archive}.
127
128     @type archive: string
129     @param archive: the name of the arhive
130
131     @type session: Session
132     @param session: Optional SQLA session object (a temporary one will be
133     generated if not supplied)
134
135     @rtype: Archive
136     @return: Archive object for the given name (None if not present)
137
138     """
139     archive = archive.lower()
140     if session is None:
141         session = DBConn().session()
142     q = session.query(Archive).filter_by(archive_name=archive)
143     if q.count() == 0:
144         return None
145     return q.one()
146
147 __all__.append('get_archive')
148
149 ################################################################################
150
151 class BinAssociation(object):
152     def __init__(self, *args, **kwargs):
153         pass
154
155     def __repr__(self):
156         return '<BinAssociation %s (%s, %s)>' % (self.ba_id, self.binary, self.suite)
157
158 __all__.append('BinAssociation')
159
160 ################################################################################
161
162 class DBBinary(object):
163     def __init__(self, *args, **kwargs):
164         pass
165
166     def __repr__(self):
167         return '<DBBinary %s (%s, %s)>' % (self.package, self.version, self.architecture)
168
169 __all__.append('DBBinary')
170
171 def get_binary_from_id(id, session=None):
172     """
173     Returns DBBinary object for given C{id}
174
175     @type id: int
176     @param id: Id of the required binary
177
178     @type session: Session
179     @param session: Optional SQLA session object (a temporary one will be
180     generated if not supplied)
181
182     @rtype: DBBinary
183     @return: DBBinary object for the given binary (None if not present)
184     """
185     if session is None:
186         session = DBConn().session()
187     q = session.query(DBBinary).filter_by(binary_id=id)
188     if q.count() == 0:
189         return None
190     return q.one()
191
192 __all__.append('get_binary_from_id')
193
194 def get_binaries_from_name(package, session=None):
195     """
196     Returns list of DBBinary objects for given C{package} name
197
198     @type package: str
199     @param package: DBBinary package name to search for
200
201     @type session: Session
202     @param session: Optional SQL session object (a temporary one will be
203     generated if not supplied)
204
205     @rtype: list
206     @return: list of DBBinary objects for the given name (may be empty)
207     """
208     if session is None:
209         session = DBConn().session()
210     return session.query(DBBinary).filter_by(package=package).all()
211
212 __all__.append('get_binaries_from_name')
213
214 def get_binary_components(package, suitename, arch, session=None):
215 # Check for packages that have moved from one component to another
216     query = """SELECT c.name FROM binaries b, bin_associations ba, suite s, location l, component c, architecture a, files f
217     WHERE b.package=:package AND s.suite_name=:suitename
218       AND (a.arch_string = :arch OR a.arch_string = 'all')
219       AND ba.bin = b.id AND ba.suite = s.id AND b.architecture = a.id
220       AND f.location = l.id
221       AND l.component = c.id
222       AND b.file = f.id"""
223
224     vals = {'package': package, 'suitename': suitename, 'arch': arch}
225
226     if session is None:
227         session = DBConn().session()
228     return session.execute(query, vals)
229
230 __all__.append('get_binary_components')
231 ################################################################################
232
233 class Component(object):
234     def __init__(self, *args, **kwargs):
235         pass
236
237     def __repr__(self):
238         return '<Component %s>' % self.component_name
239
240
241 __all__.append('Component')
242
243 def get_component(component, session=None):
244     """
245     Returns database id for given C{component}.
246
247     @type component: string
248     @param component: The name of the override type
249
250     @rtype: int
251     @return: the database id for the given component
252
253     """
254     component = component.lower()
255     if session is None:
256         session = DBConn().session()
257     q = session.query(Component).filter_by(component_name=component)
258     if q.count() == 0:
259         return None
260     return q.one()
261
262 __all__.append('get_component')
263
264 ################################################################################
265
266 class DBConfig(object):
267     def __init__(self, *args, **kwargs):
268         pass
269
270     def __repr__(self):
271         return '<DBConfig %s>' % self.name
272
273 __all__.append('DBConfig')
274
275 ################################################################################
276
277 class ContentFilename(object):
278     def __init__(self, *args, **kwargs):
279         pass
280
281     def __repr__(self):
282         return '<ContentFilename %s>' % self.filename
283
284 __all__.append('ContentFilename')
285
286 def get_or_set_contents_file_id(filename, session=None):
287     """
288     Returns database id for given filename.
289
290     If no matching file is found, a row is inserted.
291
292     @type filename: string
293     @param filename: The filename
294     @type session: SQLAlchemy
295     @param session: Optional SQL session object (a temporary one will be
296     generated if not supplied)
297
298     @rtype: int
299     @return: the database id for the given component
300     """
301     if session is None:
302         session = DBConn().session()
303
304     try:
305         q = session.query(ContentFilename).filter_by(filename=filename)
306         if q.count() < 1:
307             cf = ContentFilename()
308             cf.filename = filename
309             session.add(cf)
310             return cf.cafilename_id
311         else:
312             return q.one().cafilename_id
313
314     except:
315         traceback.print_exc()
316         raise
317
318 __all__.append('get_or_set_contents_file_id')
319
320 ################################################################################
321
322 class ContentFilepath(object):
323     def __init__(self, *args, **kwargs):
324         pass
325
326     def __repr__(self):
327         return '<ContentFilepath %s>' % self.filepath
328
329 __all__.append('ContentFilepath')
330
331 def get_or_set_contents_path_id(filepath, session):
332     """
333     Returns database id for given path.
334
335     If no matching file is found, a row is inserted.
336
337     @type filename: string
338     @param filename: The filepath
339     @type session: SQLAlchemy
340     @param session: Optional SQL session object (a temporary one will be
341     generated if not supplied)
342
343     @rtype: int
344     @return: the database id for the given path
345     """
346     if session is None:
347         session = DBConn().session()
348
349     try:
350         q = session.query(ContentFilepath).filter_by(filepath=filepath)
351         if q.count() < 1:
352             cf = ContentFilepath()
353             cf.filepath = filepath
354             session.add(cf)
355             return cf.cafilepath_id
356         else:
357             return q.one().cafilepath_id
358
359     except:
360         traceback.print_exc()
361         raise
362
363 __all__.append('get_or_set_contents_path_id')
364
365 ################################################################################
366
367 class ContentAssociation(object):
368     def __init__(self, *args, **kwargs):
369         pass
370
371     def __repr__(self):
372         return '<ContentAssociation %s>' % self.ca_id
373
374 __all__.append('ContentAssociation')
375
376 def insert_content_paths(binary_id, fullpaths, session=None):
377     """
378     Make sure given path is associated with given binary id
379
380     @type binary_id: int
381     @param binary_id: the id of the binary
382     @type fullpaths: list
383     @param fullpaths: the list of paths of the file being associated with the binary
384     @type session: SQLAlchemy session
385     @param session: Optional SQLAlchemy session.  If this is passed, the caller
386     is responsible for ensuring a transaction has begun and committing the
387     results or rolling back based on the result code.  If not passed, a commit
388     will be performed at the end of the function
389
390     @return: True upon success
391     """
392
393     privatetrans = False
394
395     if session is None:
396         session = DBConn().session()
397         privatetrans = True
398
399     try:
400         for fullpath in fullpaths:
401             (path, file) = os.path.split(fullpath)
402
403             # Get the necessary IDs ...
404             ca = ContentAssociation()
405             ca.binary_id = binary_id
406             ca.filename_id = get_or_set_contents_file_id(file)
407             ca.filepath_id = get_or_set_contents_path_id(path)
408             session.add(ca)
409
410         # Only commit if we set up the session ourself
411         if privatetrans:
412             session.commit()
413
414         return True
415     except:
416         traceback.print_exc()
417
418         # Only rollback if we set up the session ourself
419         if privatetrans:
420             session.rollback()
421
422         return False
423
424 __all__.append('insert_content_paths')
425
426 ################################################################################
427
428 class DSCFile(object):
429     def __init__(self, *args, **kwargs):
430         pass
431
432     def __repr__(self):
433         return '<DSCFile %s>' % self.dscfile_id
434
435 __all__.append('DSCFile')
436
437 ################################################################################
438
439 class PoolFile(object):
440     def __init__(self, *args, **kwargs):
441         pass
442
443     def __repr__(self):
444         return '<PoolFile %s>' % self.filename
445
446 __all__.append('PoolFile')
447
448 def get_poolfile_by_name(filename, location_id=None, session=None):
449     """
450     Returns an array of PoolFile objects for the given filename and
451     (optionally) location_id
452
453     @type filename: string
454     @param filename: the filename of the file to check against the DB
455
456     @type location_id: int
457     @param location_id: the id of the location to look in (optional)
458
459     @rtype: array
460     @return: array of PoolFile objects
461     """
462
463     if session is not None:
464         session = DBConn().session()
465
466     q = session.query(PoolFile).filter_by(filename=filename)
467
468     if location_id is not None:
469         q = q.join(Location).filter_by(location_id=location_id)
470
471     return q.all()
472
473 __all__.append('get_poolfile_by_name')
474
475 ################################################################################
476
477 class Fingerprint(object):
478     def __init__(self, *args, **kwargs):
479         pass
480
481     def __repr__(self):
482         return '<Fingerprint %s>' % self.fingerprint
483
484 __all__.append('Fingerprint')
485
486 ################################################################################
487
488 class Keyring(object):
489     def __init__(self, *args, **kwargs):
490         pass
491
492     def __repr__(self):
493         return '<Keyring %s>' % self.keyring_name
494
495 __all__.append('Keyring')
496
497 ################################################################################
498
499 class Location(object):
500     def __init__(self, *args, **kwargs):
501         pass
502
503     def __repr__(self):
504         return '<Location %s (%s)>' % (self.path, self.location_id)
505
506 __all__.append('Location')
507
508 def get_location(location, component=None, archive=None, session=None):
509     """
510     Returns Location object for the given combination of location, component
511     and archive
512
513     @type location: string
514     @param location: the path of the location, e.g. I{/srv/ftp.debian.org/ftp/pool/}
515
516     @type component: string
517     @param component: the component name (if None, no restriction applied)
518
519     @type archive: string
520     @param archive_id: the archive name (if None, no restriction applied)
521
522     @rtype: Location / None
523     @return: Either a Location object or None if one can't be found
524     """
525
526     if session is None:
527         session = DBConn().session()
528
529     q = session.query(Location).filter_by(path=location)
530
531     if archive is not None:
532         q = q.join(Archive).filter_by(archive_name=archive)
533
534     if component is not None:
535         q = q.join(Component).filter_by(component_name=component)
536
537     if q.count() < 1:
538         return None
539     else:
540         return q.one()
541
542 __all__.append('get_location')
543
544 ################################################################################
545
546 class Maintainer(object):
547     def __init__(self, *args, **kwargs):
548         pass
549
550     def __repr__(self):
551         return '''<Maintainer '%s' (%s)>''' % (self.name, self.maintainer_id)
552
553     def get_split_maintainer(self):
554         if not hasattr(self, 'name') or self.name is None:
555             return ('', '', '', '')
556
557         return fix_maintainer(self.name.strip())
558
559 __all__.append('Maintainer')
560
561 ################################################################################
562
563 class Override(object):
564     def __init__(self, *args, **kwargs):
565         pass
566
567     def __repr__(self):
568         return '<Override %s (%s)>' % (self.package, self.suite_id)
569
570 __all__.append('Override')
571
572 ################################################################################
573
574 class OverrideType(object):
575     def __init__(self, *args, **kwargs):
576         pass
577
578     def __repr__(self):
579         return '<OverrideType %s>' % self.overridetype
580
581 __all__.append('OverrideType')
582
583 def get_override_type(override_type, session=None):
584     """
585     Returns OverrideType object for given C{override type}.
586
587     @type override_type: string
588     @param override_type: The name of the override type
589
590     @type session: Session
591     @param session: Optional SQLA session object (a temporary one will be
592     generated if not supplied)
593
594     @rtype: int
595     @return: the database id for the given override type
596
597     """
598     if session is None:
599         session = DBConn().session()
600     q = session.query(OverrideType).filter_by(overridetype=override_type)
601     if q.count() == 0:
602         return None
603     return q.one()
604
605 __all__.append('get_override_type')
606
607 ################################################################################
608
609 class PendingContentAssociation(object):
610     def __init__(self, *args, **kwargs):
611         pass
612
613     def __repr__(self):
614         return '<PendingContentAssociation %s>' % self.pca_id
615
616 __all__.append('PendingContentAssociation')
617
618 def insert_pending_content_paths(package, fullpaths, session=None):
619     """
620     Make sure given paths are temporarily associated with given
621     package
622
623     @type package: dict
624     @param package: the package to associate with should have been read in from the binary control file
625     @type fullpaths: list
626     @param fullpaths: the list of paths of the file being associated with the binary
627     @type session: SQLAlchemy session
628     @param session: Optional SQLAlchemy session.  If this is passed, the caller
629     is responsible for ensuring a transaction has begun and committing the
630     results or rolling back based on the result code.  If not passed, a commit
631     will be performed at the end of the function
632
633     @return: True upon success, False if there is a problem
634     """
635
636     privatetrans = False
637
638     if session is None:
639         session = DBConn().session()
640         privatetrans = True
641
642     try:
643         arch = get_architecture(package['Architecture'], session)
644         arch_id = arch.arch_id
645
646         # Remove any already existing recorded files for this package
647         q = session.query(PendingContentAssociation)
648         q = q.filter_by(package=package['Package'])
649         q = q.filter_by(version=package['Version'])
650         q = q.filter_by(architecture=arch_id)
651         q.delete()
652
653         # Insert paths
654         for fullpath in fullpaths:
655             (path, file) = os.path.split(fullpath)
656
657             if path.startswith( "./" ):
658                 path = path[2:]
659
660             pca = PendingContentAssociation()
661             pca.package = package['Package']
662             pca.version = package['Version']
663             pca.filename_id = get_or_set_contents_file_id(file, session)
664             pca.filepath_id = get_or_set_contents_path_id(path, session)
665             pca.architecture = arch_id
666             session.add(pca)
667
668         # Only commit if we set up the session ourself
669         if privatetrans:
670             session.commit()
671
672         return True
673     except:
674         traceback.print_exc()
675
676         # Only rollback if we set up the session ourself
677         if privatetrans:
678             session.rollback()
679
680         return False
681
682 __all__.append('insert_pending_content_paths')
683
684 ################################################################################
685
686 class Priority(object):
687     def __init__(self, *args, **kwargs):
688         pass
689
690     def __repr__(self):
691         return '<Priority %s (%s)>' % (self.priority, self.priority_id)
692
693 __all__.append('Priority')
694
695 def get_priority(priority, session=None):
696     """
697     Returns Priority object for given C{priority name}.
698
699     @type priority: string
700     @param priority: The name of the priority
701
702     @type session: Session
703     @param session: Optional SQLA session object (a temporary one will be
704     generated if not supplied)
705
706     @rtype: Priority
707     @return: Priority object for the given priority
708
709     """
710     if session is None:
711         session = DBConn().session()
712     q = session.query(Priority).filter_by(priority=priority)
713     if q.count() == 0:
714         return None
715     return q.one()
716
717 __all__.append('get_priority')
718
719 ################################################################################
720
721 class Queue(object):
722     def __init__(self, *args, **kwargs):
723         pass
724
725     def __repr__(self):
726         return '<Queue %s>' % self.queue_name
727
728 __all__.append('Queue')
729
730 ################################################################################
731
732 class QueueBuild(object):
733     def __init__(self, *args, **kwargs):
734         pass
735
736     def __repr__(self):
737         return '<QueueBuild %s (%s)>' % (self.filename, self.queue_id)
738
739 __all__.append('QueueBuild')
740
741 ################################################################################
742
743 class Section(object):
744     def __init__(self, *args, **kwargs):
745         pass
746
747     def __repr__(self):
748         return '<Section %s>' % self.section
749
750 __all__.append('Section')
751
752 def get_section(section, session=None):
753     """
754     Returns Section object for given C{section name}.
755
756     @type section: string
757     @param section: The name of the section
758
759     @type session: Session
760     @param session: Optional SQLA session object (a temporary one will be
761     generated if not supplied)
762
763     @rtype: Section
764     @return: Section object for the given section name
765
766     """
767     if session is None:
768         session = DBConn().session()
769     q = session.query(Section).filter_by(section=section)
770     if q.count() == 0:
771         return None
772     return q.one()
773
774 __all__.append('get_section')
775
776 ################################################################################
777
778 class DBSource(object):
779     def __init__(self, *args, **kwargs):
780         pass
781
782     def __repr__(self):
783         return '<DBSource %s (%s)>' % (self.source, self.version)
784
785 __all__.append('DBSource')
786
787 def get_sources_from_name(source, dm_upload_allowed=None, session=None):
788     """
789     Returns list of DBSource objects for given C{source} name
790
791     @type source: str
792     @param source: DBSource package name to search for
793
794     @type dm_upload_allowed: bool
795     @param dm_upload_allowed: If None, no effect.  If True or False, only
796     return packages with that dm_upload_allowed setting
797
798     @type session: Session
799     @param session: Optional SQL session object (a temporary one will be
800     generated if not supplied)
801
802     @rtype: list
803     @return: list of DBSource objects for the given name (may be empty)
804     """
805     if session is None:
806         session = DBConn().session()
807
808     q = session.query(DBSource).filter_by(source=source)
809     if dm_upload_allowed is not None:
810         q = q.filter_by(dm_upload_allowed=dm_upload_allowed)
811
812     return q.all()
813
814 __all__.append('get_sources_from_name')
815
816 def get_source_in_suite(source, suite, session=None):
817     """
818     Returns list of DBSource objects for a combination of C{source} and C{suite}.
819
820       - B{source} - source package name, eg. I{mailfilter}, I{bbdb}, I{glibc}
821       - B{suite} - a suite name, eg. I{unstable}
822
823     @type source: string
824     @param source: source package name
825
826     @type suite: string
827     @param suite: the suite name
828
829     @rtype: string
830     @return: the version for I{source} in I{suite}
831
832     """
833     if session is None:
834         session = DBConn().session()
835     q = session.query(SrcAssociation)
836     q = q.join('source').filter_by(source=source)
837     q = q.join('suite').filter_by(suite_name=suite)
838     if q.count() == 0:
839         return None
840     # ???: Maybe we should just return the SrcAssociation object instead
841     return q.one().source
842
843 __all__.append('get_source_in_suite')
844
845 ################################################################################
846
847 class SrcAssociation(object):
848     def __init__(self, *args, **kwargs):
849         pass
850
851     def __repr__(self):
852         return '<SrcAssociation %s (%s, %s)>' % (self.sa_id, self.source, self.suite)
853
854 __all__.append('SrcAssociation')
855
856 ################################################################################
857
858 class SrcUploader(object):
859     def __init__(self, *args, **kwargs):
860         pass
861
862     def __repr__(self):
863         return '<SrcUploader %s>' % self.uploader_id
864
865 __all__.append('SrcUploader')
866
867 ################################################################################
868
869 class Suite(object):
870     def __init__(self, *args, **kwargs):
871         pass
872
873     def __repr__(self):
874         return '<Suite %s>' % self.suite_name
875
876 __all__.append('Suite')
877
878 def get_suite_architecture(suite, architecture, session=None):
879     """
880     Returns a SuiteArchitecture object given C{suite} and ${arch} or None if it
881     doesn't exist
882
883     @type suite: str
884     @param suite: Suite name to search for
885
886     @type architecture: str
887     @param architecture: Architecture name to search for
888
889     @type session: Session
890     @param session: Optional SQL session object (a temporary one will be
891     generated if not supplied)
892
893     @rtype: SuiteArchitecture
894     @return: the SuiteArchitecture object or None
895     """
896
897     if session is None:
898         session = DBConn().session()
899
900     q = session.query(SuiteArchitecture)
901     q = q.join(Architecture).filter_by(arch_string=architecture)
902     q = q.join(Suite).filter_by(suite_name=suite)
903     if q.count() == 0:
904         return None
905     return q.one()
906
907 __all__.append('get_suite_architecture')
908
909 def get_suite(suite, session=None):
910     """
911     Returns Suite object for given C{suite name}.
912
913     @type suite: string
914     @param suite: The name of the suite
915
916     @type session: Session
917     @param session: Optional SQLA session object (a temporary one will be
918     generated if not supplied)
919
920     @rtype: Suite
921     @return: Suite object for the requested suite name (None if not presenT)
922
923     """
924     if session is None:
925         session = DBConn().session()
926     q = session.query(Suite).filter_by(suite_name=suite)
927     if q.count() == 0:
928         return None
929     return q.one()
930
931 __all__.append('get_suite')
932
933 ################################################################################
934
935 class SuiteArchitecture(object):
936     def __init__(self, *args, **kwargs):
937         pass
938
939     def __repr__(self):
940         return '<SuiteArchitecture (%s, %s)>' % (self.suite_id, self.arch_id)
941
942 __all__.append('SuiteArchitecture')
943
944 def get_suite_architectures(suite, skipsrc=False, skipall=False, session=None):
945     """
946     Returns list of Architecture objects for given C{suite} name
947
948     @type source: str
949     @param source: Suite name to search for
950
951     @type skipsrc: boolean
952     @param skipsrc: Whether to skip returning the 'source' architecture entry
953     (Default False)
954
955     @type skipall: boolean
956     @param skipall: Whether to skip returning the 'all' architecture entry
957     (Default False)
958
959     @type session: Session
960     @param session: Optional SQL session object (a temporary one will be
961     generated if not supplied)
962
963     @rtype: list
964     @return: list of Architecture objects for the given name (may be empty)
965     """
966
967     if session is None:
968         session = DBConn().session()
969
970     q = session.query(Architecture)
971     q = q.join(SuiteArchitecture)
972     q = q.join(Suite).filter_by(suite_name=suite)
973     if skipsrc:
974         q = q.filter(Architecture.arch_string != 'source')
975     if skipall:
976         q = q.filter(Architecture.arch_string != 'all')
977     q = q.order_by('arch_string')
978     return q.all()
979
980 __all__.append('get_suite_architectures')
981
982 ################################################################################
983
984 class Uid(object):
985     def __init__(self, *args, **kwargs):
986         pass
987
988     def __repr__(self):
989         return '<Uid %s (%s)>' % (self.uid, self.name)
990
991 __all__.append('Uid')
992
993 def get_uid_from_fingerprint(fpr, session=None):
994     if session is None:
995         session = DBConn().session()
996
997     q = session.query(Uid)
998     q = q.join(Fingerprint).filter_by(fingerprint=fpr)
999
1000     if q.count() != 1:
1001         return None
1002     else:
1003         return q.one()
1004
1005 __all__.append('get_uid_from_fingerprint')
1006
1007 ################################################################################
1008
1009 class DBConn(Singleton):
1010     """
1011     database module init.
1012     """
1013     def __init__(self, *args, **kwargs):
1014         super(DBConn, self).__init__(*args, **kwargs)
1015
1016     def _startup(self, *args, **kwargs):
1017         self.debug = False
1018         if kwargs.has_key('debug'):
1019             self.debug = True
1020         self.__createconn()
1021
1022     def __setuptables(self):
1023         self.tbl_architecture = Table('architecture', self.db_meta, autoload=True)
1024         self.tbl_archive = Table('archive', self.db_meta, autoload=True)
1025         self.tbl_bin_associations = Table('bin_associations', self.db_meta, autoload=True)
1026         self.tbl_binaries = Table('binaries', self.db_meta, autoload=True)
1027         self.tbl_component = Table('component', self.db_meta, autoload=True)
1028         self.tbl_config = Table('config', self.db_meta, autoload=True)
1029         self.tbl_content_associations = Table('content_associations', self.db_meta, autoload=True)
1030         self.tbl_content_file_names = Table('content_file_names', self.db_meta, autoload=True)
1031         self.tbl_content_file_paths = Table('content_file_paths', self.db_meta, autoload=True)
1032         self.tbl_dsc_files = Table('dsc_files', self.db_meta, autoload=True)
1033         self.tbl_files = Table('files', self.db_meta, autoload=True)
1034         self.tbl_fingerprint = Table('fingerprint', self.db_meta, autoload=True)
1035         self.tbl_keyrings = Table('keyrings', self.db_meta, autoload=True)
1036         self.tbl_location = Table('location', self.db_meta, autoload=True)
1037         self.tbl_maintainer = Table('maintainer', self.db_meta, autoload=True)
1038         self.tbl_override = Table('override', self.db_meta, autoload=True)
1039         self.tbl_override_type = Table('override_type', self.db_meta, autoload=True)
1040         self.tbl_pending_content_associations = Table('pending_content_associations', self.db_meta, autoload=True)
1041         self.tbl_priority = Table('priority', self.db_meta, autoload=True)
1042         self.tbl_queue = Table('queue', self.db_meta, autoload=True)
1043         self.tbl_queue_build = Table('queue_build', self.db_meta, autoload=True)
1044         self.tbl_section = Table('section', self.db_meta, autoload=True)
1045         self.tbl_source = Table('source', self.db_meta, autoload=True)
1046         self.tbl_src_associations = Table('src_associations', self.db_meta, autoload=True)
1047         self.tbl_src_uploaders = Table('src_uploaders', self.db_meta, autoload=True)
1048         self.tbl_suite = Table('suite', self.db_meta, autoload=True)
1049         self.tbl_suite_architectures = Table('suite_architectures', self.db_meta, autoload=True)
1050         self.tbl_uid = Table('uid', self.db_meta, autoload=True)
1051
1052     def __setupmappers(self):
1053         mapper(Architecture, self.tbl_architecture,
1054                properties = dict(arch_id = self.tbl_architecture.c.id))
1055
1056         mapper(Archive, self.tbl_archive,
1057                properties = dict(archive_id = self.tbl_archive.c.id,
1058                                  archive_name = self.tbl_archive.c.name))
1059
1060         mapper(BinAssociation, self.tbl_bin_associations,
1061                properties = dict(ba_id = self.tbl_bin_associations.c.id,
1062                                  suite_id = self.tbl_bin_associations.c.suite,
1063                                  suite = relation(Suite),
1064                                  binary_id = self.tbl_bin_associations.c.bin,
1065                                  binary = relation(DBBinary)))
1066
1067         mapper(DBBinary, self.tbl_binaries,
1068                properties = dict(binary_id = self.tbl_binaries.c.id,
1069                                  package = self.tbl_binaries.c.package,
1070                                  version = self.tbl_binaries.c.version,
1071                                  maintainer_id = self.tbl_binaries.c.maintainer,
1072                                  maintainer = relation(Maintainer),
1073                                  source_id = self.tbl_binaries.c.source,
1074                                  source = relation(DBSource),
1075                                  arch_id = self.tbl_binaries.c.architecture,
1076                                  architecture = relation(Architecture),
1077                                  poolfile_id = self.tbl_binaries.c.file,
1078                                  poolfile = relation(PoolFile),
1079                                  binarytype = self.tbl_binaries.c.type,
1080                                  fingerprint_id = self.tbl_binaries.c.sig_fpr,
1081                                  fingerprint = relation(Fingerprint),
1082                                  install_date = self.tbl_binaries.c.install_date,
1083                                  binassociations = relation(BinAssociation,
1084                                                             primaryjoin=(self.tbl_binaries.c.id==self.tbl_bin_associations.c.bin))))
1085
1086         mapper(Component, self.tbl_component,
1087                properties = dict(component_id = self.tbl_component.c.id,
1088                                  component_name = self.tbl_component.c.name))
1089
1090         mapper(DBConfig, self.tbl_config,
1091                properties = dict(config_id = self.tbl_config.c.id))
1092
1093         mapper(ContentAssociation, self.tbl_content_associations,
1094                properties = dict(ca_id = self.tbl_content_associations.c.id,
1095                                  filename_id = self.tbl_content_associations.c.filename,
1096                                  filename    = relation(ContentFilename),
1097                                  filepath_id = self.tbl_content_associations.c.filepath,
1098                                  filepath    = relation(ContentFilepath),
1099                                  binary_id   = self.tbl_content_associations.c.binary_pkg,
1100                                  binary      = relation(DBBinary)))
1101
1102
1103         mapper(ContentFilename, self.tbl_content_file_names,
1104                properties = dict(cafilename_id = self.tbl_content_file_names.c.id,
1105                                  filename = self.tbl_content_file_names.c.file))
1106
1107         mapper(ContentFilepath, self.tbl_content_file_paths,
1108                properties = dict(cafilepath_id = self.tbl_content_file_paths.c.id,
1109                                  filepath = self.tbl_content_file_paths.c.path))
1110
1111         mapper(DSCFile, self.tbl_dsc_files,
1112                properties = dict(dscfile_id = self.tbl_dsc_files.c.id,
1113                                  source_id = self.tbl_dsc_files.c.source,
1114                                  source = relation(DBSource),
1115                                  poolfile_id = self.tbl_dsc_files.c.file,
1116                                  poolfile = relation(PoolFile)))
1117
1118         mapper(PoolFile, self.tbl_files,
1119                properties = dict(file_id = self.tbl_files.c.id,
1120                                  filesize = self.tbl_files.c.size,
1121                                  location_id = self.tbl_files.c.location,
1122                                  location = relation(Location)))
1123
1124         mapper(Fingerprint, self.tbl_fingerprint,
1125                properties = dict(fingerprint_id = self.tbl_fingerprint.c.id,
1126                                  uid_id = self.tbl_fingerprint.c.uid,
1127                                  uid = relation(Uid),
1128                                  keyring_id = self.tbl_fingerprint.c.keyring,
1129                                  keyring = relation(Keyring)))
1130
1131         mapper(Keyring, self.tbl_keyrings,
1132                properties = dict(keyring_name = self.tbl_keyrings.c.name,
1133                                  keyring_id = self.tbl_keyrings.c.id))
1134
1135         mapper(Location, self.tbl_location,
1136                properties = dict(location_id = self.tbl_location.c.id,
1137                                  component_id = self.tbl_location.c.component,
1138                                  component = relation(Component),
1139                                  archive_id = self.tbl_location.c.archive,
1140                                  archive = relation(Archive),
1141                                  archive_type = self.tbl_location.c.type))
1142
1143         mapper(Maintainer, self.tbl_maintainer,
1144                properties = dict(maintainer_id = self.tbl_maintainer.c.id))
1145
1146         mapper(Override, self.tbl_override,
1147                properties = dict(suite_id = self.tbl_override.c.suite,
1148                                  suite = relation(Suite),
1149                                  component_id = self.tbl_override.c.component,
1150                                  component = relation(Component),
1151                                  priority_id = self.tbl_override.c.priority,
1152                                  priority = relation(Priority),
1153                                  section_id = self.tbl_override.c.section,
1154                                  section = relation(Section),
1155                                  overridetype_id = self.tbl_override.c.type,
1156                                  overridetype = relation(OverrideType)))
1157
1158         mapper(OverrideType, self.tbl_override_type,
1159                properties = dict(overridetype = self.tbl_override_type.c.type,
1160                                  overridetype_id = self.tbl_override_type.c.id))
1161
1162         mapper(PendingContentAssociation, self.tbl_pending_content_associations,
1163                properties = dict(pca_id = self.tbl_pending_content_associations.c.id,
1164                                  filepath_id = self.tbl_pending_content_associations.c.filepath,
1165                                  filepath = relation(ContentFilepath),
1166                                  filename_id = self.tbl_pending_content_associations.c.filename,
1167                                  filename = relation(ContentFilename)))
1168
1169         mapper(Priority, self.tbl_priority,
1170                properties = dict(priority_id = self.tbl_priority.c.id))
1171
1172         mapper(Queue, self.tbl_queue,
1173                properties = dict(queue_id = self.tbl_queue.c.id))
1174
1175         mapper(QueueBuild, self.tbl_queue_build,
1176                properties = dict(suite_id = self.tbl_queue_build.c.suite,
1177                                  queue_id = self.tbl_queue_build.c.queue,
1178                                  queue = relation(Queue)))
1179
1180         mapper(Section, self.tbl_section,
1181                properties = dict(section_id = self.tbl_section.c.id))
1182
1183         mapper(DBSource, self.tbl_source,
1184                properties = dict(source_id = self.tbl_source.c.id,
1185                                  version = self.tbl_source.c.version,
1186                                  maintainer_id = self.tbl_source.c.maintainer,
1187                                  maintainer = relation(Maintainer,
1188                                                        primaryjoin=(self.tbl_source.c.maintainer==self.tbl_maintainer.c.id)),
1189                                  poolfile_id = self.tbl_source.c.file,
1190                                  poolfile = relation(PoolFile),
1191                                  fingerprint_id = self.tbl_source.c.sig_fpr,
1192                                  fingerprint = relation(Fingerprint),
1193                                  changedby_id = self.tbl_source.c.changedby,
1194                                  changedby = relation(Maintainer,
1195                                                       primaryjoin=(self.tbl_source.c.changedby==self.tbl_maintainer.c.id)),
1196                                  srcfiles = relation(DSCFile,
1197                                                      primaryjoin=(self.tbl_source.c.id==self.tbl_dsc_files.c.source)),
1198                                  srcassociations = relation(SrcAssociation,
1199                                                             primaryjoin=(self.tbl_source.c.id==self.tbl_src_associations.c.source))))
1200
1201         mapper(SrcAssociation, self.tbl_src_associations,
1202                properties = dict(sa_id = self.tbl_src_associations.c.id,
1203                                  suite_id = self.tbl_src_associations.c.suite,
1204                                  suite = relation(Suite),
1205                                  source_id = self.tbl_src_associations.c.source,
1206                                  source = relation(DBSource)))
1207
1208         mapper(SrcUploader, self.tbl_src_uploaders,
1209                properties = dict(uploader_id = self.tbl_src_uploaders.c.id,
1210                                  source_id = self.tbl_src_uploaders.c.source,
1211                                  source = relation(DBSource,
1212                                                    primaryjoin=(self.tbl_src_uploaders.c.source==self.tbl_source.c.id)),
1213                                  maintainer_id = self.tbl_src_uploaders.c.maintainer,
1214                                  maintainer = relation(Maintainer,
1215                                                        primaryjoin=(self.tbl_src_uploaders.c.maintainer==self.tbl_maintainer.c.id))))
1216
1217         mapper(Suite, self.tbl_suite,
1218                properties = dict(suite_id = self.tbl_suite.c.id))
1219
1220         mapper(SuiteArchitecture, self.tbl_suite_architectures,
1221                properties = dict(suite_id = self.tbl_suite_architectures.c.suite,
1222                                  suite = relation(Suite, backref='suitearchitectures'),
1223                                  arch_id = self.tbl_suite_architectures.c.architecture,
1224                                  architecture = relation(Architecture)))
1225
1226         mapper(Uid, self.tbl_uid,
1227                properties = dict(uid_id = self.tbl_uid.c.id,
1228                                  fingerprint = relation(Fingerprint)))
1229
1230     ## Connection functions
1231     def __createconn(self):
1232         from config import Config
1233         cnf = Config()
1234         if cnf["DB::Host"]:
1235             # TCP/IP
1236             connstr = "postgres://%s" % cnf["DB::Host"]
1237             if cnf["DB::Port"] and cnf["DB::Port"] != "-1":
1238                 connstr += ":%s" % cnf["DB::Port"]
1239             connstr += "/%s" % cnf["DB::Name"]
1240         else:
1241             # Unix Socket
1242             connstr = "postgres:///%s" % cnf["DB::Name"]
1243             if cnf["DB::Port"] and cnf["DB::Port"] != "-1":
1244                 connstr += "?port=%s" % cnf["DB::Port"]
1245
1246         self.db_pg   = create_engine(connstr, echo=self.debug)
1247         self.db_meta = MetaData()
1248         self.db_meta.bind = self.db_pg
1249         self.db_smaker = sessionmaker(bind=self.db_pg,
1250                                       autoflush=True,
1251                                       autocommit=False)
1252
1253         self.__setuptables()
1254         self.__setupmappers()
1255
1256     def session(self):
1257         return self.db_smaker()
1258
1259 __all__.append('DBConn')
1260