dbconn.py 38.0 KB
Newer Older
1
#!/usr/bin/python
M
Mark Hymers 已提交
2

3 4 5 6 7 8
""" DB access class

@contact: Debian FTPMaster <ftpmaster@debian.org>
@copyright: 2000, 2001, 2002, 2003, 2004, 2006  James Troup <james@nocrew.org>
@copyright: 2008-2009  Mark Hymers <mhy@debian.org>
@copyright: 2009  Joerg Jaspert <joerg@debian.org>
9
@copyright: 2009  Mike O'Connor <stew@debian.org>
10 11
@license: GNU General Public License version 2 or later
"""
M
Mark Hymers 已提交
12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35

# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.

# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.

# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

################################################################################

# < mhy> I need a funny comment
# < sgran> two peanuts were walking down a dark street
# < sgran> one was a-salted
#  * mhy looks up the definition of "funny"

################################################################################

36
import os
M
Mark Hymers 已提交
37
import psycopg2
38
import traceback
M
Mark Hymers 已提交
39

M
Mark Hymers 已提交
40
from sqlalchemy import create_engine, Table, MetaData, select
M
Mark Hymers 已提交
41
from sqlalchemy.orm import sessionmaker, mapper, relation
M
Mark Hymers 已提交
42

43
from singleton import Singleton
M
Mark Hymers 已提交
44 45 46 47 48 49 50 51

################################################################################

class Cache(object):
    def __init__(self, hashfunc=None):
        if hashfunc:
            self.hashfunc = hashfunc
        else:
M
Mark Hymers 已提交
52
            self.hashfunc = lambda x: str(x)
M
Mark Hymers 已提交
53 54 55 56 57 58 59 60 61 62 63

        self.data = {}

    def SetValue(self, keys, value):
        self.data[self.hashfunc(keys)] = value

    def GetValue(self, keys):
        return self.data.get(self.hashfunc(keys))

################################################################################

M
Mark Hymers 已提交
64
class Architecture(object):
M
Mark Hymers 已提交
65 66
    def __init__(self, *args, **kwargs):
        pass
M
Mark Hymers 已提交
67 68 69 70 71

    def __repr__(self):
        return '<Architecture %s>' % self.arch_string

class Archive(object):
M
Mark Hymers 已提交
72 73
    def __init__(self, *args, **kwargs):
        pass
M
Mark Hymers 已提交
74 75 76 77 78

    def __repr__(self):
        return '<Archive %s>' % self.name

class BinAssociation(object):
M
Mark Hymers 已提交
79 80
    def __init__(self, *args, **kwargs):
        pass
M
Mark Hymers 已提交
81 82

    def __repr__(self):
M
Mark Hymers 已提交
83
        return '<BinAssociation %s (%s, %s)>' % (self.ba_id, self.binary, self.suite)
M
Mark Hymers 已提交
84 85

class Binary(object):
M
Mark Hymers 已提交
86 87
    def __init__(self, *args, **kwargs):
        pass
M
Mark Hymers 已提交
88 89

    def __repr__(self):
M
Mark Hymers 已提交
90
        return '<Binary %s (%s, %s)>' % (self.package, self.version, self.architecture)
M
Mark Hymers 已提交
91 92 93

def binary_from_id(id):
    return DBConn().session().query(Binary).filter_by(binary_id=id).one()
M
Mark Hymers 已提交
94 95

class Component(object):
M
Mark Hymers 已提交
96 97
    def __init__(self, *args, **kwargs):
        pass
M
Mark Hymers 已提交
98 99 100 101 102

    def __repr__(self):
        return '<Component %s>' % self.component_name

class DBConfig(object):
M
Mark Hymers 已提交
103 104
    def __init__(self, *args, **kwargs):
        pass
M
Mark Hymers 已提交
105 106 107 108 109

    def __repr__(self):
        return '<DBConfig %s>' % self.name

class ContentFilename(object):
M
Mark Hymers 已提交
110 111
    def __init__(self, *args, **kwargs):
        pass
M
Mark Hymers 已提交
112 113 114 115 116

    def __repr__(self):
        return '<ContentFilename %s>' % self.filename

class ContentFilepath(object):
M
Mark Hymers 已提交
117 118
    def __init__(self, *args, **kwargs):
        pass
M
Mark Hymers 已提交
119 120 121 122 123

    def __repr__(self):
        return '<ContentFilepath %s>' % self.filepath

class ContentAssociations(object):
M
Mark Hymers 已提交
124 125
    def __init__(self, *args, **kwargs):
        pass
M
Mark Hymers 已提交
126 127 128 129 130

    def __repr__(self):
        return '<ContentAssociation %s>' % self.ca_id

class DSCFile(object):
M
Mark Hymers 已提交
131 132
    def __init__(self, *args, **kwargs):
        pass
M
Mark Hymers 已提交
133 134 135 136 137

    def __repr__(self):
        return '<DSCFile %s>' % self.dscfile_id

class PoolFile(object):
M
Mark Hymers 已提交
138 139
    def __init__(self, *args, **kwargs):
        pass
M
Mark Hymers 已提交
140 141 142 143 144

    def __repr__(self):
        return '<PoolFile %s>' % self.filename

class Fingerprint(object):
M
Mark Hymers 已提交
145 146
    def __init__(self, *args, **kwargs):
        pass
M
Mark Hymers 已提交
147 148 149 150 151

    def __repr__(self):
        return '<Fingerprint %s>' % self.fingerprint

class Keyring(object):
M
Mark Hymers 已提交
152 153
    def __init__(self, *args, **kwargs):
        pass
M
Mark Hymers 已提交
154 155 156 157 158

    def __repr__(self):
        return '<Keyring %s>' % self.keyring_name

class Location(object):
M
Mark Hymers 已提交
159 160
    def __init__(self, *args, **kwargs):
        pass
M
Mark Hymers 已提交
161 162 163 164 165

    def __repr__(self):
        return '<Location %s (%s)>' % (self.path, self.location_id)

class Maintainer(object):
M
Mark Hymers 已提交
166 167
    def __init__(self, *args, **kwargs):
        pass
M
Mark Hymers 已提交
168 169 170 171 172

    def __repr__(self):
        return '''<Maintainer '%s' (%s)>''' % (self.name, self.maintainer_id)

class Override(object):
M
Mark Hymers 已提交
173 174
    def __init__(self, *args, **kwargs):
        pass
M
Mark Hymers 已提交
175 176 177 178 179

    def __repr__(self):
        return '<Override %s (%s)>' % (self.package, self.suite_id)

class OverrideType(object):
M
Mark Hymers 已提交
180 181
    def __init__(self, *args, **kwargs):
        pass
M
Mark Hymers 已提交
182 183 184 185 186

    def __repr__(self):
        return '<OverrideType %s>' % self.overridetype

class PendingContentAssociation(object):
M
Mark Hymers 已提交
187 188
    def __init__(self, *args, **kwargs):
        pass
M
Mark Hymers 已提交
189 190 191 192 193

    def __repr__(self):
        return '<PendingContentAssociation %s>' % self.pca_id

class Priority(object):
M
Mark Hymers 已提交
194 195
    def __init__(self, *args, **kwargs):
        pass
M
Mark Hymers 已提交
196 197 198 199 200

    def __repr__(self):
        return '<Priority %s (%s)>' % (self.priority, self.priority_id)

class Queue(object):
M
Mark Hymers 已提交
201 202
    def __init__(self, *args, **kwargs):
        pass
M
Mark Hymers 已提交
203 204 205 206 207

    def __repr__(self):
        return '<Queue %s>' % self.queue_name

class QueueBuild(object):
M
Mark Hymers 已提交
208 209
    def __init__(self, *args, **kwargs):
        pass
M
Mark Hymers 已提交
210 211 212 213 214

    def __repr__(self):
        return '<QueueBuild %s (%s)>' % (self.filename, self.queue_id)

class Section(object):
M
Mark Hymers 已提交
215 216
    def __init__(self, *args, **kwargs):
        pass
M
Mark Hymers 已提交
217 218 219 220 221

    def __repr__(self):
        return '<Section %s>' % self.section

class Source(object):
M
Mark Hymers 已提交
222 223
    def __init__(self, *args, **kwargs):
        pass
M
Mark Hymers 已提交
224 225 226 227 228

    def __repr__(self):
        return '<Source %s (%s)>' % (self.source, self.version)

class SrcAssociation(object):
M
Mark Hymers 已提交
229 230
    def __init__(self, *args, **kwargs):
        pass
M
Mark Hymers 已提交
231 232

    def __repr__(self):
M
Mark Hymers 已提交
233
        return '<SrcAssociation %s (%s, %s)>' % (self.sa_id, self.source, self.suite)
M
Mark Hymers 已提交
234 235

class SrcUploader(object):
M
Mark Hymers 已提交
236 237
    def __init__(self, *args, **kwargs):
        pass
M
Mark Hymers 已提交
238 239 240 241 242

    def __repr__(self):
        return '<SrcUploader %s>' % self.uploader_id

class Suite(object):
M
Mark Hymers 已提交
243 244
    def __init__(self, *args, **kwargs):
        pass
M
Mark Hymers 已提交
245 246 247 248 249

    def __repr__(self):
        return '<Suite %s>' % self.suite_name

class SuiteArchitecture(object):
M
Mark Hymers 已提交
250 251
    def __init__(self, *args, **kwargs):
        pass
M
Mark Hymers 已提交
252 253 254 255 256

    def __repr__(self):
        return '<SuiteArchitecture (%s, %s)>' % (self.suite_id, self.arch_id)

class Uid(object):
M
Mark Hymers 已提交
257 258
    def __init__(self, *args, **kwargs):
        pass
M
Mark Hymers 已提交
259 260 261 262 263 264

    def __repr__(self):
        return '<Uid %s (%s)>' % (self.uid, self.name)

################################################################################

M
Mark Hymers 已提交
265 266
class DBConn(Singleton):
    """
267
    database module init.
M
Mark Hymers 已提交
268 269 270 271 272 273 274 275
    """
    def __init__(self, *args, **kwargs):
        super(DBConn, self).__init__(*args, **kwargs)

    def _startup(self, *args, **kwargs):
        self.__createconn()
        self.__init_caches()

M
Mark Hymers 已提交
276 277
    def __setuptables(self):
        self.tbl_architecture = Table('architecture', self.db_meta, autoload=True)
M
Mark Hymers 已提交
278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306
        self.tbl_archive = Table('archive', self.db_meta, autoload=True)
        self.tbl_bin_associations = Table('bin_associations', self.db_meta, autoload=True)
        self.tbl_binaries = Table('binaries', self.db_meta, autoload=True)
        self.tbl_component = Table('component', self.db_meta, autoload=True)
        self.tbl_config = Table('config', self.db_meta, autoload=True)
        self.tbl_content_associations = Table('content_associations', self.db_meta, autoload=True)
        self.tbl_content_file_names = Table('content_file_names', self.db_meta, autoload=True)
        self.tbl_content_file_paths = Table('content_file_paths', self.db_meta, autoload=True)
        self.tbl_dsc_files = Table('dsc_files', self.db_meta, autoload=True)
        self.tbl_files = Table('files', self.db_meta, autoload=True)
        self.tbl_fingerprint = Table('fingerprint', self.db_meta, autoload=True)
        self.tbl_keyrings = Table('keyrings', self.db_meta, autoload=True)
        self.tbl_location = Table('location', self.db_meta, autoload=True)
        self.tbl_maintainer = Table('maintainer', self.db_meta, autoload=True)
        self.tbl_override = Table('override', self.db_meta, autoload=True)
        self.tbl_override_type = Table('override_type', self.db_meta, autoload=True)
        self.tbl_pending_content_associations = Table('pending_content_associations', self.db_meta, autoload=True)
        self.tbl_priority = Table('priority', self.db_meta, autoload=True)
        self.tbl_queue = Table('queue', self.db_meta, autoload=True)
        self.tbl_queue_build = Table('queue_build', self.db_meta, autoload=True)
        self.tbl_section = Table('section', self.db_meta, autoload=True)
        self.tbl_source = Table('source', self.db_meta, autoload=True)
        self.tbl_src_associations = Table('src_associations', self.db_meta, autoload=True)
        self.tbl_src_uploaders = Table('src_uploaders', self.db_meta, autoload=True)
        self.tbl_suite = Table('suite', self.db_meta, autoload=True)
        self.tbl_suite_architectures = Table('suite_architectures', self.db_meta, autoload=True)
        self.tbl_uid = Table('uid', self.db_meta, autoload=True)

    def __setupmappers(self):
M
Mark Hymers 已提交
307 308 309 310 311 312 313 314 315 316
        mapper(Architecture, self.tbl_architecture,
               properties = dict(arch_id = self.tbl_architecture.c.id))

        mapper(Archive, self.tbl_archive,
               properties = dict(archive_id = self.tbl_archive.c.id,
                                 archive_name = self.tbl_archive.c.name))

        mapper(BinAssociation, self.tbl_bin_associations,
               properties = dict(ba_id = self.tbl_bin_associations.c.id,
                                 suite_id = self.tbl_bin_associations.c.suite,
M
Mark Hymers 已提交
317 318 319
                                 suite = relation(Suite),
                                 binary_id = self.tbl_bin_associations.c.bin,
                                 binary = relation(Binary)))
M
Mark Hymers 已提交
320 321 322

        mapper(Binary, self.tbl_binaries,
               properties = dict(binary_id = self.tbl_binaries.c.id,
M
Mark Hymers 已提交
323 324
                                 package = self.tbl_binaries.c.package,
                                 version = self.tbl_binaries.c.version,
M
Mark Hymers 已提交
325
                                 maintainer_id = self.tbl_binaries.c.maintainer,
M
Mark Hymers 已提交
326
                                 maintainer = relation(Maintainer),
M
Mark Hymers 已提交
327
                                 source_id = self.tbl_binaries.c.source,
M
Mark Hymers 已提交
328
                                 source = relation(Source),
M
Mark Hymers 已提交
329
                                 arch_id = self.tbl_binaries.c.architecture,
M
Mark Hymers 已提交
330 331 332 333 334 335 336 337 338
                                 architecture = relation(Architecture),
                                 poolfile_id = self.tbl_binaries.c.file,
                                 poolfile = relation(PoolFile),
                                 binarytype = self.tbl_binaries.c.type,
                                 fingerprint_id = self.tbl_binaries.c.sig_fpr,
                                 fingerprint = relation(Fingerprint),
                                 install_date = self.tbl_binaries.c.install_date,
                                 binassociations = relation(BinAssociation,
                                                            primaryjoin=(self.tbl_binaries.c.id==self.tbl_bin_associations.c.bin))))
M
Mark Hymers 已提交
339 340 341 342 343 344 345 346 347 348 349

        mapper(Component, self.tbl_component,
               properties = dict(component_id = self.tbl_component.c.id,
                                 component_name = self.tbl_component.c.name))

        mapper(DBConfig, self.tbl_config,
               properties = dict(config_id = self.tbl_config.c.id))

        mapper(ContentAssociations, self.tbl_content_associations,
               properties = dict(ca_id = self.tbl_content_associations.c.id,
                                 filename_id = self.tbl_content_associations.c.filename,
M
Mark Hymers 已提交
350
                                 filename    = relation(ContentFilename),
M
Mark Hymers 已提交
351
                                 filepath_id = self.tbl_content_associations.c.filepath,
M
Mark Hymers 已提交
352 353 354 355
                                 filepath    = relation(ContentFilepath),
                                 binary_id   = self.tbl_content_associations.c.binary_pkg,
                                 binary      = relation(Binary)))

M
Mark Hymers 已提交
356 357 358 359 360 361 362 363 364 365 366 367

        mapper(ContentFilename, self.tbl_content_file_names,
               properties = dict(cafilename_id = self.tbl_content_file_names.c.id,
                                 filename = self.tbl_content_file_names.c.file))

        mapper(ContentFilepath, self.tbl_content_file_paths,
               properties = dict(cafilepath_id = self.tbl_content_file_paths.c.id,
                                 filepath = self.tbl_content_file_paths.c.path))

        mapper(DSCFile, self.tbl_dsc_files,
               properties = dict(dscfile_id = self.tbl_dsc_files.c.id,
                                 source_id = self.tbl_dsc_files.c.source,
M
Mark Hymers 已提交
368 369 370
                                 source = relation(Source),
                                 poolfile_id = self.tbl_dsc_files.c.file,
                                 poolfile = relation(PoolFile)))
M
Mark Hymers 已提交
371 372 373 374

        mapper(PoolFile, self.tbl_files,
               properties = dict(file_id = self.tbl_files.c.id,
                                 filesize = self.tbl_files.c.size,
M
Mark Hymers 已提交
375 376
                                 location_id = self.tbl_files.c.location,
                                 location = relation(Location)))
M
Mark Hymers 已提交
377 378 379 380

        mapper(Fingerprint, self.tbl_fingerprint,
               properties = dict(fingerprint_id = self.tbl_fingerprint.c.id,
                                 uid_id = self.tbl_fingerprint.c.uid,
M
Mark Hymers 已提交
381 382 383
                                 uid = relation(Uid),
                                 keyring_id = self.tbl_fingerprint.c.keyring,
                                 keyring = relation(Keyring)))
M
Mark Hymers 已提交
384 385 386 387 388 389 390 391

        mapper(Keyring, self.tbl_keyrings,
               properties = dict(keyring_name = self.tbl_keyrings.c.name,
                                 keyring_id = self.tbl_keyrings.c.id))

        mapper(Location, self.tbl_location,
               properties = dict(location_id = self.tbl_location.c.id,
                                 component_id = self.tbl_location.c.component,
M
Mark Hymers 已提交
392
                                 component = relation(Component),
M
Mark Hymers 已提交
393
                                 archive_id = self.tbl_location.c.archive,
M
Mark Hymers 已提交
394
                                 archive = relation(Archive),
M
Mark Hymers 已提交
395 396 397 398 399 400 401
                                 archive_type = self.tbl_location.c.type))

        mapper(Maintainer, self.tbl_maintainer,
               properties = dict(maintainer_id = self.tbl_maintainer.c.id))

        mapper(Override, self.tbl_override,
               properties = dict(suite_id = self.tbl_override.c.suite,
M
Mark Hymers 已提交
402
                                 suite = relation(Suite),
M
Mark Hymers 已提交
403
                                 component_id = self.tbl_override.c.component,
M
Mark Hymers 已提交
404
                                 component = relation(Component),
M
Mark Hymers 已提交
405
                                 priority_id = self.tbl_override.c.priority,
M
Mark Hymers 已提交
406
                                 priority = relation(Priority),
M
Mark Hymers 已提交
407
                                 section_id = self.tbl_override.c.section,
M
Mark Hymers 已提交
408 409 410
                                 section = relation(Section),
                                 overridetype_id = self.tbl_override.c.type,
                                 overridetype = relation(OverrideType)))
M
Mark Hymers 已提交
411 412 413 414 415 416 417 418

        mapper(OverrideType, self.tbl_override_type,
               properties = dict(overridetype = self.tbl_override_type.c.type,
                                 overridetype_id = self.tbl_override_type.c.id))

        mapper(PendingContentAssociation, self.tbl_pending_content_associations,
               properties = dict(pca_id = self.tbl_pending_content_associations.c.id,
                                 filepath_id = self.tbl_pending_content_associations.c.filepath,
M
Mark Hymers 已提交
419 420 421
                                 filepath = relation(ContentFilepath),
                                 filename_id = self.tbl_pending_content_associations.c.filename,
                                 filename = relation(ContentFilename)))
M
Mark Hymers 已提交
422 423 424 425 426 427 428 429 430

        mapper(Priority, self.tbl_priority,
               properties = dict(priority_id = self.tbl_priority.c.id))

        mapper(Queue, self.tbl_queue,
               properties = dict(queue_id = self.tbl_queue.c.id))

        mapper(QueueBuild, self.tbl_queue_build,
               properties = dict(suite_id = self.tbl_queue_build.c.suite,
M
Mark Hymers 已提交
431 432
                                 queue_id = self.tbl_queue_build.c.queue,
                                 queue = relation(Queue)))
M
Mark Hymers 已提交
433 434 435 436 437 438

        mapper(Section, self.tbl_section,
               properties = dict(section_id = self.tbl_section.c.id))

        mapper(Source, self.tbl_source,
               properties = dict(source_id = self.tbl_source.c.id,
M
Mark Hymers 已提交
439
                                 version = self.tbl_source.c.version,
M
Mark Hymers 已提交
440
                                 maintainer_id = self.tbl_source.c.maintainer,
M
Mark Hymers 已提交
441 442 443 444
                                 maintainer = relation(Maintainer,
                                                       primaryjoin=(self.tbl_source.c.maintainer==self.tbl_maintainer.c.id)),
                                 poolfile_id = self.tbl_source.c.file,
                                 poolfile = relation(PoolFile),
M
Mark Hymers 已提交
445
                                 fingerprint_id = self.tbl_source.c.sig_fpr,
M
Mark Hymers 已提交
446 447 448 449 450 451 452 453
                                 fingerprint = relation(Fingerprint),
                                 changedby_id = self.tbl_source.c.changedby,
                                 changedby = relation(Maintainer,
                                                      primaryjoin=(self.tbl_source.c.changedby==self.tbl_maintainer.c.id)),
                                 srcfiles = relation(DSCFile,
                                                     primaryjoin=(self.tbl_source.c.id==self.tbl_dsc_files.c.source)),
                                 srcassociations = relation(SrcAssociation,
                                                            primaryjoin=(self.tbl_source.c.id==self.tbl_src_associations.c.source))))
M
Mark Hymers 已提交
454 455

        mapper(SrcAssociation, self.tbl_src_associations,
M
Mark Hymers 已提交
456 457 458 459 460
               properties = dict(sa_id = self.tbl_src_associations.c.id,
                                 suite_id = self.tbl_src_associations.c.suite,
                                 suite = relation(Suite),
                                 source_id = self.tbl_src_associations.c.source,
                                 source = relation(Source)))
M
Mark Hymers 已提交
461 462 463 464

        mapper(SrcUploader, self.tbl_src_uploaders,
               properties = dict(uploader_id = self.tbl_src_uploaders.c.id,
                                 source_id = self.tbl_src_uploaders.c.source,
M
Mark Hymers 已提交
465 466 467 468 469
                                 source = relation(Source,
                                                   primaryjoin=(self.tbl_src_uploaders.c.source==self.tbl_source.c.id)),
                                 maintainer_id = self.tbl_src_uploaders.c.maintainer,
                                 maintainer = relation(Maintainer,
                                                       primaryjoin=(self.tbl_src_uploaders.c.maintainer==self.tbl_maintainer.c.id))))
M
Mark Hymers 已提交
470 471 472 473 474 475

        mapper(Suite, self.tbl_suite,
               properties = dict(suite_id = self.tbl_suite.c.id))

        mapper(SuiteArchitecture, self.tbl_suite_architectures,
               properties = dict(suite_id = self.tbl_suite_architectures.c.suite,
M
Mark Hymers 已提交
476 477 478
                                 suite = relation(Suite),
                                 arch_id = self.tbl_suite_architectures.c.architecture,
                                 architecture = relation(Architecture)))
M
Mark Hymers 已提交
479 480 481

        mapper(Uid, self.tbl_uid,
               properties = dict(uid_id = self.tbl_uid.c.id))
M
Mark Hymers 已提交
482

M
Mark Hymers 已提交
483 484
    ## Connection functions
    def __createconn(self):
M
Mark Hymers 已提交
485
        from config import Config
486 487
        cnf = Config()
        if cnf["DB::Host"]:
M
Mark Hymers 已提交
488 489 490 491 492 493 494 495 496 497
            # TCP/IP
            connstr = "postgres://%s" % cnf["DB::Host"]
            if cnf["DB::Port"] and cnf["DB::Port"] != "-1":
                connstr += ":%s" % cnf["DB::Port"]
            connstr += "/%s" % cnf["DB::Name"]
        else:
            # Unix Socket
            connstr = "postgres:///%s" % cnf["DB::Name"]
            if cnf["DB::Port"] and cnf["DB::Port"] != "-1":
                connstr += "?port=%s" % cnf["DB::Port"]
498

M
Mark Hymers 已提交
499 500 501 502 503 504
        self.db_pg   = create_engine(connstr)
        self.db_meta = MetaData()
        self.db_meta.bind = self.db_pg
        self.db_smaker = sessionmaker(bind=self.db_pg,
                                      autoflush=True,
                                      transactional=True)
M
Mark Hymers 已提交
505

M
Mark Hymers 已提交
506
        self.__setuptables()
M
Mark Hymers 已提交
507
        self.__setupmappers()
M
Mark Hymers 已提交
508

M
Mark Hymers 已提交
509 510
    def session(self):
        return self.db_smaker()
M
Mark Hymers 已提交
511 512 513

    ## Cache functions
    def __init_caches(self):
514
        self.caches = {'suite':         Cache(),
M
Mark Hymers 已提交
515 516 517 518 519 520
                       'section':       Cache(),
                       'priority':      Cache(),
                       'override_type': Cache(),
                       'architecture':  Cache(),
                       'archive':       Cache(),
                       'component':     Cache(),
521 522
                       'content_path_names':     Cache(),
                       'content_file_names':     Cache(),
M
Mark Hymers 已提交
523 524 525 526
                       'location':      Cache(lambda x: '%s_%s_%s' % (x['location'], x['component'], x['location'])),
                       'maintainer':    {}, # TODO
                       'keyring':       {}, # TODO
                       'source':        Cache(lambda x: '%s_%s_' % (x['source'], x['version'])),
527
                       'files':         Cache(lambda x: '%s_%s_' % (x['filename'], x['location'])),
M
Mark Hymers 已提交
528 529 530 531 532 533 534
                       'maintainer':    {}, # TODO
                       'fingerprint':   {}, # TODO
                       'queue':         {}, # TODO
                       'uid':           {}, # TODO
                       'suite_version': Cache(lambda x: '%s_%s' % (x['source'], x['suite'])),
                      }

M
Mike O'Connor 已提交
535 536 537 538
        self.prepared_statements = {}

    def prepare(self,name,statement):
        if not self.prepared_statements.has_key(name):
M
Mark Hymers 已提交
539
            pgc.execute(statement)
M
Mike O'Connor 已提交
540 541
            self.prepared_statements[name] = statement

M
Mark Hymers 已提交
542 543 544 545
    def clear_caches(self):
        self.__init_caches()

    ## Get functions
M
Mark Hymers 已提交
546
    def __get_id(self, retfield, selectobj, cachekey, cachename=None):
M
Mark Hymers 已提交
547 548
        # This is a bit of a hack but it's an internal function only
        if cachename is not None:
M
Mark Hymers 已提交
549
            res = self.caches[cachename].GetValue(cachekey)
M
Mark Hymers 已提交
550 551 552
            if res:
                return res

M
Mark Hymers 已提交
553
        c = selectobj.execute()
M
Mark Hymers 已提交
554 555 556 557

        if c.rowcount != 1:
            return None

M
Mark Hymers 已提交
558 559 560 561 562 563
        res = c.fetchone()

        if retfield not in res.keys():
            return None

        res = res[retfield]
M
Mark Hymers 已提交
564 565

        if cachename is not None:
M
Mark Hymers 已提交
566
            self.caches[cachename].SetValue(cachekey, res)
567

M
Mark Hymers 已提交
568
        return res
569

M
Mark Hymers 已提交
570
    def get_suite_id(self, suite):
571 572 573 574 575 576 577 578 579 580 581
        """
        Returns database id for given C{suite}.
        Results are kept in a cache during runtime to minimize database queries.

        @type suite: string
        @param suite: The name of the suite

        @rtype: int
        @return: the database id for the given suite

        """
M
Mark Hymers 已提交
582 583 584 585
        return int(self.__get_id('id',
                                 self.tbl_suite.select(self.tbl_suite.columns.suite_name == suite),
                                 suite,
                                 'suite'))
M
Mark Hymers 已提交
586 587

    def get_section_id(self, section):
588 589 590 591 592 593 594 595 596 597 598
        """
        Returns database id for given C{section}.
        Results are kept in a cache during runtime to minimize database queries.

        @type section: string
        @param section: The name of the section

        @rtype: int
        @return: the database id for the given section

        """
M
Mark Hymers 已提交
599 600 601 602
        return self.__get_id('id',
                             self.tbl_section.select(self.tbl_section.columns.section == section),
                             section,
                             'section')
M
Mark Hymers 已提交
603 604

    def get_priority_id(self, priority):
605 606 607 608 609 610 611 612 613 614 615
        """
        Returns database id for given C{priority}.
        Results are kept in a cache during runtime to minimize database queries.

        @type priority: string
        @param priority: The name of the priority

        @rtype: int
        @return: the database id for the given priority

        """
M
Mark Hymers 已提交
616 617 618 619
        return self.__get_id('id',
                             self.tbl_priority.select(self.tbl_priority.columns.priority == priority),
                             priority,
                             'priority')
M
Mark Hymers 已提交
620 621

    def get_override_type_id(self, override_type):
622 623 624 625
        """
        Returns database id for given override C{type}.
        Results are kept in a cache during runtime to minimize database queries.

J
Various  
Joerg Jaspert 已提交
626 627
        @type override_type: string
        @param override_type: The name of the override type
628 629 630 631 632

        @rtype: int
        @return: the database id for the given override type

        """
M
Mark Hymers 已提交
633 634 635 636
        return self.__get_id('id',
                             self.tbl_override_type.select(self.tbl_override_type.columns.type == override_type),
                             override_type,
                             'override_type')
M
Mark Hymers 已提交
637 638

    def get_architecture_id(self, architecture):
639 640 641 642 643 644 645 646 647 648 649
        """
        Returns database id for given C{architecture}.
        Results are kept in a cache during runtime to minimize database queries.

        @type architecture: string
        @param architecture: The name of the override type

        @rtype: int
        @return: the database id for the given architecture

        """
M
Mark Hymers 已提交
650 651 652 653
        return self.__get_id('id',
                             self.tbl_architecture.select(self.tbl_architecture.columns.arch_string == architecture),
                             architecture,
                             'architecture')
M
Mark Hymers 已提交
654 655

    def get_archive_id(self, archive):
656 657 658 659 660 661 662 663 664 665 666
        """
        returns database id for given c{archive}.
        results are kept in a cache during runtime to minimize database queries.

        @type archive: string
        @param archive: the name of the override type

        @rtype: int
        @return: the database id for the given archive

        """
M
Mark Hymers 已提交
667 668 669 670 671
        archive = archive.lower()
        return self.__get_id('id',
                             self.tbl_archive.select(self.tbl_archive.columns.name == archive),
                             archive,
                             'archive')
M
Mark Hymers 已提交
672 673

    def get_component_id(self, component):
674 675 676 677 678 679 680 681 682 683 684
        """
        Returns database id for given C{component}.
        Results are kept in a cache during runtime to minimize database queries.

        @type component: string
        @param component: The name of the override type

        @rtype: int
        @return: the database id for the given component

        """
M
Mark Hymers 已提交
685 686 687 688 689
        component = component.lower()
        return self.__get_id('id',
                             self.tbl_component.select(self.tbl_component.columns.name == component),
                             component.lower(),
                             'component')
M
Mark Hymers 已提交
690 691

    def get_location_id(self, location, component, archive):
692 693 694 695 696 697 698 699 700 701
        """
        Returns database id for the location behind the given combination of
          - B{location} - the path of the location, eg. I{/srv/ftp.debian.org/ftp/pool/}
          - B{component} - the id of the component as returned by L{get_component_id}
          - B{archive} - the id of the archive as returned by L{get_archive_id}
        Results are kept in a cache during runtime to minimize database queries.

        @type location: string
        @param location: the path of the location

M
Mark Hymers 已提交
702 703
        @type component: string
        @param component: the name of the component
704

M
Mark Hymers 已提交
705 706
        @type archive: string
        @param archive: the name of the archive
707 708 709 710 711 712

        @rtype: int
        @return: the database id for the location

        """

M
Mark Hymers 已提交
713 714
        archive = archive.lower()
        component = component.lower()
M
Mark Hymers 已提交
715

M
Mark Hymers 已提交
716
        values = {'archive': archive, 'location': location, 'component': component}
M
Mark Hymers 已提交
717

M
Mark Hymers 已提交
718
        s = self.tbl_location.join(self.tbl_archive).join(self.tbl_component)
M
Mark Hymers 已提交
719

M
Mark Hymers 已提交
720 721 722
        s = s.select(self.tbl_location.columns.path == location)
        s = s.where(self.tbl_archive.columns.name == archive)
        s = s.where(self.tbl_component.columns.name == component)
M
Mark Hymers 已提交
723

M
Mark Hymers 已提交
724
        return self.__get_id('location.id', s, values, 'location')
M
Mark Hymers 已提交
725 726

    def get_source_id(self, source, version):
727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742
        """
        Returns database id for the combination of C{source} and C{version}
          - B{source} - source package name, eg. I{mailfilter}, I{bbdb}, I{glibc}
          - B{version}
        Results are kept in a cache during runtime to minimize database queries.

        @type source: string
        @param source: source package name

        @type version: string
        @param version: the source version

        @rtype: int
        @return: the database id for the source

        """
M
Mark Hymers 已提交
743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760
        s = self.tbl_source.select()
        s = s.where(self.tbl_source.columns.source  == source)
        s = s.where(self.tbl_source.columns.version == version)

        return self.__get_id('id', s, {'source': source, 'version': version}, 'source')

    def get_suite(self, suite):
        if isinstance(suite, str):
            suite_id = self.get_suite_id(suite.lower())
        elif type(suite) == int:
            suite_id = suite

        s = self.tbl_suite.select(self.tbl_suite.columns.id == suite_id)
        c = s.execute()
        if c.rowcount < 1:
            return None
        else:
            return c.fetchone()
M
Mark Hymers 已提交
761 762

    def get_suite_version(self, source, suite):
763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780
        """
        Returns database id for a combination of C{source} and C{suite}.

          - B{source} - source package name, eg. I{mailfilter}, I{bbdb}, I{glibc}
          - B{suite} - a suite name, eg. I{unstable}

        Results are kept in a cache during runtime to minimize database queries.

        @type source: string
        @param source: source package name

        @type suite: string
        @param suite: the suite name

        @rtype: string
        @return: the version for I{source} in I{suite}

        """
M
Mark Hymers 已提交
781 782 783 784 785 786 787
        s = select([self.tbl_source.columns.source, self.tbl_source.columns.version])
#        s = self.tbl_source.join(self.tbl_src_associations).join(self.tbl_suite)

        s = s.select(self.tbl_suite.columns.suite_name == suite, use_labels=True)
        s = s.select(self.tbl_source.columns.source == source)

        return self.__get_id('source.version', s, {'suite': suite, 'source': source}, 'suite_version')
M
Mark Hymers 已提交
788

789

790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844
    def get_files_id (self, filename, size, md5sum, location_id):
        """
        Returns -1, -2 or the file_id for filename, if its C{size} and C{md5sum} match an
        existing copy.

        The database is queried using the C{filename} and C{location_id}. If a file does exist
        at that location, the existing size and md5sum are checked against the provided
        parameters. A size or checksum mismatch returns -2. If more than one entry is
        found within the database, a -1 is returned, no result returns None, otherwise
        the file id.

        Results are kept in a cache during runtime to minimize database queries.

        @type filename: string
        @param filename: the filename of the file to check against the DB

        @type size: int
        @param size: the size of the file to check against the DB

        @type md5sum: string
        @param md5sum: the md5sum of the file to check against the DB

        @type location_id: int
        @param location_id: the id of the location as returned by L{get_location_id}

        @rtype: int / None
        @return: Various return values are possible:
                   - -2: size/checksum error
                   - -1: more than one file found in database
                   - None: no file found in database
                   - int: file id

        """
        values = {'filename' : filename,
                  'location' : location_id}

        res = self.caches['files'].GetValue( values )

        if not res:
            query = """SELECT id, size, md5sum
                       FROM files
                       WHERE filename = %(filename)s AND location = %(location)s"""

            cursor = self.db_con.cursor()
            cursor.execute( query, values )

            if cursor.rowcount == 0:
                res = None

            elif cursor.rowcount != 1:
                res = -1

            else:
                row = cursor.fetchone()

845
                if row[1] != int(size) or row[2] != md5sum:
846 847 848
                    res =  -2

                else:
849
                    self.caches['files'].SetValue(values, row[0])
850 851 852 853 854
                    res = row[0]

        return res


855 856 857 858 859 860 861 862 863 864 865 866 867
    def get_or_set_contents_file_id(self, filename):
        """
        Returns database id for given filename.

        Results are kept in a cache during runtime to minimize database queries.
        If no matching file is found, a row is inserted.

        @type filename: string
        @param filename: The filename

        @rtype: int
        @return: the database id for the given component
        """
868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883
        try:
            values={'value': filename}
            query = "SELECT id FROM content_file_names WHERE file = %(value)s"
            id = self.__get_single_id(query, values, cachename='content_file_names')
            if not id:
                c = self.db_con.cursor()
                c.execute( "INSERT INTO content_file_names VALUES (DEFAULT, %(value)s) RETURNING id",
                           values )

                id = c.fetchone()[0]
                self.caches['content_file_names'].SetValue(values, id)

            return id
        except:
            traceback.print_exc()
            raise
884 885 886 887 888 889 890 891 892 893 894 895 896 897

    def get_or_set_contents_path_id(self, path):
        """
        Returns database id for given path.

        Results are kept in a cache during runtime to minimize database queries.
        If no matching file is found, a row is inserted.

        @type path: string
        @param path: The filename

        @rtype: int
        @return: the database id for the given component
        """
898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913
        try:
            values={'value': path}
            query = "SELECT id FROM content_file_paths WHERE path = %(value)s"
            id = self.__get_single_id(query, values, cachename='content_path_names')
            if not id:
                c = self.db_con.cursor()
                c.execute( "INSERT INTO content_file_paths VALUES (DEFAULT, %(value)s) RETURNING id",
                           values )

                id = c.fetchone()[0]
                self.caches['content_path_names'].SetValue(values, id)

            return id
        except:
            traceback.print_exc()
            raise
914

915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940
    def get_suite_architectures(self, suite):
        """
        Returns list of architectures for C{suite}.

        @type suite: string, int
        @param suite: the suite name or the suite_id

        @rtype: list
        @return: the list of architectures for I{suite}
        """

        suite_id = None
        if type(suite) == str:
            suite_id = self.get_suite_id(suite)
        elif type(suite) == int:
            suite_id = suite
        else:
            return None

        c = self.db_con.cursor()
        c.execute( """SELECT a.arch_string FROM suite_architectures sa
                      JOIN architecture a ON (a.id = sa.architecture)
                      WHERE suite='%s'""" % suite_id )

        return map(lambda x: x[0], c.fetchall())

M
Mike O'Connor 已提交
941
    def insert_content_paths(self, bin_id, fullpaths):
942 943 944 945 946
        """
        Make sure given path is associated with given binary id

        @type bin_id: int
        @param bin_id: the id of the binary
J
Various  
Joerg Jaspert 已提交
947 948
        @type fullpaths: list
        @param fullpaths: the list of paths of the file being associated with the binary
949

J
Various  
Joerg Jaspert 已提交
950
        @return: True upon success
951 952 953
        """

        c = self.db_con.cursor()
M
Mike O'Connor 已提交
954 955 956 957 958 959 960

        c.execute("BEGIN WORK")
        try:

            for fullpath in fullpaths:
                (path, file) = os.path.split(fullpath)

961 962
                if path.startswith( "./" ):
                    path = path[2:]
M
Mike O'Connor 已提交
963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987
                # Get the necessary IDs ...
                file_id = self.get_or_set_contents_file_id(file)
                path_id = self.get_or_set_contents_path_id(path)

                c.execute("""INSERT INTO content_associations
                               (binary_pkg, filepath, filename)
                           VALUES ( '%d', '%d', '%d')""" % (bin_id, path_id, file_id) )

            c.execute("COMMIT")
            return True
        except:
            traceback.print_exc()
            c.execute("ROLLBACK")
            return False

    def insert_pending_content_paths(self, package, fullpaths):
        """
        Make sure given paths are temporarily associated with given
        package

        @type package: dict
        @param package: the package to associate with should have been read in from the binary control file
        @type fullpaths: list
        @param fullpaths: the list of paths of the file being associated with the binary

J
Various  
Joerg Jaspert 已提交
988
        @return: True upon success
M
Mike O'Connor 已提交
989 990 991
        """

        c = self.db_con.cursor()
992

993 994
        c.execute("BEGIN WORK")
        try:
995
            arch_id = self.get_architecture_id(package['Architecture'])
996

M
Mark Hymers 已提交
997
            # Remove any already existing recorded files for this package
998
            c.execute("""DELETE FROM pending_content_associations
999
                         WHERE package=%(Package)s
1000
                         AND version=%(Version)s
M
Mark Hymers 已提交
1001 1002 1003
                         AND architecture=%(ArchID)s""", {'Package': package['Package'],
                                                          'Version': package['Version'],
                                                          'ArchID':  arch_id})
1004 1005 1006 1007

            for fullpath in fullpaths:
                (path, file) = os.path.split(fullpath)

1008 1009
                if path.startswith( "./" ):
                    path = path[2:]
1010 1011 1012 1013
                # Get the necessary IDs ...
                file_id = self.get_or_set_contents_file_id(file)
                path_id = self.get_or_set_contents_path_id(path)

1014
                c.execute("""INSERT INTO pending_content_associations
1015 1016 1017 1018
                               (package, version, architecture, filepath, filename)
                            VALUES (%%(Package)s, %%(Version)s, '%d', '%d', '%d')"""
                    % (arch_id, path_id, file_id), package )

1019 1020 1021 1022 1023 1024
            c.execute("COMMIT")
            return True
        except:
            traceback.print_exc()
            c.execute("ROLLBACK")
            return False
M
Mark Hymers 已提交
1025