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
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.
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.
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
27 ################################################################################
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"
34 ################################################################################
40 from sqlalchemy import create_engine, Table, MetaData, select
41 from sqlalchemy.orm import sessionmaker, mapper, relation
43 # Don't remove this, we re-export the exceptions to scripts which import us
44 from sqlalchemy.exc import *
46 from singleton import Singleton
48 ################################################################################
50 class Architecture(object):
51 def __init__(self, *args, **kwargs):
55 return '<Architecture %s>' % self.arch_string
57 def get_architecture(architecture, session=None):
59 Returns database id for given C{architecture}.
61 @type architecture: string
62 @param architecture: The name of the architecture
64 @type session: Session
65 @param session: Optional SQLA session object (a temporary one will be
66 generated if not supplied)
69 @return: Architecture object for the given arch (None if not present)
73 session = DBConn().session()
74 q = session.query(Architecture).filter_by(arch_string=architecture)
79 def get_architecture_suites(architecture, session=None):
81 Returns list of Suite objects for given C{architecture} name
84 @param source: Architecture name to search for
86 @type session: Session
87 @param session: Optional SQL session object (a temporary one will be
88 generated if not supplied)
91 @return: list of Suite objects for the given name (may be empty)
95 session = DBConn().session()
97 q = session.query(Suite)
98 q = q.join(SuiteArchitecture)
99 q = q.join(Architecture).filter_by(arch_string=architecture).order_by('suite_name')
102 class Archive(object):
103 def __init__(self, *args, **kwargs):
107 return '<Archive %s>' % self.name
109 def get_archive(archive, session=None):
111 returns database id for given c{archive}.
113 @type archive: string
114 @param archive: the name of the arhive
116 @type session: Session
117 @param session: Optional SQLA session object (a temporary one will be
118 generated if not supplied)
121 @return: Archive object for the given name (None if not present)
124 archive = archive.lower()
126 session = DBConn().session()
127 q = session.query(Archive).filter_by(archive_name=archive)
133 class BinAssociation(object):
134 def __init__(self, *args, **kwargs):
138 return '<BinAssociation %s (%s, %s)>' % (self.ba_id, self.binary, self.suite)
140 class Binary(object):
141 def __init__(self, *args, **kwargs):
145 return '<Binary %s (%s, %s)>' % (self.package, self.version, self.architecture)
147 def get_binary_from_id(id, session=None):
149 Returns Binary object for given C{id}
152 @param id: Id of the required binary
154 @type session: Session
155 @param session: Optional SQLA session object (a temporary one will be
156 generated if not supplied)
159 @return: Binary object for the given binary (None if not present)
162 session = DBConn().session()
163 q = session.query(Binary).filter_by(binary_id=id)
168 def get_binaries_from_name(package, session=None):
170 Returns list of Binary objects for given C{package} name
173 @param package: Binary package name to search for
175 @type session: Session
176 @param session: Optional SQL session object (a temporary one will be
177 generated if not supplied)
180 @return: list of Binary objects for the given name (may be empty)
183 session = DBConn().session()
184 return session.query(Binary).filter_by(package=package).all()
186 class Component(object):
187 def __init__(self, *args, **kwargs):
191 return '<Component %s>' % self.component_name
193 def get_component(component, session=None):
195 Returns database id for given C{component}.
197 @type component: string
198 @param component: The name of the override type
201 @return: the database id for the given component
204 component = component.lower()
206 session = DBConn().session()
207 q = session.query(Component).filter_by(component_name=component)
212 class DBConfig(object):
213 def __init__(self, *args, **kwargs):
217 return '<DBConfig %s>' % self.name
219 class ContentFilename(object):
220 def __init__(self, *args, **kwargs):
224 return '<ContentFilename %s>' % self.filename
226 class ContentFilepath(object):
227 def __init__(self, *args, **kwargs):
231 return '<ContentFilepath %s>' % self.filepath
233 class ContentAssociation(object):
234 def __init__(self, *args, **kwargs):
238 return '<ContentAssociation %s>' % self.ca_id
240 class DSCFile(object):
241 def __init__(self, *args, **kwargs):
245 return '<DSCFile %s>' % self.dscfile_id
247 class PoolFile(object):
248 def __init__(self, *args, **kwargs):
252 return '<PoolFile %s>' % self.filename
254 class Fingerprint(object):
255 def __init__(self, *args, **kwargs):
259 return '<Fingerprint %s>' % self.fingerprint
261 class Keyring(object):
262 def __init__(self, *args, **kwargs):
266 return '<Keyring %s>' % self.keyring_name
268 class Location(object):
269 def __init__(self, *args, **kwargs):
273 return '<Location %s (%s)>' % (self.path, self.location_id)
275 class Maintainer(object):
276 def __init__(self, *args, **kwargs):
280 return '''<Maintainer '%s' (%s)>''' % (self.name, self.maintainer_id)
282 class Override(object):
283 def __init__(self, *args, **kwargs):
287 return '<Override %s (%s)>' % (self.package, self.suite_id)
289 class OverrideType(object):
290 def __init__(self, *args, **kwargs):
294 return '<OverrideType %s>' % self.overridetype
296 def get_override_type(override_type, session=None):
298 Returns OverrideType object for given C{override type}.
300 @type override_type: string
301 @param override_type: The name of the override type
303 @type session: Session
304 @param session: Optional SQLA session object (a temporary one will be
305 generated if not supplied)
308 @return: the database id for the given override type
312 session = DBConn().session()
313 q = session.query(Priority).filter_by(priority=priority)
318 class PendingContentAssociation(object):
319 def __init__(self, *args, **kwargs):
323 return '<PendingContentAssociation %s>' % self.pca_id
325 class Priority(object):
326 def __init__(self, *args, **kwargs):
330 return '<Priority %s (%s)>' % (self.priority, self.priority_id)
332 def get_priority(priority, session=None):
334 Returns Priority object for given C{priority name}.
336 @type priority: string
337 @param priority: The name of the priority
339 @type session: Session
340 @param session: Optional SQLA session object (a temporary one will be
341 generated if not supplied)
344 @return: Priority object for the given priority
348 session = DBConn().session()
349 q = session.query(Priority).filter_by(priority=priority)
355 def __init__(self, *args, **kwargs):
359 return '<Queue %s>' % self.queue_name
361 class QueueBuild(object):
362 def __init__(self, *args, **kwargs):
366 return '<QueueBuild %s (%s)>' % (self.filename, self.queue_id)
368 class Section(object):
369 def __init__(self, *args, **kwargs):
373 return '<Section %s>' % self.section
375 def get_section(section, session=None):
377 Returns Section object for given C{section name}.
379 @type section: string
380 @param section: The name of the section
382 @type session: Session
383 @param session: Optional SQLA session object (a temporary one will be
384 generated if not supplied)
387 @return: Section object for the given section name
391 session = DBConn().session()
392 q = session.query(Section).filter_by(section=section)
397 class Source(object):
398 def __init__(self, *args, **kwargs):
402 return '<Source %s (%s)>' % (self.source, self.version)
404 def get_sources_from_name(source, session=None):
406 Returns list of Source objects for given C{source} name
409 @param source: Source package name to search for
411 @type session: Session
412 @param session: Optional SQL session object (a temporary one will be
413 generated if not supplied)
416 @return: list of Source objects for the given name (may be empty)
419 session = DBConn().session()
420 return session.query(Source).filter_by(source=source).all()
422 def get_source_in_suite(source, suite, session=None):
424 Returns list of Source objects for a combination of C{source} and C{suite}.
426 - B{source} - source package name, eg. I{mailfilter}, I{bbdb}, I{glibc}
427 - B{suite} - a suite name, eg. I{unstable}
430 @param source: source package name
433 @param suite: the suite name
436 @return: the version for I{source} in I{suite}
440 session = DBConn().session()
441 q = session.query(SrcAssociation)
442 q = q.join('source').filter_by(source=source)
443 q = q.join('suite').filter_by(suite_name=suite)
446 # ???: Maybe we should just return the SrcAssociation object instead
447 return q.one().source
449 class SrcAssociation(object):
450 def __init__(self, *args, **kwargs):
454 return '<SrcAssociation %s (%s, %s)>' % (self.sa_id, self.source, self.suite)
456 class SrcUploader(object):
457 def __init__(self, *args, **kwargs):
461 return '<SrcUploader %s>' % self.uploader_id
464 def __init__(self, *args, **kwargs):
468 return '<Suite %s>' % self.suite_name
470 def get_suite_architecture(suite, architecture, session=None):
472 Returns a SuiteArchitecture object given C{suite} and ${arch} or None if it
476 @param suite: Suite name to search for
478 @type architecture: str
479 @param architecture: Architecture name to search for
481 @type session: Session
482 @param session: Optional SQL session object (a temporary one will be
483 generated if not supplied)
485 @rtype: SuiteArchitecture
486 @return: the SuiteArchitecture object or None
490 session = DBConn().session()
492 q = session.query(SuiteArchitecture)
493 q = q.join(Architecture).filter_by(arch_string=architecture)
494 q = q.join(Suite).filter_by(suite_name=suite)
500 def get_suite(suite, session=None):
502 Returns Suite object for given C{suite name}.
505 @param suite: The name of the suite
507 @type session: Session
508 @param session: Optional SQLA session object (a temporary one will be
509 generated if not supplied)
512 @return: Suite object for the requested suite name (None if not presenT)
516 session = DBConn().session()
517 q = session.query(Suite).filter_by(suite_name=suite)
522 class SuiteArchitecture(object):
523 def __init__(self, *args, **kwargs):
527 return '<SuiteArchitecture (%s, %s)>' % (self.suite_id, self.arch_id)
529 def get_suite_architectures(suite, session=None):
531 Returns list of Architecture objects for given C{suite} name
534 @param source: Suite name to search for
536 @type session: Session
537 @param session: Optional SQL session object (a temporary one will be
538 generated if not supplied)
541 @return: list of Architecture objects for the given name (may be empty)
545 session = DBConn().session()
547 q = session.query(Architecture)
548 q = q.join(SuiteArchitecture)
549 q = q.join(Suite).filter_by(suite_name=suite).order_by('arch_string')
554 def __init__(self, *args, **kwargs):
558 return '<Uid %s (%s)>' % (self.uid, self.name)
560 ################################################################################
562 class DBConn(Singleton):
564 database module init.
566 def __init__(self, *args, **kwargs):
567 super(DBConn, self).__init__(*args, **kwargs)
569 def _startup(self, *args, **kwargs):
571 if kwargs.has_key('debug'):
575 def __setuptables(self):
576 self.tbl_architecture = Table('architecture', self.db_meta, autoload=True)
577 self.tbl_archive = Table('archive', self.db_meta, autoload=True)
578 self.tbl_bin_associations = Table('bin_associations', self.db_meta, autoload=True)
579 self.tbl_binaries = Table('binaries', self.db_meta, autoload=True)
580 self.tbl_component = Table('component', self.db_meta, autoload=True)
581 self.tbl_config = Table('config', self.db_meta, autoload=True)
582 self.tbl_content_associations = Table('content_associations', self.db_meta, autoload=True)
583 self.tbl_content_file_names = Table('content_file_names', self.db_meta, autoload=True)
584 self.tbl_content_file_paths = Table('content_file_paths', self.db_meta, autoload=True)
585 self.tbl_dsc_files = Table('dsc_files', self.db_meta, autoload=True)
586 self.tbl_files = Table('files', self.db_meta, autoload=True)
587 self.tbl_fingerprint = Table('fingerprint', self.db_meta, autoload=True)
588 self.tbl_keyrings = Table('keyrings', self.db_meta, autoload=True)
589 self.tbl_location = Table('location', self.db_meta, autoload=True)
590 self.tbl_maintainer = Table('maintainer', self.db_meta, autoload=True)
591 self.tbl_override = Table('override', self.db_meta, autoload=True)
592 self.tbl_override_type = Table('override_type', self.db_meta, autoload=True)
593 self.tbl_pending_content_associations = Table('pending_content_associations', self.db_meta, autoload=True)
594 self.tbl_priority = Table('priority', self.db_meta, autoload=True)
595 self.tbl_queue = Table('queue', self.db_meta, autoload=True)
596 self.tbl_queue_build = Table('queue_build', self.db_meta, autoload=True)
597 self.tbl_section = Table('section', self.db_meta, autoload=True)
598 self.tbl_source = Table('source', self.db_meta, autoload=True)
599 self.tbl_src_associations = Table('src_associations', self.db_meta, autoload=True)
600 self.tbl_src_uploaders = Table('src_uploaders', self.db_meta, autoload=True)
601 self.tbl_suite = Table('suite', self.db_meta, autoload=True)
602 self.tbl_suite_architectures = Table('suite_architectures', self.db_meta, autoload=True)
603 self.tbl_uid = Table('uid', self.db_meta, autoload=True)
605 def __setupmappers(self):
606 mapper(Architecture, self.tbl_architecture,
607 properties = dict(arch_id = self.tbl_architecture.c.id))
609 mapper(Archive, self.tbl_archive,
610 properties = dict(archive_id = self.tbl_archive.c.id,
611 archive_name = self.tbl_archive.c.name))
613 mapper(BinAssociation, self.tbl_bin_associations,
614 properties = dict(ba_id = self.tbl_bin_associations.c.id,
615 suite_id = self.tbl_bin_associations.c.suite,
616 suite = relation(Suite),
617 binary_id = self.tbl_bin_associations.c.bin,
618 binary = relation(Binary)))
620 mapper(Binary, self.tbl_binaries,
621 properties = dict(binary_id = self.tbl_binaries.c.id,
622 package = self.tbl_binaries.c.package,
623 version = self.tbl_binaries.c.version,
624 maintainer_id = self.tbl_binaries.c.maintainer,
625 maintainer = relation(Maintainer),
626 source_id = self.tbl_binaries.c.source,
627 source = relation(Source),
628 arch_id = self.tbl_binaries.c.architecture,
629 architecture = relation(Architecture),
630 poolfile_id = self.tbl_binaries.c.file,
631 poolfile = relation(PoolFile),
632 binarytype = self.tbl_binaries.c.type,
633 fingerprint_id = self.tbl_binaries.c.sig_fpr,
634 fingerprint = relation(Fingerprint),
635 install_date = self.tbl_binaries.c.install_date,
636 binassociations = relation(BinAssociation,
637 primaryjoin=(self.tbl_binaries.c.id==self.tbl_bin_associations.c.bin))))
639 mapper(Component, self.tbl_component,
640 properties = dict(component_id = self.tbl_component.c.id,
641 component_name = self.tbl_component.c.name))
643 mapper(DBConfig, self.tbl_config,
644 properties = dict(config_id = self.tbl_config.c.id))
646 mapper(ContentAssociation, self.tbl_content_associations,
647 properties = dict(ca_id = self.tbl_content_associations.c.id,
648 filename_id = self.tbl_content_associations.c.filename,
649 filename = relation(ContentFilename),
650 filepath_id = self.tbl_content_associations.c.filepath,
651 filepath = relation(ContentFilepath),
652 binary_id = self.tbl_content_associations.c.binary_pkg,
653 binary = relation(Binary)))
656 mapper(ContentFilename, self.tbl_content_file_names,
657 properties = dict(cafilename_id = self.tbl_content_file_names.c.id,
658 filename = self.tbl_content_file_names.c.file))
660 mapper(ContentFilepath, self.tbl_content_file_paths,
661 properties = dict(cafilepath_id = self.tbl_content_file_paths.c.id,
662 filepath = self.tbl_content_file_paths.c.path))
664 mapper(DSCFile, self.tbl_dsc_files,
665 properties = dict(dscfile_id = self.tbl_dsc_files.c.id,
666 source_id = self.tbl_dsc_files.c.source,
667 source = relation(Source),
668 poolfile_id = self.tbl_dsc_files.c.file,
669 poolfile = relation(PoolFile)))
671 mapper(PoolFile, self.tbl_files,
672 properties = dict(file_id = self.tbl_files.c.id,
673 filesize = self.tbl_files.c.size,
674 location_id = self.tbl_files.c.location,
675 location = relation(Location)))
677 mapper(Fingerprint, self.tbl_fingerprint,
678 properties = dict(fingerprint_id = self.tbl_fingerprint.c.id,
679 uid_id = self.tbl_fingerprint.c.uid,
681 keyring_id = self.tbl_fingerprint.c.keyring,
682 keyring = relation(Keyring)))
684 mapper(Keyring, self.tbl_keyrings,
685 properties = dict(keyring_name = self.tbl_keyrings.c.name,
686 keyring_id = self.tbl_keyrings.c.id))
688 mapper(Location, self.tbl_location,
689 properties = dict(location_id = self.tbl_location.c.id,
690 component_id = self.tbl_location.c.component,
691 component = relation(Component),
692 archive_id = self.tbl_location.c.archive,
693 archive = relation(Archive),
694 archive_type = self.tbl_location.c.type))
696 mapper(Maintainer, self.tbl_maintainer,
697 properties = dict(maintainer_id = self.tbl_maintainer.c.id))
699 mapper(Override, self.tbl_override,
700 properties = dict(suite_id = self.tbl_override.c.suite,
701 suite = relation(Suite),
702 component_id = self.tbl_override.c.component,
703 component = relation(Component),
704 priority_id = self.tbl_override.c.priority,
705 priority = relation(Priority),
706 section_id = self.tbl_override.c.section,
707 section = relation(Section),
708 overridetype_id = self.tbl_override.c.type,
709 overridetype = relation(OverrideType)))
711 mapper(OverrideType, self.tbl_override_type,
712 properties = dict(overridetype = self.tbl_override_type.c.type,
713 overridetype_id = self.tbl_override_type.c.id))
715 mapper(PendingContentAssociation, self.tbl_pending_content_associations,
716 properties = dict(pca_id = self.tbl_pending_content_associations.c.id,
717 filepath_id = self.tbl_pending_content_associations.c.filepath,
718 filepath = relation(ContentFilepath),
719 filename_id = self.tbl_pending_content_associations.c.filename,
720 filename = relation(ContentFilename)))
722 mapper(Priority, self.tbl_priority,
723 properties = dict(priority_id = self.tbl_priority.c.id))
725 mapper(Queue, self.tbl_queue,
726 properties = dict(queue_id = self.tbl_queue.c.id))
728 mapper(QueueBuild, self.tbl_queue_build,
729 properties = dict(suite_id = self.tbl_queue_build.c.suite,
730 queue_id = self.tbl_queue_build.c.queue,
731 queue = relation(Queue)))
733 mapper(Section, self.tbl_section,
734 properties = dict(section_id = self.tbl_section.c.id))
736 mapper(Source, self.tbl_source,
737 properties = dict(source_id = self.tbl_source.c.id,
738 version = self.tbl_source.c.version,
739 maintainer_id = self.tbl_source.c.maintainer,
740 maintainer = relation(Maintainer,
741 primaryjoin=(self.tbl_source.c.maintainer==self.tbl_maintainer.c.id)),
742 poolfile_id = self.tbl_source.c.file,
743 poolfile = relation(PoolFile),
744 fingerprint_id = self.tbl_source.c.sig_fpr,
745 fingerprint = relation(Fingerprint),
746 changedby_id = self.tbl_source.c.changedby,
747 changedby = relation(Maintainer,
748 primaryjoin=(self.tbl_source.c.changedby==self.tbl_maintainer.c.id)),
749 srcfiles = relation(DSCFile,
750 primaryjoin=(self.tbl_source.c.id==self.tbl_dsc_files.c.source)),
751 srcassociations = relation(SrcAssociation,
752 primaryjoin=(self.tbl_source.c.id==self.tbl_src_associations.c.source))))
754 mapper(SrcAssociation, self.tbl_src_associations,
755 properties = dict(sa_id = self.tbl_src_associations.c.id,
756 suite_id = self.tbl_src_associations.c.suite,
757 suite = relation(Suite),
758 source_id = self.tbl_src_associations.c.source,
759 source = relation(Source)))
761 mapper(SrcUploader, self.tbl_src_uploaders,
762 properties = dict(uploader_id = self.tbl_src_uploaders.c.id,
763 source_id = self.tbl_src_uploaders.c.source,
764 source = relation(Source,
765 primaryjoin=(self.tbl_src_uploaders.c.source==self.tbl_source.c.id)),
766 maintainer_id = self.tbl_src_uploaders.c.maintainer,
767 maintainer = relation(Maintainer,
768 primaryjoin=(self.tbl_src_uploaders.c.maintainer==self.tbl_maintainer.c.id))))
770 mapper(Suite, self.tbl_suite,
771 properties = dict(suite_id = self.tbl_suite.c.id))
773 mapper(SuiteArchitecture, self.tbl_suite_architectures,
774 properties = dict(suite_id = self.tbl_suite_architectures.c.suite,
775 suite = relation(Suite),
776 arch_id = self.tbl_suite_architectures.c.architecture,
777 architecture = relation(Architecture)))
779 mapper(Uid, self.tbl_uid,
780 properties = dict(uid_id = self.tbl_uid.c.id))
782 ## Connection functions
783 def __createconn(self):
784 from config import Config
788 connstr = "postgres://%s" % cnf["DB::Host"]
789 if cnf["DB::Port"] and cnf["DB::Port"] != "-1":
790 connstr += ":%s" % cnf["DB::Port"]
791 connstr += "/%s" % cnf["DB::Name"]
794 connstr = "postgres:///%s" % cnf["DB::Name"]
795 if cnf["DB::Port"] and cnf["DB::Port"] != "-1":
796 connstr += "?port=%s" % cnf["DB::Port"]
798 self.db_pg = create_engine(connstr, echo=self.debug)
799 self.db_meta = MetaData()
800 self.db_meta.bind = self.db_pg
801 self.db_smaker = sessionmaker(bind=self.db_pg,
806 self.__setupmappers()
809 return self.db_smaker()
811 def prepare(self,name,statement):
812 if not self.prepared_statements.has_key(name):
813 pgc.execute(statement)
814 self.prepared_statements[name] = statement
817 def get_location_id(self, location, component, archive):
819 Returns database id for the location behind the given combination of
820 - B{location} - the path of the location, eg. I{/srv/ftp.debian.org/ftp/pool/}
821 - B{component} - the id of the component as returned by L{get_component_id}
822 - B{archive} - the id of the archive as returned by L{get_archive_id}
823 Results are kept in a cache during runtime to minimize database queries.
825 @type location: string
826 @param location: the path of the location
829 @param component: the id of the component
832 @param archive: the id of the archive
835 @return: the database id for the location
839 archive_id = self.get_archive_id(archive)
847 component_id = self.get_component_id(component)
849 res = self.__get_single_id("SELECT id FROM location WHERE path=%(location)s AND component=%(component)s AND archive=%(archive)s",
850 {'location': location,
851 'archive': int(archive_id),
852 'component': component_id}, cachename='location')
854 res = self.__get_single_id("SELECT id FROM location WHERE path=%(location)s AND archive=%(archive)d",
855 {'location': location, 'archive': archive_id, 'component': ''}, cachename='location')
861 def get_files_id (self, filename, size, md5sum, location_id):
863 Returns -1, -2 or the file_id for filename, if its C{size} and C{md5sum} match an
866 The database is queried using the C{filename} and C{location_id}. If a file does exist
867 at that location, the existing size and md5sum are checked against the provided
868 parameters. A size or checksum mismatch returns -2. If more than one entry is
869 found within the database, a -1 is returned, no result returns None, otherwise
872 @type filename: string
873 @param filename: the filename of the file to check against the DB
876 @param size: the size of the file to check against the DB
879 @param md5sum: the md5sum of the file to check against the DB
881 @type location_id: int
882 @param location_id: the id of the location as returned by L{get_location_id}
885 @return: Various return values are possible:
886 - -2: size/checksum error
887 - -1: more than one file found in database
888 - None: no file found in database
892 values = {'filename' : filename,
893 'location' : location_id}
896 query = """SELECT id, size, md5sum
898 WHERE filename = %(filename)s AND location = %(location)s"""
900 cursor = self.db_con.cursor()
901 cursor.execute( query, values )
903 if cursor.rowcount == 0:
906 elif cursor.rowcount != 1:
910 row = cursor.fetchone()
912 if row[1] != int(size) or row[2] != md5sum:
921 def get_or_set_contents_file_id(self, filename):
923 Returns database id for given filename.
925 If no matching file is found, a row is inserted.
927 @type filename: string
928 @param filename: The filename
931 @return: the database id for the given component
934 values={'value': filename}
935 query = "SELECT id FROM content_file_names WHERE file = %(value)s"
937 c = self.db_con.cursor()
938 c.execute( "INSERT INTO content_file_names VALUES (DEFAULT, %(value)s) RETURNING id",
945 traceback.print_exc()
948 def get_or_set_contents_path_id(self, path):
950 Returns database id for given path.
952 If no matching file is found, a row is inserted.
955 @param path: The filename
958 @return: the database id for the given component
961 values={'value': path}
962 query = "SELECT id FROM content_file_paths WHERE path = %(value)s"
964 c = self.db_con.cursor()
965 c.execute( "INSERT INTO content_file_paths VALUES (DEFAULT, %(value)s) RETURNING id",
972 traceback.print_exc()
976 def insert_content_paths(self, bin_id, fullpaths):
978 Make sure given path is associated with given binary id
981 @param bin_id: the id of the binary
982 @type fullpaths: list
983 @param fullpaths: the list of paths of the file being associated with the binary
985 @return: True upon success
988 c = self.db_con.cursor()
990 c.execute("BEGIN WORK")
993 for fullpath in fullpaths:
994 (path, file) = os.path.split(fullpath)
996 # Get the necessary IDs ...
997 file_id = self.get_or_set_contents_file_id(file)
998 path_id = self.get_or_set_contents_path_id(path)
1000 c.execute("""INSERT INTO content_associations
1001 (binary_pkg, filepath, filename)
1002 VALUES ( '%d', '%d', '%d')""" % (bin_id, path_id, file_id) )
1007 traceback.print_exc()
1008 c.execute("ROLLBACK")
1011 def insert_pending_content_paths(self, package, fullpaths):
1013 Make sure given paths are temporarily associated with given
1017 @param package: the package to associate with should have been read in from the binary control file
1018 @type fullpaths: list
1019 @param fullpaths: the list of paths of the file being associated with the binary
1021 @return: True upon success
1024 c = self.db_con.cursor()
1026 c.execute("BEGIN WORK")
1028 arch_id = self.get_architecture_id(package['Architecture'])
1030 # Remove any already existing recorded files for this package
1031 c.execute("""DELETE FROM pending_content_associations
1032 WHERE package=%(Package)s
1033 AND version=%(Version)s
1034 AND architecture=%(ArchID)s""", {'Package': package['Package'],
1035 'Version': package['Version'],
1038 for fullpath in fullpaths:
1039 (path, file) = os.path.split(fullpath)
1041 if path.startswith( "./" ):
1043 # Get the necessary IDs ...
1044 file_id = self.get_or_set_contents_file_id(file)
1045 path_id = self.get_or_set_contents_path_id(path)
1047 c.execute("""INSERT INTO pending_content_associations
1048 (package, version, architecture, filepath, filename)
1049 VALUES (%%(Package)s, %%(Version)s, '%d', '%d', '%d')"""
1050 % (arch_id, path_id, file_id), package )
1055 traceback.print_exc()
1056 c.execute("ROLLBACK")