]> git.decadent.org.uk Git - dak.git/blob - daklib/dbconn.py
add basic mapper classes for new tables
[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 inspect import getargspec
41
42 from sqlalchemy import create_engine, Table, MetaData
43 from sqlalchemy.orm import sessionmaker, mapper, relation
44
45 # Don't remove this, we re-export the exceptions to scripts which import us
46 from sqlalchemy.exc import *
47 from sqlalchemy.orm.exc import NoResultFound
48
49 # Only import Config until Queue stuff is changed to store its config
50 # in the database
51 from config import Config
52 from singleton import Singleton
53 from textutils import fix_maintainer
54
55 ################################################################################
56
57 __all__ = ['IntegrityError', 'SQLAlchemyError']
58
59 ################################################################################
60
61 def session_wrapper(fn):
62     """
63     Wrapper around common ".., session=None):" handling. If the wrapped
64     function is called without passing 'session', we create a local one
65     and destroy it when the function ends.
66
67     Also attaches a commit_or_flush method to the session; if we created a
68     local session, this is a synonym for session.commit(), otherwise it is a
69     synonym for session.flush().
70     """
71
72     def wrapped(*args, **kwargs):
73         private_transaction = False
74
75         # Find the session object
76         session = kwargs.get('session')
77
78         if session is None:
79             if len(args) <= len(getargspec(fn)[0]) - 1:
80                 # No session specified as last argument or in kwargs
81                 private_transaction = True
82                 session = kwargs['session'] = DBConn().session()
83             else:
84                 # Session is last argument in args
85                 session = args[-1]
86                 if session is None:
87                     args = list(args)
88                     session = args[-1] = DBConn().session()
89                     private_transaction = True
90
91         if private_transaction:
92             session.commit_or_flush = session.commit
93         else:
94             session.commit_or_flush = session.flush
95
96         try:
97             return fn(*args, **kwargs)
98         finally:
99             if private_transaction:
100                 # We created a session; close it.
101                 session.close()
102
103     wrapped.__doc__ = fn.__doc__
104     wrapped.func_name = fn.func_name
105
106     return wrapped
107
108 ################################################################################
109
110 class Architecture(object):
111     def __init__(self, *args, **kwargs):
112         pass
113
114     def __eq__(self, val):
115         if isinstance(val, str):
116             return (self.arch_string== val)
117         # This signals to use the normal comparison operator
118         return NotImplemented
119
120     def __ne__(self, val):
121         if isinstance(val, str):
122             return (self.arch_string != val)
123         # This signals to use the normal comparison operator
124         return NotImplemented
125
126     def __repr__(self):
127         return '<Architecture %s>' % self.arch_string
128
129 __all__.append('Architecture')
130
131 @session_wrapper
132 def get_architecture(architecture, session=None):
133     """
134     Returns database id for given C{architecture}.
135
136     @type architecture: string
137     @param architecture: The name of the architecture
138
139     @type session: Session
140     @param session: Optional SQLA session object (a temporary one will be
141     generated if not supplied)
142
143     @rtype: Architecture
144     @return: Architecture object for the given arch (None if not present)
145     """
146
147     q = session.query(Architecture).filter_by(arch_string=architecture)
148
149     try:
150         return q.one()
151     except NoResultFound:
152         return None
153
154 __all__.append('get_architecture')
155
156 @session_wrapper
157 def get_architecture_suites(architecture, session=None):
158     """
159     Returns list of Suite objects for given C{architecture} name
160
161     @type source: str
162     @param source: Architecture name to search for
163
164     @type session: Session
165     @param session: Optional SQL session object (a temporary one will be
166     generated if not supplied)
167
168     @rtype: list
169     @return: list of Suite objects for the given name (may be empty)
170     """
171
172     q = session.query(Suite)
173     q = q.join(SuiteArchitecture)
174     q = q.join(Architecture).filter_by(arch_string=architecture).order_by('suite_name')
175
176     ret = q.all()
177
178     return ret
179
180 __all__.append('get_architecture_suites')
181
182 ################################################################################
183
184 class Archive(object):
185     def __init__(self, *args, **kwargs):
186         pass
187
188     def __repr__(self):
189         return '<Archive %s>' % self.archive_name
190
191 __all__.append('Archive')
192
193 @session_wrapper
194 def get_archive(archive, session=None):
195     """
196     returns database id for given C{archive}.
197
198     @type archive: string
199     @param archive: the name of the arhive
200
201     @type session: Session
202     @param session: Optional SQLA session object (a temporary one will be
203     generated if not supplied)
204
205     @rtype: Archive
206     @return: Archive object for the given name (None if not present)
207
208     """
209     archive = archive.lower()
210
211     q = session.query(Archive).filter_by(archive_name=archive)
212
213     try:
214         return q.one()
215     except NoResultFound:
216         return None
217
218 __all__.append('get_archive')
219
220 ################################################################################
221
222 class BinAssociation(object):
223     def __init__(self, *args, **kwargs):
224         pass
225
226     def __repr__(self):
227         return '<BinAssociation %s (%s, %s)>' % (self.ba_id, self.binary, self.suite)
228
229 __all__.append('BinAssociation')
230
231 ################################################################################
232
233 class DBBinary(object):
234     def __init__(self, *args, **kwargs):
235         pass
236
237     def __repr__(self):
238         return '<DBBinary %s (%s, %s)>' % (self.package, self.version, self.architecture)
239
240 __all__.append('DBBinary')
241
242 @session_wrapper
243 def get_suites_binary_in(package, session=None):
244     """
245     Returns list of Suite objects which given C{package} name is in
246
247     @type source: str
248     @param source: DBBinary package name to search for
249
250     @rtype: list
251     @return: list of Suite objects for the given package
252     """
253
254     return session.query(Suite).join(BinAssociation).join(DBBinary).filter_by(package=package).all()
255
256 __all__.append('get_suites_binary_in')
257
258 @session_wrapper
259 def get_binary_from_id(id, session=None):
260     """
261     Returns DBBinary object for given C{id}
262
263     @type id: int
264     @param id: Id of the required binary
265
266     @type session: Session
267     @param session: Optional SQLA session object (a temporary one will be
268     generated if not supplied)
269
270     @rtype: DBBinary
271     @return: DBBinary object for the given binary (None if not present)
272     """
273
274     q = session.query(DBBinary).filter_by(binary_id=id)
275
276     try:
277         return q.one()
278     except NoResultFound:
279         return None
280
281 __all__.append('get_binary_from_id')
282
283 @session_wrapper
284 def get_binaries_from_name(package, version=None, architecture=None, session=None):
285     """
286     Returns list of DBBinary objects for given C{package} name
287
288     @type package: str
289     @param package: DBBinary package name to search for
290
291     @type version: str or None
292     @param version: Version to search for (or None)
293
294     @type package: str, list or None
295     @param package: Architectures to limit to (or None if no limit)
296
297     @type session: Session
298     @param session: Optional SQL session object (a temporary one will be
299     generated if not supplied)
300
301     @rtype: list
302     @return: list of DBBinary objects for the given name (may be empty)
303     """
304
305     q = session.query(DBBinary).filter_by(package=package)
306
307     if version is not None:
308         q = q.filter_by(version=version)
309
310     if architecture is not None:
311         if not isinstance(architecture, list):
312             architecture = [architecture]
313         q = q.join(Architecture).filter(Architecture.arch_string.in_(architecture))
314
315     ret = q.all()
316
317     return ret
318
319 __all__.append('get_binaries_from_name')
320
321 @session_wrapper
322 def get_binaries_from_source_id(source_id, session=None):
323     """
324     Returns list of DBBinary objects for given C{source_id}
325
326     @type source_id: int
327     @param source_id: source_id to search for
328
329     @type session: Session
330     @param session: Optional SQL session object (a temporary one will be
331     generated if not supplied)
332
333     @rtype: list
334     @return: list of DBBinary objects for the given name (may be empty)
335     """
336
337     return session.query(DBBinary).filter_by(source_id=source_id).all()
338
339 __all__.append('get_binaries_from_source_id')
340
341 @session_wrapper
342 def get_binary_from_name_suite(package, suitename, session=None):
343     ### For dak examine-package
344     ### XXX: Doesn't use object API yet
345
346     sql = """SELECT DISTINCT(b.package), b.version, c.name, su.suite_name
347              FROM binaries b, files fi, location l, component c, bin_associations ba, suite su
348              WHERE b.package=:package
349                AND b.file = fi.id
350                AND fi.location = l.id
351                AND l.component = c.id
352                AND ba.bin=b.id
353                AND ba.suite = su.id
354                AND su.suite_name=:suitename
355           ORDER BY b.version DESC"""
356
357     return session.execute(sql, {'package': package, 'suitename': suitename})
358
359 __all__.append('get_binary_from_name_suite')
360
361 @session_wrapper
362 def get_binary_components(package, suitename, arch, session=None):
363     # Check for packages that have moved from one component to another
364     query = """SELECT c.name FROM binaries b, bin_associations ba, suite s, location l, component c, architecture a, files f
365     WHERE b.package=:package AND s.suite_name=:suitename
366       AND (a.arch_string = :arch OR a.arch_string = 'all')
367       AND ba.bin = b.id AND ba.suite = s.id AND b.architecture = a.id
368       AND f.location = l.id
369       AND l.component = c.id
370       AND b.file = f.id"""
371
372     vals = {'package': package, 'suitename': suitename, 'arch': arch}
373
374     return session.execute(query, vals)
375
376 __all__.append('get_binary_components')
377
378 ################################################################################
379
380 class BinaryACL(object):
381     def __init__(self, *args, **kwargs):
382         pass
383
384 __all__.append('BinaryACL')
385
386 ################################################################################
387
388 class BinaryACLMap(object):
389     def __init__(self, *args, **kwargs):
390         pass
391
392 __all__.append('BinaryACLMap')
393
394 ################################################################################
395
396 class Component(object):
397     def __init__(self, *args, **kwargs):
398         pass
399
400     def __eq__(self, val):
401         if isinstance(val, str):
402             return (self.component_name == val)
403         # This signals to use the normal comparison operator
404         return NotImplemented
405
406     def __ne__(self, val):
407         if isinstance(val, str):
408             return (self.component_name != val)
409         # This signals to use the normal comparison operator
410         return NotImplemented
411
412     def __repr__(self):
413         return '<Component %s>' % self.component_name
414
415
416 __all__.append('Component')
417
418 @session_wrapper
419 def get_component(component, session=None):
420     """
421     Returns database id for given C{component}.
422
423     @type component: string
424     @param component: The name of the override type
425
426     @rtype: int
427     @return: the database id for the given component
428
429     """
430     component = component.lower()
431
432     q = session.query(Component).filter_by(component_name=component)
433
434     try:
435         return q.one()
436     except NoResultFound:
437         return None
438
439 __all__.append('get_component')
440
441 ################################################################################
442
443 class DBConfig(object):
444     def __init__(self, *args, **kwargs):
445         pass
446
447     def __repr__(self):
448         return '<DBConfig %s>' % self.name
449
450 __all__.append('DBConfig')
451
452 ################################################################################
453
454 class ContentFilename(object):
455     def __init__(self, *args, **kwargs):
456         pass
457
458     def __repr__(self):
459         return '<ContentFilename %s>' % self.filename
460
461 __all__.append('ContentFilename')
462
463 @session_wrapper
464 def get_or_set_contents_file_id(filename, session=None):
465     """
466     Returns database id for given filename.
467
468     If no matching file is found, a row is inserted.
469
470     @type filename: string
471     @param filename: The filename
472     @type session: SQLAlchemy
473     @param session: Optional SQL session object (a temporary one will be
474     generated if not supplied).  If not passed, a commit will be performed at
475     the end of the function, otherwise the caller is responsible for commiting.
476
477     @rtype: int
478     @return: the database id for the given component
479     """
480
481     q = session.query(ContentFilename).filter_by(filename=filename)
482
483     try:
484         ret = q.one().cafilename_id
485     except NoResultFound:
486         cf = ContentFilename()
487         cf.filename = filename
488         session.add(cf)
489         session.commit_or_flush()
490         ret = cf.cafilename_id
491
492     return ret
493
494 __all__.append('get_or_set_contents_file_id')
495
496 @session_wrapper
497 def get_contents(suite, overridetype, section=None, session=None):
498     """
499     Returns contents for a suite / overridetype combination, limiting
500     to a section if not None.
501
502     @type suite: Suite
503     @param suite: Suite object
504
505     @type overridetype: OverrideType
506     @param overridetype: OverrideType object
507
508     @type section: Section
509     @param section: Optional section object to limit results to
510
511     @type session: SQLAlchemy
512     @param session: Optional SQL session object (a temporary one will be
513     generated if not supplied)
514
515     @rtype: ResultsProxy
516     @return: ResultsProxy object set up to return tuples of (filename, section,
517     package, arch_id)
518     """
519
520     # find me all of the contents for a given suite
521     contents_q = """SELECT (p.path||'/'||n.file) AS fn,
522                             s.section,
523                             b.package,
524                             b.architecture
525                    FROM content_associations c join content_file_paths p ON (c.filepath=p.id)
526                    JOIN content_file_names n ON (c.filename=n.id)
527                    JOIN binaries b ON (b.id=c.binary_pkg)
528                    JOIN override o ON (o.package=b.package)
529                    JOIN section s ON (s.id=o.section)
530                    WHERE o.suite = :suiteid AND o.type = :overridetypeid
531                    AND b.type=:overridetypename"""
532
533     vals = {'suiteid': suite.suite_id,
534             'overridetypeid': overridetype.overridetype_id,
535             'overridetypename': overridetype.overridetype}
536
537     if section is not None:
538         contents_q += " AND s.id = :sectionid"
539         vals['sectionid'] = section.section_id
540
541     contents_q += " ORDER BY fn"
542
543     return session.execute(contents_q, vals)
544
545 __all__.append('get_contents')
546
547 ################################################################################
548
549 class ContentFilepath(object):
550     def __init__(self, *args, **kwargs):
551         pass
552
553     def __repr__(self):
554         return '<ContentFilepath %s>' % self.filepath
555
556 __all__.append('ContentFilepath')
557
558 @session_wrapper
559 def get_or_set_contents_path_id(filepath, session=None):
560     """
561     Returns database id for given path.
562
563     If no matching file is found, a row is inserted.
564
565     @type filename: string
566     @param filename: The filepath
567     @type session: SQLAlchemy
568     @param session: Optional SQL session object (a temporary one will be
569     generated if not supplied).  If not passed, a commit will be performed at
570     the end of the function, otherwise the caller is responsible for commiting.
571
572     @rtype: int
573     @return: the database id for the given path
574     """
575
576     q = session.query(ContentFilepath).filter_by(filepath=filepath)
577
578     try:
579         ret = q.one().cafilepath_id
580     except NoResultFound:
581         cf = ContentFilepath()
582         cf.filepath = filepath
583         session.add(cf)
584         session.commit_or_flush()
585         ret = cf.cafilepath_id
586
587     return ret
588
589 __all__.append('get_or_set_contents_path_id')
590
591 ################################################################################
592
593 class ContentAssociation(object):
594     def __init__(self, *args, **kwargs):
595         pass
596
597     def __repr__(self):
598         return '<ContentAssociation %s>' % self.ca_id
599
600 __all__.append('ContentAssociation')
601
602 def insert_content_paths(binary_id, fullpaths, session=None):
603     """
604     Make sure given path is associated with given binary id
605
606     @type binary_id: int
607     @param binary_id: the id of the binary
608     @type fullpaths: list
609     @param fullpaths: the list of paths of the file being associated with the binary
610     @type session: SQLAlchemy session
611     @param session: Optional SQLAlchemy session.  If this is passed, the caller
612     is responsible for ensuring a transaction has begun and committing the
613     results or rolling back based on the result code.  If not passed, a commit
614     will be performed at the end of the function, otherwise the caller is
615     responsible for commiting.
616
617     @return: True upon success
618     """
619
620     privatetrans = False
621     if session is None:
622         session = DBConn().session()
623         privatetrans = True
624
625     try:
626         # Insert paths
627         pathcache = {}
628         for fullpath in fullpaths:
629             # Get the necessary IDs ...
630             (path, file) = os.path.split(fullpath)
631
632             filepath_id = get_or_set_contents_path_id(path, session)
633             filename_id = get_or_set_contents_file_id(file, session)
634
635             pathcache[fullpath] = (filepath_id, filename_id)
636
637         for fullpath, dat in pathcache.items():
638             ca = ContentAssociation()
639             ca.binary_id = binary_id
640             ca.filepath_id = dat[0]
641             ca.filename_id = dat[1]
642             session.add(ca)
643
644         # Only commit if we set up the session ourself
645         if privatetrans:
646             session.commit()
647             session.close()
648         else:
649             session.flush()
650
651         return True
652
653     except:
654         traceback.print_exc()
655
656         # Only rollback if we set up the session ourself
657         if privatetrans:
658             session.rollback()
659             session.close()
660
661         return False
662
663 __all__.append('insert_content_paths')
664
665 ################################################################################
666
667 class DSCFile(object):
668     def __init__(self, *args, **kwargs):
669         pass
670
671     def __repr__(self):
672         return '<DSCFile %s>' % self.dscfile_id
673
674 __all__.append('DSCFile')
675
676 @session_wrapper
677 def get_dscfiles(dscfile_id=None, source_id=None, poolfile_id=None, session=None):
678     """
679     Returns a list of DSCFiles which may be empty
680
681     @type dscfile_id: int (optional)
682     @param dscfile_id: the dscfile_id of the DSCFiles to find
683
684     @type source_id: int (optional)
685     @param source_id: the source id related to the DSCFiles to find
686
687     @type poolfile_id: int (optional)
688     @param poolfile_id: the poolfile id related to the DSCFiles to find
689
690     @rtype: list
691     @return: Possibly empty list of DSCFiles
692     """
693
694     q = session.query(DSCFile)
695
696     if dscfile_id is not None:
697         q = q.filter_by(dscfile_id=dscfile_id)
698
699     if source_id is not None:
700         q = q.filter_by(source_id=source_id)
701
702     if poolfile_id is not None:
703         q = q.filter_by(poolfile_id=poolfile_id)
704
705     return q.all()
706
707 __all__.append('get_dscfiles')
708
709 ################################################################################
710
711 class PoolFile(object):
712     def __init__(self, *args, **kwargs):
713         pass
714
715     def __repr__(self):
716         return '<PoolFile %s>' % self.filename
717
718 __all__.append('PoolFile')
719
720 @session_wrapper
721 def check_poolfile(filename, filesize, md5sum, location_id, session=None):
722     """
723     Returns a tuple:
724      (ValidFileFound [boolean or None], PoolFile object or None)
725
726     @type filename: string
727     @param filename: the filename of the file to check against the DB
728
729     @type filesize: int
730     @param filesize: the size of the file to check against the DB
731
732     @type md5sum: string
733     @param md5sum: the md5sum of the file to check against the DB
734
735     @type location_id: int
736     @param location_id: the id of the location to look in
737
738     @rtype: tuple
739     @return: Tuple of length 2.
740              If more than one file found with that name:
741                     (None,  None)
742              If valid pool file found: (True, PoolFile object)
743              If valid pool file not found:
744                     (False, None) if no file found
745                     (False, PoolFile object) if file found with size/md5sum mismatch
746     """
747
748     q = session.query(PoolFile).filter_by(filename=filename)
749     q = q.join(Location).filter_by(location_id=location_id)
750
751     ret = None
752
753     if q.count() > 1:
754         ret = (None, None)
755     elif q.count() < 1:
756         ret = (False, None)
757     else:
758         obj = q.one()
759         if obj.md5sum != md5sum or obj.filesize != filesize:
760             ret = (False, obj)
761
762     if ret is None:
763         ret = (True, obj)
764
765     return ret
766
767 __all__.append('check_poolfile')
768
769 @session_wrapper
770 def get_poolfile_by_id(file_id, session=None):
771     """
772     Returns a PoolFile objects or None for the given id
773
774     @type file_id: int
775     @param file_id: the id of the file to look for
776
777     @rtype: PoolFile or None
778     @return: either the PoolFile object or None
779     """
780
781     q = session.query(PoolFile).filter_by(file_id=file_id)
782
783     try:
784         return q.one()
785     except NoResultFound:
786         return None
787
788 __all__.append('get_poolfile_by_id')
789
790
791 @session_wrapper
792 def get_poolfile_by_name(filename, location_id=None, session=None):
793     """
794     Returns an array of PoolFile objects for the given filename and
795     (optionally) location_id
796
797     @type filename: string
798     @param filename: the filename of the file to check against the DB
799
800     @type location_id: int
801     @param location_id: the id of the location to look in (optional)
802
803     @rtype: array
804     @return: array of PoolFile objects
805     """
806
807     q = session.query(PoolFile).filter_by(filename=filename)
808
809     if location_id is not None:
810         q = q.join(Location).filter_by(location_id=location_id)
811
812     return q.all()
813
814 __all__.append('get_poolfile_by_name')
815
816 @session_wrapper
817 def get_poolfile_like_name(filename, session=None):
818     """
819     Returns an array of PoolFile objects which are like the given name
820
821     @type filename: string
822     @param filename: the filename of the file to check against the DB
823
824     @rtype: array
825     @return: array of PoolFile objects
826     """
827
828     # TODO: There must be a way of properly using bind parameters with %FOO%
829     q = session.query(PoolFile).filter(PoolFile.filename.like('%%%s%%' % filename))
830
831     return q.all()
832
833 __all__.append('get_poolfile_like_name')
834
835 ################################################################################
836
837 class Fingerprint(object):
838     def __init__(self, *args, **kwargs):
839         pass
840
841     def __repr__(self):
842         return '<Fingerprint %s>' % self.fingerprint
843
844 __all__.append('Fingerprint')
845
846 @session_wrapper
847 def get_or_set_fingerprint(fpr, session=None):
848     """
849     Returns Fingerprint object for given fpr.
850
851     If no matching fpr is found, a row is inserted.
852
853     @type fpr: string
854     @param fpr: The fpr to find / add
855
856     @type session: SQLAlchemy
857     @param session: Optional SQL session object (a temporary one will be
858     generated if not supplied).  If not passed, a commit will be performed at
859     the end of the function, otherwise the caller is responsible for commiting.
860     A flush will be performed either way.
861
862     @rtype: Fingerprint
863     @return: the Fingerprint object for the given fpr
864     """
865
866     q = session.query(Fingerprint).filter_by(fingerprint=fpr)
867
868     try:
869         ret = q.one()
870     except NoResultFound:
871         fingerprint = Fingerprint()
872         fingerprint.fingerprint = fpr
873         session.add(fingerprint)
874         session.commit_or_flush()
875         ret = fingerprint
876
877     return ret
878
879 __all__.append('get_or_set_fingerprint')
880
881 ################################################################################
882
883 class Keyring(object):
884     def __init__(self, *args, **kwargs):
885         pass
886
887     def __repr__(self):
888         return '<Keyring %s>' % self.keyring_name
889
890 __all__.append('Keyring')
891
892 @session_wrapper
893 def get_or_set_keyring(keyring, session=None):
894     """
895     If C{keyring} does not have an entry in the C{keyrings} table yet, create one
896     and return the new Keyring
897     If C{keyring} already has an entry, simply return the existing Keyring
898
899     @type keyring: string
900     @param keyring: the keyring name
901
902     @rtype: Keyring
903     @return: the Keyring object for this keyring
904     """
905
906     q = session.query(Keyring).filter_by(keyring_name=keyring)
907
908     try:
909         return q.one()
910     except NoResultFound:
911         obj = Keyring(keyring_name=keyring)
912         session.add(obj)
913         session.commit_or_flush()
914         return obj
915
916 __all__.append('get_or_set_keyring')
917
918 ################################################################################
919
920 class Location(object):
921     def __init__(self, *args, **kwargs):
922         pass
923
924     def __repr__(self):
925         return '<Location %s (%s)>' % (self.path, self.location_id)
926
927 __all__.append('Location')
928
929 @session_wrapper
930 def get_location(location, component=None, archive=None, session=None):
931     """
932     Returns Location object for the given combination of location, component
933     and archive
934
935     @type location: string
936     @param location: the path of the location, e.g. I{/srv/ftp.debian.org/ftp/pool/}
937
938     @type component: string
939     @param component: the component name (if None, no restriction applied)
940
941     @type archive: string
942     @param archive_id: the archive name (if None, no restriction applied)
943
944     @rtype: Location / None
945     @return: Either a Location object or None if one can't be found
946     """
947
948     q = session.query(Location).filter_by(path=location)
949
950     if archive is not None:
951         q = q.join(Archive).filter_by(archive_name=archive)
952
953     if component is not None:
954         q = q.join(Component).filter_by(component_name=component)
955
956     try:
957         return q.one()
958     except NoResultFound:
959         return None
960
961 __all__.append('get_location')
962
963 ################################################################################
964
965 class Maintainer(object):
966     def __init__(self, *args, **kwargs):
967         pass
968
969     def __repr__(self):
970         return '''<Maintainer '%s' (%s)>''' % (self.name, self.maintainer_id)
971
972     def get_split_maintainer(self):
973         if not hasattr(self, 'name') or self.name is None:
974             return ('', '', '', '')
975
976         return fix_maintainer(self.name.strip())
977
978 __all__.append('Maintainer')
979
980 @session_wrapper
981 def get_or_set_maintainer(name, session=None):
982     """
983     Returns Maintainer object for given maintainer name.
984
985     If no matching maintainer name is found, a row is inserted.
986
987     @type name: string
988     @param name: The maintainer name to add
989
990     @type session: SQLAlchemy
991     @param session: Optional SQL session object (a temporary one will be
992     generated if not supplied).  If not passed, a commit will be performed at
993     the end of the function, otherwise the caller is responsible for commiting.
994     A flush will be performed either way.
995
996     @rtype: Maintainer
997     @return: the Maintainer object for the given maintainer
998     """
999
1000     q = session.query(Maintainer).filter_by(name=name)
1001     try:
1002         ret = q.one()
1003     except NoResultFound:
1004         maintainer = Maintainer()
1005         maintainer.name = name
1006         session.add(maintainer)
1007         session.commit_or_flush()
1008         ret = maintainer
1009
1010     return ret
1011
1012 __all__.append('get_or_set_maintainer')
1013
1014 @session_wrapper
1015 def get_maintainer(maintainer_id, session=None):
1016     """
1017     Return the name of the maintainer behind C{maintainer_id} or None if that
1018     maintainer_id is invalid.
1019
1020     @type maintainer_id: int
1021     @param maintainer_id: the id of the maintainer
1022
1023     @rtype: Maintainer
1024     @return: the Maintainer with this C{maintainer_id}
1025     """
1026
1027     return session.query(Maintainer).get(maintainer_id)
1028
1029 __all__.append('get_maintainer')
1030
1031 ################################################################################
1032
1033 class NewComment(object):
1034     def __init__(self, *args, **kwargs):
1035         pass
1036
1037     def __repr__(self):
1038         return '''<NewComment for '%s %s' (%s)>''' % (self.package, self.version, self.comment_id)
1039
1040 __all__.append('NewComment')
1041
1042 @session_wrapper
1043 def has_new_comment(package, version, session=None):
1044     """
1045     Returns true if the given combination of C{package}, C{version} has a comment.
1046
1047     @type package: string
1048     @param package: name of the package
1049
1050     @type version: string
1051     @param version: package version
1052
1053     @type session: Session
1054     @param session: Optional SQLA session object (a temporary one will be
1055     generated if not supplied)
1056
1057     @rtype: boolean
1058     @return: true/false
1059     """
1060
1061     q = session.query(NewComment)
1062     q = q.filter_by(package=package)
1063     q = q.filter_by(version=version)
1064
1065     return bool(q.count() > 0)
1066
1067 __all__.append('has_new_comment')
1068
1069 @session_wrapper
1070 def get_new_comments(package=None, version=None, comment_id=None, session=None):
1071     """
1072     Returns (possibly empty) list of NewComment objects for the given
1073     parameters
1074
1075     @type package: string (optional)
1076     @param package: name of the package
1077
1078     @type version: string (optional)
1079     @param version: package version
1080
1081     @type comment_id: int (optional)
1082     @param comment_id: An id of a comment
1083
1084     @type session: Session
1085     @param session: Optional SQLA session object (a temporary one will be
1086     generated if not supplied)
1087
1088     @rtype: list
1089     @return: A (possibly empty) list of NewComment objects will be returned
1090     """
1091
1092     q = session.query(NewComment)
1093     if package is not None: q = q.filter_by(package=package)
1094     if version is not None: q = q.filter_by(version=version)
1095     if comment_id is not None: q = q.filter_by(comment_id=comment_id)
1096
1097     return q.all()
1098
1099 __all__.append('get_new_comments')
1100
1101 ################################################################################
1102
1103 class Override(object):
1104     def __init__(self, *args, **kwargs):
1105         pass
1106
1107     def __repr__(self):
1108         return '<Override %s (%s)>' % (self.package, self.suite_id)
1109
1110 __all__.append('Override')
1111
1112 @session_wrapper
1113 def get_override(package, suite=None, component=None, overridetype=None, session=None):
1114     """
1115     Returns Override object for the given parameters
1116
1117     @type package: string
1118     @param package: The name of the package
1119
1120     @type suite: string, list or None
1121     @param suite: The name of the suite (or suites if a list) to limit to.  If
1122                   None, don't limit.  Defaults to None.
1123
1124     @type component: string, list or None
1125     @param component: The name of the component (or components if a list) to
1126                       limit to.  If None, don't limit.  Defaults to None.
1127
1128     @type overridetype: string, list or None
1129     @param overridetype: The name of the overridetype (or overridetypes if a list) to
1130                          limit to.  If None, don't limit.  Defaults to None.
1131
1132     @type session: Session
1133     @param session: Optional SQLA session object (a temporary one will be
1134     generated if not supplied)
1135
1136     @rtype: list
1137     @return: A (possibly empty) list of Override objects will be returned
1138     """
1139
1140     q = session.query(Override)
1141     q = q.filter_by(package=package)
1142
1143     if suite is not None:
1144         if not isinstance(suite, list): suite = [suite]
1145         q = q.join(Suite).filter(Suite.suite_name.in_(suite))
1146
1147     if component is not None:
1148         if not isinstance(component, list): component = [component]
1149         q = q.join(Component).filter(Component.component_name.in_(component))
1150
1151     if overridetype is not None:
1152         if not isinstance(overridetype, list): overridetype = [overridetype]
1153         q = q.join(OverrideType).filter(OverrideType.overridetype.in_(overridetype))
1154
1155     return q.all()
1156
1157 __all__.append('get_override')
1158
1159
1160 ################################################################################
1161
1162 class OverrideType(object):
1163     def __init__(self, *args, **kwargs):
1164         pass
1165
1166     def __repr__(self):
1167         return '<OverrideType %s>' % self.overridetype
1168
1169 __all__.append('OverrideType')
1170
1171 @session_wrapper
1172 def get_override_type(override_type, session=None):
1173     """
1174     Returns OverrideType object for given C{override type}.
1175
1176     @type override_type: string
1177     @param override_type: The name of the override type
1178
1179     @type session: Session
1180     @param session: Optional SQLA session object (a temporary one will be
1181     generated if not supplied)
1182
1183     @rtype: int
1184     @return: the database id for the given override type
1185     """
1186
1187     q = session.query(OverrideType).filter_by(overridetype=override_type)
1188
1189     try:
1190         return q.one()
1191     except NoResultFound:
1192         return None
1193
1194 __all__.append('get_override_type')
1195
1196 ################################################################################
1197
1198 class PendingContentAssociation(object):
1199     def __init__(self, *args, **kwargs):
1200         pass
1201
1202     def __repr__(self):
1203         return '<PendingContentAssociation %s>' % self.pca_id
1204
1205 __all__.append('PendingContentAssociation')
1206
1207 def insert_pending_content_paths(package, fullpaths, session=None):
1208     """
1209     Make sure given paths are temporarily associated with given
1210     package
1211
1212     @type package: dict
1213     @param package: the package to associate with should have been read in from the binary control file
1214     @type fullpaths: list
1215     @param fullpaths: the list of paths of the file being associated with the binary
1216     @type session: SQLAlchemy session
1217     @param session: Optional SQLAlchemy session.  If this is passed, the caller
1218     is responsible for ensuring a transaction has begun and committing the
1219     results or rolling back based on the result code.  If not passed, a commit
1220     will be performed at the end of the function
1221
1222     @return: True upon success, False if there is a problem
1223     """
1224
1225     privatetrans = False
1226
1227     if session is None:
1228         session = DBConn().session()
1229         privatetrans = True
1230
1231     try:
1232         arch = get_architecture(package['Architecture'], session)
1233         arch_id = arch.arch_id
1234
1235         # Remove any already existing recorded files for this package
1236         q = session.query(PendingContentAssociation)
1237         q = q.filter_by(package=package['Package'])
1238         q = q.filter_by(version=package['Version'])
1239         q = q.filter_by(architecture=arch_id)
1240         q.delete()
1241
1242         # Insert paths
1243         pathcache = {}
1244         for fullpath in fullpaths:
1245             (path, file) = os.path.split(fullpath)
1246
1247             if path.startswith( "./" ):
1248                 path = path[2:]
1249
1250             filepath_id = get_or_set_contents_path_id(path, session)
1251             filename_id = get_or_set_contents_file_id(file, session)
1252
1253             pathcache[fullpath] = (filepath_id, filename_id)
1254
1255         for fullpath, dat in pathcache.items():
1256             pca = PendingContentAssociation()
1257             pca.package = package['Package']
1258             pca.version = package['Version']
1259             pca.filepath_id = dat[0]
1260             pca.filename_id = dat[1]
1261             pca.architecture = arch_id
1262             session.add(pca)
1263
1264         # Only commit if we set up the session ourself
1265         if privatetrans:
1266             session.commit()
1267             session.close()
1268         else:
1269             session.flush()
1270
1271         return True
1272     except Exception, e:
1273         traceback.print_exc()
1274
1275         # Only rollback if we set up the session ourself
1276         if privatetrans:
1277             session.rollback()
1278             session.close()
1279
1280         return False
1281
1282 __all__.append('insert_pending_content_paths')
1283
1284 ################################################################################
1285
1286 class Priority(object):
1287     def __init__(self, *args, **kwargs):
1288         pass
1289
1290     def __eq__(self, val):
1291         if isinstance(val, str):
1292             return (self.priority == val)
1293         # This signals to use the normal comparison operator
1294         return NotImplemented
1295
1296     def __ne__(self, val):
1297         if isinstance(val, str):
1298             return (self.priority != val)
1299         # This signals to use the normal comparison operator
1300         return NotImplemented
1301
1302     def __repr__(self):
1303         return '<Priority %s (%s)>' % (self.priority, self.priority_id)
1304
1305 __all__.append('Priority')
1306
1307 @session_wrapper
1308 def get_priority(priority, session=None):
1309     """
1310     Returns Priority object for given C{priority name}.
1311
1312     @type priority: string
1313     @param priority: The name of the priority
1314
1315     @type session: Session
1316     @param session: Optional SQLA session object (a temporary one will be
1317     generated if not supplied)
1318
1319     @rtype: Priority
1320     @return: Priority object for the given priority
1321     """
1322
1323     q = session.query(Priority).filter_by(priority=priority)
1324
1325     try:
1326         return q.one()
1327     except NoResultFound:
1328         return None
1329
1330 __all__.append('get_priority')
1331
1332 @session_wrapper
1333 def get_priorities(session=None):
1334     """
1335     Returns dictionary of priority names -> id mappings
1336
1337     @type session: Session
1338     @param session: Optional SQL session object (a temporary one will be
1339     generated if not supplied)
1340
1341     @rtype: dictionary
1342     @return: dictionary of priority names -> id mappings
1343     """
1344
1345     ret = {}
1346     q = session.query(Priority)
1347     for x in q.all():
1348         ret[x.priority] = x.priority_id
1349
1350     return ret
1351
1352 __all__.append('get_priorities')
1353
1354 ################################################################################
1355
1356 class Queue(object):
1357     def __init__(self, *args, **kwargs):
1358         pass
1359
1360     def __repr__(self):
1361         return '<Queue %s>' % self.queue_name
1362
1363     def autobuild_upload(self, changes, srcpath, session=None):
1364         """
1365         Update queue_build database table used for incoming autobuild support.
1366
1367         @type changes: Changes
1368         @param changes: changes object for the upload to process
1369
1370         @type srcpath: string
1371         @param srcpath: path for the queue file entries/link destinations
1372
1373         @type session: SQLAlchemy session
1374         @param session: Optional SQLAlchemy session.  If this is passed, the
1375         caller is responsible for ensuring a transaction has begun and
1376         committing the results or rolling back based on the result code.  If
1377         not passed, a commit will be performed at the end of the function,
1378         otherwise the caller is responsible for commiting.
1379
1380         @rtype: NoneType or string
1381         @return: None if the operation failed, a string describing the error if not
1382         """
1383
1384         privatetrans = False
1385         if session is None:
1386             session = DBConn().session()
1387             privatetrans = True
1388
1389         # TODO: Remove by moving queue config into the database
1390         conf = Config()
1391
1392         for suitename in changes.changes["distribution"].keys():
1393             # TODO: Move into database as:
1394             #       buildqueuedir TEXT DEFAULT NULL (i.e. NULL is no build)
1395             #       buildqueuecopy BOOLEAN NOT NULL DEFAULT FALSE (i.e. default is symlink)
1396             #       This also gets rid of the SecurityQueueBuild hack below
1397             if suitename not in conf.ValueList("Dinstall::QueueBuildSuites"):
1398                 continue
1399
1400             # Find suite object
1401             s = get_suite(suitename, session)
1402             if s is None:
1403                 return "INTERNAL ERROR: Could not find suite %s" % suitename
1404
1405             # TODO: Get from database as above
1406             dest_dir = conf["Dir::QueueBuild"]
1407
1408             # TODO: Move into database as above
1409             if conf.FindB("Dinstall::SecurityQueueBuild"):
1410                 dest_dir = os.path.join(dest_dir, suitename)
1411
1412             for file_entry in changes.files.keys():
1413                 src = os.path.join(srcpath, file_entry)
1414                 dest = os.path.join(dest_dir, file_entry)
1415
1416                 # TODO: Move into database as above
1417                 if conf.FindB("Dinstall::SecurityQueueBuild"):
1418                     # Copy it since the original won't be readable by www-data
1419                     import utils
1420                     utils.copy(src, dest)
1421                 else:
1422                     # Create a symlink to it
1423                     os.symlink(src, dest)
1424
1425                 qb = QueueBuild()
1426                 qb.suite_id = s.suite_id
1427                 qb.queue_id = self.queue_id
1428                 qb.filename = dest
1429                 qb.in_queue = True
1430
1431                 session.add(qb)
1432
1433             # If the .orig tarballs are in the pool, create a symlink to
1434             # them (if one doesn't already exist)
1435             for dsc_file in changes.dsc_files.keys():
1436                 # Skip all files except orig tarballs
1437                 from daklib.regexes import re_is_orig_source
1438                 if not re_is_orig_source.match(dsc_file):
1439                     continue
1440                 # Skip orig files not identified in the pool
1441                 if not (changes.orig_files.has_key(dsc_file) and
1442                         changes.orig_files[dsc_file].has_key("id")):
1443                     continue
1444                 orig_file_id = changes.orig_files[dsc_file]["id"]
1445                 dest = os.path.join(dest_dir, dsc_file)
1446
1447                 # If it doesn't exist, create a symlink
1448                 if not os.path.exists(dest):
1449                     q = session.execute("SELECT l.path, f.filename FROM location l, files f WHERE f.id = :id and f.location = l.id",
1450                                         {'id': orig_file_id})
1451                     res = q.fetchone()
1452                     if not res:
1453                         return "[INTERNAL ERROR] Couldn't find id %s in files table." % (orig_file_id)
1454
1455                     src = os.path.join(res[0], res[1])
1456                     os.symlink(src, dest)
1457
1458                     # Add it to the list of packages for later processing by apt-ftparchive
1459                     qb = QueueBuild()
1460                     qb.suite_id = s.suite_id
1461                     qb.queue_id = self.queue_id
1462                     qb.filename = dest
1463                     qb.in_queue = True
1464                     session.add(qb)
1465
1466                 # If it does, update things to ensure it's not removed prematurely
1467                 else:
1468                     qb = get_queue_build(dest, s.suite_id, session)
1469                     if qb is None:
1470                         qb.in_queue = True
1471                         qb.last_used = None
1472                         session.add(qb)
1473
1474         if privatetrans:
1475             session.commit()
1476             session.close()
1477
1478         return None
1479
1480 __all__.append('Queue')
1481
1482 @session_wrapper
1483 def get_or_set_queue(queuename, session=None):
1484     """
1485     Returns Queue object for given C{queue name}, creating it if it does not
1486     exist.
1487
1488     @type queuename: string
1489     @param queuename: The name of the queue
1490
1491     @type session: Session
1492     @param session: Optional SQLA session object (a temporary one will be
1493     generated if not supplied)
1494
1495     @rtype: Queue
1496     @return: Queue object for the given queue
1497     """
1498
1499     q = session.query(Queue).filter_by(queue_name=queuename)
1500
1501     try:
1502         ret = q.one()
1503     except NoResultFound:
1504         queue = Queue()
1505         queue.queue_name = queuename
1506         session.add(queue)
1507         session.commit_or_flush()
1508         ret = queue
1509
1510     return ret
1511
1512 __all__.append('get_or_set_queue')
1513
1514 ################################################################################
1515
1516 class QueueBuild(object):
1517     def __init__(self, *args, **kwargs):
1518         pass
1519
1520     def __repr__(self):
1521         return '<QueueBuild %s (%s)>' % (self.filename, self.queue_id)
1522
1523 __all__.append('QueueBuild')
1524
1525 @session_wrapper
1526 def get_queue_build(filename, suite, session=None):
1527     """
1528     Returns QueueBuild object for given C{filename} and C{suite}.
1529
1530     @type filename: string
1531     @param filename: The name of the file
1532
1533     @type suiteid: int or str
1534     @param suiteid: Suite name or ID
1535
1536     @type session: Session
1537     @param session: Optional SQLA session object (a temporary one will be
1538     generated if not supplied)
1539
1540     @rtype: Queue
1541     @return: Queue object for the given queue
1542     """
1543
1544     if isinstance(suite, int):
1545         q = session.query(QueueBuild).filter_by(filename=filename).filter_by(suite_id=suite)
1546     else:
1547         q = session.query(QueueBuild).filter_by(filename=filename)
1548         q = q.join(Suite).filter_by(suite_name=suite)
1549
1550     try:
1551         return q.one()
1552     except NoResultFound:
1553         return None
1554
1555 __all__.append('get_queue_build')
1556
1557 ################################################################################
1558
1559 class Section(object):
1560     def __init__(self, *args, **kwargs):
1561         pass
1562
1563     def __eq__(self, val):
1564         if isinstance(val, str):
1565             return (self.section == val)
1566         # This signals to use the normal comparison operator
1567         return NotImplemented
1568
1569     def __ne__(self, val):
1570         if isinstance(val, str):
1571             return (self.section != val)
1572         # This signals to use the normal comparison operator
1573         return NotImplemented
1574
1575     def __repr__(self):
1576         return '<Section %s>' % self.section
1577
1578 __all__.append('Section')
1579
1580 @session_wrapper
1581 def get_section(section, session=None):
1582     """
1583     Returns Section object for given C{section name}.
1584
1585     @type section: string
1586     @param section: The name of the section
1587
1588     @type session: Session
1589     @param session: Optional SQLA session object (a temporary one will be
1590     generated if not supplied)
1591
1592     @rtype: Section
1593     @return: Section object for the given section name
1594     """
1595
1596     q = session.query(Section).filter_by(section=section)
1597
1598     try:
1599         return q.one()
1600     except NoResultFound:
1601         return None
1602
1603 __all__.append('get_section')
1604
1605 @session_wrapper
1606 def get_sections(session=None):
1607     """
1608     Returns dictionary of section names -> id mappings
1609
1610     @type session: Session
1611     @param session: Optional SQL session object (a temporary one will be
1612     generated if not supplied)
1613
1614     @rtype: dictionary
1615     @return: dictionary of section names -> id mappings
1616     """
1617
1618     ret = {}
1619     q = session.query(Section)
1620     for x in q.all():
1621         ret[x.section] = x.section_id
1622
1623     return ret
1624
1625 __all__.append('get_sections')
1626
1627 ################################################################################
1628
1629 class DBSource(object):
1630     def __init__(self, *args, **kwargs):
1631         pass
1632
1633     def __repr__(self):
1634         return '<DBSource %s (%s)>' % (self.source, self.version)
1635
1636 __all__.append('DBSource')
1637
1638 @session_wrapper
1639 def source_exists(source, source_version, suites = ["any"], session=None):
1640     """
1641     Ensure that source exists somewhere in the archive for the binary
1642     upload being processed.
1643       1. exact match     => 1.0-3
1644       2. bin-only NMU    => 1.0-3+b1 , 1.0-3.1+b1
1645
1646     @type package: string
1647     @param package: package source name
1648
1649     @type source_version: string
1650     @param source_version: expected source version
1651
1652     @type suites: list
1653     @param suites: list of suites to check in, default I{any}
1654
1655     @type session: Session
1656     @param session: Optional SQLA session object (a temporary one will be
1657     generated if not supplied)
1658
1659     @rtype: int
1660     @return: returns 1 if a source with expected version is found, otherwise 0
1661
1662     """
1663
1664     cnf = Config()
1665     ret = 1
1666
1667     for suite in suites:
1668         q = session.query(DBSource).filter_by(source=source)
1669         if suite != "any":
1670             # source must exist in suite X, or in some other suite that's
1671             # mapped to X, recursively... silent-maps are counted too,
1672             # unreleased-maps aren't.
1673             maps = cnf.ValueList("SuiteMappings")[:]
1674             maps.reverse()
1675             maps = [ m.split() for m in maps ]
1676             maps = [ (x[1], x[2]) for x in maps
1677                             if x[0] == "map" or x[0] == "silent-map" ]
1678             s = [suite]
1679             for x in maps:
1680                 if x[1] in s and x[0] not in s:
1681                     s.append(x[0])
1682
1683             q = q.join(SrcAssociation).join(Suite)
1684             q = q.filter(Suite.suite_name.in_(s))
1685
1686         # Reduce the query results to a list of version numbers
1687         ql = [ j.version for j in q.all() ]
1688
1689         # Try (1)
1690         if source_version in ql:
1691             continue
1692
1693         # Try (2)
1694         from daklib.regexes import re_bin_only_nmu
1695         orig_source_version = re_bin_only_nmu.sub('', source_version)
1696         if orig_source_version in ql:
1697             continue
1698
1699         # No source found so return not ok
1700         ret = 0
1701
1702     return ret
1703
1704 __all__.append('source_exists')
1705
1706 @session_wrapper
1707 def get_suites_source_in(source, session=None):
1708     """
1709     Returns list of Suite objects which given C{source} name is in
1710
1711     @type source: str
1712     @param source: DBSource package name to search for
1713
1714     @rtype: list
1715     @return: list of Suite objects for the given source
1716     """
1717
1718     return session.query(Suite).join(SrcAssociation).join(DBSource).filter_by(source=source).all()
1719
1720 __all__.append('get_suites_source_in')
1721
1722 @session_wrapper
1723 def get_sources_from_name(source, version=None, dm_upload_allowed=None, session=None):
1724     """
1725     Returns list of DBSource objects for given C{source} name and other parameters
1726
1727     @type source: str
1728     @param source: DBSource package name to search for
1729
1730     @type source: str or None
1731     @param source: DBSource version name to search for or None if not applicable
1732
1733     @type dm_upload_allowed: bool
1734     @param dm_upload_allowed: If None, no effect.  If True or False, only
1735     return packages with that dm_upload_allowed setting
1736
1737     @type session: Session
1738     @param session: Optional SQL session object (a temporary one will be
1739     generated if not supplied)
1740
1741     @rtype: list
1742     @return: list of DBSource objects for the given name (may be empty)
1743     """
1744
1745     q = session.query(DBSource).filter_by(source=source)
1746
1747     if version is not None:
1748         q = q.filter_by(version=version)
1749
1750     if dm_upload_allowed is not None:
1751         q = q.filter_by(dm_upload_allowed=dm_upload_allowed)
1752
1753     return q.all()
1754
1755 __all__.append('get_sources_from_name')
1756
1757 @session_wrapper
1758 def get_source_in_suite(source, suite, session=None):
1759     """
1760     Returns list of DBSource objects for a combination of C{source} and C{suite}.
1761
1762       - B{source} - source package name, eg. I{mailfilter}, I{bbdb}, I{glibc}
1763       - B{suite} - a suite name, eg. I{unstable}
1764
1765     @type source: string
1766     @param source: source package name
1767
1768     @type suite: string
1769     @param suite: the suite name
1770
1771     @rtype: string
1772     @return: the version for I{source} in I{suite}
1773
1774     """
1775
1776     q = session.query(SrcAssociation)
1777     q = q.join('source').filter_by(source=source)
1778     q = q.join('suite').filter_by(suite_name=suite)
1779
1780     try:
1781         return q.one().source
1782     except NoResultFound:
1783         return None
1784
1785 __all__.append('get_source_in_suite')
1786
1787 ################################################################################
1788
1789 class SourceACL(object):
1790     def __init__(self, *args, **kwargs):
1791         pass
1792
1793 __all__.append('SourceACL')
1794
1795 ################################################################################
1796
1797 class SrcAssociation(object):
1798     def __init__(self, *args, **kwargs):
1799         pass
1800
1801     def __repr__(self):
1802         return '<SrcAssociation %s (%s, %s)>' % (self.sa_id, self.source, self.suite)
1803
1804 __all__.append('SrcAssociation')
1805
1806 ################################################################################
1807
1808 class SrcFormat(object):
1809     def __init__(self, *args, **kwargs):
1810         pass
1811
1812     def __repr__(self):
1813         return '<SrcFormat %s>' % (self.format_name)
1814
1815 __all__.append('SrcFormat')
1816
1817 ################################################################################
1818
1819 class SrcUploader(object):
1820     def __init__(self, *args, **kwargs):
1821         pass
1822
1823     def __repr__(self):
1824         return '<SrcUploader %s>' % self.uploader_id
1825
1826 __all__.append('SrcUploader')
1827
1828 ################################################################################
1829
1830 SUITE_FIELDS = [ ('SuiteName', 'suite_name'),
1831                  ('SuiteID', 'suite_id'),
1832                  ('Version', 'version'),
1833                  ('Origin', 'origin'),
1834                  ('Label', 'label'),
1835                  ('Description', 'description'),
1836                  ('Untouchable', 'untouchable'),
1837                  ('Announce', 'announce'),
1838                  ('Codename', 'codename'),
1839                  ('OverrideCodename', 'overridecodename'),
1840                  ('ValidTime', 'validtime'),
1841                  ('Priority', 'priority'),
1842                  ('NotAutomatic', 'notautomatic'),
1843                  ('CopyChanges', 'copychanges'),
1844                  ('CopyDotDak', 'copydotdak'),
1845                  ('CommentsDir', 'commentsdir'),
1846                  ('OverrideSuite', 'overridesuite'),
1847                  ('ChangelogBase', 'changelogbase')]
1848
1849
1850 class Suite(object):
1851     def __init__(self, *args, **kwargs):
1852         pass
1853
1854     def __repr__(self):
1855         return '<Suite %s>' % self.suite_name
1856
1857     def __eq__(self, val):
1858         if isinstance(val, str):
1859             return (self.suite_name == val)
1860         # This signals to use the normal comparison operator
1861         return NotImplemented
1862
1863     def __ne__(self, val):
1864         if isinstance(val, str):
1865             return (self.suite_name != val)
1866         # This signals to use the normal comparison operator
1867         return NotImplemented
1868
1869     def details(self):
1870         ret = []
1871         for disp, field in SUITE_FIELDS:
1872             val = getattr(self, field, None)
1873             if val is not None:
1874                 ret.append("%s: %s" % (disp, val))
1875
1876         return "\n".join(ret)
1877
1878 __all__.append('Suite')
1879
1880 @session_wrapper
1881 def get_suite_architecture(suite, architecture, session=None):
1882     """
1883     Returns a SuiteArchitecture object given C{suite} and ${arch} or None if it
1884     doesn't exist
1885
1886     @type suite: str
1887     @param suite: Suite name to search for
1888
1889     @type architecture: str
1890     @param architecture: Architecture name to search for
1891
1892     @type session: Session
1893     @param session: Optional SQL session object (a temporary one will be
1894     generated if not supplied)
1895
1896     @rtype: SuiteArchitecture
1897     @return: the SuiteArchitecture object or None
1898     """
1899
1900     q = session.query(SuiteArchitecture)
1901     q = q.join(Architecture).filter_by(arch_string=architecture)
1902     q = q.join(Suite).filter_by(suite_name=suite)
1903
1904     try:
1905         return q.one()
1906     except NoResultFound:
1907         return None
1908
1909 __all__.append('get_suite_architecture')
1910
1911 @session_wrapper
1912 def get_suite(suite, session=None):
1913     """
1914     Returns Suite object for given C{suite name}.
1915
1916     @type suite: string
1917     @param suite: The name of the suite
1918
1919     @type session: Session
1920     @param session: Optional SQLA session object (a temporary one will be
1921     generated if not supplied)
1922
1923     @rtype: Suite
1924     @return: Suite object for the requested suite name (None if not present)
1925     """
1926
1927     q = session.query(Suite).filter_by(suite_name=suite)
1928
1929     try:
1930         return q.one()
1931     except NoResultFound:
1932         return None
1933
1934 __all__.append('get_suite')
1935
1936 ################################################################################
1937
1938 class SuiteArchitecture(object):
1939     def __init__(self, *args, **kwargs):
1940         pass
1941
1942     def __repr__(self):
1943         return '<SuiteArchitecture (%s, %s)>' % (self.suite_id, self.arch_id)
1944
1945 __all__.append('SuiteArchitecture')
1946
1947 @session_wrapper
1948 def get_suite_architectures(suite, skipsrc=False, skipall=False, session=None):
1949     """
1950     Returns list of Architecture objects for given C{suite} name
1951
1952     @type source: str
1953     @param source: Suite name to search for
1954
1955     @type skipsrc: boolean
1956     @param skipsrc: Whether to skip returning the 'source' architecture entry
1957     (Default False)
1958
1959     @type skipall: boolean
1960     @param skipall: Whether to skip returning the 'all' architecture entry
1961     (Default False)
1962
1963     @type session: Session
1964     @param session: Optional SQL session object (a temporary one will be
1965     generated if not supplied)
1966
1967     @rtype: list
1968     @return: list of Architecture objects for the given name (may be empty)
1969     """
1970
1971     q = session.query(Architecture)
1972     q = q.join(SuiteArchitecture)
1973     q = q.join(Suite).filter_by(suite_name=suite)
1974
1975     if skipsrc:
1976         q = q.filter(Architecture.arch_string != 'source')
1977
1978     if skipall:
1979         q = q.filter(Architecture.arch_string != 'all')
1980
1981     q = q.order_by('arch_string')
1982
1983     return q.all()
1984
1985 __all__.append('get_suite_architectures')
1986
1987 ################################################################################
1988
1989 class SuiteSrcFormat(object):
1990     def __init__(self, *args, **kwargs):
1991         pass
1992
1993     def __repr__(self):
1994         return '<SuiteSrcFormat (%s, %s)>' % (self.suite_id, self.src_format_id)
1995
1996 __all__.append('SuiteSrcFormat')
1997
1998 @session_wrapper
1999 def get_suite_src_formats(suite, session=None):
2000     """
2001     Returns list of allowed SrcFormat for C{suite}.
2002
2003     @type suite: str
2004     @param suite: Suite name to search for
2005
2006     @type session: Session
2007     @param session: Optional SQL session object (a temporary one will be
2008     generated if not supplied)
2009
2010     @rtype: list
2011     @return: the list of allowed source formats for I{suite}
2012     """
2013
2014     q = session.query(SrcFormat)
2015     q = q.join(SuiteSrcFormat)
2016     q = q.join(Suite).filter_by(suite_name=suite)
2017     q = q.order_by('format_name')
2018
2019     return q.all()
2020
2021 __all__.append('get_suite_src_formats')
2022
2023 ################################################################################
2024
2025 class Uid(object):
2026     def __init__(self, *args, **kwargs):
2027         pass
2028
2029     def __eq__(self, val):
2030         if isinstance(val, str):
2031             return (self.uid == val)
2032         # This signals to use the normal comparison operator
2033         return NotImplemented
2034
2035     def __ne__(self, val):
2036         if isinstance(val, str):
2037             return (self.uid != val)
2038         # This signals to use the normal comparison operator
2039         return NotImplemented
2040
2041     def __repr__(self):
2042         return '<Uid %s (%s)>' % (self.uid, self.name)
2043
2044 __all__.append('Uid')
2045
2046 @session_wrapper
2047 def add_database_user(uidname, session=None):
2048     """
2049     Adds a database user
2050
2051     @type uidname: string
2052     @param uidname: The uid of the user to add
2053
2054     @type session: SQLAlchemy
2055     @param session: Optional SQL session object (a temporary one will be
2056     generated if not supplied).  If not passed, a commit will be performed at
2057     the end of the function, otherwise the caller is responsible for commiting.
2058
2059     @rtype: Uid
2060     @return: the uid object for the given uidname
2061     """
2062
2063     session.execute("CREATE USER :uid", {'uid': uidname})
2064     session.commit_or_flush()
2065
2066 __all__.append('add_database_user')
2067
2068 @session_wrapper
2069 def get_or_set_uid(uidname, session=None):
2070     """
2071     Returns uid object for given uidname.
2072
2073     If no matching uidname is found, a row is inserted.
2074
2075     @type uidname: string
2076     @param uidname: The uid to add
2077
2078     @type session: SQLAlchemy
2079     @param session: Optional SQL session object (a temporary one will be
2080     generated if not supplied).  If not passed, a commit will be performed at
2081     the end of the function, otherwise the caller is responsible for commiting.
2082
2083     @rtype: Uid
2084     @return: the uid object for the given uidname
2085     """
2086
2087     q = session.query(Uid).filter_by(uid=uidname)
2088
2089     try:
2090         ret = q.one()
2091     except NoResultFound:
2092         uid = Uid()
2093         uid.uid = uidname
2094         session.add(uid)
2095         session.commit_or_flush()
2096         ret = uid
2097
2098     return ret
2099
2100 __all__.append('get_or_set_uid')
2101
2102 @session_wrapper
2103 def get_uid_from_fingerprint(fpr, session=None):
2104     q = session.query(Uid)
2105     q = q.join(Fingerprint).filter_by(fingerprint=fpr)
2106
2107     try:
2108         return q.one()
2109     except NoResultFound:
2110         return None
2111
2112 __all__.append('get_uid_from_fingerprint')
2113
2114 ################################################################################
2115
2116 class UploadBlock(object):
2117     def __init__(self, *args, **kwargs):
2118         pass
2119
2120 __all__.append('UploadBlock')
2121
2122 ################################################################################
2123
2124 class DBConn(Singleton):
2125     """
2126     database module init.
2127     """
2128     def __init__(self, *args, **kwargs):
2129         super(DBConn, self).__init__(*args, **kwargs)
2130
2131     def _startup(self, *args, **kwargs):
2132         self.debug = False
2133         if kwargs.has_key('debug'):
2134             self.debug = True
2135         self.__createconn()
2136
2137     def __setuptables(self):
2138         self.tbl_architecture = Table('architecture', self.db_meta, autoload=True)
2139         self.tbl_archive = Table('archive', self.db_meta, autoload=True)
2140         self.tbl_bin_associations = Table('bin_associations', self.db_meta, autoload=True)
2141         self.tbl_binaries = Table('binaries', self.db_meta, autoload=True)
2142         self.tbl_binary_acl = Table('binary_acl', self.db_meta, autoload=True)
2143         self.tbl_binary_acl_map = Table('binary_acl_map', self.db_meta, autoload=True)
2144         self.tbl_component = Table('component', self.db_meta, autoload=True)
2145         self.tbl_config = Table('config', self.db_meta, autoload=True)
2146         self.tbl_content_associations = Table('content_associations', self.db_meta, autoload=True)
2147         self.tbl_content_file_names = Table('content_file_names', self.db_meta, autoload=True)
2148         self.tbl_content_file_paths = Table('content_file_paths', self.db_meta, autoload=True)
2149         self.tbl_dsc_files = Table('dsc_files', self.db_meta, autoload=True)
2150         self.tbl_files = Table('files', self.db_meta, autoload=True)
2151         self.tbl_fingerprint = Table('fingerprint', self.db_meta, autoload=True)
2152         self.tbl_keyrings = Table('keyrings', self.db_meta, autoload=True)
2153         self.tbl_location = Table('location', self.db_meta, autoload=True)
2154         self.tbl_maintainer = Table('maintainer', self.db_meta, autoload=True)
2155         self.tbl_new_comments = Table('new_comments', self.db_meta, autoload=True)
2156         self.tbl_override = Table('override', self.db_meta, autoload=True)
2157         self.tbl_override_type = Table('override_type', self.db_meta, autoload=True)
2158         self.tbl_pending_content_associations = Table('pending_content_associations', self.db_meta, autoload=True)
2159         self.tbl_priority = Table('priority', self.db_meta, autoload=True)
2160         self.tbl_queue = Table('queue', self.db_meta, autoload=True)
2161         self.tbl_queue_build = Table('queue_build', self.db_meta, autoload=True)
2162         self.tbl_section = Table('section', self.db_meta, autoload=True)
2163         self.tbl_source = Table('source', self.db_meta, autoload=True)
2164         self.tbl_source_acl = Table('source_acl', self.db_meta, autoload=True)
2165         self.tbl_src_associations = Table('src_associations', self.db_meta, autoload=True)
2166         self.tbl_src_format = Table('src_format', self.db_meta, autoload=True)
2167         self.tbl_src_uploaders = Table('src_uploaders', self.db_meta, autoload=True)
2168         self.tbl_suite = Table('suite', self.db_meta, autoload=True)
2169         self.tbl_suite_architectures = Table('suite_architectures', self.db_meta, autoload=True)
2170         self.tbl_suite_src_formats = Table('suite_src_formats', self.db_meta, autoload=True)
2171         self.tbl_uid = Table('uid', self.db_meta, autoload=True)
2172         self.tbl_upload_blocks = Table('upload_blocks', self.db_meta, autoload=True)
2173
2174     def __setupmappers(self):
2175         mapper(Architecture, self.tbl_architecture,
2176                properties = dict(arch_id = self.tbl_architecture.c.id))
2177
2178         mapper(Archive, self.tbl_archive,
2179                properties = dict(archive_id = self.tbl_archive.c.id,
2180                                  archive_name = self.tbl_archive.c.name))
2181
2182         mapper(BinAssociation, self.tbl_bin_associations,
2183                properties = dict(ba_id = self.tbl_bin_associations.c.id,
2184                                  suite_id = self.tbl_bin_associations.c.suite,
2185                                  suite = relation(Suite),
2186                                  binary_id = self.tbl_bin_associations.c.bin,
2187                                  binary = relation(DBBinary)))
2188
2189         mapper(DBBinary, self.tbl_binaries,
2190                properties = dict(binary_id = self.tbl_binaries.c.id,
2191                                  package = self.tbl_binaries.c.package,
2192                                  version = self.tbl_binaries.c.version,
2193                                  maintainer_id = self.tbl_binaries.c.maintainer,
2194                                  maintainer = relation(Maintainer),
2195                                  source_id = self.tbl_binaries.c.source,
2196                                  source = relation(DBSource),
2197                                  arch_id = self.tbl_binaries.c.architecture,
2198                                  architecture = relation(Architecture),
2199                                  poolfile_id = self.tbl_binaries.c.file,
2200                                  poolfile = relation(PoolFile),
2201                                  binarytype = self.tbl_binaries.c.type,
2202                                  fingerprint_id = self.tbl_binaries.c.sig_fpr,
2203                                  fingerprint = relation(Fingerprint),
2204                                  install_date = self.tbl_binaries.c.install_date,
2205                                  binassociations = relation(BinAssociation,
2206                                                             primaryjoin=(self.tbl_binaries.c.id==self.tbl_bin_associations.c.bin))))
2207
2208         mapper(BinaryACL, self.tbl_binary_acl,
2209                properties = dict(binary_acl_id = self.tbl_binary_acl.c.id))
2210
2211         mapper(BinaryACLMap, self.tbl_binary_acl_map,
2212                properties = dict(binary_acl_map_id = self.tbl_binary_acl_map.c.id))
2213
2214         mapper(Component, self.tbl_component,
2215                properties = dict(component_id = self.tbl_component.c.id,
2216                                  component_name = self.tbl_component.c.name))
2217
2218         mapper(DBConfig, self.tbl_config,
2219                properties = dict(config_id = self.tbl_config.c.id))
2220
2221         mapper(ContentAssociation, self.tbl_content_associations,
2222                properties = dict(ca_id = self.tbl_content_associations.c.id,
2223                                  filename_id = self.tbl_content_associations.c.filename,
2224                                  filename    = relation(ContentFilename),
2225                                  filepath_id = self.tbl_content_associations.c.filepath,
2226                                  filepath    = relation(ContentFilepath),
2227                                  binary_id   = self.tbl_content_associations.c.binary_pkg,
2228                                  binary      = relation(DBBinary)))
2229
2230
2231         mapper(ContentFilename, self.tbl_content_file_names,
2232                properties = dict(cafilename_id = self.tbl_content_file_names.c.id,
2233                                  filename = self.tbl_content_file_names.c.file))
2234
2235         mapper(ContentFilepath, self.tbl_content_file_paths,
2236                properties = dict(cafilepath_id = self.tbl_content_file_paths.c.id,
2237                                  filepath = self.tbl_content_file_paths.c.path))
2238
2239         mapper(DSCFile, self.tbl_dsc_files,
2240                properties = dict(dscfile_id = self.tbl_dsc_files.c.id,
2241                                  source_id = self.tbl_dsc_files.c.source,
2242                                  source = relation(DBSource),
2243                                  poolfile_id = self.tbl_dsc_files.c.file,
2244                                  poolfile = relation(PoolFile)))
2245
2246         mapper(PoolFile, self.tbl_files,
2247                properties = dict(file_id = self.tbl_files.c.id,
2248                                  filesize = self.tbl_files.c.size,
2249                                  location_id = self.tbl_files.c.location,
2250                                  location = relation(Location)))
2251
2252         mapper(Fingerprint, self.tbl_fingerprint,
2253                properties = dict(fingerprint_id = self.tbl_fingerprint.c.id,
2254                                  uid_id = self.tbl_fingerprint.c.uid,
2255                                  uid = relation(Uid),
2256                                  keyring_id = self.tbl_fingerprint.c.keyring,
2257                                  keyring = relation(Keyring)))
2258
2259         mapper(Keyring, self.tbl_keyrings,
2260                properties = dict(keyring_name = self.tbl_keyrings.c.name,
2261                                  keyring_id = self.tbl_keyrings.c.id))
2262
2263         mapper(Location, self.tbl_location,
2264                properties = dict(location_id = self.tbl_location.c.id,
2265                                  component_id = self.tbl_location.c.component,
2266                                  component = relation(Component),
2267                                  archive_id = self.tbl_location.c.archive,
2268                                  archive = relation(Archive),
2269                                  archive_type = self.tbl_location.c.type))
2270
2271         mapper(Maintainer, self.tbl_maintainer,
2272                properties = dict(maintainer_id = self.tbl_maintainer.c.id))
2273
2274         mapper(NewComment, self.tbl_new_comments,
2275                properties = dict(comment_id = self.tbl_new_comments.c.id))
2276
2277         mapper(Override, self.tbl_override,
2278                properties = dict(suite_id = self.tbl_override.c.suite,
2279                                  suite = relation(Suite),
2280                                  component_id = self.tbl_override.c.component,
2281                                  component = relation(Component),
2282                                  priority_id = self.tbl_override.c.priority,
2283                                  priority = relation(Priority),
2284                                  section_id = self.tbl_override.c.section,
2285                                  section = relation(Section),
2286                                  overridetype_id = self.tbl_override.c.type,
2287                                  overridetype = relation(OverrideType)))
2288
2289         mapper(OverrideType, self.tbl_override_type,
2290                properties = dict(overridetype = self.tbl_override_type.c.type,
2291                                  overridetype_id = self.tbl_override_type.c.id))
2292
2293         mapper(PendingContentAssociation, self.tbl_pending_content_associations,
2294                properties = dict(pca_id = self.tbl_pending_content_associations.c.id,
2295                                  filepath_id = self.tbl_pending_content_associations.c.filepath,
2296                                  filepath = relation(ContentFilepath),
2297                                  filename_id = self.tbl_pending_content_associations.c.filename,
2298                                  filename = relation(ContentFilename)))
2299
2300         mapper(Priority, self.tbl_priority,
2301                properties = dict(priority_id = self.tbl_priority.c.id))
2302
2303         mapper(Queue, self.tbl_queue,
2304                properties = dict(queue_id = self.tbl_queue.c.id))
2305
2306         mapper(QueueBuild, self.tbl_queue_build,
2307                properties = dict(suite_id = self.tbl_queue_build.c.suite,
2308                                  queue_id = self.tbl_queue_build.c.queue,
2309                                  queue = relation(Queue, backref='queuebuild')))
2310
2311         mapper(Section, self.tbl_section,
2312                properties = dict(section_id = self.tbl_section.c.id))
2313
2314         mapper(DBSource, self.tbl_source,
2315                properties = dict(source_id = self.tbl_source.c.id,
2316                                  version = self.tbl_source.c.version,
2317                                  maintainer_id = self.tbl_source.c.maintainer,
2318                                  maintainer = relation(Maintainer,
2319                                                        primaryjoin=(self.tbl_source.c.maintainer==self.tbl_maintainer.c.id)),
2320                                  poolfile_id = self.tbl_source.c.file,
2321                                  poolfile = relation(PoolFile),
2322                                  fingerprint_id = self.tbl_source.c.sig_fpr,
2323                                  fingerprint = relation(Fingerprint),
2324                                  changedby_id = self.tbl_source.c.changedby,
2325                                  changedby = relation(Maintainer,
2326                                                       primaryjoin=(self.tbl_source.c.changedby==self.tbl_maintainer.c.id)),
2327                                  srcfiles = relation(DSCFile,
2328                                                      primaryjoin=(self.tbl_source.c.id==self.tbl_dsc_files.c.source)),
2329                                  srcassociations = relation(SrcAssociation,
2330                                                             primaryjoin=(self.tbl_source.c.id==self.tbl_src_associations.c.source))))
2331
2332         mapper(SourceACL, self.tbl_source_acl,
2333                properties = dict(source_acl_id = self.tbl_source_acl.c.id))
2334
2335         mapper(SrcAssociation, self.tbl_src_associations,
2336                properties = dict(sa_id = self.tbl_src_associations.c.id,
2337                                  suite_id = self.tbl_src_associations.c.suite,
2338                                  suite = relation(Suite),
2339                                  source_id = self.tbl_src_associations.c.source,
2340                                  source = relation(DBSource)))
2341
2342         mapper(SrcFormat, self.tbl_src_format,
2343                properties = dict(src_format_id = self.tbl_src_format.c.id,
2344                                  format_name = self.tbl_src_format.c.format_name))
2345
2346         mapper(SrcUploader, self.tbl_src_uploaders,
2347                properties = dict(uploader_id = self.tbl_src_uploaders.c.id,
2348                                  source_id = self.tbl_src_uploaders.c.source,
2349                                  source = relation(DBSource,
2350                                                    primaryjoin=(self.tbl_src_uploaders.c.source==self.tbl_source.c.id)),
2351                                  maintainer_id = self.tbl_src_uploaders.c.maintainer,
2352                                  maintainer = relation(Maintainer,
2353                                                        primaryjoin=(self.tbl_src_uploaders.c.maintainer==self.tbl_maintainer.c.id))))
2354
2355         mapper(Suite, self.tbl_suite,
2356                properties = dict(suite_id = self.tbl_suite.c.id))
2357
2358         mapper(SuiteArchitecture, self.tbl_suite_architectures,
2359                properties = dict(suite_id = self.tbl_suite_architectures.c.suite,
2360                                  suite = relation(Suite, backref='suitearchitectures'),
2361                                  arch_id = self.tbl_suite_architectures.c.architecture,
2362                                  architecture = relation(Architecture)))
2363
2364         mapper(SuiteSrcFormat, self.tbl_suite_src_formats,
2365                properties = dict(suite_id = self.tbl_suite_src_formats.c.suite,
2366                                  suite = relation(Suite, backref='suitesrcformats'),
2367                                  src_format_id = self.tbl_suite_src_formats.c.src_format,
2368                                  src_format = relation(SrcFormat)))
2369
2370         mapper(Uid, self.tbl_uid,
2371                properties = dict(uid_id = self.tbl_uid.c.id,
2372                                  fingerprint = relation(Fingerprint)))
2373
2374         mapper(UploadBlock, self.tbl_upload_blocks,
2375                properties = dict(upload_block_id = self.tbl_upload_blocks.c.id))
2376
2377     ## Connection functions
2378     def __createconn(self):
2379         from config import Config
2380         cnf = Config()
2381         if cnf["DB::Host"]:
2382             # TCP/IP
2383             connstr = "postgres://%s" % cnf["DB::Host"]
2384             if cnf["DB::Port"] and cnf["DB::Port"] != "-1":
2385                 connstr += ":%s" % cnf["DB::Port"]
2386             connstr += "/%s" % cnf["DB::Name"]
2387         else:
2388             # Unix Socket
2389             connstr = "postgres:///%s" % cnf["DB::Name"]
2390             if cnf["DB::Port"] and cnf["DB::Port"] != "-1":
2391                 connstr += "?port=%s" % cnf["DB::Port"]
2392
2393         self.db_pg   = create_engine(connstr, echo=self.debug)
2394         self.db_meta = MetaData()
2395         self.db_meta.bind = self.db_pg
2396         self.db_smaker = sessionmaker(bind=self.db_pg,
2397                                       autoflush=True,
2398                                       autocommit=False)
2399
2400         self.__setuptables()
2401         self.__setupmappers()
2402
2403     def session(self):
2404         return self.db_smaker()
2405
2406 __all__.append('DBConn')
2407
2408