dbconn.py 37.5 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

M
Mark Hymers 已提交
43 44 45
# Don't remove this, we re-export the exceptions to scripts which import us
from sqlalchemy.exc import *

46
from singleton import Singleton
M
Mark Hymers 已提交
47 48 49

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

50 51 52 53
__all__ = []

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

M
Mark Hymers 已提交
54
class Architecture(object):
M
Mark Hymers 已提交
55 56
    def __init__(self, *args, **kwargs):
        pass
M
Mark Hymers 已提交
57 58 59 60

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

61 62
__all__.append('Architecture')

63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84
def get_architecture(architecture, session=None):
    """
    Returns database id for given C{architecture}.

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

    @type session: Session
    @param session: Optional SQLA session object (a temporary one will be
    generated if not supplied)

    @rtype: Architecture
    @return: Architecture object for the given arch (None if not present)

    """
    if session is None:
        session = DBConn().session()
    q = session.query(Architecture).filter_by(arch_string=architecture)
    if q.count() == 0:
        return None
    return q.one()

85 86
__all__.append('get_architecture')

M
Mark Hymers 已提交
87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109
def get_architecture_suites(architecture, session=None):
    """
    Returns list of Suite objects for given C{architecture} name

    @type source: str
    @param source: Architecture name to search for

    @type session: Session
    @param session: Optional SQL session object (a temporary one will be
    generated if not supplied)

    @rtype: list
    @return: list of Suite objects for the given name (may be empty)
    """

    if session is None:
        session = DBConn().session()

    q = session.query(Suite)
    q = q.join(SuiteArchitecture)
    q = q.join(Architecture).filter_by(arch_string=architecture).order_by('suite_name')
    return q.all()

110 111
__all__.append('get_architecture_suites')

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

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

119 120
__all__.append('Archive')

121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143
def get_archive(archive, session=None):
    """
    returns database id for given c{archive}.

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

    @type session: Session
    @param session: Optional SQLA session object (a temporary one will be
    generated if not supplied)

    @rtype: Archive
    @return: Archive object for the given name (None if not present)

    """
    archive = archive.lower()
    if session is None:
        session = DBConn().session()
    q = session.query(Archive).filter_by(archive_name=archive)
    if q.count() == 0:
        return None
    return q.one()

144
__all__.append('get_archive')
145

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

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

153 154
__all__.append('BinAssociation')

M
Mark Hymers 已提交
155
class Binary(object):
M
Mark Hymers 已提交
156 157
    def __init__(self, *args, **kwargs):
        pass
M
Mark Hymers 已提交
158 159

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

162 163
__all__.append('Binary')

164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183
def get_binary_from_id(id, session=None):
    """
    Returns Binary object for given C{id}

    @type id: int
    @param id: Id of the required binary

    @type session: Session
    @param session: Optional SQLA session object (a temporary one will be
    generated if not supplied)

    @rtype: Binary
    @return: Binary object for the given binary (None if not present)
    """
    if session is None:
        session = DBConn().session()
    q = session.query(Binary).filter_by(binary_id=id)
    if q.count() == 0:
        return None
    return q.one()
M
Mark Hymers 已提交
184

185 186
__all__.append('get_binary_from_id')

M
Mark Hymers 已提交
187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204
def get_binaries_from_name(package, session=None):
    """
    Returns list of Binary objects for given C{package} name

    @type package: str
    @param package: Binary package name to search for

    @type session: Session
    @param session: Optional SQL session object (a temporary one will be
    generated if not supplied)

    @rtype: list
    @return: list of Binary objects for the given name (may be empty)
    """
    if session is None:
        session = DBConn().session()
    return session.query(Binary).filter_by(package=package).all()

205 206
__all__.append('get_binaries_from_name')

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

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

214 215 216

__all__.append('Component')

217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235
def get_component(component, session=None):
    """
    Returns database id for given C{component}.

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

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

    """
    component = component.lower()
    if session is None:
        session = DBConn().session()
    q = session.query(Component).filter_by(component_name=component)
    if q.count() == 0:
        return None
    return q.one()

236 237
__all__.append('get_component')

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

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

245 246
__all__.append('DBConfig')

M
Mark Hymers 已提交
247
class ContentFilename(object):
M
Mark Hymers 已提交
248 249
    def __init__(self, *args, **kwargs):
        pass
M
Mark Hymers 已提交
250 251 252 253

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

254 255
__all__.append('ContentFilename')

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

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

263 264
__all__.append('ContentFilepath')

265
class ContentAssociation(object):
M
Mark Hymers 已提交
266 267
    def __init__(self, *args, **kwargs):
        pass
M
Mark Hymers 已提交
268 269 270 271

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

272 273
__all__.append('ContentAssociation')

M
Mark Hymers 已提交
274
class DSCFile(object):
M
Mark Hymers 已提交
275 276
    def __init__(self, *args, **kwargs):
        pass
M
Mark Hymers 已提交
277 278 279 280

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

281 282
__all__.append('DSCFile')

M
Mark Hymers 已提交
283
class PoolFile(object):
M
Mark Hymers 已提交
284 285
    def __init__(self, *args, **kwargs):
        pass
M
Mark Hymers 已提交
286 287 288 289

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

290 291
__all__.append('PoolFile')

M
Mark Hymers 已提交
292
class Fingerprint(object):
M
Mark Hymers 已提交
293 294
    def __init__(self, *args, **kwargs):
        pass
M
Mark Hymers 已提交
295 296 297 298

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

299 300
__all__.append('Fingerprint')

M
Mark Hymers 已提交
301
class Keyring(object):
M
Mark Hymers 已提交
302 303
    def __init__(self, *args, **kwargs):
        pass
M
Mark Hymers 已提交
304 305 306 307

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

308 309
__all__.append('Keyring')

M
Mark Hymers 已提交
310
class Location(object):
M
Mark Hymers 已提交
311 312
    def __init__(self, *args, **kwargs):
        pass
M
Mark Hymers 已提交
313 314 315 316

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

317 318
__all__.append('Location')

M
Mark Hymers 已提交
319
class Maintainer(object):
M
Mark Hymers 已提交
320 321
    def __init__(self, *args, **kwargs):
        pass
M
Mark Hymers 已提交
322 323 324 325

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

326 327
__all__.append('Maintainer')

M
Mark Hymers 已提交
328
class Override(object):
M
Mark Hymers 已提交
329 330
    def __init__(self, *args, **kwargs):
        pass
M
Mark Hymers 已提交
331 332 333 334

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

335 336
__all__.append('Override')

M
Mark Hymers 已提交
337
class OverrideType(object):
M
Mark Hymers 已提交
338 339
    def __init__(self, *args, **kwargs):
        pass
M
Mark Hymers 已提交
340 341 342 343

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

344 345
__all__.append('OverrideType')

346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367
def get_override_type(override_type, session=None):
    """
    Returns OverrideType object for given C{override type}.

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

    @type session: Session
    @param session: Optional SQLA session object (a temporary one will be
    generated if not supplied)

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

    """
    if session is None:
        session = DBConn().session()
    q = session.query(Priority).filter_by(priority=priority)
    if q.count() == 0:
        return None
    return q.one()

368 369
__all__.append('get_override_type')

M
Mark Hymers 已提交
370
class PendingContentAssociation(object):
M
Mark Hymers 已提交
371 372
    def __init__(self, *args, **kwargs):
        pass
M
Mark Hymers 已提交
373 374 375 376

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

377 378
__all__.append('PendingContentAssociation')

M
Mark Hymers 已提交
379
class Priority(object):
M
Mark Hymers 已提交
380 381
    def __init__(self, *args, **kwargs):
        pass
M
Mark Hymers 已提交
382 383 384 385

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

386 387
__all__.append('Priority')

388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409
def get_priority(priority, session=None):
    """
    Returns Priority object for given C{priority name}.

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

    @type session: Session
    @param session: Optional SQLA session object (a temporary one will be
    generated if not supplied)

    @rtype: Priority
    @return: Priority object for the given priority

    """
    if session is None:
        session = DBConn().session()
    q = session.query(Priority).filter_by(priority=priority)
    if q.count() == 0:
        return None
    return q.one()

410 411
__all__.append('get_priority')

M
Mark Hymers 已提交
412
class Queue(object):
M
Mark Hymers 已提交
413 414
    def __init__(self, *args, **kwargs):
        pass
M
Mark Hymers 已提交
415 416 417 418

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

419 420
__all__.append('Queue')

M
Mark Hymers 已提交
421
class QueueBuild(object):
M
Mark Hymers 已提交
422 423
    def __init__(self, *args, **kwargs):
        pass
M
Mark Hymers 已提交
424 425 426 427

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

428 429
__all__.append('QueueBuild')

M
Mark Hymers 已提交
430
class Section(object):
M
Mark Hymers 已提交
431 432
    def __init__(self, *args, **kwargs):
        pass
M
Mark Hymers 已提交
433 434 435 436

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

437 438
__all__.append('Section')

439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460
def get_section(section, session=None):
    """
    Returns Section object for given C{section name}.

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

    @type session: Session
    @param session: Optional SQLA session object (a temporary one will be
    generated if not supplied)

    @rtype: Section
    @return: Section object for the given section name

    """
    if session is None:
        session = DBConn().session()
    q = session.query(Section).filter_by(section=section)
    if q.count() == 0:
        return None
    return q.one()

461 462
__all__.append('get_section')

M
Mark Hymers 已提交
463
class Source(object):
M
Mark Hymers 已提交
464 465
    def __init__(self, *args, **kwargs):
        pass
M
Mark Hymers 已提交
466 467 468 469

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

470 471
__all__.append('Source')

M
Mark Hymers 已提交
472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489
def get_sources_from_name(source, session=None):
    """
    Returns list of Source objects for given C{source} name

    @type source: str
    @param source: Source package name to search for

    @type session: Session
    @param session: Optional SQL session object (a temporary one will be
    generated if not supplied)

    @rtype: list
    @return: list of Source objects for the given name (may be empty)
    """
    if session is None:
        session = DBConn().session()
    return session.query(Source).filter_by(source=source).all()

490 491
__all__.append('get_sources_from_name')

492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510
def get_source_in_suite(source, suite, session=None):
    """
    Returns list of Source objects 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}

    @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}

    """
    if session is None:
        session = DBConn().session()
M
updates  
Mark Hymers 已提交
511 512 513
    q = session.query(SrcAssociation)
    q = q.join('source').filter_by(source=source)
    q = q.join('suite').filter_by(suite_name=suite)
514 515
    if q.count() == 0:
        return None
M
updates  
Mark Hymers 已提交
516 517
    # ???: Maybe we should just return the SrcAssociation object instead
    return q.one().source
518

519 520
__all__.append('get_source_in_suite')

M
Mark Hymers 已提交
521
class SrcAssociation(object):
M
Mark Hymers 已提交
522 523
    def __init__(self, *args, **kwargs):
        pass
M
Mark Hymers 已提交
524 525

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

528 529
__all__.append('SrcAssociation')

M
Mark Hymers 已提交
530
class SrcUploader(object):
M
Mark Hymers 已提交
531 532
    def __init__(self, *args, **kwargs):
        pass
M
Mark Hymers 已提交
533 534 535 536

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

537 538
__all__.append('SrcUploader')

M
Mark Hymers 已提交
539
class Suite(object):
M
Mark Hymers 已提交
540 541
    def __init__(self, *args, **kwargs):
        pass
M
Mark Hymers 已提交
542 543 544 545

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

546 547
__all__.append('Suite')

M
Mark Hymers 已提交
548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576
def get_suite_architecture(suite, architecture, session=None):
    """
    Returns a SuiteArchitecture object given C{suite} and ${arch} or None if it
    doesn't exist

    @type suite: str
    @param suite: Suite name to search for

    @type architecture: str
    @param architecture: Architecture name to search for

    @type session: Session
    @param session: Optional SQL session object (a temporary one will be
    generated if not supplied)

    @rtype: SuiteArchitecture
    @return: the SuiteArchitecture object or None
    """

    if session is None:
        session = DBConn().session()

    q = session.query(SuiteArchitecture)
    q = q.join(Architecture).filter_by(arch_string=architecture)
    q = q.join(Suite).filter_by(suite_name=suite)
    if q.count() == 0:
        return None
    return q.one()

577
__all__.append('get_suite_architecture')
M
Mark Hymers 已提交
578

579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600
def get_suite(suite, session=None):
    """
    Returns Suite object for given C{suite name}.

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

    @type session: Session
    @param session: Optional SQLA session object (a temporary one will be
    generated if not supplied)

    @rtype: Suite
    @return: Suite object for the requested suite name (None if not presenT)

    """
    if session is None:
        session = DBConn().session()
    q = session.query(Suite).filter_by(suite_name=suite)
    if q.count() == 0:
        return None
    return q.one()

601 602
__all__.append('get_suite')

M
Mark Hymers 已提交
603
class SuiteArchitecture(object):
M
Mark Hymers 已提交
604 605
    def __init__(self, *args, **kwargs):
        pass
M
Mark Hymers 已提交
606 607 608 609

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

610 611
__all__.append('SuiteArchitecture')

M
Mark Hymers 已提交
612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634
def get_suite_architectures(suite, session=None):
    """
    Returns list of Architecture objects for given C{suite} name

    @type source: str
    @param source: Suite name to search for

    @type session: Session
    @param session: Optional SQL session object (a temporary one will be
    generated if not supplied)

    @rtype: list
    @return: list of Architecture objects for the given name (may be empty)
    """

    if session is None:
        session = DBConn().session()

    q = session.query(Architecture)
    q = q.join(SuiteArchitecture)
    q = q.join(Suite).filter_by(suite_name=suite).order_by('arch_string')
    return q.all()

635
__all__.append('get_suite_architectures')
M
Mark Hymers 已提交
636

M
Mark Hymers 已提交
637
class Uid(object):
M
Mark Hymers 已提交
638 639
    def __init__(self, *args, **kwargs):
        pass
M
Mark Hymers 已提交
640 641 642 643

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

644 645
__all__.append('Uid')

M
Mark Hymers 已提交
646 647
################################################################################

M
Mark Hymers 已提交
648 649
class DBConn(Singleton):
    """
650
    database module init.
M
Mark Hymers 已提交
651 652 653 654 655
    """
    def __init__(self, *args, **kwargs):
        super(DBConn, self).__init__(*args, **kwargs)

    def _startup(self, *args, **kwargs):
M
updates  
Mark Hymers 已提交
656 657 658
        self.debug = False
        if kwargs.has_key('debug'):
            self.debug = True
M
Mark Hymers 已提交
659 660
        self.__createconn()

M
Mark Hymers 已提交
661 662
    def __setuptables(self):
        self.tbl_architecture = Table('architecture', self.db_meta, autoload=True)
M
Mark Hymers 已提交
663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691
        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 已提交
692 693 694 695 696 697 698 699 700 701
        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 已提交
702 703 704
                                 suite = relation(Suite),
                                 binary_id = self.tbl_bin_associations.c.bin,
                                 binary = relation(Binary)))
M
Mark Hymers 已提交
705 706 707

        mapper(Binary, self.tbl_binaries,
               properties = dict(binary_id = self.tbl_binaries.c.id,
M
Mark Hymers 已提交
708 709
                                 package = self.tbl_binaries.c.package,
                                 version = self.tbl_binaries.c.version,
M
Mark Hymers 已提交
710
                                 maintainer_id = self.tbl_binaries.c.maintainer,
M
Mark Hymers 已提交
711
                                 maintainer = relation(Maintainer),
M
Mark Hymers 已提交
712
                                 source_id = self.tbl_binaries.c.source,
M
Mark Hymers 已提交
713
                                 source = relation(Source),
M
Mark Hymers 已提交
714
                                 arch_id = self.tbl_binaries.c.architecture,
M
Mark Hymers 已提交
715 716 717 718 719 720 721 722 723
                                 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 已提交
724 725 726 727 728 729 730 731

        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))

732
        mapper(ContentAssociation, self.tbl_content_associations,
M
Mark Hymers 已提交
733 734
               properties = dict(ca_id = self.tbl_content_associations.c.id,
                                 filename_id = self.tbl_content_associations.c.filename,
M
Mark Hymers 已提交
735
                                 filename    = relation(ContentFilename),
M
Mark Hymers 已提交
736
                                 filepath_id = self.tbl_content_associations.c.filepath,
M
Mark Hymers 已提交
737 738 739 740
                                 filepath    = relation(ContentFilepath),
                                 binary_id   = self.tbl_content_associations.c.binary_pkg,
                                 binary      = relation(Binary)))

M
Mark Hymers 已提交
741 742 743 744 745 746 747 748 749 750 751 752

        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 已提交
753 754 755
                                 source = relation(Source),
                                 poolfile_id = self.tbl_dsc_files.c.file,
                                 poolfile = relation(PoolFile)))
M
Mark Hymers 已提交
756 757 758 759

        mapper(PoolFile, self.tbl_files,
               properties = dict(file_id = self.tbl_files.c.id,
                                 filesize = self.tbl_files.c.size,
M
Mark Hymers 已提交
760 761
                                 location_id = self.tbl_files.c.location,
                                 location = relation(Location)))
M
Mark Hymers 已提交
762 763 764 765

        mapper(Fingerprint, self.tbl_fingerprint,
               properties = dict(fingerprint_id = self.tbl_fingerprint.c.id,
                                 uid_id = self.tbl_fingerprint.c.uid,
M
Mark Hymers 已提交
766 767 768
                                 uid = relation(Uid),
                                 keyring_id = self.tbl_fingerprint.c.keyring,
                                 keyring = relation(Keyring)))
M
Mark Hymers 已提交
769 770 771 772 773 774 775 776

        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 已提交
777
                                 component = relation(Component),
M
Mark Hymers 已提交
778
                                 archive_id = self.tbl_location.c.archive,
M
Mark Hymers 已提交
779
                                 archive = relation(Archive),
M
Mark Hymers 已提交
780 781 782 783 784 785 786
                                 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 已提交
787
                                 suite = relation(Suite),
M
Mark Hymers 已提交
788
                                 component_id = self.tbl_override.c.component,
M
Mark Hymers 已提交
789
                                 component = relation(Component),
M
Mark Hymers 已提交
790
                                 priority_id = self.tbl_override.c.priority,
M
Mark Hymers 已提交
791
                                 priority = relation(Priority),
M
Mark Hymers 已提交
792
                                 section_id = self.tbl_override.c.section,
M
Mark Hymers 已提交
793 794 795
                                 section = relation(Section),
                                 overridetype_id = self.tbl_override.c.type,
                                 overridetype = relation(OverrideType)))
M
Mark Hymers 已提交
796 797 798 799 800 801 802 803

        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 已提交
804 805 806
                                 filepath = relation(ContentFilepath),
                                 filename_id = self.tbl_pending_content_associations.c.filename,
                                 filename = relation(ContentFilename)))
M
Mark Hymers 已提交
807 808 809 810 811 812 813 814 815

        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 已提交
816 817
                                 queue_id = self.tbl_queue_build.c.queue,
                                 queue = relation(Queue)))
M
Mark Hymers 已提交
818 819 820 821 822 823

        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 已提交
824
                                 version = self.tbl_source.c.version,
M
Mark Hymers 已提交
825
                                 maintainer_id = self.tbl_source.c.maintainer,
M
Mark Hymers 已提交
826 827 828 829
                                 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 已提交
830
                                 fingerprint_id = self.tbl_source.c.sig_fpr,
M
Mark Hymers 已提交
831 832 833 834 835 836 837 838
                                 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 已提交
839 840

        mapper(SrcAssociation, self.tbl_src_associations,
M
Mark Hymers 已提交
841 842 843 844 845
               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 已提交
846 847 848 849

        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 已提交
850 851 852 853 854
                                 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 已提交
855 856 857 858 859 860

        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 已提交
861 862 863
                                 suite = relation(Suite),
                                 arch_id = self.tbl_suite_architectures.c.architecture,
                                 architecture = relation(Architecture)))
M
Mark Hymers 已提交
864 865 866

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

M
Mark Hymers 已提交
868 869
    ## Connection functions
    def __createconn(self):
M
Mark Hymers 已提交
870
        from config import Config
871 872
        cnf = Config()
        if cnf["DB::Host"]:
M
Mark Hymers 已提交
873 874 875 876 877 878 879 880 881 882
            # 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"]
883

M
updates  
Mark Hymers 已提交
884
        self.db_pg   = create_engine(connstr, echo=self.debug)
M
Mark Hymers 已提交
885 886 887 888
        self.db_meta = MetaData()
        self.db_meta.bind = self.db_pg
        self.db_smaker = sessionmaker(bind=self.db_pg,
                                      autoflush=True,
889
                                      autocommit=False)
M
Mark Hymers 已提交
890

M
Mark Hymers 已提交
891
        self.__setuptables()
M
Mark Hymers 已提交
892
        self.__setupmappers()
M
Mark Hymers 已提交
893

M
Mark Hymers 已提交
894 895
    def session(self):
        return self.db_smaker()
M
Mark Hymers 已提交
896

M
Mark Hymers 已提交
897
__all__.append('DBConn')
M
Mike O'Connor 已提交
898

M
Mark Hymers 已提交
899 900

    def get_location_id(self, location, component, archive):
901 902 903 904 905 906 907 908 909 910
        """
        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

911 912
        @type component: int
        @param component: the id of the component
913

914 915
        @type archive: int
        @param archive: the id of the archive
916 917 918 919 920 921

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

        """

922
        archive_id = self.get_archive_id(archive)
M
Mark Hymers 已提交
923

924
        if not archive_id:
M
Mark Hymers 已提交
925
            return None
M
Mark Hymers 已提交
926

927
        res = None
928

929 930 931 932 933 934 935 936 937 938
        if component:
            component_id = self.get_component_id(component)
            if component_id:
                res = self.__get_single_id("SELECT id FROM location WHERE path=%(location)s AND component=%(component)s AND archive=%(archive)s",
                        {'location': location,
                         'archive': int(archive_id),
                         'component': component_id}, cachename='location')
        else:
            res = self.__get_single_id("SELECT id FROM location WHERE path=%(location)s AND archive=%(archive)d",
                    {'location': location, 'archive': archive_id, 'component': ''}, cachename='location')
M
Mark Hymers 已提交
939

940
        return res
M
Mark Hymers 已提交
941

M
Mark Hymers 已提交
942

943

944 945 946 947
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.
948

949 950 951 952 953
    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.
954

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

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

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

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

967 968 969 970 971 972
    @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
973

974 975 976
    """
    values = {'filename' : filename,
              'location' : location_id}
977

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

983 984
        cursor = self.db_con.cursor()
        cursor.execute( query, values )
985

986 987
        if cursor.rowcount == 0:
            res = None
988

989 990
        elif cursor.rowcount != 1:
            res = -1
991

992 993
        else:
            row = cursor.fetchone()
994

995 996
            if row[1] != int(size) or row[2] != md5sum:
                res =  -2
997 998

            else:
999
                res = row[0]
1000

1001
    return res
1002

1003

1004 1005 1006
def get_or_set_contents_file_id(self, filename):
    """
    Returns database id for given filename.
1007

1008
    If no matching file is found, a row is inserted.
1009

1010 1011
    @type filename: string
    @param filename: The filename
1012

1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033
    @rtype: int
    @return: the database id for the given component
    """
    try:
        values={'value': filename}
        query = "SELECT id FROM content_file_names WHERE file = %(value)s"
        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]

        return id
    except:
        traceback.print_exc()
        raise

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

1035
    If no matching file is found, a row is inserted.
1036

1037 1038
    @type path: string
    @param path: The filename
1039

1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057
    @rtype: int
    @return: the database id for the given component
    """
    try:
        values={'value': path}
        query = "SELECT id FROM content_file_paths WHERE path = %(value)s"
        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]

        return id
    except:
        traceback.print_exc()
        raise

1058

1059 1060 1061
def insert_content_paths(self, bin_id, fullpaths):
    """
    Make sure given path is associated with given binary id
1062

1063 1064 1065 1066
    @type bin_id: int
    @param bin_id: the id of the binary
    @type fullpaths: list
    @param fullpaths: the list of paths of the file being associated with the binary
1067

1068 1069
    @return: True upon success
    """
M
Mike O'Connor 已提交
1070

1071
    c = self.db_con.cursor()
M
Mike O'Connor 已提交
1072

1073 1074
    c.execute("BEGIN WORK")
    try:
M
Mike O'Connor 已提交
1075

1076 1077
        for fullpath in fullpaths:
            (path, file) = os.path.split(fullpath)
M
Mike O'Connor 已提交
1078

1079 1080 1081
            # Get the necessary IDs ...
            file_id = self.get_or_set_contents_file_id(file)
            path_id = self.get_or_set_contents_path_id(path)
M
Mike O'Connor 已提交
1082

1083 1084 1085
            c.execute("""INSERT INTO content_associations
                           (binary_pkg, filepath, filename)
                       VALUES ( '%d', '%d', '%d')""" % (bin_id, path_id, file_id) )
M
Mike O'Connor 已提交
1086

1087 1088 1089 1090 1091 1092
        c.execute("COMMIT")
        return True
    except:
        traceback.print_exc()
        c.execute("ROLLBACK")
        return False
M
Mike O'Connor 已提交
1093

1094 1095 1096 1097
def insert_pending_content_paths(self, package, fullpaths):
    """
    Make sure given paths are temporarily associated with given
    package
M
Mike O'Connor 已提交
1098

1099 1100 1101 1102
    @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
M
Mike O'Connor 已提交
1103

1104 1105
    @return: True upon success
    """
M
Mark Hymers 已提交
1106

1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140
    c = self.db_con.cursor()

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

        # Remove any already existing recorded files for this package
        c.execute("""DELETE FROM pending_content_associations
                     WHERE package=%(Package)s
                     AND version=%(Version)s
                     AND architecture=%(ArchID)s""", {'Package': package['Package'],
                                                      'Version': package['Version'],
                                                      'ArchID':  arch_id})

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

            if path.startswith( "./" ):
                path = path[2:]
            # 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 pending_content_associations
                           (package, version, architecture, filepath, filename)
                        VALUES (%%(Package)s, %%(Version)s, '%d', '%d', '%d')"""
                % (arch_id, path_id, file_id), package )

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