<commit_before>"""
# Database schema for benchbuild

The schema should initialize itself on an empty database. For now, we do not
support automatic upgrades on schema changes. You might encounter some
roadbumps when using an older version of benchbuild.

Furthermore, for now, we are restricted to postgresql databases, although we
already support arbitrary connection strings via config.

If you want to use reports that use one of our SQL functions, you need to
initialize the functions first using the following command:

```bash
  > BB_DB_CREATE_FUNCTIONS=true benchbuild run -E empty -l
```

After that you (normally) do not need to do this agains, unless we supply
a new version that you are interested in.
As soon as we have alembic running, we can provide automatic up/downgrade
paths for you.
"""

import logging
import sys
import uuid

import migrate.versioning.api as migrate
import sqlalchemy as sa
from sqlalchemy import (Column, DateTime, Enum, ForeignKey,
                        ForeignKeyConstraint, Integer, String, create_engine)
from sqlalchemy.dialects.postgresql import UUID
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker
from sqlalchemy.types import (CHAR, BigInteger, Float, Numeric, SmallInteger,
                              TypeDecorator)

import benchbuild.settings as settings
import benchbuild.utils.user_interface as ui
from benchbuild.utils import path as bbpath

BASE = declarative_base()
LOG = logging.getLogger(__name__)


def metadata():
    return BASE.metadata


class GUID(TypeDecorator):
    """Platform-independent GUID type.

    Uses Postgresql's UUID type, otherwise uses
    CHAR(32), storing as stringified hex values.
    """
    impl = CHAR
    as_uuid = False

    def __init__(self, *args, as_uuid=False, **kwargs):
        self.as_uuid = as_uuid
        super(GUID, self).__init__(*args, **kwargs)

    def load_dialect_impl(self, dialect):
        if dialect.name == 'postgresql':
            return dialect.type_descriptor(UUID(as_uuid=self.as_uuid))
        else:
            return dialect.type_descriptor(CHAR(32))

    def process_bind_param(self, value, dialect):
        if value is None:
            return value
        elif dialect.name == 'postgresql':
            return str(value)
        else:
            if not isinstance(value, uuid.UUID):
                return "%s" % uuid.UUID(value)
            else:
                # hexstring
                return "%x" % value.int

    def process_result_value(self, value, dialect):
        if value is None:
            return value

        if isinstance(value, uuid.UUID):
            return value
        else:
            LOG.error(str(value))
            return uuid.UUID(str(value))


class Run(BASE):
    """Store a run for each executed test binary."""

    __tablename__ = 'run'
    __table_args__ = (ForeignKeyConstraint(
        ['project_name', 'project_group'],
        ['project.name', 'project.group_name'],
        onupdate="CASCADE",
        ondelete="CASCADE"), )

    id = Column(Integer, primary_key=True)
    command = Column(String)
    project_name = Column(String, index=True)
    project_group = Column(String, index=True)
    experiment_name = Column(String, index=True)
    run_group = Column(GUID(as_uuid=True), index=True)
    experiment_group = Column(
        GUID(as_uuid=True),
        ForeignKey("experiment.id", ondelete="CASCADE", onupdate="CASCADE"),
        index=True)

    begin = Column(DateTime(timezone=False))
    end = Column(DateTime(timezone=False))
    status = Column(Enum('completed', 'running', 'failed', name="run_state"))

    metrics = sa.orm.relationship(
        "Metric",
        cascade="all, delete-orphan",
        passive_deletes=True,
        passive_updates=True)
    events = sa.orm.relationship(
        "Event",
        cascade="all, delete-orphan",
        passive_deletes=True,
        passive_updates=True)
    logs = sa.orm.relationship(
        "RunLog",
        cascade="all, delete-orphan",
        passive_deletes=True,
        passive_updates=True)
    stored_data = sa.orm.relationship(
        "Metadata",
        cascade="all, delete-orphan",
        passive_deletes=True,
        passive_updates=True)
    configurations = sa.orm.relationship(
        "Config",
        cascade="all, delete-orphan",
        passive_deletes=True,
        passive_updates=True)
    tests = sa.orm.relationship(
        "RegressionTest",
        cascade="all, delete-orphan",
        passive_deletes=True,
        passive_updates=True)

    def __repr__(self):
        return ("<Run: {0} status={1} run={2}>").format(
            self.project_name, self.status, self.id)


class RunGroup(BASE):
    """ Store information about a run group. """

    __tablename__ = 'rungroup'

    id = Column(GUID(as_uuid=True), primary_key=True, index=True)
    experiment = Column(
        GUID(as_uuid=True),
        ForeignKey("experiment.id", ondelete="CASCADE", onupdate="CASCADE"),
        index=True)

    begin = Column(DateTime(timezone=False))<commit_msg>utils/schema: remove tests relation from Run<commit_after>"""
# Database schema for benchbuild

The schema should initialize itself on an empty database. For now, we do not
support automatic upgrades on schema changes. You might encounter some
roadbumps when using an older version of benchbuild.

Furthermore, for now, we are restricted to postgresql databases, although we
already support arbitrary connection strings via config.

If you want to use reports that use one of our SQL functions, you need to
initialize the functions first using the following command:

```bash
  > BB_DB_CREATE_FUNCTIONS=true benchbuild run -E empty -l
```

After that you (normally) do not need to do this agains, unless we supply
a new version that you are interested in.
As soon as we have alembic running, we can provide automatic up/downgrade
paths for you.
"""

import logging
import sys
import uuid

import migrate.versioning.api as migrate
import sqlalchemy as sa
from sqlalchemy import (Column, DateTime, Enum, ForeignKey,
                        ForeignKeyConstraint, Integer, String, create_engine)
from sqlalchemy.dialects.postgresql import UUID
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker
from sqlalchemy.types import (CHAR, BigInteger, Float, Numeric, SmallInteger,
                              TypeDecorator)

import benchbuild.settings as settings
import benchbuild.utils.user_interface as ui
from benchbuild.utils import path as bbpath

BASE = declarative_base()
LOG = logging.getLogger(__name__)


def metadata():
    return BASE.metadata


class GUID(TypeDecorator):
    """Platform-independent GUID type.

    Uses Postgresql's UUID type, otherwise uses
    CHAR(32), storing as stringified hex values.
    """
    impl = CHAR
    as_uuid = False

    def __init__(self, *args, as_uuid=False, **kwargs):
        self.as_uuid = as_uuid
        super(GUID, self).__init__(*args, **kwargs)

    def load_dialect_impl(self, dialect):
        if dialect.name == 'postgresql':
            return dialect.type_descriptor(UUID(as_uuid=self.as_uuid))
        else:
            return dialect.type_descriptor(CHAR(32))

    def process_bind_param(self, value, dialect):
        if value is None:
            return value
        elif dialect.name == 'postgresql':
            return str(value)
        else:
            if not isinstance(value, uuid.UUID):
                return "%s" % uuid.UUID(value)
            else:
                # hexstring
                return "%x" % value.int

    def process_result_value(self, value, dialect):
        if value is None:
            return value

        if isinstance(value, uuid.UUID):
            return value
        else:
            LOG.error(str(value))
            return uuid.UUID(str(value))


class Run(BASE):
    """Store a run for each executed test binary."""

    __tablename__ = 'run'
    __table_args__ = (ForeignKeyConstraint(
        ['project_name', 'project_group'],
        ['project.name', 'project.group_name'],
        onupdate="CASCADE",
        ondelete="CASCADE"), )

    id = Column(Integer, primary_key=True)
    command = Column(String)
    project_name = Column(String, index=True)
    project_group = Column(String, index=True)
    experiment_name = Column(String, index=True)
    run_group = Column(GUID(as_uuid=True), index=True)
    experiment_group = Column(
        GUID(as_uuid=True),
        ForeignKey("experiment.id", ondelete="CASCADE", onupdate="CASCADE"),
        index=True)

    begin = Column(DateTime(timezone=False))
    end = Column(DateTime(timezone=False))
    status = Column(Enum('completed', 'running', 'failed', name="run_state"))

    metrics = sa.orm.relationship(
        "Metric",
        cascade="all, delete-orphan",
        passive_deletes=True,
        passive_updates=True)
    events = sa.orm.relationship(
        "Event",
        cascade="all, delete-orphan",
        passive_deletes=True,
        passive_updates=True)
    logs = sa.orm.relationship(
        "RunLog",
        cascade="all, delete-orphan",
        passive_deletes=True,
        passive_updates=True)
    stored_data = sa.orm.relationship(
        "Metadata",
        cascade="all, delete-orphan",
        passive_deletes=True,
        passive_updates=True)
    configurations = sa.orm.relationship(
        "Config",
        cascade="all, delete-orphan",
        passive_deletes=True,
        passive_updates=True)

    def __repr__(self):
        return ("<Run: {0} status={1} run={2}>").format(
            self.project_name, self.status, self.id)


class RunGroup(BASE):
    """ Store information about a run group. """

    __tablename__ = 'rungroup'

    id = Column(GUID(as_uuid=True), primary_key=True, index=True)
    experiment = Column(
        GUID(as_uuid=True),
        ForeignKey("experiment.id", ondelete="CASCADE", onupdate="CASCADE"),
        index=True)

    begin = Column(DateTime(timezone=False))
{'fraction_non_alphanumeric': 0.09850299401197604, 'fraction_numerical': 0.0013972055888223553, 'mean_word_length': 2.9608695652173913, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 7, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '89422', 'n_tokens_mistral': 2971, 'n_tokens_neox': 2881, 'n_words': 813}