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