Skip to content

Commit 234e7d0

Browse files
committed
BUG#38040736 Dropping a Routine leads to a shell crash losing the http server
When dropping a function using the context menu in the VSCode Extension, the following commands will be triggered in the backend: - "gui.sqlEditor.execute", "args": {"sql": "drop function `sakila`.`test_function`"} - "gui.db.get_routines_metadata", "args": {"schema_name": "sakila"} Commands normally are handled in separate threads by adding tasks that get processed in a separate thread, by locking the session until each task is completed. The issue is caused because the get_routines_metadata task performs SQL execution before adding such task. In the above scenario, the task for the drop operation is added and starts executing and then the get_routines_metadata task attempts to use the session, causing a race condition. The problem got solved by properly locking the session for its use in get_routines_metadata. Change-Id: I794c5a2e5b90bd596e8f9b39eada77e9a29cad98
1 parent 5a57caa commit 234e7d0

File tree

1 file changed

+19
-12
lines changed

1 file changed

+19
-12
lines changed

gui/backend/gui_plugin/core/dbms/DbMySQLSession.py

Lines changed: 19 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@
4343
MySQLColumnsListTask,
4444
MySQLRoutinesListTask)
4545
from gui_plugin.core.dbms.DbSession import (DbSession, DbSessionFactory,
46-
ReconnectionMode)
46+
ReconnectionMode, lock_usage)
4747
from gui_plugin.core.dbms.DbSessionTasks import (DbExecuteTask,
4848
check_supported_type)
4949
from gui_plugin.core.Error import MSGException
@@ -131,6 +131,12 @@ def is_connection_error(self, error):
131131
return False
132132

133133
def run_sql(self, sql, args=None):
134+
"""
135+
Executes an sql statement, returns a result object.
136+
137+
WARNING: Use LOCKED state to call this function anc consume it's result,
138+
otherwise may lead to race conditions and shell crashes (see lock_usage)
139+
"""
134140
return self.session.run_sql(sql, args)
135141

136142
def on_shell_prompt(self, text, options):
@@ -373,8 +379,9 @@ def start_transaction(self):
373379
self.execute("START TRANSACTION")
374380

375381
def kill_query(self, user_session):
376-
user_session._killed = True
377-
self.session.run_sql(f"KILL QUERY {user_session.connection_id}")
382+
with lock_usage(self._mutex, 5):
383+
user_session._killed = True
384+
self.run_sql(f"KILL QUERY {user_session.connection_id}")
378385

379386
def get_default_schema(self):
380387
return self._connection_options['schema'] if 'schema' in self._connection_options else ''
@@ -753,11 +760,11 @@ def get_columns_metadata(self, names):
753760
f"The columns {column_names} do not exist.")
754761
return {"columns": result}
755762

756-
757763
def get_routines_metadata(self, schema_name):
758764
params = (schema_name,)
759765

760-
has_external_language = self._column_exists("ROUTINES", "EXTERNAL_LANGUAGE")
766+
has_external_language = self._column_exists(
767+
"ROUTINES", "EXTERNAL_LANGUAGE")
761768
if has_external_language:
762769
sql = """SELECT ROUTINE_NAME as 'name', ROUTINE_TYPE as 'type', EXTERNAL_LANGUAGE as 'language'
763770
FROM information_schema.ROUTINES
@@ -783,10 +790,9 @@ def get_routines_metadata(self, schema_name):
783790
result = []
784791
if not result:
785792
raise MSGException(Error.DB_OBJECT_DOES_NOT_EXISTS,
786-
f"The '{schema_name}' does not exist.")
793+
f"The '{schema_name}' does not exist.")
787794
return {"routines": result}
788795

789-
790796
def _column_exists(self, table_name, column_name):
791797
"""Check if a column exists in INFORMATION_SCHEMA table."""
792798

@@ -796,9 +802,10 @@ def _column_exists(self, table_name, column_name):
796802
AND TABLE_NAME = ?
797803
AND COLUMN_NAME = ?"""
798804

799-
cursor = self.cursor = self.run_sql(sql, (table_name, column_name))
805+
with lock_usage(self._mutex, 5):
806+
cursor = self.cursor = self.run_sql(sql, (table_name, column_name))
800807

801-
if cursor:
802-
result = cursor.fetch_one()
803-
return result and result.get_field("count") > 0
804-
return False
808+
if cursor:
809+
result = cursor.fetch_one()
810+
return result and result.get_field("count") > 0
811+
return False

0 commit comments

Comments
 (0)