Fixed #33715 -- Allowed keyboard interrupt to abort queries in MySQL dbshell.

This commit is contained in:
Hasan Ramezani 2022-05-20 07:11:51 +02:00 committed by GitHub
parent e89f957135
commit 1a78ef2b85
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 31 additions and 0 deletions

View File

@ -1,3 +1,5 @@
import signal
from django.db.backends.base.client import BaseDatabaseClient from django.db.backends.base.client import BaseDatabaseClient
@ -58,3 +60,13 @@ class DatabaseClient(BaseDatabaseClient):
args += [database] args += [database]
args.extend(parameters) args.extend(parameters)
return args, env return args, env
def runshell(self, parameters):
sigint_handler = signal.getsignal(signal.SIGINT)
try:
# Allow SIGINT to pass to mysql to abort queries.
signal.signal(signal.SIGINT, signal.SIG_IGN)
super().runshell(parameters)
finally:
# Restore the original SIGINT handler.
signal.signal(signal.SIGINT, sigint_handler)

View File

@ -1,8 +1,11 @@
import os import os
import signal
import subprocess import subprocess
import sys import sys
from pathlib import Path from pathlib import Path
from unittest import mock, skipUnless
from django.db import connection
from django.db.backends.mysql.client import DatabaseClient from django.db.backends.mysql.client import DatabaseClient
from django.test import SimpleTestCase from django.test import SimpleTestCase
@ -218,3 +221,19 @@ class MySqlDbshellCommandTestCase(SimpleTestCase):
with self.assertRaises(subprocess.CalledProcessError) as ctx: with self.assertRaises(subprocess.CalledProcessError) as ctx:
subprocess.run(args, check=True, env=env) subprocess.run(args, check=True, env=env)
self.assertNotIn("somepassword", str(ctx.exception)) self.assertNotIn("somepassword", str(ctx.exception))
@skipUnless(connection.vendor == "mysql", "Requires a MySQL connection")
def test_sigint_handler(self):
"""SIGINT is ignored in Python and passed to mysql to abort queries."""
def _mock_subprocess_run(*args, **kwargs):
handler = signal.getsignal(signal.SIGINT)
self.assertEqual(handler, signal.SIG_IGN)
sigint_handler = signal.getsignal(signal.SIGINT)
# The default handler isn't SIG_IGN.
self.assertNotEqual(sigint_handler, signal.SIG_IGN)
with mock.patch("subprocess.run", new=_mock_subprocess_run):
connection.client.runshell([])
# dbshell restores the original handler.
self.assertEqual(sigint_handler, signal.getsignal(signal.SIGINT))