diff --git a/frontend/coprs_frontend/coprs/__init__.py b/frontend/coprs_frontend/coprs/__init__.py index a78b97ce7..d6b8f5ae4 100644 --- a/frontend/coprs_frontend/coprs/__init__.py +++ b/frontend/coprs_frontend/coprs/__init__.py @@ -41,7 +41,8 @@ session = Session(app) -db = SQLAlchemy(app) +# Set `future=True` to ensure compatibility between SQLAlchemy 1.x and 2.0 +db = SQLAlchemy(app, engine_options={"future": True}) @contextmanager def db_session_scope(): diff --git a/frontend/coprs_frontend/coprs/logic/batches_logic.py b/frontend/coprs_frontend/coprs/logic/batches_logic.py index d9434a964..42e182ad3 100644 --- a/frontend/coprs_frontend/coprs/logic/batches_logic.py +++ b/frontend/coprs_frontend/coprs/logic/batches_logic.py @@ -6,6 +6,7 @@ import anytree import backoff +from sqlalchemy import text from sqlalchemy.exc import SQLAlchemyError from sqlalchemy_utils.functions import quote as sa_quote from coprs import app, db, cache @@ -28,9 +29,10 @@ def _lock_table(table): # EXCLUSIVE mode locks the whole table for write operations # (while reading is not blocked) until the end of the transaction # (commit / rollback) - db.session.execute("LOCK TABLE {} IN EXCLUSIVE MODE;".format( - sa_quote(db.engine, table), - )) + with db.engine.connect() as connection: + connection.execute("LOCK TABLE {} IN EXCLUSIVE MODE;".format( + sa_quote(db.engine, table), + )) @contextlib.contextmanager diff --git a/frontend/coprs_frontend/coprs/logic/builds_logic.py b/frontend/coprs_frontend/coprs/logic/builds_logic.py index 3e1d4c282..2f9d5b413 100644 --- a/frontend/coprs_frontend/coprs/logic/builds_logic.py +++ b/frontend/coprs_frontend/coprs/logic/builds_logic.py @@ -166,7 +166,13 @@ def get_pending_jobs_bucket(cls, start, end): AND NOT build.canceled """) - res = db.engine.execute(query, start=start, end=end, status=StatusEnum("pending")) + with db.engine.connect() as connection: + params = { + "start": start, + "end": end, + "status": StatusEnum("pending") + } + res = connection.execute(query, params) return res.first().result @classmethod @@ -181,7 +187,13 @@ def get_running_jobs_bucket(cls, start, end): -- builds that have ended_on=NULL """) - res = db.engine.execute(query, start=start, end=end, status=StatusEnum("running")) + with db.engine.connect() as connection: + params = { + "start": start, + "end": end, + "status": StatusEnum("running") + } + res = connection.execute(query, params) return res.first().result @classmethod diff --git a/frontend/coprs_frontend/coprs/whoosheers.py b/frontend/coprs_frontend/coprs/whoosheers.py index c23d69b2d..7ed9aa463 100644 --- a/frontend/coprs_frontend/coprs/whoosheers.py +++ b/frontend/coprs_frontend/coprs/whoosheers.py @@ -4,6 +4,7 @@ from subprocess import Popen, PIPE from flask_whooshee import AbstractWhoosheer +from sqlalchemy import text from coprs import app from coprs import models @@ -78,25 +79,28 @@ def delete_package(cls, writer, package): def get_chroot_info(cls, copr): # NOTE: orm db session for Copr model is already committed at the point insert_*/update_* methods are called. # Hence we use db.engine directly (for a new session). - result = db.engine.execute( - """ - SELECT os_release, os_version, arch - FROM mock_chroot - JOIN copr_chroot ON copr_chroot.mock_chroot_id=mock_chroot.id - WHERE copr_chroot.copr_id={0} - """.format(copr.id) - ) + + with db.engine.connect() as connection: + result = connection.execute(text( + """ + SELECT os_release, os_version, arch + FROM mock_chroot + JOIN copr_chroot ON copr_chroot.mock_chroot_id=mock_chroot.id + WHERE copr_chroot.copr_id={0} + """.format(copr.id) + )) return ["{}-{}-{}".format(t[0], t[1], t[2]) for t in result.fetchall()] @classmethod def get_package_names(cls, copr): - result = db.engine.execute( - """ - SELECT name - FROM package - WHERE copr_id={0} - """.format(copr.id) - ) + with db.engine.connect() as connection: + result = connection.execute(text( + """ + SELECT name + FROM package + WHERE copr_id={0} + """.format(copr.id) + )) return [row[0] for row in result.fetchall()] @classmethod @@ -105,12 +109,13 @@ def on_commit(cls, app, changes): for change in changes: if change[0].__class__ in cls.models: copr_id = change[0].get_search_related_copr_id() - db.engine.execute( - """ - UPDATE copr SET latest_indexed_data_update = {0} - WHERE copr.id = {1} - """.format(int(time.time()), copr_id) - ) + with db.engine.connect() as connection: + connection.execute(text( + """ + UPDATE copr SET latest_indexed_data_update = {0} + WHERE copr.id = {1} + """.format(int(time.time()), copr_id) + )) class WhoosheeStamp(object): diff --git a/frontend/coprs_frontend/tests/coprs_test_case.py b/frontend/coprs_frontend/tests/coprs_test_case.py index 9b97ae99a..fbcd1678f 100644 --- a/frontend/coprs_frontend/tests/coprs_test_case.py +++ b/frontend/coprs_frontend/tests/coprs_test_case.py @@ -9,7 +9,7 @@ import uuid import pytest -from sqlalchemy import desc +from sqlalchemy import desc, text import decorator import coprs @@ -78,12 +78,15 @@ def setup_method(self, method): def teardown_method(self, method): # delete just data, not the tables self.db.session.rollback() - for tbl in reversed(self.db.metadata.sorted_tables): - self.db.engine.execute(tbl.delete()) - # This table has after_create() hook with default data in models.py, so - # we actually need to drop it so the setup_method() re-creates the data - self.db.engine.execute('drop table dist_git_instance') + with self.db.engine.connect() as connection: + for tbl in reversed(self.db.metadata.sorted_tables): + connection.execute(tbl.delete()) + # This table has after_create() hook with default data in models.py, + # so we actually need to drop it so the setup_method() re-creates + # the data + connection.execute(text('drop table dist_git_instance')) + connection.commit() self.app.config = self.original_config.copy() cache.clear()