Skip to content

Commit

Permalink
frontend: use context manager for SQLAlchemy execute
Browse files Browse the repository at this point in the history
See #3130

    AttributeError: 'Engine' object has no attribute 'execute'

And also use text for raw SQL commands

    Use the text() construct, or the Connection.exec_driver_sql()
    method to invoke a driver-level SQL string.

And finially setting `future=True` which is going to be passed to
`create_engine` by Flask-SQLAlchemy to ensure compatibility between
SQLAlchemy 1.x and 2.0. Otherwise we would get

    AttributeError: 'Connection' object has no attribute 'commit'
  • Loading branch information
FrostyX authored and praiskup committed Mar 7, 2024
1 parent a0a16cc commit 99cc3bc
Show file tree
Hide file tree
Showing 5 changed files with 56 additions and 33 deletions.
3 changes: 2 additions & 1 deletion frontend/coprs_frontend/coprs/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -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():
Expand Down
8 changes: 5 additions & 3 deletions frontend/coprs_frontend/coprs/logic/batches_logic.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down
16 changes: 14 additions & 2 deletions frontend/coprs_frontend/coprs/logic/builds_logic.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down
47 changes: 26 additions & 21 deletions frontend/coprs_frontend/coprs/whoosheers.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand All @@ -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):
Expand Down
15 changes: 9 additions & 6 deletions frontend/coprs_frontend/tests/coprs_test_case.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
import uuid

import pytest
from sqlalchemy import desc
from sqlalchemy import desc, text
import decorator

import coprs
Expand Down Expand Up @@ -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()
Expand Down

0 comments on commit 99cc3bc

Please sign in to comment.