|
# This file is part of Buildbot. Buildbot 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, version 2. # # 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., 51 # Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. # # Copyright Buildbot Team Members
_hush_pyflakes = exceptions
# # schema #
# NOTES
# * server_defaults here are included to match those added by the migration # scripts, but they should not be depended on - all code accessing these # tables should supply default values as necessary. The defaults are # required during migration when adding non-nullable columns to existing # tables. # # * dates are stored as unix timestamps (UTC-ish epoch time) # # * sqlalchemy does not handle sa.Boolean very well on MySQL or Postgres; # use sa.Integer instead
# build requests
# A BuildRequest is a request for a particular build to be performed. Each # BuildRequest is a part of a Buildset. BuildRequests are claimed by # masters, to avoid multiple masters running the same build. sa.Column('id', sa.Integer, primary_key=True), sa.Column('buildsetid', sa.Integer, sa.ForeignKey("buildsets.id"), nullable=False), sa.Column('buildername', sa.String(length=256), nullable=False), sa.Column('priority', sa.Integer, nullable=False, server_default=sa.DefaultClause("0")),
# if this is zero, then the build is still pending sa.Column('complete', sa.Integer, server_default=sa.DefaultClause("0")),
# results is only valid when complete == 1; 0 = SUCCESS, 1 = WARNINGS, # etc - see master/buildbot/status/builder.py sa.Column('results', sa.SmallInteger),
# time the buildrequest was created sa.Column('submitted_at', sa.Integer, nullable=False),
# time the buildrequest was completed, or NULL sa.Column('complete_at', sa.Integer), )
# Each row in this table represents a claimed build request, where the # claim is made by the object referenced by objectid. sa.Column('brid', sa.Integer, sa.ForeignKey('buildrequests.id'), index=True, unique=True), sa.Column('objectid', sa.Integer, sa.ForeignKey('objects.id'), index=True, nullable=True), sa.Column('claimed_at', sa.Integer, nullable=False), )
# builds
# This table contains basic information about each build. Note that most # data about a build is still stored in on-disk pickles. sa.Column('id', sa.Integer, primary_key=True), sa.Column('number', sa.Integer, nullable=False), sa.Column('brid', sa.Integer, sa.ForeignKey('buildrequests.id'), nullable=False), sa.Column('start_time', sa.Integer, nullable=False), sa.Column('finish_time', sa.Integer), )
# buildsets
# This table contains input properties for buildsets sa.Column('buildsetid', sa.Integer, sa.ForeignKey('buildsets.id'), nullable=False), sa.Column('property_name', sa.String(256), nullable=False), # JSON-encoded tuple of (value, source) sa.Column('property_value', sa.String(1024), nullable=False), )
# This table represents Buildsets - sets of BuildRequests that share the # same original cause and source information. sa.Column('id', sa.Integer, primary_key=True),
# a simple external identifier to track down this buildset later, e.g., # for try requests sa.Column('external_idstring', sa.String(256)),
# a short string giving the reason the buildset was created sa.Column('reason', sa.String(256)), sa.Column('submitted_at', sa.Integer, nullable=False),
# if this is zero, then the build set is still pending sa.Column('complete', sa.SmallInteger, nullable=False, server_default=sa.DefaultClause("0")), sa.Column('complete_at', sa.Integer),
# results is only valid when complete == 1; 0 = SUCCESS, 1 = WARNINGS, # etc - see master/buildbot/status/builder.py sa.Column('results', sa.SmallInteger),
# buildset belongs to all sourcestamps with setid sa.Column('sourcestampsetid', sa.Integer, sa.ForeignKey('sourcestampsets.id')), )
# changes
# Files touched in changes sa.Column('changeid', sa.Integer, sa.ForeignKey('changes.changeid'), nullable=False), sa.Column('filename', sa.String(1024), nullable=False), )
# Properties for changes sa.Column('changeid', sa.Integer, sa.ForeignKey('changes.changeid'), nullable=False), sa.Column('property_name', sa.String(256), nullable=False), # JSON-encoded tuple of (value, source) sa.Column('property_value', sa.String(1024), nullable=False), )
# users associated with this change; this allows multiple users for # situations where a version-control system can represent both an author # and committer, for example. sa.Column("changeid", sa.Integer, sa.ForeignKey('changes.changeid'), nullable=False), # uid for the author of the change with the given changeid sa.Column("uid", sa.Integer, sa.ForeignKey('users.uid'), nullable=False) )
# Changes to the source code, produced by ChangeSources # changeid also serves as 'change number' sa.Column('changeid', sa.Integer, primary_key=True),
# author's name (usually an email address) sa.Column('author', sa.String(256), nullable=False),
# commit comment sa.Column('comments', sa.String(1024), nullable=False),
# old, CVS-related boolean sa.Column('is_dir', sa.SmallInteger, nullable=False), # old, for CVS
# The branch where this change occurred. When branch is NULL, that # means the main branch (trunk, master, etc.) sa.Column('branch', sa.String(256)),
# revision identifier for this change sa.Column('revision', sa.String(256)), # CVS uses NULL
sa.Column('revlink', sa.String(256)),
# this is the timestamp of the change - it is usually copied from the # version-control system, and may be long in the past or even in the # future! sa.Column('when_timestamp', sa.Integer, nullable=False),
# an arbitrary string used for filtering changes sa.Column('category', sa.String(256)),
# repository specifies, along with revision and branch, the # source tree in which this change was detected. sa.Column('repository', sa.String(length=512), nullable=False, server_default=''),
# codebase is a logical name to specify what is in the repository sa.Column('codebase', sa.String(256), nullable=False, server_default=sa.DefaultClause("")),
# project names the project this source code represents. It is used # later to filter changes sa.Column('project', sa.String(length=512), nullable=False, server_default=''), )
# sourcestamps
# Patches for SourceStamps that were generated through the try mechanism sa.Column('id', sa.Integer, primary_key=True),
# number of directory levels to strip off (patch -pN) sa.Column('patchlevel', sa.Integer, nullable=False),
# base64-encoded version of the patch file sa.Column('patch_base64', sa.Text, nullable=False),
# patch author, if known sa.Column('patch_author', sa.Text, nullable=False),
# patch comment sa.Column('patch_comment', sa.Text, nullable=False),
# subdirectory in which the patch should be applied; NULL for top-level sa.Column('subdir', sa.Text), )
# The changes that led up to a particular source stamp. sa.Column('sourcestampid', sa.Integer, sa.ForeignKey('sourcestamps.id'), nullable=False), sa.Column('changeid', sa.Integer, sa.ForeignKey('changes.changeid'), nullable=False), )
# A sourcestampset identifies a set of sourcestamps. A sourcestamp belongs # to a particular set if the sourcestamp has the same setid sa.Column('id', sa.Integer, primary_key=True), )
# A sourcestamp identifies a particular instance of the source code. # Ideally, this would always be absolute, but in practice source stamps can # also mean "latest" (when revision is NULL), which is of course a # time-dependent definition. sa.Column('id', sa.Integer, primary_key=True),
# the branch to check out. When branch is NULL, that means # the main branch (trunk, master, etc.) sa.Column('branch', sa.String(256)),
# the revision to check out, or the latest if NULL sa.Column('revision', sa.String(256)),
# the patch to apply to generate this source code sa.Column('patchid', sa.Integer, sa.ForeignKey('patches.id')),
# the repository from which this source should be checked out sa.Column('repository', sa.String(length=512), nullable=False, server_default=''),
# codebase is a logical name to specify what is in the repository sa.Column('codebase', sa.String(256), nullable=False, server_default=sa.DefaultClause("")),
# the project this source code represents sa.Column('project', sa.String(length=512), nullable=False, server_default=''),
# each sourcestamp belongs to a set of sourcestamps sa.Column('sourcestampsetid', sa.Integer, sa.ForeignKey('sourcestampsets.id')), )
# schedulers
# This table references "classified" changes that have not yet been # "processed". That is, the scheduler has looked at these changes and # determined that something should be done, but that hasn't happened yet. # Rows are deleted from this table as soon as the scheduler is done with # the change. sa.Column('objectid', sa.Integer, sa.ForeignKey('objects.id')), sa.Column('changeid', sa.Integer, sa.ForeignKey('changes.changeid')), # true (nonzero) if this change is important to this scheduler sa.Column('important', sa.Integer), )
# objects
# This table uniquely identifies objects that need to maintain state across # invocations. # unique ID for this object sa.Column("id", sa.Integer, primary_key=True), # object's user-given name sa.Column('name', sa.String(128), nullable=False), # object's class name, basically representing a "type" for the state sa.Column('class_name', sa.String(128), nullable=False), )
# This table stores key/value pairs for objects, where the key is a string # and the value is a JSON string. # object for which this value is set sa.Column("objectid", sa.Integer, sa.ForeignKey('objects.id'), nullable=False), # name for this value (local to the object) sa.Column("name", sa.String(length=256), nullable=False), # value, as a JSON string sa.Column("value_json", sa.Text, nullable=False), )
#users
# This table identifies individual users, and contains buildbot-specific # information about those users. # unique user id number sa.Column("uid", sa.Integer, primary_key=True),
# identifier (nickname) for this user; used for display sa.Column("identifier", sa.String(256), nullable=False),
# username portion of user credentials for authentication sa.Column("bb_username", sa.String(128)),
# password portion of user credentials for authentication sa.Column("bb_password", sa.String(128)), )
# This table stores information identifying a user that's related to a # particular interface - a version-control system, status plugin, etc. # unique user id number sa.Column("uid", sa.Integer, sa.ForeignKey('users.uid'), nullable=False),
# type of user attribute, such as 'git' sa.Column("attr_type", sa.String(128), nullable=False),
# data for given user attribute, such as a commit string or password sa.Column("attr_data", sa.String(128), nullable=False), )
# indexes
buildset_properties.c.buildsetid) scheduler_changes.c.changeid, unique=True) sourcestamp_changes.c.sourcestampid) unique=False) users_info.c.attr_type, unique=True) users_info.c.attr_data, unique=True) unique=True) unique=True)
# MySQl creates indexes for foreign keys, and these appear in the # reflection. This is a list of (table, index) names that should be # expected on this platform
('change_users', dict(unique=False, column_names=['uid'], name='uid')), ('sourcestamps', dict(unique=False, column_names=['patchid'], name='patchid')), ('sourcestamp_changes', dict(unique=False, column_names=['changeid'], name='changeid')), ('buildsets', dict(unique=False, column_names=['sourcestampsetid'], name='buildsets_sourcestampsetid_fkey')), ]
# # migration support #
# this is a bit more complicated than might be expected because the first # seven database versions were once implemented using a homespun migration # system, and we need to support upgrading masters from that system. The # old system used a 'version' table, where SQLAlchemy-Migrate uses # 'migrate_version'
# we don't even have to look at the old version table - if there's # no migrate_version, then we're not up to date. # migrate.api doesn't let us hand in an engine self.repo_path)
# here, things are a little tricky. If we have a 'version' table, then # we need to version_control the database with the proper version # number, drop 'version', and then upgrade. If we have no 'version' # table and no 'migrate_version' table, then we need to version_control # the database. Otherwise, we just need to upgrade it.
# http://code.google.com/p/sqlalchemy-migrate/issues/detail?id=100 # means we cannot use the migrate.versioning.api module. So these # methods perform similar wrapping functions to what is done by the API # functions, but without disposing of the engine. self.repo_path) % (version, version + 1))
# sqlalchemy-migrate started including a version number in 0.7; we # support back to 0.6.1, but not 0.6. We'll use some discovered # differences between 0.6.1 and 0.6 to get that resolution. try: from migrate.versioning import schemadiff if hasattr(schemadiff, 'ColDiff'): version = "0.6.1" else: version = "0.6" except: version = "0.0" raise RuntimeError("You are using SQLAlchemy-Migrate %s. " "The minimum version is 0.6.1." % (version,))
self.repo_path, version)
# the upgrade process must run in a db thread # if the migrate_version table exists, we can just let migrate # take care of this process.
# if the version table exists, then we can version_control things # at that version, drop the version table, and let migrate take # care of the rest. # get the existing version
# set up migrate at the same version
# drop the no-longer-required version table, using a dummy # metadata entry sa.Column('x', sa.Integer))
# clear the dummy metadata entry
# and, finally, upgrade using migrate
# otherwise, this db is uncontrolled, so we just version control it # and update it. else:
# migrate has a bug in one of its warnings; this is fixed in version control # (3ba66abc4d), but not yet released. It can't hurt to fix it here, too, so we # get realistic tracebacks import migrate.changeset.exceptions as ex2 ex1.MigrateDeprecationWarning = ex2.MigrateDeprecationWarning pass |