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
43 from singleton import Singleton
44 from config import Config
46 ################################################################################
49 def __init__(self, hashfunc=None):
51 self.hashfunc = hashfunc
53 self.hashfunc = lambda x: str(x)
57 def SetValue(self, keys, value):
58 self.data[self.hashfunc(keys)] = value
60 def GetValue(self, keys):
61 return self.data.get(self.hashfunc(keys))
63 ################################################################################
65 class Architecture(object):
66 def __init__(self, *args, **kwargs):
70 return '<Architecture %s>' % self.arch_string
72 class Archive(object):
73 def __init__(self, *args, **kwargs):
77 return '<Archive %s>' % self.name
79 class BinAssociation(object):
80 def __init__(self, *args, **kwargs):
84 return '<BinAssociation %s>' % self.ba_id
87 def __init__(self, *args, **kwargs):
91 return '<Binary %s (%s) %s>' % (self.package, self.version, self.architecture)
93 def binary_from_id(id):
94 return DBConn().session().query(Binary).filter_by(binary_id=id).one()
96 class Component(object):
97 def __init__(self, *args, **kwargs):
101 return '<Component %s>' % self.component_name
103 class DBConfig(object):
104 def __init__(self, *args, **kwargs):
108 return '<DBConfig %s>' % self.name
110 class ContentFilename(object):
111 def __init__(self, *args, **kwargs):
115 return '<ContentFilename %s>' % self.filename
117 class ContentFilepath(object):
118 def __init__(self, *args, **kwargs):
122 return '<ContentFilepath %s>' % self.filepath
124 class ContentAssociations(object):
125 def __init__(self, *args, **kwargs):
129 return '<ContentAssociation %s>' % self.ca_id
131 class DSCFile(object):
132 def __init__(self, *args, **kwargs):
136 return '<DSCFile %s>' % self.dscfile_id
138 class PoolFile(object):
139 def __init__(self, *args, **kwargs):
143 return '<PoolFile %s>' % self.filename
145 class Fingerprint(object):
146 def __init__(self, *args, **kwargs):
150 return '<Fingerprint %s>' % self.fingerprint
152 class Keyring(object):
153 def __init__(self, *args, **kwargs):
157 return '<Keyring %s>' % self.keyring_name
159 class Location(object):
160 def __init__(self, *args, **kwargs):
164 return '<Location %s (%s)>' % (self.path, self.location_id)
166 class Maintainer(object):
167 def __init__(self, *args, **kwargs):
171 return '''<Maintainer '%s' (%s)>''' % (self.name, self.maintainer_id)
173 class Override(object):
174 def __init__(self, *args, **kwargs):
178 return '<Override %s (%s)>' % (self.package, self.suite_id)
180 class OverrideType(object):
181 def __init__(self, *args, **kwargs):
185 return '<OverrideType %s>' % self.overridetype
187 class PendingContentAssociation(object):
188 def __init__(self, *args, **kwargs):
192 return '<PendingContentAssociation %s>' % self.pca_id
194 class Priority(object):
195 def __init__(self, *args, **kwargs):
199 return '<Priority %s (%s)>' % (self.priority, self.priority_id)
202 def __init__(self, *args, **kwargs):
206 return '<Queue %s>' % self.queue_name
208 class QueueBuild(object):
209 def __init__(self, *args, **kwargs):
213 return '<QueueBuild %s (%s)>' % (self.filename, self.queue_id)
215 class Section(object):
216 def __init__(self, *args, **kwargs):
220 return '<Section %s>' % self.section
222 class Source(object):
223 def __init__(self, *args, **kwargs):
227 return '<Source %s (%s)>' % (self.source, self.version)
229 class SrcAssociation(object):
230 def __init__(self, *args, **kwargs):
234 return '<SrcAssociation %s>' % self.sa_id
236 class SrcUploader(object):
237 def __init__(self, *args, **kwargs):
241 return '<SrcUploader %s>' % self.uploader_id
244 def __init__(self, *args, **kwargs):
248 return '<Suite %s>' % self.suite_name
250 class SuiteArchitecture(object):
251 def __init__(self, *args, **kwargs):
255 return '<SuiteArchitecture (%s, %s)>' % (self.suite_id, self.arch_id)
258 def __init__(self, *args, **kwargs):
262 return '<Uid %s (%s)>' % (self.uid, self.name)
264 ################################################################################
266 class DBConn(Singleton):
268 database module init.
270 def __init__(self, *args, **kwargs):
271 super(DBConn, self).__init__(*args, **kwargs)
273 def _startup(self, *args, **kwargs):
277 def __setuptables(self):
278 self.tbl_architecture = Table('architecture', self.db_meta, autoload=True)
279 mapper(Architecture, self.tbl_architecture,
280 properties = dict(arch_id = self.tbl_architecture.c.id))
282 self.tbl_archive = Table('archive', self.db_meta, autoload=True)
283 mapper(Archive, self.tbl_archive,
284 properties = dict(archive_id = self.tbl_archive.c.id,
285 archive_name = self.tbl_archive.c.name))
287 self.tbl_bin_associations = Table('bin_associations', self.db_meta, autoload=True)
288 mapper(BinAssociation, self.tbl_bin_associations,
289 properties = dict(ba_id = self.tbl_bin_associations.c.id,
290 suite_id = self.tbl_bin_associations.c.suite,
291 bin_id = self.tbl_bin_associations.c.bin))
293 self.tbl_binaries = Table('binaries', self.db_meta, autoload=True)
294 mapper(Binary, self.tbl_binaries,
295 properties = dict(binary_id = self.tbl_binaries.c.id,
296 file_id = self.tbl_binaries.c.file,
297 filetype = self.tbl_binaries.c.type,
298 maintainer_id = self.tbl_binaries.c.maintainer,
299 source_id = self.tbl_binaries.c.source,
300 arch_id = self.tbl_binaries.c.architecture,
301 fingerprint_id = self.tbl_binaries.c.sig_fpr))
303 self.tbl_component = Table('component', self.db_meta, autoload=True)
304 mapper(Component, self.tbl_component,
305 properties = dict(component_id = self.tbl_component.c.id,
306 component_name = self.tbl_component.c.name))
308 self.tbl_config = Table('config', self.db_meta, autoload=True)
309 mapper(DBConfig, self.tbl_config,
310 properties = dict(config_id = self.tbl_config.c.id))
312 self.tbl_content_associations = Table('content_associations', self.db_meta, autoload=True)
313 mapper(ContentAssociations, self.tbl_content_associations,
314 properties = dict(ca_id = self.tbl_content_associations.c.id,
315 filename_id = self.tbl_content_associations.c.filename,
316 filepath_id = self.tbl_content_associations.c.filepath,
317 binary_id = self.tbl_content_associations.c.binary_pkg))
319 self.tbl_content_file_names = Table('content_file_names', self.db_meta, autoload=True)
320 mapper(ContentFilename, self.tbl_content_file_names,
321 properties = dict(cafilename_id = self.tbl_content_file_names.c.id,
322 filename = self.tbl_content_file_names.c.file))
324 self.tbl_content_file_paths = Table('content_file_paths', self.db_meta, autoload=True)
325 mapper(ContentFilepath, self.tbl_content_file_paths,
326 properties = dict(cafilepath_id = self.tbl_content_file_paths.c.id,
327 filepath = self.tbl_content_file_paths.c.path))
329 self.tbl_dsc_files = Table('dsc_files', self.db_meta, autoload=True)
330 mapper(DSCFile, self.tbl_dsc_files,
331 properties = dict(dscfile_id = self.tbl_dsc_files.c.id,
332 source_id = self.tbl_dsc_files.c.source,
333 file_id = self.tbl_dsc_files.c.file))
335 self.tbl_files = Table('files', self.db_meta, autoload=True)
336 mapper(PoolFile, self.tbl_files,
337 properties = dict(file_id = self.tbl_files.c.id,
338 filesize = self.tbl_files.c.size,
339 location_id = self.tbl_files.c.location))
341 self.tbl_fingerprint = Table('fingerprint', self.db_meta, autoload=True)
342 mapper(Fingerprint, self.tbl_fingerprint,
343 properties = dict(fingerprint_id = self.tbl_fingerprint.c.id,
344 uid_id = self.tbl_fingerprint.c.uid,
345 keyring_id = self.tbl_fingerprint.c.keyring))
347 self.tbl_keyrings = Table('keyrings', self.db_meta, autoload=True)
348 mapper(Keyring, self.tbl_keyrings,
349 properties = dict(keyring_name = self.tbl_keyrings.c.name,
350 keyring_id = self.tbl_keyrings.c.id))
352 self.tbl_location = Table('location', self.db_meta, autoload=True)
353 mapper(Location, self.tbl_location,
354 properties = dict(location_id = self.tbl_location.c.id,
355 component_id = self.tbl_location.c.component,
356 archive_id = self.tbl_location.c.archive,
357 archive_type = self.tbl_location.c.type))
359 self.tbl_maintainer = Table('maintainer', self.db_meta, autoload=True)
360 mapper(Maintainer, self.tbl_maintainer,
361 properties = dict(maintainer_id = self.tbl_maintainer.c.id))
363 self.tbl_override = Table('override', self.db_meta, autoload=True)
364 mapper(Override, self.tbl_override,
365 properties = dict(suite_id = self.tbl_override.c.suite,
366 component_id = self.tbl_override.c.component,
367 priority_id = self.tbl_override.c.priority,
368 section_id = self.tbl_override.c.section,
369 overridetype_id = self.tbl_override.c.type))
371 self.tbl_override_type = Table('override_type', self.db_meta, autoload=True)
372 mapper(OverrideType, self.tbl_override_type,
373 properties = dict(overridetype = self.tbl_override_type.c.type,
374 overridetype_id = self.tbl_override_type.c.id))
376 self.tbl_pending_content_associations = Table('pending_content_associations', self.db_meta, autoload=True)
377 mapper(PendingContentAssociation, self.tbl_pending_content_associations,
378 properties = dict(pca_id = self.tbl_pending_content_associations.c.id,
379 filepath_id = self.tbl_pending_content_associations.c.filepath,
380 filename_id = self.tbl_pending_content_associations.c.filename))
382 self.tbl_priority = Table('priority', self.db_meta, autoload=True)
383 mapper(Priority, self.tbl_priority,
384 properties = dict(priority_id = self.tbl_priority.c.id))
386 self.tbl_queue = Table('queue', self.db_meta, autoload=True)
387 mapper(Queue, self.tbl_queue,
388 properties = dict(queue_id = self.tbl_queue.c.id))
390 self.tbl_queue_build = Table('queue_build', self.db_meta, autoload=True)
391 mapper(QueueBuild, self.tbl_queue_build,
392 properties = dict(suite_id = self.tbl_queue_build.c.suite,
393 queue_id = self.tbl_queue_build.c.queue))
395 self.tbl_section = Table('section', self.db_meta, autoload=True)
396 mapper(Section, self.tbl_section,
397 properties = dict(section_id = self.tbl_section.c.id))
399 self.tbl_source = Table('source', self.db_meta, autoload=True)
400 mapper(Source, self.tbl_source,
401 properties = dict(source_id = self.tbl_source.c.id,
402 maintainer_id = self.tbl_source.c.maintainer,
403 file_id = self.tbl_source.c.file,
404 fingerprint_id = self.tbl_source.c.sig_fpr,
405 changedby_id = self.tbl_source.c.changedby))
407 self.tbl_src_associations = Table('src_associations', self.db_meta, autoload=True)
408 mapper(SrcAssociation, self.tbl_src_associations,
409 properties = dict(sa_id = self.tbl_src_associations.c.id))
411 self.tbl_src_uploaders = Table('src_uploaders', self.db_meta, autoload=True)
412 mapper(SrcUploader, self.tbl_src_uploaders,
413 properties = dict(uploader_id = self.tbl_src_uploaders.c.id,
414 source_id = self.tbl_src_uploaders.c.source,
415 maintainer_id = self.tbl_src_uploaders.c.maintainer))
417 self.tbl_suite = Table('suite', self.db_meta, autoload=True)
418 mapper(Suite, self.tbl_suite,
419 properties = dict(suite_id = self.tbl_suite.c.id))
421 self.tbl_suite_architectures = Table('suite_architectures', self.db_meta, autoload=True)
422 mapper(SuiteArchitecture, self.tbl_suite_architectures,
423 properties = dict(suite_id = self.tbl_suite_architectures.c.suite,
424 arch_id = self.tbl_suite_architectures.c.architecture))
426 self.tbl_uid = Table('uid', self.db_meta, autoload=True)
427 mapper(Uid, self.tbl_uid,
428 properties = dict(uid_id = self.tbl_uid.c.id))
430 ## Connection functions
431 def __createconn(self):
435 connstr = "postgres://%s" % cnf["DB::Host"]
436 if cnf["DB::Port"] and cnf["DB::Port"] != "-1":
437 connstr += ":%s" % cnf["DB::Port"]
438 connstr += "/%s" % cnf["DB::Name"]
441 connstr = "postgres:///%s" % cnf["DB::Name"]
442 if cnf["DB::Port"] and cnf["DB::Port"] != "-1":
443 connstr += "?port=%s" % cnf["DB::Port"]
445 self.db_pg = create_engine(connstr)
446 self.db_meta = MetaData()
447 self.db_meta.bind = self.db_pg
448 self.db_smaker = sessionmaker(bind=self.db_pg,
455 return self.db_smaker()
458 def __init_caches(self):
459 self.caches = {'suite': Cache(),
462 'override_type': Cache(),
463 'architecture': Cache(),
465 'component': Cache(),
466 'content_path_names': Cache(),
467 'content_file_names': Cache(),
468 'location': Cache(lambda x: '%s_%s_%s' % (x['location'], x['component'], x['location'])),
469 'maintainer': {}, # TODO
470 'keyring': {}, # TODO
471 'source': Cache(lambda x: '%s_%s_' % (x['source'], x['version'])),
472 'files': Cache(lambda x: '%s_%s_' % (x['filename'], x['location'])),
473 'maintainer': {}, # TODO
474 'fingerprint': {}, # TODO
477 'suite_version': Cache(lambda x: '%s_%s' % (x['source'], x['suite'])),
480 self.prepared_statements = {}
482 def prepare(self,name,statement):
483 if not self.prepared_statements.has_key(name):
484 pgc.execute(statement)
485 self.prepared_statements[name] = statement
487 def clear_caches(self):
491 def __get_id(self, retfield, selectobj, cachekey, cachename=None):
492 # This is a bit of a hack but it's an internal function only
493 if cachename is not None:
494 res = self.caches[cachename].GetValue(cachekey)
498 c = selectobj.execute()
505 if retfield not in res.keys():
510 if cachename is not None:
511 self.caches[cachename].SetValue(cachekey, res)
515 def get_suite_id(self, suite):
517 Returns database id for given C{suite}.
518 Results are kept in a cache during runtime to minimize database queries.
521 @param suite: The name of the suite
524 @return: the database id for the given suite
527 return int(self.__get_id('id',
528 self.tbl_suite.select(self.tbl_suite.columns.suite_name == suite),
532 def get_section_id(self, section):
534 Returns database id for given C{section}.
535 Results are kept in a cache during runtime to minimize database queries.
537 @type section: string
538 @param section: The name of the section
541 @return: the database id for the given section
544 return self.__get_id('id',
545 self.tbl_section.select(self.tbl_section.columns.section == section),
549 def get_priority_id(self, priority):
551 Returns database id for given C{priority}.
552 Results are kept in a cache during runtime to minimize database queries.
554 @type priority: string
555 @param priority: The name of the priority
558 @return: the database id for the given priority
561 return self.__get_id('id',
562 self.tbl_priority.select(self.tbl_priority.columns.priority == priority),
566 def get_override_type_id(self, override_type):
568 Returns database id for given override C{type}.
569 Results are kept in a cache during runtime to minimize database queries.
571 @type override_type: string
572 @param override_type: The name of the override type
575 @return: the database id for the given override type
578 return self.__get_id('id',
579 self.tbl_override_type.select(self.tbl_override_type.columns.type == override_type),
583 def get_architecture_id(self, architecture):
585 Returns database id for given C{architecture}.
586 Results are kept in a cache during runtime to minimize database queries.
588 @type architecture: string
589 @param architecture: The name of the override type
592 @return: the database id for the given architecture
595 return self.__get_id('id',
596 self.tbl_architecture.select(self.tbl_architecture.columns.arch_string == architecture),
600 def get_archive_id(self, archive):
602 returns database id for given c{archive}.
603 results are kept in a cache during runtime to minimize database queries.
605 @type archive: string
606 @param archive: the name of the override type
609 @return: the database id for the given archive
612 archive = archive.lower()
613 return self.__get_id('id',
614 self.tbl_archive.select(self.tbl_archive.columns.name == archive),
618 def get_component_id(self, component):
620 Returns database id for given C{component}.
621 Results are kept in a cache during runtime to minimize database queries.
623 @type component: string
624 @param component: The name of the override type
627 @return: the database id for the given component
630 component = component.lower()
631 return self.__get_id('id',
632 self.tbl_component.select(self.tbl_component.columns.name == component),
636 def get_location_id(self, location, component, archive):
638 Returns database id for the location behind the given combination of
639 - B{location} - the path of the location, eg. I{/srv/ftp.debian.org/ftp/pool/}
640 - B{component} - the id of the component as returned by L{get_component_id}
641 - B{archive} - the id of the archive as returned by L{get_archive_id}
642 Results are kept in a cache during runtime to minimize database queries.
644 @type location: string
645 @param location: the path of the location
647 @type component: string
648 @param component: the name of the component
650 @type archive: string
651 @param archive: the name of the archive
654 @return: the database id for the location
658 archive = archive.lower()
659 component = component.lower()
661 values = {'archive': archive, 'location': location, 'component': component}
663 s = self.tbl_location.join(self.tbl_archive).join(self.tbl_component)
665 s = s.select(self.tbl_location.columns.path == location)
666 s = s.where(self.tbl_archive.columns.name == archive)
667 s = s.where(self.tbl_component.columns.name == component)
669 return self.__get_id('location.id', s, values, 'location')
671 def get_source_id(self, source, version):
673 Returns database id for the combination of C{source} and C{version}
674 - B{source} - source package name, eg. I{mailfilter}, I{bbdb}, I{glibc}
676 Results are kept in a cache during runtime to minimize database queries.
679 @param source: source package name
681 @type version: string
682 @param version: the source version
685 @return: the database id for the source
688 s = self.tbl_source.select()
689 s = s.where(self.tbl_source.columns.source == source)
690 s = s.where(self.tbl_source.columns.version == version)
692 return self.__get_id('id', s, {'source': source, 'version': version}, 'source')
694 def get_suite(self, suite):
695 if isinstance(suite, str):
696 suite_id = self.get_suite_id(suite.lower())
697 elif type(suite) == int:
700 s = self.tbl_suite.select(self.tbl_suite.columns.id == suite_id)
707 def get_suite_version(self, source, suite):
709 Returns database id for a combination of C{source} and C{suite}.
711 - B{source} - source package name, eg. I{mailfilter}, I{bbdb}, I{glibc}
712 - B{suite} - a suite name, eg. I{unstable}
714 Results are kept in a cache during runtime to minimize database queries.
717 @param source: source package name
720 @param suite: the suite name
723 @return: the version for I{source} in I{suite}
726 s = select([self.tbl_source.columns.source, self.tbl_source.columns.version])
727 # s = self.tbl_source.join(self.tbl_src_associations).join(self.tbl_suite)
729 s = s.select(self.tbl_suite.columns.suite_name == suite, use_labels=True)
730 s = s.select(self.tbl_source.columns.source == source)
732 return self.__get_id('source.version', s, {'suite': suite, 'source': source}, 'suite_version')
735 def get_files_id (self, filename, size, md5sum, location_id):
737 Returns -1, -2 or the file_id for filename, if its C{size} and C{md5sum} match an
740 The database is queried using the C{filename} and C{location_id}. If a file does exist
741 at that location, the existing size and md5sum are checked against the provided
742 parameters. A size or checksum mismatch returns -2. If more than one entry is
743 found within the database, a -1 is returned, no result returns None, otherwise
746 Results are kept in a cache during runtime to minimize database queries.
748 @type filename: string
749 @param filename: the filename of the file to check against the DB
752 @param size: the size of the file to check against the DB
755 @param md5sum: the md5sum of the file to check against the DB
757 @type location_id: int
758 @param location_id: the id of the location as returned by L{get_location_id}
761 @return: Various return values are possible:
762 - -2: size/checksum error
763 - -1: more than one file found in database
764 - None: no file found in database
768 values = {'filename' : filename,
769 'location' : location_id}
771 res = self.caches['files'].GetValue( values )
774 query = """SELECT id, size, md5sum
776 WHERE filename = %(filename)s AND location = %(location)s"""
778 cursor = self.db_con.cursor()
779 cursor.execute( query, values )
781 if cursor.rowcount == 0:
784 elif cursor.rowcount != 1:
788 row = cursor.fetchone()
790 if row[1] != int(size) or row[2] != md5sum:
794 self.caches['files'].SetValue(values, row[0])
800 def get_or_set_contents_file_id(self, filename):
802 Returns database id for given filename.
804 Results are kept in a cache during runtime to minimize database queries.
805 If no matching file is found, a row is inserted.
807 @type filename: string
808 @param filename: The filename
811 @return: the database id for the given component
814 values={'value': filename}
815 query = "SELECT id FROM content_file_names WHERE file = %(value)s"
816 id = self.__get_single_id(query, values, cachename='content_file_names')
818 c = self.db_con.cursor()
819 c.execute( "INSERT INTO content_file_names VALUES (DEFAULT, %(value)s) RETURNING id",
823 self.caches['content_file_names'].SetValue(values, id)
827 traceback.print_exc()
830 def get_or_set_contents_path_id(self, path):
832 Returns database id for given path.
834 Results are kept in a cache during runtime to minimize database queries.
835 If no matching file is found, a row is inserted.
838 @param path: The filename
841 @return: the database id for the given component
844 values={'value': path}
845 query = "SELECT id FROM content_file_paths WHERE path = %(value)s"
846 id = self.__get_single_id(query, values, cachename='content_path_names')
848 c = self.db_con.cursor()
849 c.execute( "INSERT INTO content_file_paths VALUES (DEFAULT, %(value)s) RETURNING id",
853 self.caches['content_path_names'].SetValue(values, id)
857 traceback.print_exc()
860 def get_suite_architectures(self, suite):
862 Returns list of architectures for C{suite}.
864 @type suite: string, int
865 @param suite: the suite name or the suite_id
868 @return: the list of architectures for I{suite}
872 if type(suite) == str:
873 suite_id = self.get_suite_id(suite)
874 elif type(suite) == int:
879 c = self.db_con.cursor()
880 c.execute( """SELECT a.arch_string FROM suite_architectures sa
881 JOIN architecture a ON (a.id = sa.architecture)
882 WHERE suite='%s'""" % suite_id )
884 return map(lambda x: x[0], c.fetchall())
886 def insert_content_paths(self, bin_id, fullpaths):
888 Make sure given path is associated with given binary id
891 @param bin_id: the id of the binary
892 @type fullpaths: list
893 @param fullpaths: the list of paths of the file being associated with the binary
895 @return: True upon success
898 c = self.db_con.cursor()
900 c.execute("BEGIN WORK")
903 for fullpath in fullpaths:
904 (path, file) = os.path.split(fullpath)
906 if path.startswith( "./" ):
908 # Get the necessary IDs ...
909 file_id = self.get_or_set_contents_file_id(file)
910 path_id = self.get_or_set_contents_path_id(path)
912 c.execute("""INSERT INTO content_associations
913 (binary_pkg, filepath, filename)
914 VALUES ( '%d', '%d', '%d')""" % (bin_id, path_id, file_id) )
919 traceback.print_exc()
920 c.execute("ROLLBACK")
923 def insert_pending_content_paths(self, package, fullpaths):
925 Make sure given paths are temporarily associated with given
929 @param package: the package to associate with should have been read in from the binary control file
930 @type fullpaths: list
931 @param fullpaths: the list of paths of the file being associated with the binary
933 @return: True upon success
936 c = self.db_con.cursor()
938 c.execute("BEGIN WORK")
940 arch_id = self.get_architecture_id(package['Architecture'])
942 # Remove any already existing recorded files for this package
943 c.execute("""DELETE FROM pending_content_associations
944 WHERE package=%(Package)s
945 AND version=%(Version)s
946 AND architecture=%(ArchID)s""", {'Package': package['Package'],
947 'Version': package['Version'],
950 for fullpath in fullpaths:
951 (path, file) = os.path.split(fullpath)
953 if path.startswith( "./" ):
955 # Get the necessary IDs ...
956 file_id = self.get_or_set_contents_file_id(file)
957 path_id = self.get_or_set_contents_path_id(path)
959 c.execute("""INSERT INTO pending_content_associations
960 (package, version, architecture, filepath, filename)
961 VALUES (%%(Package)s, %%(Version)s, '%d', '%d', '%d')"""
962 % (arch_id, path_id, file_id), package )
967 traceback.print_exc()
968 c.execute("ROLLBACK")