Skip to content

Commit

Permalink
Add MySQL functionality, tests; refactor
Browse files Browse the repository at this point in the history
  • Loading branch information
jdavcs committed Mar 24, 2021
1 parent 4d8b185 commit 57782d5
Show file tree
Hide file tree
Showing 5 changed files with 78 additions and 25 deletions.
24 changes: 24 additions & 0 deletions lib/galaxy/model/database_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@ def make_manager(db_url, database):
return PosgresDatabaseManager(db_url, database)
elif db_url.startswith('sqlite'):
return SqliteDatabaseManager(db_url, database)
elif db_url.startswith('mysql'):
return MySQLDatabaseManager(db_url, database)
else:
raise ConfigurationError(f'Invalid database URL: {db_url}')

Expand Down Expand Up @@ -99,3 +101,25 @@ def can_connect_to_dbfile():
def create(self, *args):
# Ignore any args (encoding, template)
sqlite3.connect(f'file:{self.url.database}', uri=True)


class MySQLDatabaseManager(DatabaseManager):

def _handle_no_database(self):
self.database = self.url.database # use database from db_url

def exists(self):
with sqlalchemy_engine(self.url) as engine:
stmt = text("SELECT schema_name FROM information_schema.schemata WHERE schema_name=:database")
stmt = stmt.bindparams(database=self.database)
with engine.connect() as conn:
return bool(conn.scalar(stmt))

def create(self, encoding, *arg):
# Ignore any args (template)
with sqlalchemy_engine(self.url) as engine:
preparer = IdentifierPreparer(engine.dialect)
database = preparer.quote(self.database)
stmt = f"CREATE DATABASE {database} CHARACTER SET = '{encoding}'"
with engine.connect().execution_options(isolation_level='AUTOCOMMIT') as conn:
conn.execute(stmt)
10 changes: 8 additions & 2 deletions test/unit/model/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,18 @@

from galaxy.model.database_utils import sqlalchemy_engine

# GALAXY_TEST_CONNECT_POSTGRES_URI='postgresql://postgres@localhost:5432/postgres' pytest test/unit/util/test_database.py
# GALAXY_TEST_CONNECT_POSTGRES_URI='postgresql://postgres@localhost:5432/postgres' pytest test/unit/model
skip_if_not_postgres_uri = pytest.mark.skipif(
not os.environ.get('GALAXY_TEST_CONNECT_POSTGRES_URI'),
reason="GALAXY_TEST_CONNECT_POSTGRES_URI not set"
)

# GALAXY_TEST_CONNECT_MYSQL_URI='mysql+mysqldb://root@localhost/mysql' pytest test/unit/model
skip_if_not_mysql_uri = pytest.mark.skipif(
not os.environ.get('GALAXY_TEST_CONNECT_MYSQL_URI'),
reason="GALAXY_TEST_CONNECT_MYSQL_URI not set"
)


def replace_database_in_url(url, database_name):
"""
Expand All @@ -29,7 +35,7 @@ def drop_database(db_url, database):
Used only for test purposes to cleanup after creating a test database.
"""
if db_url.startswith('postgresql'):
if db_url.startswith('postgresql') or db_url.startswith('mysql'):
with sqlalchemy_engine(db_url) as engine:
preparer = IdentifierPreparer(engine.dialect)
database = preparer.quote(database)
Expand Down
5 changes: 5 additions & 0 deletions test/unit/model/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,11 @@ def postgres_url():
return os.environ.get('GALAXY_TEST_CONNECT_POSTGRES_URI')


@pytest.fixture
def mysql_url():
return os.environ.get('GALAXY_TEST_CONNECT_MYSQL_URI')


@pytest.fixture
def sqlite_memory_url():
return 'sqlite:///:memory:'
10 changes: 10 additions & 0 deletions test/unit/model/test_database_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
from .common import (
drop_database,
replace_database_in_url,
skip_if_not_mysql_uri,
skip_if_not_postgres_uri,
)

Expand Down Expand Up @@ -60,6 +61,15 @@ def test_exists_sqlite_in_memory_database(database_name, sqlite_memory_url):
assert database_exists(sqlite_memory_url)


@skip_if_not_mysql_uri
def test_create_exists_mysql_database(database_name, mysql_url):
assert not database_exists(mysql_url, database_name)
create_database(mysql_url, database_name)
assert database_exists(mysql_url, database_name)
drop_database(mysql_url, database_name)
assert not database_exists(mysql_url, database_name)


def make_sqlite_url(tmp_dir, database_name):
path = os.path.join(tmp_dir, database_name)
return f'sqlite:///{path}'
54 changes: 31 additions & 23 deletions test/unit/model/test_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
Column,
Integer,
MetaData,
String,
Table,
)
from sqlalchemy.sql import (
Expand All @@ -22,6 +21,7 @@
from .common import (
drop_database,
replace_database_in_url,
skip_if_not_mysql_uri,
skip_if_not_postgres_uri,
)

Expand All @@ -31,9 +31,9 @@ def view():
# A View class we would add to galaxy.model.view
class TestView(View):
name = 'testview'
__view__ = text('select id, first_name from testusers').columns(
__view__ = text('SELECT id, foo FROM testfoo').columns(
column('id', Integer),
column('first_name', String)
column('foo', Integer)
)
pkeys = {'id'}
View._make_table(name, __view__, pkeys)
Expand All @@ -45,37 +45,45 @@ class TestView(View):
def test_postgres_create_view(database_name, postgres_url, view):
metadata = MetaData()
make_table(metadata) # table from which the view will select
create_database(postgres_url, database_name)

url = replace_database_in_url(postgres_url, database_name)
with sqlalchemy_engine(url) as engine:
with engine.connect() as conn:
metadata.create_all(conn) # create table in database
conn.execute(CreateView(view.name, view.__view__)) # create view in database
query = f"select * from information_schema.tables where table_name = '{view.name}' and table_type = 'VIEW'"
result = conn.execute(query)
assert result.rowcount == 1 # assert that view exists in database

query = f"SELECT 1 FROM information_schema.views WHERE table_name = '{view.name}'"
create_database(postgres_url, database_name)
run_view_test(url, metadata, view, query)
drop_database(postgres_url, database_name)


def test_sqlite_create_view(sqlite_memory_url, view):
metadata = MetaData()
make_table(metadata) # table from which the view will select
url = sqlite_memory_url
query = f"SELECT 1 FROM sqlite_master WHERE type='view' AND name='{view.name}'"
run_view_test(url, metadata, view, query)

with sqlalchemy_engine(sqlite_memory_url) as engine:
with engine.connect() as conn:
metadata.create_all(conn) # create table in database
conn.execute(CreateView(view.name, view.__view__)) # create view in database
query = f"SELECT name FROM sqlite_master WHERE type='view' AND name='{view.name}'"
result = conn.execute(query).fetchall()
assert len(result) == 1 # assert that view exists in database

@skip_if_not_mysql_uri
def test_mysql_create_view(database_name, mysql_url, view):
metadata = MetaData()
make_table(metadata) # table from which the view will select
url = replace_database_in_url(mysql_url, database_name)
query = f"SELECT 1 FROM information_schema.views WHERE table_name = '{view.name}'"
create_database(mysql_url, database_name)
run_view_test(url, metadata, view, query)
drop_database(mysql_url, database_name)


def make_table(metadata):
users = Table('testusers', metadata,
users = Table('testfoo', metadata,
Column('id', Integer, primary_key=True),
Column('first_name', String),
Column('last_name', String)
Column('foo', Integer),
Column('bar', Integer)
)
return users


def run_view_test(url, metadata, view, query):
with sqlalchemy_engine(url) as engine:
with engine.connect() as conn:
metadata.create_all(conn) # create table in database
conn.execute(CreateView(view.name, view.__view__)) # create view in database
result = conn.execute(query).fetchall()
assert len(result) == 1 # assert that view exists in database

0 comments on commit 57782d5

Please sign in to comment.