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

40 41
from inspect import getargspec

C
Chris Lamb 已提交
42
from sqlalchemy import create_engine, Table, MetaData
M
Mark Hymers 已提交
43
from sqlalchemy.orm import sessionmaker, mapper, relation
M
Mark Hymers 已提交
44

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

49 50 51
# Only import Config until Queue stuff is changed to store its config
# in the database
from config import Config
52
from singleton import Singleton
M
Mark Hymers 已提交
53
from textutils import fix_maintainer
M
Mark Hymers 已提交
54 55 56

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

M
Mark Hymers 已提交
57
__all__ = ['IntegrityError', 'SQLAlchemyError']
58 59 60

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

61
def session_wrapper(fn):
C
Chris Lamb 已提交
62 63 64 65
    """
    Wrapper around common ".., session=None):" handling. If the wrapped
    function is called without passing 'session', we create a local one
    and destroy it when the function ends.
66 67 68 69

    Also attaches a commit_or_flush method to the session; if we created a
    local session, this is a synonym for session.commit(), otherwise it is a
    synonym for session.flush().
C
Chris Lamb 已提交
70 71
    """

72 73 74
    def wrapped(*args, **kwargs):
        private_transaction = False

75
        # Find the session object
C
Chris Lamb 已提交
76 77 78
        session = kwargs.get('session')

        if session is None:
79 80 81 82 83 84 85
            if len(args) <= len(getargspec(fn)[0]) - 1:
                # No session specified as last argument or in kwargs
                private_transaction = True
                session = kwargs['session'] = DBConn().session()
            else:
                # Session is last argument in args
                session = args[-1]
86
                if session is None:
M
fixup  
Mark Hymers 已提交
87
                    args = list(args)
88 89
                    session = args[-1] = DBConn().session()
                    private_transaction = True
90 91 92 93 94

        if private_transaction:
            session.commit_or_flush = session.commit
        else:
            session.commit_or_flush = session.flush
95 96 97 98 99 100

        try:
            return fn(*args, **kwargs)
        finally:
            if private_transaction:
                # We created a session; close it.
101
                session.close()
102

103 104 105
    wrapped.__doc__ = fn.__doc__
    wrapped.func_name = fn.func_name

106 107 108 109
    return wrapped

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

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

114 115 116 117 118 119 120 121 122 123 124 125
    def __eq__(self, val):
        if isinstance(val, str):
            return (self.arch_string== val)
        # This signals to use the normal comparison operator
        return NotImplemented

    def __ne__(self, val):
        if isinstance(val, str):
            return (self.arch_string != val)
        # This signals to use the normal comparison operator
        return NotImplemented

M
Mark Hymers 已提交
126 127 128
    def __repr__(self):
        return '<Architecture %s>' % self.arch_string

129 130
__all__.append('Architecture')

131
@session_wrapper
132 133 134 135 136 137 138 139 140 141 142 143 144 145
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)
    """
146

147
    q = session.query(Architecture).filter_by(arch_string=architecture)
148

149 150 151 152
    try:
        return q.one()
    except NoResultFound:
        return None
153

154 155
__all__.append('get_architecture')

156
@session_wrapper
M
Mark Hymers 已提交
157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174
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)
    """

    q = session.query(Suite)
    q = q.join(SuiteArchitecture)
    q = q.join(Architecture).filter_by(arch_string=architecture).order_by('suite_name')
175 176 177 178

    ret = q.all()

    return ret
M
Mark Hymers 已提交
179

180 181
__all__.append('get_architecture_suites')

M
Mark Hymers 已提交
182 183
################################################################################

M
Mark Hymers 已提交
184
class Archive(object):
M
Mark Hymers 已提交
185 186
    def __init__(self, *args, **kwargs):
        pass
M
Mark Hymers 已提交
187 188

    def __repr__(self):
C
Chris Lamb 已提交
189
        return '<Archive %s>' % self.archive_name
M
Mark Hymers 已提交
190

191 192
__all__.append('Archive')

193
@session_wrapper
194 195
def get_archive(archive, session=None):
    """
F
Frank Lichtenheld 已提交
196
    returns database id for given C{archive}.
197 198 199 200 201 202 203 204 205 206 207 208 209

    @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()
210

211
    q = session.query(Archive).filter_by(archive_name=archive)
212

213 214 215 216
    try:
        return q.one()
    except NoResultFound:
        return None
217

218
__all__.append('get_archive')
219

M
Mark Hymers 已提交
220 221
################################################################################

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

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

229 230
__all__.append('BinAssociation')

M
Mark Hymers 已提交
231 232
################################################################################

233
class DBBinary(object):
M
Mark Hymers 已提交
234 235
    def __init__(self, *args, **kwargs):
        pass
M
Mark Hymers 已提交
236 237

    def __repr__(self):
238
        return '<DBBinary %s (%s, %s)>' % (self.package, self.version, self.architecture)
M
Mark Hymers 已提交
239

240
__all__.append('DBBinary')
241

242
@session_wrapper
243 244 245 246 247 248 249 250 251 252 253
def get_suites_binary_in(package, session=None):
    """
    Returns list of Suite objects which given C{package} name is in

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

    @rtype: list
    @return: list of Suite objects for the given package
    """

254
    return session.query(Suite).join(BinAssociation).join(DBBinary).filter_by(package=package).all()
255 256 257

__all__.append('get_suites_binary_in')

258
@session_wrapper
259 260
def get_binary_from_id(id, session=None):
    """
261
    Returns DBBinary object for given C{id}
262 263 264 265 266 267 268 269

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

270 271
    @rtype: DBBinary
    @return: DBBinary object for the given binary (None if not present)
272
    """
273

274
    q = session.query(DBBinary).filter_by(binary_id=id)
275

276 277 278 279
    try:
        return q.one()
    except NoResultFound:
        return None
M
Mark Hymers 已提交
280

281 282
__all__.append('get_binary_from_id')

283
@session_wrapper
284
def get_binaries_from_name(package, version=None, architecture=None, session=None):
M
Mark Hymers 已提交
285
    """
286
    Returns list of DBBinary objects for given C{package} name
M
Mark Hymers 已提交
287 288

    @type package: str
289
    @param package: DBBinary package name to search for
M
Mark Hymers 已提交
290

291 292 293 294 295 296
    @type version: str or None
    @param version: Version to search for (or None)

    @type package: str, list or None
    @param package: Architectures to limit to (or None if no limit)

M
Mark Hymers 已提交
297 298 299 300 301
    @type session: Session
    @param session: Optional SQL session object (a temporary one will be
    generated if not supplied)

    @rtype: list
302
    @return: list of DBBinary objects for the given name (may be empty)
M
Mark Hymers 已提交
303
    """
304 305 306 307 308 309 310 311 312 313 314

    q = session.query(DBBinary).filter_by(package=package)

    if version is not None:
        q = q.filter_by(version=version)

    if architecture is not None:
        if not isinstance(architecture, list):
            architecture = [architecture]
        q = q.join(Architecture).filter(Architecture.arch_string.in_(architecture))

315 316 317
    ret = q.all()

    return ret
M
Mark Hymers 已提交
318

319 320
__all__.append('get_binaries_from_name')

321
@session_wrapper
322 323 324 325 326 327 328 329 330 331 332 333 334 335
def get_binaries_from_source_id(source_id, session=None):
    """
    Returns list of DBBinary objects for given C{source_id}

    @type source_id: int
    @param source_id: source_id 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 DBBinary objects for the given name (may be empty)
    """
336

337
    return session.query(DBBinary).filter_by(source_id=source_id).all()
338 339 340

__all__.append('get_binaries_from_source_id')

341
@session_wrapper
M
Mark Hymers 已提交
342 343 344 345 346 347 348 349 350 351 352 353 354 355 356
def get_binary_from_name_suite(package, suitename, session=None):
    ### For dak examine-package
    ### XXX: Doesn't use object API yet

    sql = """SELECT DISTINCT(b.package), b.version, c.name, su.suite_name
             FROM binaries b, files fi, location l, component c, bin_associations ba, suite su
             WHERE b.package=:package
               AND b.file = fi.id
               AND fi.location = l.id
               AND l.component = c.id
               AND ba.bin=b.id
               AND ba.suite = su.id
               AND su.suite_name=:suitename
          ORDER BY b.version DESC"""

357
    return session.execute(sql, {'package': package, 'suitename': suitename})
M
Mark Hymers 已提交
358 359 360

__all__.append('get_binary_from_name_suite')

361
@session_wrapper
362
def get_binary_components(package, suitename, arch, session=None):
363
    # Check for packages that have moved from one component to another
364 365 366 367 368 369 370 371 372 373
    query = """SELECT c.name FROM binaries b, bin_associations ba, suite s, location l, component c, architecture a, files f
    WHERE b.package=:package AND s.suite_name=:suitename
      AND (a.arch_string = :arch OR a.arch_string = 'all')
      AND ba.bin = b.id AND ba.suite = s.id AND b.architecture = a.id
      AND f.location = l.id
      AND l.component = c.id
      AND b.file = f.id"""

    vals = {'package': package, 'suitename': suitename, 'arch': arch}

374
    return session.execute(query, vals)
375 376

__all__.append('get_binary_components')
377

M
Mark Hymers 已提交
378 379
################################################################################

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

384 385 386 387 388 389 390 391 392 393 394 395
    def __eq__(self, val):
        if isinstance(val, str):
            return (self.component_name == val)
        # This signals to use the normal comparison operator
        return NotImplemented

    def __ne__(self, val):
        if isinstance(val, str):
            return (self.component_name != val)
        # This signals to use the normal comparison operator
        return NotImplemented

M
Mark Hymers 已提交
396 397 398
    def __repr__(self):
        return '<Component %s>' % self.component_name

399 400 401

__all__.append('Component')

402
@session_wrapper
403 404 405 406 407 408 409 410 411 412 413 414
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()
415

416
    q = session.query(Component).filter_by(component_name=component)
417

418 419 420 421
    try:
        return q.one()
    except NoResultFound:
        return None
422

423 424
__all__.append('get_component')

M
Mark Hymers 已提交
425 426
################################################################################

M
Mark Hymers 已提交
427
class DBConfig(object):
M
Mark Hymers 已提交
428 429
    def __init__(self, *args, **kwargs):
        pass
M
Mark Hymers 已提交
430 431 432 433

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

434 435
__all__.append('DBConfig')

M
Mark Hymers 已提交
436 437
################################################################################

M
Mark Hymers 已提交
438
class ContentFilename(object):
M
Mark Hymers 已提交
439 440
    def __init__(self, *args, **kwargs):
        pass
M
Mark Hymers 已提交
441 442 443 444

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

445 446
__all__.append('ContentFilename')

447
@session_wrapper
448 449 450 451 452 453 454 455 456 457
def get_or_set_contents_file_id(filename, session=None):
    """
    Returns database id for given filename.

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

    @type filename: string
    @param filename: The filename
    @type session: SQLAlchemy
    @param session: Optional SQL session object (a temporary one will be
M
Mark Hymers 已提交
458 459
    generated if not supplied).  If not passed, a commit will be performed at
    the end of the function, otherwise the caller is responsible for commiting.
460 461 462 463 464

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

465
    q = session.query(ContentFilename).filter_by(filename=filename)
466 467 468 469

    try:
        ret = q.one().cafilename_id
    except NoResultFound:
470 471 472
        cf = ContentFilename()
        cf.filename = filename
        session.add(cf)
473
        session.commit_or_flush()
474
        ret = cf.cafilename_id
475

476
    return ret
477 478 479

__all__.append('get_or_set_contents_file_id')

480
@session_wrapper
M
Mark Hymers 已提交
481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526
def get_contents(suite, overridetype, section=None, session=None):
    """
    Returns contents for a suite / overridetype combination, limiting
    to a section if not None.

    @type suite: Suite
    @param suite: Suite object

    @type overridetype: OverrideType
    @param overridetype: OverrideType object

    @type section: Section
    @param section: Optional section object to limit results to

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

    @rtype: ResultsProxy
    @return: ResultsProxy object set up to return tuples of (filename, section,
    package, arch_id)
    """

    # find me all of the contents for a given suite
    contents_q = """SELECT (p.path||'/'||n.file) AS fn,
                            s.section,
                            b.package,
                            b.architecture
                   FROM content_associations c join content_file_paths p ON (c.filepath=p.id)
                   JOIN content_file_names n ON (c.filename=n.id)
                   JOIN binaries b ON (b.id=c.binary_pkg)
                   JOIN override o ON (o.package=b.package)
                   JOIN section s ON (s.id=o.section)
                   WHERE o.suite = :suiteid AND o.type = :overridetypeid
                   AND b.type=:overridetypename"""

    vals = {'suiteid': suite.suite_id,
            'overridetypeid': overridetype.overridetype_id,
            'overridetypename': overridetype.overridetype}

    if section is not None:
        contents_q += " AND s.id = :sectionid"
        vals['sectionid'] = section.section_id

    contents_q += " ORDER BY fn"

527
    return session.execute(contents_q, vals)
M
Mark Hymers 已提交
528 529 530

__all__.append('get_contents')

M
Mark Hymers 已提交
531 532
################################################################################

M
Mark Hymers 已提交
533
class ContentFilepath(object):
M
Mark Hymers 已提交
534 535
    def __init__(self, *args, **kwargs):
        pass
M
Mark Hymers 已提交
536 537 538 539

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

540 541
__all__.append('ContentFilepath')

542
@session_wrapper
M
Mark Hymers 已提交
543
def get_or_set_contents_path_id(filepath, session=None):
544 545 546 547 548 549 550 551 552
    """
    Returns database id for given path.

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

    @type filename: string
    @param filename: The filepath
    @type session: SQLAlchemy
    @param session: Optional SQL session object (a temporary one will be
M
Mark Hymers 已提交
553 554
    generated if not supplied).  If not passed, a commit will be performed at
    the end of the function, otherwise the caller is responsible for commiting.
555 556 557 558 559

    @rtype: int
    @return: the database id for the given path
    """

560
    q = session.query(ContentFilepath).filter_by(filepath=filepath)
561 562 563 564

    try:
        ret = q.one().cafilepath_id
    except NoResultFound:
565 566 567
        cf = ContentFilepath()
        cf.filepath = filepath
        session.add(cf)
568
        session.commit_or_flush()
569
        ret = cf.cafilepath_id
570

571
    return ret
572 573 574

__all__.append('get_or_set_contents_path_id')

M
Mark Hymers 已提交
575 576
################################################################################

577
class ContentAssociation(object):
M
Mark Hymers 已提交
578 579
    def __init__(self, *args, **kwargs):
        pass
M
Mark Hymers 已提交
580 581 582 583

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

584 585
__all__.append('ContentAssociation')

586 587 588 589 590 591 592 593 594 595 596 597
def insert_content_paths(binary_id, fullpaths, session=None):
    """
    Make sure given path is associated with given binary id

    @type binary_id: int
    @param binary_id: the id of the binary
    @type fullpaths: list
    @param fullpaths: the list of paths of the file being associated with the binary
    @type session: SQLAlchemy session
    @param session: Optional SQLAlchemy session.  If this is passed, the caller
    is responsible for ensuring a transaction has begun and committing the
    results or rolling back based on the result code.  If not passed, a commit
M
Mark Hymers 已提交
598 599
    will be performed at the end of the function, otherwise the caller is
    responsible for commiting.
600 601 602 603 604 605 606 607 608 609

    @return: True upon success
    """

    privatetrans = False
    if session is None:
        session = DBConn().session()
        privatetrans = True

    try:
M
Mark Hymers 已提交
610 611
        # Insert paths
        pathcache = {}
612
        for fullpath in fullpaths:
M
Mark Hymers 已提交
613
            # Get the necessary IDs ...
614 615
            (path, file) = os.path.split(fullpath)

M
Mark Hymers 已提交
616 617 618 619 620 621
            filepath_id = get_or_set_contents_path_id(path, session)
            filename_id = get_or_set_contents_file_id(file, session)

            pathcache[fullpath] = (filepath_id, filename_id)

        for fullpath, dat in pathcache.items():
622 623
            ca = ContentAssociation()
            ca.binary_id = binary_id
M
Mark Hymers 已提交
624 625
            ca.filepath_id = dat[0]
            ca.filename_id = dat[1]
626 627 628 629 630
            session.add(ca)

        # Only commit if we set up the session ourself
        if privatetrans:
            session.commit()
M
Mark Hymers 已提交
631 632 633
            session.close()
        else:
            session.flush()
634 635

        return True
M
Mark Hymers 已提交
636

637 638 639 640 641 642
    except:
        traceback.print_exc()

        # Only rollback if we set up the session ourself
        if privatetrans:
            session.rollback()
M
Mark Hymers 已提交
643
            session.close()
644 645 646 647 648

        return False

__all__.append('insert_content_paths')

M
Mark Hymers 已提交
649 650
################################################################################

M
Mark Hymers 已提交
651
class DSCFile(object):
M
Mark Hymers 已提交
652 653
    def __init__(self, *args, **kwargs):
        pass
M
Mark Hymers 已提交
654 655 656 657

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

658 659
__all__.append('DSCFile')

660
@session_wrapper
M
Mark Hymers 已提交
661 662 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
def get_dscfiles(dscfile_id=None, source_id=None, poolfile_id=None, session=None):
    """
    Returns a list of DSCFiles which may be empty

    @type dscfile_id: int (optional)
    @param dscfile_id: the dscfile_id of the DSCFiles to find

    @type source_id: int (optional)
    @param source_id: the source id related to the DSCFiles to find

    @type poolfile_id: int (optional)
    @param poolfile_id: the poolfile id related to the DSCFiles to find

    @rtype: list
    @return: Possibly empty list of DSCFiles
    """

    q = session.query(DSCFile)

    if dscfile_id is not None:
        q = q.filter_by(dscfile_id=dscfile_id)

    if source_id is not None:
        q = q.filter_by(source_id=source_id)

    if poolfile_id is not None:
        q = q.filter_by(poolfile_id=poolfile_id)

689
    return q.all()
M
Mark Hymers 已提交
690 691 692

__all__.append('get_dscfiles')

M
Mark Hymers 已提交
693 694
################################################################################

M
Mark Hymers 已提交
695
class PoolFile(object):
M
Mark Hymers 已提交
696 697
    def __init__(self, *args, **kwargs):
        pass
M
Mark Hymers 已提交
698 699 700 701

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

702 703
__all__.append('PoolFile')

704
@session_wrapper
705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734
def check_poolfile(filename, filesize, md5sum, location_id, session=None):
    """
    Returns a tuple:
     (ValidFileFound [boolean or None], PoolFile object or None)

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

    @type filesize: int
    @param filesize: 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 to look in

    @rtype: tuple
    @return: Tuple of length 2.
             If more than one file found with that name:
                    (None,  None)
             If valid pool file found: (True, PoolFile object)
             If valid pool file not found:
                    (False, None) if no file found
                    (False, PoolFile object) if file found with size/md5sum mismatch
    """

    q = session.query(PoolFile).filter_by(filename=filename)
    q = q.join(Location).filter_by(location_id=location_id)

735 736
    ret = None

737
    if q.count() > 1:
738 739 740 741 742 743 744
        ret = (None, None)
    elif q.count() < 1:
        ret = (False, None)
    else:
        obj = q.one()
        if obj.md5sum != md5sum or obj.filesize != filesize:
            ret = (False, obj)
745

746 747
    if ret is None:
        ret = (True, obj)
748

749
    return ret
750 751 752

__all__.append('check_poolfile')

753
@session_wrapper
M
Mark Hymers 已提交
754 755 756 757 758 759 760 761 762 763 764 765 766
def get_poolfile_by_id(file_id, session=None):
    """
    Returns a PoolFile objects or None for the given id

    @type file_id: int
    @param file_id: the id of the file to look for

    @rtype: PoolFile or None
    @return: either the PoolFile object or None
    """

    q = session.query(PoolFile).filter_by(file_id=file_id)

767 768 769 770
    try:
        return q.one()
    except NoResultFound:
        return None
M
Mark Hymers 已提交
771 772 773

__all__.append('get_poolfile_by_id')

774

775
@session_wrapper
776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795
def get_poolfile_by_name(filename, location_id=None, session=None):
    """
    Returns an array of PoolFile objects for the given filename and
    (optionally) location_id

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

    @type location_id: int
    @param location_id: the id of the location to look in (optional)

    @rtype: array
    @return: array of PoolFile objects
    """

    q = session.query(PoolFile).filter_by(filename=filename)

    if location_id is not None:
        q = q.join(Location).filter_by(location_id=location_id)

796
    return q.all()
797 798 799

__all__.append('get_poolfile_by_name')

800
@session_wrapper
801 802 803 804 805 806 807 808 809 810 811 812 813 814
def get_poolfile_like_name(filename, session=None):
    """
    Returns an array of PoolFile objects which are like the given name

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

    @rtype: array
    @return: array of PoolFile objects
    """

    # TODO: There must be a way of properly using bind parameters with %FOO%
    q = session.query(PoolFile).filter(PoolFile.filename.like('%%%s%%' % filename))

815
    return q.all()
816 817 818

__all__.append('get_poolfile_like_name')

M
Mark Hymers 已提交
819 820
################################################################################

M
Mark Hymers 已提交
821
class Fingerprint(object):
M
Mark Hymers 已提交
822 823
    def __init__(self, *args, **kwargs):
        pass
M
Mark Hymers 已提交
824 825 826 827

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

828 829
__all__.append('Fingerprint')

830
@session_wrapper
M
Mark Hymers 已提交
831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849
def get_or_set_fingerprint(fpr, session=None):
    """
    Returns Fingerprint object for given fpr.

    If no matching fpr is found, a row is inserted.

    @type fpr: string
    @param fpr: The fpr to find / add

    @type session: SQLAlchemy
    @param session: Optional SQL session object (a temporary one will be
    generated if not supplied).  If not passed, a commit will be performed at
    the end of the function, otherwise the caller is responsible for commiting.
    A flush will be performed either way.

    @rtype: Fingerprint
    @return: the Fingerprint object for the given fpr
    """

850
    q = session.query(Fingerprint).filter_by(fingerprint=fpr)
851 852 853 854

    try:
        ret = q.one()
    except NoResultFound:
855 856 857
        fingerprint = Fingerprint()
        fingerprint.fingerprint = fpr
        session.add(fingerprint)
858
        session.commit_or_flush()
859
        ret = fingerprint
M
Mark Hymers 已提交
860

861
    return ret
M
Mark Hymers 已提交
862 863 864

__all__.append('get_or_set_fingerprint')

M
Mark Hymers 已提交
865 866
################################################################################

M
Mark Hymers 已提交
867
class Keyring(object):
M
Mark Hymers 已提交
868 869
    def __init__(self, *args, **kwargs):
        pass
M
Mark Hymers 已提交
870 871 872 873

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

874 875
__all__.append('Keyring')

876
@session_wrapper
877 878 879 880 881 882 883 884 885 886 887 888 889
def get_or_set_keyring(keyring, session=None):
    """
    If C{keyring} does not have an entry in the C{keyrings} table yet, create one
    and return the new Keyring
    If C{keyring} already has an entry, simply return the existing Keyring

    @type keyring: string
    @param keyring: the keyring name

    @rtype: Keyring
    @return: the Keyring object for this keyring
    """

890
    q = session.query(Keyring).filter_by(keyring_name=keyring)
891

892 893 894 895 896 897
    try:
        return q.one()
    except NoResultFound:
        obj = Keyring(keyring_name=keyring)
        session.add(obj)
        session.commit_or_flush()
898 899 900 901
        return obj

__all__.append('get_or_set_keyring')

M
Mark Hymers 已提交
902 903
################################################################################

M
Mark Hymers 已提交
904
class Location(object):
M
Mark Hymers 已提交
905 906
    def __init__(self, *args, **kwargs):
        pass
M
Mark Hymers 已提交
907 908 909 910

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

911 912
__all__.append('Location')

913
@session_wrapper
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
def get_location(location, component=None, archive=None, session=None):
    """
    Returns Location object for the given combination of location, component
    and archive

    @type location: string
    @param location: the path of the location, e.g. I{/srv/ftp.debian.org/ftp/pool/}

    @type component: string
    @param component: the component name (if None, no restriction applied)

    @type archive: string
    @param archive_id: the archive name (if None, no restriction applied)

    @rtype: Location / None
    @return: Either a Location object or None if one can't be found
    """

    q = session.query(Location).filter_by(path=location)

    if archive is not None:
        q = q.join(Archive).filter_by(archive_name=archive)

    if component is not None:
        q = q.join(Component).filter_by(component_name=component)

940 941 942 943
    try:
        return q.one()
    except NoResultFound:
        return None
944 945 946

__all__.append('get_location')

M
Mark Hymers 已提交
947 948
################################################################################

M
Mark Hymers 已提交
949
class Maintainer(object):
M
Mark Hymers 已提交
950 951
    def __init__(self, *args, **kwargs):
        pass
M
Mark Hymers 已提交
952 953 954 955

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

M
Mark Hymers 已提交
956 957 958 959 960 961
    def get_split_maintainer(self):
        if not hasattr(self, 'name') or self.name is None:
            return ('', '', '', '')

        return fix_maintainer(self.name.strip())

962 963
__all__.append('Maintainer')

964
@session_wrapper
M
Mark Hymers 已提交
965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983
def get_or_set_maintainer(name, session=None):
    """
    Returns Maintainer object for given maintainer name.

    If no matching maintainer name is found, a row is inserted.

    @type name: string
    @param name: The maintainer name to add

    @type session: SQLAlchemy
    @param session: Optional SQL session object (a temporary one will be
    generated if not supplied).  If not passed, a commit will be performed at
    the end of the function, otherwise the caller is responsible for commiting.
    A flush will be performed either way.

    @rtype: Maintainer
    @return: the Maintainer object for the given maintainer
    """

984
    q = session.query(Maintainer).filter_by(name=name)
985 986 987
    try:
        ret = q.one()
    except NoResultFound:
988 989 990
        maintainer = Maintainer()
        maintainer.name = name
        session.add(maintainer)
991
        session.commit_or_flush()
992
        ret = maintainer
M
Mark Hymers 已提交
993

994
    return ret
M
Mark Hymers 已提交
995 996 997

__all__.append('get_or_set_maintainer')

998
@session_wrapper
C
Chris Lamb 已提交
999
def get_maintainer(maintainer_id, session=None):
C
Chris Lamb 已提交
1000
    """
1001 1002
    Return the name of the maintainer behind C{maintainer_id} or None if that
    maintainer_id is invalid.
C
Chris Lamb 已提交
1003 1004 1005 1006

    @type maintainer_id: int
    @param maintainer_id: the id of the maintainer

1007 1008
    @rtype: Maintainer
    @return: the Maintainer with this C{maintainer_id}
C
Chris Lamb 已提交
1009 1010
    """

1011
    return session.query(Maintainer).get(maintainer_id)
C
Chris Lamb 已提交
1012 1013 1014

__all__.append('get_maintainer')

M
Mark Hymers 已提交
1015 1016
################################################################################

M
Mark Hymers 已提交
1017 1018 1019 1020 1021 1022 1023 1024 1025
class NewComment(object):
    def __init__(self, *args, **kwargs):
        pass

    def __repr__(self):
        return '''<NewComment for '%s %s' (%s)>''' % (self.package, self.version, self.comment_id)

__all__.append('NewComment')

1026
@session_wrapper
M
Mark Hymers 已提交
1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047
def has_new_comment(package, version, session=None):
    """
    Returns true if the given combination of C{package}, C{version} has a comment.

    @type package: string
    @param package: name of the package

    @type version: string
    @param version: package version

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

    @rtype: boolean
    @return: true/false
    """

    q = session.query(NewComment)
    q = q.filter_by(package=package)
    q = q.filter_by(version=version)
1048

1049
    return bool(q.count() > 0)
M
Mark Hymers 已提交
1050 1051 1052

__all__.append('has_new_comment')

1053
@session_wrapper
M
Mark Hymers 已提交
1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080
def get_new_comments(package=None, version=None, comment_id=None, session=None):
    """
    Returns (possibly empty) list of NewComment objects for the given
    parameters

    @type package: string (optional)
    @param package: name of the package

    @type version: string (optional)
    @param version: package version

    @type comment_id: int (optional)
    @param comment_id: An id of a comment

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

    @rtype: list
    @return: A (possibly empty) list of NewComment objects will be returned
    """

    q = session.query(NewComment)
    if package is not None: q = q.filter_by(package=package)
    if version is not None: q = q.filter_by(version=version)
    if comment_id is not None: q = q.filter_by(comment_id=comment_id)

1081
    return q.all()
M
Mark Hymers 已提交
1082 1083 1084 1085 1086

__all__.append('get_new_comments')

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

M
Mark Hymers 已提交
1087
class Override(object):
M
Mark Hymers 已提交
1088 1089
    def __init__(self, *args, **kwargs):
        pass
M
Mark Hymers 已提交
1090 1091 1092 1093

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

1094 1095
__all__.append('Override')

1096
@session_wrapper
1097 1098 1099 1100 1101 1102 1103 1104 1105 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
def get_override(package, suite=None, component=None, overridetype=None, session=None):
    """
    Returns Override object for the given parameters

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

    @type suite: string, list or None
    @param suite: The name of the suite (or suites if a list) to limit to.  If
                  None, don't limit.  Defaults to None.

    @type component: string, list or None
    @param component: The name of the component (or components if a list) to
                      limit to.  If None, don't limit.  Defaults to None.

    @type overridetype: string, list or None
    @param overridetype: The name of the overridetype (or overridetypes if a list) to
                         limit to.  If None, don't limit.  Defaults to None.

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

    @rtype: list
    @return: A (possibly empty) list of Override objects will be returned
    """

    q = session.query(Override)
    q = q.filter_by(package=package)

    if suite is not None:
        if not isinstance(suite, list): suite = [suite]
        q = q.join(Suite).filter(Suite.suite_name.in_(suite))

    if component is not None:
        if not isinstance(component, list): component = [component]
        q = q.join(Component).filter(Component.component_name.in_(component))

    if overridetype is not None:
        if not isinstance(overridetype, list): overridetype = [overridetype]
        q = q.join(OverrideType).filter(OverrideType.overridetype.in_(overridetype))

1139
    return q.all()
1140 1141 1142 1143

__all__.append('get_override')


M
Mark Hymers 已提交
1144 1145
################################################################################

M
Mark Hymers 已提交
1146
class OverrideType(object):
M
Mark Hymers 已提交
1147 1148
    def __init__(self, *args, **kwargs):
        pass
M
Mark Hymers 已提交
1149 1150 1151 1152

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

1153 1154
__all__.append('OverrideType')

1155
@session_wrapper
1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169
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
    """
1170

M
Mark Hymers 已提交
1171
    q = session.query(OverrideType).filter_by(overridetype=override_type)
1172

1173 1174 1175 1176
    try:
        return q.one()
    except NoResultFound:
        return None
1177

1178 1179
__all__.append('get_override_type')

M
Mark Hymers 已提交
1180 1181
################################################################################

M
Mark Hymers 已提交
1182
class PendingContentAssociation(object):
M
Mark Hymers 已提交
1183 1184
    def __init__(self, *args, **kwargs):
        pass
M
Mark Hymers 已提交
1185 1186 1187 1188

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

1189 1190
__all__.append('PendingContentAssociation')

1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226
def insert_pending_content_paths(package, fullpaths, session=None):
    """
    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
    @type session: SQLAlchemy session
    @param session: Optional SQLAlchemy session.  If this is passed, the caller
    is responsible for ensuring a transaction has begun and committing the
    results or rolling back based on the result code.  If not passed, a commit
    will be performed at the end of the function

    @return: True upon success, False if there is a problem
    """

    privatetrans = False

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

    try:
        arch = get_architecture(package['Architecture'], session)
        arch_id = arch.arch_id

        # Remove any already existing recorded files for this package
        q = session.query(PendingContentAssociation)
        q = q.filter_by(package=package['Package'])
        q = q.filter_by(version=package['Version'])
        q = q.filter_by(architecture=arch_id)
        q.delete()

        # Insert paths
M
Mark Hymers 已提交
1227
        pathcache = {}
1228 1229 1230 1231 1232 1233
        for fullpath in fullpaths:
            (path, file) = os.path.split(fullpath)

            if path.startswith( "./" ):
                path = path[2:]

M
Mark Hymers 已提交
1234 1235 1236 1237 1238 1239
            filepath_id = get_or_set_contents_path_id(path, session)
            filename_id = get_or_set_contents_file_id(file, session)

            pathcache[fullpath] = (filepath_id, filename_id)

        for fullpath, dat in pathcache.items():
1240 1241 1242
            pca = PendingContentAssociation()
            pca.package = package['Package']
            pca.version = package['Version']
M
Mark Hymers 已提交
1243 1244
            pca.filepath_id = dat[0]
            pca.filename_id = dat[1]
1245 1246 1247 1248 1249 1250
            pca.architecture = arch_id
            session.add(pca)

        # Only commit if we set up the session ourself
        if privatetrans:
            session.commit()
1251
            session.close()
M
Mark Hymers 已提交
1252 1253
        else:
            session.flush()
1254 1255

        return True
1256
    except Exception, e:
1257 1258 1259 1260 1261
        traceback.print_exc()

        # Only rollback if we set up the session ourself
        if privatetrans:
            session.rollback()
1262
            session.close()
1263 1264 1265 1266 1267

        return False

__all__.append('insert_pending_content_paths')

M
Mark Hymers 已提交
1268 1269
################################################################################

M
Mark Hymers 已提交
1270
class Priority(object):
M
Mark Hymers 已提交
1271 1272
    def __init__(self, *args, **kwargs):
        pass
M
Mark Hymers 已提交
1273

1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285
    def __eq__(self, val):
        if isinstance(val, str):
            return (self.priority == val)
        # This signals to use the normal comparison operator
        return NotImplemented

    def __ne__(self, val):
        if isinstance(val, str):
            return (self.priority != val)
        # This signals to use the normal comparison operator
        return NotImplemented

M
Mark Hymers 已提交
1286 1287 1288
    def __repr__(self):
        return '<Priority %s (%s)>' % (self.priority, self.priority_id)

1289 1290
__all__.append('Priority')

1291
@session_wrapper
1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305
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
    """
1306

1307
    q = session.query(Priority).filter_by(priority=priority)
1308

1309 1310 1311 1312
    try:
        return q.one()
    except NoResultFound:
        return None
1313

1314 1315
__all__.append('get_priority')

1316
@session_wrapper
1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337
def get_priorities(session=None):
    """
    Returns dictionary of priority names -> id mappings

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

    @rtype: dictionary
    @return: dictionary of priority names -> id mappings
    """

    ret = {}
    q = session.query(Priority)
    for x in q.all():
        ret[x.priority] = x.priority_id

    return ret

__all__.append('get_priorities')

M
Mark Hymers 已提交
1338 1339
################################################################################

M
Mark Hymers 已提交
1340
class Queue(object):
M
Mark Hymers 已提交
1341 1342
    def __init__(self, *args, **kwargs):
        pass
M
Mark Hymers 已提交
1343 1344 1345 1346

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

1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367
    def autobuild_upload(self, changes, srcpath, session=None):
        """
        Update queue_build database table used for incoming autobuild support.

        @type changes: Changes
        @param changes: changes object for the upload to process

        @type srcpath: string
        @param srcpath: path for the queue file entries/link destinations

        @type session: SQLAlchemy session
        @param session: Optional SQLAlchemy session.  If this is passed, the
        caller is responsible for ensuring a transaction has begun and
        committing the results or rolling back based on the result code.  If
        not passed, a commit will be performed at the end of the function,
        otherwise the caller is responsible for commiting.

        @rtype: NoneType or string
        @return: None if the operation failed, a string describing the error if not
        """

1368
        privatetrans = False
1369 1370
        if session is None:
            session = DBConn().session()
1371
            privatetrans = True
1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400

        # TODO: Remove by moving queue config into the database
        conf = Config()

        for suitename in changes.changes["distribution"].keys():
            # TODO: Move into database as:
            #       buildqueuedir TEXT DEFAULT NULL (i.e. NULL is no build)
            #       buildqueuecopy BOOLEAN NOT NULL DEFAULT FALSE (i.e. default is symlink)
            #       This also gets rid of the SecurityQueueBuild hack below
            if suitename not in conf.ValueList("Dinstall::QueueBuildSuites"):
                continue

            # Find suite object
            s = get_suite(suitename, session)
            if s is None:
                return "INTERNAL ERROR: Could not find suite %s" % suitename

            # TODO: Get from database as above
            dest_dir = conf["Dir::QueueBuild"]

            # TODO: Move into database as above
            if conf.FindB("Dinstall::SecurityQueueBuild"):
                dest_dir = os.path.join(dest_dir, suitename)

            for file_entry in changes.files.keys():
                src = os.path.join(srcpath, file_entry)
                dest = os.path.join(dest_dir, file_entry)

                # TODO: Move into database as above
M
Mark Hymers 已提交
1401
                if conf.FindB("Dinstall::SecurityQueueBuild"):
1402
                    # Copy it since the original won't be readable by www-data
1403
                    import utils
1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416
                    utils.copy(src, dest)
                else:
                    # Create a symlink to it
                    os.symlink(src, dest)

                qb = QueueBuild()
                qb.suite_id = s.suite_id
                qb.queue_id = self.queue_id
                qb.filename = dest
                qb.in_queue = True

                session.add(qb)

1417
            exists, symlinked = utils.ensure_orig_files(changes, dest, session)
1418

1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432
            # Add symlinked files to the list of packages for later processing
            # by apt-ftparchive
            for filename in symlinked:
                qb = QueueBuild()
                qb.suite_id = s.suite_id
                qb.queue_id = self.queue_id
                qb.filename = filename
                qb.in_queue = True
                session.add(qb)

            # Update files to ensure they are not removed prematurely
            for filename in exists:
                qb = get_queue_build(filename, s.suite_id, session)
                if qb is None:
1433
                    qb.in_queue = True
1434
                    qb.last_used = None
1435 1436
                    session.add(qb)

1437
        if privatetrans:
1438
            session.commit()
1439
            session.close()
1440 1441 1442

        return None

1443 1444
__all__.append('Queue')

1445
@session_wrapper
C
Chris Lamb 已提交
1446
def get_or_set_queue(queuename, session=None):
1447
    """
C
Chris Lamb 已提交
1448 1449
    Returns Queue object for given C{queue name}, creating it if it does not
    exist.
1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 1460

    @type queuename: string
    @param queuename: The name of the queue

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

    @rtype: Queue
    @return: Queue object for the given queue
    """
1461

1462
    q = session.query(Queue).filter_by(queue_name=queuename)
1463

1464
    try:
C
Chris Lamb 已提交
1465
        ret = q.one()
1466
    except NoResultFound:
C
Chris Lamb 已提交
1467 1468 1469 1470 1471 1472 1473
        queue = Queue()
        queue.queue_name = queuename
        session.add(queue)
        session.commit_or_flush()
        ret = queue

    return ret
1474

C
Chris Lamb 已提交
1475
__all__.append('get_or_set_queue')
1476

M
Mark Hymers 已提交
1477 1478
################################################################################

M
Mark Hymers 已提交
1479
class QueueBuild(object):
M
Mark Hymers 已提交
1480 1481
    def __init__(self, *args, **kwargs):
        pass
M
Mark Hymers 已提交
1482 1483 1484 1485

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

1486 1487
__all__.append('QueueBuild')

1488
@session_wrapper
1489
def get_queue_build(filename, suite, session=None):
1490
    """
1491
    Returns QueueBuild object for given C{filename} and C{suite}.
1492 1493 1494 1495

    @type filename: string
    @param filename: The name of the file

1496 1497
    @type suiteid: int or str
    @param suiteid: Suite name or ID
1498 1499 1500 1501 1502 1503 1504 1505

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

    @rtype: Queue
    @return: Queue object for the given queue
    """
1506

1507 1508 1509 1510 1511 1512
    if isinstance(suite, int):
        q = session.query(QueueBuild).filter_by(filename=filename).filter_by(suite_id=suite)
    else:
        q = session.query(QueueBuild).filter_by(filename=filename)
        q = q.join(Suite).filter_by(suite_name=suite)

1513 1514 1515 1516
    try:
        return q.one()
    except NoResultFound:
        return None
1517 1518 1519

__all__.append('get_queue_build')

M
Mark Hymers 已提交
1520 1521
################################################################################

M
Mark Hymers 已提交
1522
class Section(object):
M
Mark Hymers 已提交
1523 1524
    def __init__(self, *args, **kwargs):
        pass
M
Mark Hymers 已提交
1525

1526 1527 1528 1529 1530 1531 1532 1533 1534 1535 1536 1537
    def __eq__(self, val):
        if isinstance(val, str):
            return (self.section == val)
        # This signals to use the normal comparison operator
        return NotImplemented

    def __ne__(self, val):
        if isinstance(val, str):
            return (self.section != val)
        # This signals to use the normal comparison operator
        return NotImplemented

M
Mark Hymers 已提交
1538 1539 1540
    def __repr__(self):
        return '<Section %s>' % self.section

1541 1542
__all__.append('Section')

1543
@session_wrapper
1544 1545 1546 1547 1548 1549 1550 1551 1552 1553 1554 1555 1556 1557
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
    """
1558

1559
    q = session.query(Section).filter_by(section=section)
1560

1561 1562 1563 1564
    try:
        return q.one()
    except NoResultFound:
        return None
1565

1566 1567
__all__.append('get_section')

1568
@session_wrapper
1569 1570 1571 1572 1573 1574 1575 1576 1577 1578 1579 1580 1581 1582 1583 1584 1585 1586 1587 1588 1589
def get_sections(session=None):
    """
    Returns dictionary of section names -> id mappings

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

    @rtype: dictionary
    @return: dictionary of section names -> id mappings
    """

    ret = {}
    q = session.query(Section)
    for x in q.all():
        ret[x.section] = x.section_id

    return ret

__all__.append('get_sections')

M
Mark Hymers 已提交
1590 1591
################################################################################

1592
class DBSource(object):
M
Mark Hymers 已提交
1593 1594
    def __init__(self, *args, **kwargs):
        pass
M
Mark Hymers 已提交
1595 1596

    def __repr__(self):
1597
        return '<DBSource %s (%s)>' % (self.source, self.version)
M
Mark Hymers 已提交
1598

1599
__all__.append('DBSource')
1600

1601
@session_wrapper
1602 1603 1604 1605 1606 1607 1608 1609 1610 1611 1612 1613 1614 1615 1616 1617 1618 1619 1620 1621 1622 1623 1624 1625 1626 1627
def source_exists(source, source_version, suites = ["any"], session=None):
    """
    Ensure that source exists somewhere in the archive for the binary
    upload being processed.
      1. exact match     => 1.0-3
      2. bin-only NMU    => 1.0-3+b1 , 1.0-3.1+b1

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

    @type source_version: string
    @param source_version: expected source version

    @type suites: list
    @param suites: list of suites to check in, default I{any}

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

    @rtype: int
    @return: returns 1 if a source with expected version is found, otherwise 0

    """

    cnf = Config()
1628
    ret = 1
1629 1630 1631 1632 1633 1634 1635 1636 1637 1638 1639 1640 1641 1642 1643 1644 1645 1646 1647 1648 1649 1650 1651 1652 1653 1654 1655 1656 1657 1658 1659 1660 1661 1662

    for suite in suites:
        q = session.query(DBSource).filter_by(source=source)
        if suite != "any":
            # source must exist in suite X, or in some other suite that's
            # mapped to X, recursively... silent-maps are counted too,
            # unreleased-maps aren't.
            maps = cnf.ValueList("SuiteMappings")[:]
            maps.reverse()
            maps = [ m.split() for m in maps ]
            maps = [ (x[1], x[2]) for x in maps
                            if x[0] == "map" or x[0] == "silent-map" ]
            s = [suite]
            for x in maps:
                if x[1] in s and x[0] not in s:
                    s.append(x[0])

            q = q.join(SrcAssociation).join(Suite)
            q = q.filter(Suite.suite_name.in_(s))

        # Reduce the query results to a list of version numbers
        ql = [ j.version for j in q.all() ]

        # Try (1)
        if source_version in ql:
            continue

        # Try (2)
        from daklib.regexes import re_bin_only_nmu
        orig_source_version = re_bin_only_nmu.sub('', source_version)
        if orig_source_version in ql:
            continue

        # No source found so return not ok
1663 1664 1665
        ret = 0

    return ret
1666 1667 1668

__all__.append('source_exists')

1669
@session_wrapper
1670 1671 1672 1673 1674 1675 1676 1677 1678 1679 1680
def get_suites_source_in(source, session=None):
    """
    Returns list of Suite objects which given C{source} name is in

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

    @rtype: list
    @return: list of Suite objects for the given source
    """

1681
    return session.query(Suite).join(SrcAssociation).join(DBSource).filter_by(source=source).all()
1682 1683 1684

__all__.append('get_suites_source_in')

1685
@session_wrapper
1686
def get_sources_from_name(source, version=None, dm_upload_allowed=None, session=None):
M
Mark Hymers 已提交
1687
    """
1688
    Returns list of DBSource objects for given C{source} name and other parameters
M
Mark Hymers 已提交
1689 1690

    @type source: str
1691
    @param source: DBSource package name to search for
M
Mark Hymers 已提交
1692

1693 1694 1695
    @type source: str or None
    @param source: DBSource version name to search for or None if not applicable

1696 1697 1698 1699
    @type dm_upload_allowed: bool
    @param dm_upload_allowed: If None, no effect.  If True or False, only
    return packages with that dm_upload_allowed setting

M
Mark Hymers 已提交
1700 1701 1702 1703 1704
    @type session: Session
    @param session: Optional SQL session object (a temporary one will be
    generated if not supplied)

    @rtype: list
1705
    @return: list of DBSource objects for the given name (may be empty)
M
Mark Hymers 已提交
1706
    """
1707 1708

    q = session.query(DBSource).filter_by(source=source)
1709 1710 1711 1712

    if version is not None:
        q = q.filter_by(version=version)

1713 1714 1715
    if dm_upload_allowed is not None:
        q = q.filter_by(dm_upload_allowed=dm_upload_allowed)

1716
    return q.all()
M
Mark Hymers 已提交
1717

1718 1719
__all__.append('get_sources_from_name')

1720
@session_wrapper
1721 1722
def get_source_in_suite(source, suite, session=None):
    """
1723
    Returns list of DBSource objects for a combination of C{source} and C{suite}.
1724 1725 1726 1727 1728 1729 1730 1731 1732 1733 1734 1735 1736 1737

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

    """
1738

M
updates  
Mark Hymers 已提交
1739 1740 1741
    q = session.query(SrcAssociation)
    q = q.join('source').filter_by(source=source)
    q = q.join('suite').filter_by(suite_name=suite)
1742

1743 1744 1745 1746
    try:
        return q.one().source
    except NoResultFound:
        return None
1747

1748 1749
__all__.append('get_source_in_suite')

M
Mark Hymers 已提交
1750 1751
################################################################################

M
Mark Hymers 已提交
1752
class SrcAssociation(object):
M
Mark Hymers 已提交
1753 1754
    def __init__(self, *args, **kwargs):
        pass
M
Mark Hymers 已提交
1755 1756

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

1759 1760
__all__.append('SrcAssociation')

M
Mark Hymers 已提交
1761 1762
################################################################################

1763 1764 1765 1766 1767 1768 1769 1770 1771 1772 1773
class SrcFormat(object):
    def __init__(self, *args, **kwargs):
        pass

    def __repr__(self):
        return '<SrcFormat %s>' % (self.format_name)

__all__.append('SrcFormat')

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

M
Mark Hymers 已提交
1774
class SrcUploader(object):
M
Mark Hymers 已提交
1775 1776
    def __init__(self, *args, **kwargs):
        pass
M
Mark Hymers 已提交
1777 1778 1779 1780

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

1781 1782
__all__.append('SrcUploader')

M
Mark Hymers 已提交
1783 1784
################################################################################

M
Mark Hymers 已提交
1785 1786 1787 1788 1789 1790 1791 1792 1793 1794 1795 1796 1797 1798 1799 1800 1801 1802 1803 1804
SUITE_FIELDS = [ ('SuiteName', 'suite_name'),
                 ('SuiteID', 'suite_id'),
                 ('Version', 'version'),
                 ('Origin', 'origin'),
                 ('Label', 'label'),
                 ('Description', 'description'),
                 ('Untouchable', 'untouchable'),
                 ('Announce', 'announce'),
                 ('Codename', 'codename'),
                 ('OverrideCodename', 'overridecodename'),
                 ('ValidTime', 'validtime'),
                 ('Priority', 'priority'),
                 ('NotAutomatic', 'notautomatic'),
                 ('CopyChanges', 'copychanges'),
                 ('CopyDotDak', 'copydotdak'),
                 ('CommentsDir', 'commentsdir'),
                 ('OverrideSuite', 'overridesuite'),
                 ('ChangelogBase', 'changelogbase')]


M
Mark Hymers 已提交
1805
class Suite(object):
M
Mark Hymers 已提交
1806 1807
    def __init__(self, *args, **kwargs):
        pass
M
Mark Hymers 已提交
1808 1809 1810 1811

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

1812 1813 1814 1815 1816 1817 1818 1819 1820 1821 1822 1823
    def __eq__(self, val):
        if isinstance(val, str):
            return (self.suite_name == val)
        # This signals to use the normal comparison operator
        return NotImplemented

    def __ne__(self, val):
        if isinstance(val, str):
            return (self.suite_name != val)
        # This signals to use the normal comparison operator
        return NotImplemented

M
Mark Hymers 已提交
1824 1825 1826 1827 1828 1829 1830 1831 1832
    def details(self):
        ret = []
        for disp, field in SUITE_FIELDS:
            val = getattr(self, field, None)
            if val is not None:
                ret.append("%s: %s" % (disp, val))

        return "\n".join(ret)

1833 1834
__all__.append('Suite')

1835
@session_wrapper
M
Mark Hymers 已提交
1836 1837 1838 1839 1840 1841 1842 1843 1844 1845 1846 1847 1848 1849 1850 1851 1852 1853 1854 1855 1856 1857
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
    """

    q = session.query(SuiteArchitecture)
    q = q.join(Architecture).filter_by(arch_string=architecture)
    q = q.join(Suite).filter_by(suite_name=suite)
1858

1859 1860 1861 1862
    try:
        return q.one()
    except NoResultFound:
        return None
M
Mark Hymers 已提交
1863

1864
__all__.append('get_suite_architecture')
M
Mark Hymers 已提交
1865

1866
@session_wrapper
1867 1868 1869 1870 1871 1872 1873 1874 1875 1876 1877 1878
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
F
Frank Lichtenheld 已提交
1879
    @return: Suite object for the requested suite name (None if not present)
1880
    """
1881

1882
    q = session.query(Suite).filter_by(suite_name=suite)
1883

1884 1885 1886 1887
    try:
        return q.one()
    except NoResultFound:
        return None
1888

1889 1890
__all__.append('get_suite')

M
Mark Hymers 已提交
1891 1892
################################################################################

M
Mark Hymers 已提交
1893
class SuiteArchitecture(object):
M
Mark Hymers 已提交
1894 1895
    def __init__(self, *args, **kwargs):
        pass
M
Mark Hymers 已提交
1896 1897 1898 1899

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

1900 1901
__all__.append('SuiteArchitecture')

1902
@session_wrapper
1903
def get_suite_architectures(suite, skipsrc=False, skipall=False, session=None):
M
Mark Hymers 已提交
1904 1905 1906 1907 1908 1909
    """
    Returns list of Architecture objects for given C{suite} name

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

1910 1911 1912 1913 1914 1915 1916 1917
    @type skipsrc: boolean
    @param skipsrc: Whether to skip returning the 'source' architecture entry
    (Default False)

    @type skipall: boolean
    @param skipall: Whether to skip returning the 'all' architecture entry
    (Default False)

M
Mark Hymers 已提交
1918 1919 1920 1921 1922 1923 1924 1925 1926 1927
    @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)
    """

    q = session.query(Architecture)
    q = q.join(SuiteArchitecture)
1928
    q = q.join(Suite).filter_by(suite_name=suite)
1929

1930 1931
    if skipsrc:
        q = q.filter(Architecture.arch_string != 'source')
1932

1933 1934
    if skipall:
        q = q.filter(Architecture.arch_string != 'all')
1935

1936
    q = q.order_by('arch_string')
1937

1938
    return q.all()
M
Mark Hymers 已提交
1939

1940
__all__.append('get_suite_architectures')
M
Mark Hymers 已提交
1941

M
Mark Hymers 已提交
1942 1943
################################################################################

1944 1945 1946 1947 1948 1949 1950 1951 1952
class SuiteSrcFormat(object):
    def __init__(self, *args, **kwargs):
        pass

    def __repr__(self):
        return '<SuiteSrcFormat (%s, %s)>' % (self.suite_id, self.src_format_id)

__all__.append('SuiteSrcFormat')

1953
@session_wrapper
1954 1955 1956 1957 1958 1959 1960 1961 1962 1963 1964 1965 1966 1967 1968 1969 1970 1971 1972 1973
def get_suite_src_formats(suite, session=None):
    """
    Returns list of allowed SrcFormat for C{suite}.

    @type suite: str
    @param suite: 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: the list of allowed source formats for I{suite}
    """

    q = session.query(SrcFormat)
    q = q.join(SuiteSrcFormat)
    q = q.join(Suite).filter_by(suite_name=suite)
    q = q.order_by('format_name')

1974
    return q.all()
1975 1976 1977 1978 1979

__all__.append('get_suite_src_formats')

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

M
Mark Hymers 已提交
1980
class Uid(object):
M
Mark Hymers 已提交
1981 1982
    def __init__(self, *args, **kwargs):
        pass
M
Mark Hymers 已提交
1983

1984 1985 1986 1987 1988 1989 1990 1991 1992 1993 1994 1995
    def __eq__(self, val):
        if isinstance(val, str):
            return (self.uid == val)
        # This signals to use the normal comparison operator
        return NotImplemented

    def __ne__(self, val):
        if isinstance(val, str):
            return (self.uid != val)
        # This signals to use the normal comparison operator
        return NotImplemented

M
Mark Hymers 已提交
1996 1997 1998
    def __repr__(self):
        return '<Uid %s (%s)>' % (self.uid, self.name)

1999 2000
__all__.append('Uid')

2001
@session_wrapper
M
Mark Hymers 已提交
2002 2003 2004 2005 2006 2007 2008 2009 2010 2011 2012 2013 2014 2015 2016
def add_database_user(uidname, session=None):
    """
    Adds a database user

    @type uidname: string
    @param uidname: The uid of the user to add

    @type session: SQLAlchemy
    @param session: Optional SQL session object (a temporary one will be
    generated if not supplied).  If not passed, a commit will be performed at
    the end of the function, otherwise the caller is responsible for commiting.

    @rtype: Uid
    @return: the uid object for the given uidname
    """
2017 2018

    session.execute("CREATE USER :uid", {'uid': uidname})
2019
    session.commit_or_flush()
M
Mark Hymers 已提交
2020 2021 2022

__all__.append('add_database_user')

2023
@session_wrapper
M
Mark Hymers 已提交
2024 2025 2026 2027 2028 2029 2030 2031 2032 2033 2034 2035 2036 2037 2038 2039 2040
def get_or_set_uid(uidname, session=None):
    """
    Returns uid object for given uidname.

    If no matching uidname is found, a row is inserted.

    @type uidname: string
    @param uidname: The uid to add

    @type session: SQLAlchemy
    @param session: Optional SQL session object (a temporary one will be
    generated if not supplied).  If not passed, a commit will be performed at
    the end of the function, otherwise the caller is responsible for commiting.

    @rtype: Uid
    @return: the uid object for the given uidname
    """
2041 2042 2043

    q = session.query(Uid).filter_by(uid=uidname)

2044 2045 2046
    try:
        ret = q.one()
    except NoResultFound:
2047 2048 2049
        uid = Uid()
        uid.uid = uidname
        session.add(uid)
2050
        session.commit_or_flush()
2051
        ret = uid
M
Mark Hymers 已提交
2052

2053
    return ret
M
Mark Hymers 已提交
2054 2055 2056

__all__.append('get_or_set_uid')

2057
@session_wrapper
2058 2059 2060 2061
def get_uid_from_fingerprint(fpr, session=None):
    q = session.query(Uid)
    q = q.join(Fingerprint).filter_by(fingerprint=fpr)

2062 2063 2064 2065
    try:
        return q.one()
    except NoResultFound:
        return None
2066 2067 2068

__all__.append('get_uid_from_fingerprint')

M
Mark Hymers 已提交
2069 2070
################################################################################

M
Mark Hymers 已提交
2071 2072
class DBConn(Singleton):
    """
2073
    database module init.
M
Mark Hymers 已提交
2074 2075 2076 2077 2078
    """
    def __init__(self, *args, **kwargs):
        super(DBConn, self).__init__(*args, **kwargs)

    def _startup(self, *args, **kwargs):
M
updates  
Mark Hymers 已提交
2079 2080 2081
        self.debug = False
        if kwargs.has_key('debug'):
            self.debug = True
M
Mark Hymers 已提交
2082 2083
        self.__createconn()

M
Mark Hymers 已提交
2084 2085
    def __setuptables(self):
        self.tbl_architecture = Table('architecture', self.db_meta, autoload=True)
M
Mark Hymers 已提交
2086 2087 2088 2089 2090 2091 2092 2093 2094 2095 2096 2097 2098 2099
        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)
M
Mark Hymers 已提交
2100
        self.tbl_new_comments = Table('new_comments', self.db_meta, autoload=True)
M
Mark Hymers 已提交
2101 2102 2103 2104 2105 2106 2107 2108 2109
        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)
2110
        self.tbl_src_format = Table('src_format', self.db_meta, autoload=True)
M
Mark Hymers 已提交
2111 2112 2113
        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)
2114
        self.tbl_suite_src_formats = Table('suite_src_formats', self.db_meta, autoload=True)
M
Mark Hymers 已提交
2115 2116 2117
        self.tbl_uid = Table('uid', self.db_meta, autoload=True)

    def __setupmappers(self):
M
Mark Hymers 已提交
2118 2119 2120 2121 2122 2123 2124 2125 2126 2127
        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 已提交
2128 2129
                                 suite = relation(Suite),
                                 binary_id = self.tbl_bin_associations.c.bin,
2130
                                 binary = relation(DBBinary)))
M
Mark Hymers 已提交
2131

2132
        mapper(DBBinary, self.tbl_binaries,
M
Mark Hymers 已提交
2133
               properties = dict(binary_id = self.tbl_binaries.c.id,
M
Mark Hymers 已提交
2134 2135
                                 package = self.tbl_binaries.c.package,
                                 version = self.tbl_binaries.c.version,
M
Mark Hymers 已提交
2136
                                 maintainer_id = self.tbl_binaries.c.maintainer,
M
Mark Hymers 已提交
2137
                                 maintainer = relation(Maintainer),
M
Mark Hymers 已提交
2138
                                 source_id = self.tbl_binaries.c.source,
2139
                                 source = relation(DBSource),
M
Mark Hymers 已提交
2140
                                 arch_id = self.tbl_binaries.c.architecture,
M
Mark Hymers 已提交
2141 2142 2143 2144 2145 2146 2147 2148 2149
                                 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 已提交
2150 2151 2152 2153 2154 2155 2156 2157

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

2158
        mapper(ContentAssociation, self.tbl_content_associations,
M
Mark Hymers 已提交
2159 2160
               properties = dict(ca_id = self.tbl_content_associations.c.id,
                                 filename_id = self.tbl_content_associations.c.filename,
M
Mark Hymers 已提交
2161
                                 filename    = relation(ContentFilename),
M
Mark Hymers 已提交
2162
                                 filepath_id = self.tbl_content_associations.c.filepath,
M
Mark Hymers 已提交
2163 2164
                                 filepath    = relation(ContentFilepath),
                                 binary_id   = self.tbl_content_associations.c.binary_pkg,
2165
                                 binary      = relation(DBBinary)))
M
Mark Hymers 已提交
2166

M
Mark Hymers 已提交
2167 2168 2169 2170 2171 2172 2173 2174 2175 2176 2177 2178

        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,
2179
                                 source = relation(DBSource),
M
Mark Hymers 已提交
2180 2181
                                 poolfile_id = self.tbl_dsc_files.c.file,
                                 poolfile = relation(PoolFile)))
M
Mark Hymers 已提交
2182 2183 2184 2185

        mapper(PoolFile, self.tbl_files,
               properties = dict(file_id = self.tbl_files.c.id,
                                 filesize = self.tbl_files.c.size,
M
Mark Hymers 已提交
2186 2187
                                 location_id = self.tbl_files.c.location,
                                 location = relation(Location)))
M
Mark Hymers 已提交
2188 2189 2190 2191

        mapper(Fingerprint, self.tbl_fingerprint,
               properties = dict(fingerprint_id = self.tbl_fingerprint.c.id,
                                 uid_id = self.tbl_fingerprint.c.uid,
M
Mark Hymers 已提交
2192 2193 2194
                                 uid = relation(Uid),
                                 keyring_id = self.tbl_fingerprint.c.keyring,
                                 keyring = relation(Keyring)))
M
Mark Hymers 已提交
2195 2196 2197 2198 2199 2200 2201 2202

        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 已提交
2203
                                 component = relation(Component),
M
Mark Hymers 已提交
2204
                                 archive_id = self.tbl_location.c.archive,
M
Mark Hymers 已提交
2205
                                 archive = relation(Archive),
M
Mark Hymers 已提交
2206 2207 2208 2209 2210
                                 archive_type = self.tbl_location.c.type))

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

M
Mark Hymers 已提交
2211 2212 2213
        mapper(NewComment, self.tbl_new_comments,
               properties = dict(comment_id = self.tbl_new_comments.c.id))

M
Mark Hymers 已提交
2214 2215
        mapper(Override, self.tbl_override,
               properties = dict(suite_id = self.tbl_override.c.suite,
M
Mark Hymers 已提交
2216
                                 suite = relation(Suite),
M
Mark Hymers 已提交
2217
                                 component_id = self.tbl_override.c.component,
M
Mark Hymers 已提交
2218
                                 component = relation(Component),
M
Mark Hymers 已提交
2219
                                 priority_id = self.tbl_override.c.priority,
M
Mark Hymers 已提交
2220
                                 priority = relation(Priority),
M
Mark Hymers 已提交
2221
                                 section_id = self.tbl_override.c.section,
M
Mark Hymers 已提交
2222 2223 2224
                                 section = relation(Section),
                                 overridetype_id = self.tbl_override.c.type,
                                 overridetype = relation(OverrideType)))
M
Mark Hymers 已提交
2225 2226 2227 2228 2229 2230 2231 2232

        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 已提交
2233 2234 2235
                                 filepath = relation(ContentFilepath),
                                 filename_id = self.tbl_pending_content_associations.c.filename,
                                 filename = relation(ContentFilename)))
M
Mark Hymers 已提交
2236 2237 2238 2239 2240 2241 2242 2243 2244

        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 已提交
2245
                                 queue_id = self.tbl_queue_build.c.queue,
2246
                                 queue = relation(Queue, backref='queuebuild')))
M
Mark Hymers 已提交
2247 2248 2249 2250

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

2251
        mapper(DBSource, self.tbl_source,
M
Mark Hymers 已提交
2252
               properties = dict(source_id = self.tbl_source.c.id,
M
Mark Hymers 已提交
2253
                                 version = self.tbl_source.c.version,
M
Mark Hymers 已提交
2254
                                 maintainer_id = self.tbl_source.c.maintainer,
M
Mark Hymers 已提交
2255 2256 2257 2258
                                 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 已提交
2259
                                 fingerprint_id = self.tbl_source.c.sig_fpr,
M
Mark Hymers 已提交
2260 2261 2262 2263 2264 2265 2266 2267
                                 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 已提交
2268 2269

        mapper(SrcAssociation, self.tbl_src_associations,
M
Mark Hymers 已提交
2270 2271 2272 2273
               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,
2274
                                 source = relation(DBSource)))
M
Mark Hymers 已提交
2275

2276 2277 2278 2279
        mapper(SrcFormat, self.tbl_src_format,
               properties = dict(src_format_id = self.tbl_src_format.c.id,
                                 format_name = self.tbl_src_format.c.format_name))

M
Mark Hymers 已提交
2280 2281 2282
        mapper(SrcUploader, self.tbl_src_uploaders,
               properties = dict(uploader_id = self.tbl_src_uploaders.c.id,
                                 source_id = self.tbl_src_uploaders.c.source,
2283
                                 source = relation(DBSource,
M
Mark Hymers 已提交
2284 2285 2286 2287
                                                   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 已提交
2288 2289 2290 2291 2292 2293

        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,
2294
                                 suite = relation(Suite, backref='suitearchitectures'),
M
Mark Hymers 已提交
2295 2296
                                 arch_id = self.tbl_suite_architectures.c.architecture,
                                 architecture = relation(Architecture)))
M
Mark Hymers 已提交
2297

2298 2299 2300 2301 2302 2303
        mapper(SuiteSrcFormat, self.tbl_suite_src_formats,
               properties = dict(suite_id = self.tbl_suite_src_formats.c.suite,
                                 suite = relation(Suite, backref='suitesrcformats'),
                                 src_format_id = self.tbl_suite_src_formats.c.src_format,
                                 src_format = relation(SrcFormat)))

M
Mark Hymers 已提交
2304
        mapper(Uid, self.tbl_uid,
2305 2306
               properties = dict(uid_id = self.tbl_uid.c.id,
                                 fingerprint = relation(Fingerprint)))
M
Mark Hymers 已提交
2307

M
Mark Hymers 已提交
2308 2309
    ## Connection functions
    def __createconn(self):
M
Mark Hymers 已提交
2310
        from config import Config
2311 2312
        cnf = Config()
        if cnf["DB::Host"]:
M
Mark Hymers 已提交
2313 2314 2315 2316 2317 2318 2319 2320 2321 2322
            # 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"]
2323

M
updates  
Mark Hymers 已提交
2324
        self.db_pg   = create_engine(connstr, echo=self.debug)
M
Mark Hymers 已提交
2325 2326 2327 2328
        self.db_meta = MetaData()
        self.db_meta.bind = self.db_pg
        self.db_smaker = sessionmaker(bind=self.db_pg,
                                      autoflush=True,
2329
                                      autocommit=False)
M
Mark Hymers 已提交
2330

M
Mark Hymers 已提交
2331
        self.__setuptables()
M
Mark Hymers 已提交
2332
        self.__setupmappers()
M
Mark Hymers 已提交
2333

M
Mark Hymers 已提交
2334 2335
    def session(self):
        return self.db_smaker()
M
Mark Hymers 已提交
2336

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

2339