bench_executor.mysql

MySQL is an open-source relational database management system developed by Oracle Corporation.

Website: https://www.mysql.com/
Repository: https://github.com/mysql/mysql-server

  1#!/usr/bin/env python3
  2
  3"""
  4MySQL is an open-source relational database management system developed by
  5Oracle Corporation.
  6
  7**Website**: https://www.mysql.com/<br>
  8**Repository**: https://github.com/mysql/mysql-server
  9"""
 10
 11import os
 12import pymysql
 13import tempfile
 14from csv import reader
 15from typing import List, Tuple
 16from timeout_decorator import timeout, TimeoutError  # type: ignore
 17from bench_executor.container import Container
 18from bench_executor.logger import Logger
 19
 20VERSION = '8.0'
 21HOST = 'localhost'
 22USER = 'root'
 23PASSWORD = 'root'
 24DB = 'db'
 25PORT = '3306'
 26CLEAR_TABLES_TIMEOUT = 5 * 60  # 5 minutes
 27
 28
 29class MySQL(Container):
 30    """MySQL container for executing SQL queries."""
 31    def __init__(self, data_path: str, config_path: str, directory: str,
 32                 verbose: bool):
 33        """Creates an instance of the MySQL class.
 34
 35        Parameters
 36        ----------
 37        data_path : str
 38            Path to the data directory of the case.
 39        config_path : str
 40            Path to the config directory of the case.
 41        directory : str
 42            Path to the directory to store logs.
 43        verbose : bool
 44            Enable verbose logs.
 45        """
 46        self._data_path = os.path.abspath(data_path)
 47        self._config_path = os.path.abspath(config_path)
 48        self._logger = Logger(__name__, directory, verbose)
 49        self._tables: List[str] = []
 50        tmp_dir = os.path.join(tempfile.gettempdir(), 'mysql')
 51        os.umask(0)
 52        os.makedirs(tmp_dir, exist_ok=True)
 53        os.makedirs(os.path.join(self._data_path, 'mysql'), exist_ok=True)
 54
 55        super().__init__(f'blindreviewing/mysql:v{VERSION}', 'MySQL',
 56                         self._logger,
 57                         ports={PORT: PORT},
 58                         environment={'MYSQL_ROOT_PASSWORD': 'root',
 59                                      'MYSQL_DATABASE': 'db'},
 60                         volumes=[f'{self._data_path}/shared/:/data/shared',
 61                                  f'{self._config_path}/mysql/'
 62                                  f'mysql-secure-file-prive.cnf:'
 63                                  f'/etc/mysql/conf.d/'
 64                                  f'mysql-secure-file-prive.cnf',
 65                                  f'{tmp_dir}:/var/lib/mysql'])
 66
 67    def initialization(self) -> bool:
 68        """Initialize MySQL's database.
 69
 70        Returns
 71        -------
 72        success : bool
 73            Whether the initialization was successfull or not.
 74        """
 75        # MySQL should start with a initialized database, start MySQL
 76        # if not initialized to avoid the pre-run start during benchmark
 77        # execution
 78        success = self.wait_until_ready()
 79        if not success:
 80            return False
 81        success = self.stop()
 82
 83        return success
 84
 85    @property
 86    def root_mount_directory(self) -> str:
 87        """Subdirectory in the root directory of the case for MySQL.
 88
 89        Returns
 90        -------
 91        subdirectory : str
 92            Subdirectory of the root directory for MySQL.
 93        """
 94        return __name__.lower()
 95
 96    def wait_until_ready(self, command: str = '') -> bool:
 97        """Wait until MySQL is ready to execute SQL queries.
 98
 99        Parameters
100        ----------
101        command : str
102            Command to execute in the MySQL container, optionally, defaults to
103            no command.
104
105        Returns
106        -------
107        success : bool
108            Whether the MySQL was initialized successfull or not.
109        """
110        log_line = f'port: {PORT}  MySQL Community Server - GPL.'
111        return self.run_and_wait_for_log(log_line, command=command)
112
113    def load(self, csv_file: str, table: str) -> bool:
114        """Load a single CSV file into MySQL.
115
116        Parameters
117        ----------
118        csv_file : str
119            Name of the CSV file.
120        table : str
121            Name of the table.
122
123        Returns
124        -------
125        success : bool
126            Whether the execution was successfull or not.
127        """
128        return self._load_csv(csv_file, table, True)
129
130    def load_multiple(self, csv_files: List[dict]) -> bool:
131        """Load multiple CSV files into MySQL.
132
133        Parameters
134        ----------
135        csv_files : list
136            List of CSV files to load. Each entry consist of a `file` and
137            `table` key.
138
139        Returns
140        -------
141        success : bool
142            Whether the execution was successfull or not.
143        """
144        for entry in csv_files:
145            if not self._load_csv(entry['file'], entry['table'], True):
146                return False
147        return True
148
149    def load_sql_schema(self, schema_file: str,
150                        csv_files: List[Tuple[str, str]]) -> bool:
151        """Execute SQL schema with MySQL.
152
153        Executes a .sql file with MySQL.
154        If the data is not loaded by the .sql file but only the schema is
155        provided through the .sql file, a list of CSV files can be provided to
156        load them as well.
157
158        Parameters
159        ----------
160        schema_file : str
161            Name of the .sql file.
162        csv_files : List[Tuple[str, str]]
163            List of CSV file names to load in the tables created with the .sql
164            file, may also be an empty list. Each entry contains a Tuple with
165            the name of the CSV file and the table name.
166
167        Returns
168        -------
169        success : bool
170            Whether the execution was successfull or not.
171        """
172        success = True
173
174        # Load SQL schema
175        cmd = f'/bin/sh -c \'mysql --host={HOST} --port={PORT} ' + \
176              f'--user={USER} --password={PASSWORD} --database={DB} ' + \
177              f'< /data/shared/{schema_file}\''
178        success, output = self.exec(cmd)
179
180        # Load CSVs
181        if success:
182            for csv_file, table in csv_files:
183                success = self._load_csv(csv_file, table, False)
184                if not success:
185                    break
186
187        return success
188
189    def _load_csv(self, csv_file: str, table: str, create: bool) -> bool:
190        """Load a single CSV file into MySQL.
191
192        Parameters
193        ----------
194        csv_file : str
195            Name of the CSV file.
196        table : str
197            Name of the table to store the data in.
198        create : bool
199            Whether to drop and create the table or re-use it
200
201        Returns
202        -------
203        success : bool
204            Whether the execution was successfull or not.
205        """
206        success = True
207        columns = None
208        table = table.lower()
209        path = os.path.join(self._data_path, 'shared', csv_file)
210        path2 = os.path.join(self._data_path, 'shared', f'tmp_{csv_file}')
211
212        self._tables.append(table)
213
214        # Analyze CSV for loading
215        if not os.path.exists(path):
216            self._logger.error(f'CSV file "{path}" does not exist')
217            return False
218
219        with open(path, 'r') as f:
220            csv_reader = reader(f)
221            columns = next(csv_reader)
222            columns = [x.lower() for x in columns]
223
224        # MySQL cannot set NULL as NULL keyword, use their own specific syntax
225        # for this: \N
226        with open(path, 'r') as f:
227            data = f.read()
228            data = data.replace('NULL', '\\N')
229
230            with open(path2, 'w') as f2:
231                f2.write(data)
232
233        # Load CSV
234        connection = pymysql.connect(host=HOST, user=USER, password=PASSWORD,
235                                     db=DB)
236        try:
237            cursor = connection.cursor()
238
239            if create:
240                cursor.execute(f'DROP TABLE IF EXISTS {table};')
241                c = ' TEXT , '.join(columns) + ' TEXT'
242                cursor.execute(f'CREATE TABLE {table} (k INT ZEROFILL '
243                               f'NOT NULL AUTO_INCREMENT, {c}, '
244                               f'PRIMARY KEY(k));')
245            c = ','.join(columns)
246            cursor.execute(f'LOAD DATA INFILE \'/data/shared/tmp_{csv_file}\' '
247                           f'INTO TABLE {table} FIELDS TERMINATED BY \',\' '
248                           f'ENCLOSED BY \'\\"\' LINES TERMINATED BY \'\\n\' '
249                           f'IGNORE 1 ROWS ({c});')
250            cursor.execute('COMMIT;')
251
252            header = '| ID | ' + ' | '.join(columns) + ' |'
253            self._logger.debug(header)
254            self._logger.debug('-' * len(header))
255
256            cursor.execute(f'SELECT * FROM {table};')
257            number_of_records = 0
258            for record in cursor:
259                number_of_records += 1
260                self._logger.debug(record)
261
262            if number_of_records == 0:
263                success = False
264        except Exception as e:
265            self._logger.error(f'Failed to load CSV: "{e}"')
266            success = False
267        finally:
268            connection.close()
269
270        return success
271
272    @timeout(CLEAR_TABLES_TIMEOUT)
273    def _clear_tables(self):
274        """Clears all tables with a provided timeout."""
275        connection = pymysql.connect(host=HOST, database=DB,
276                                     user=PASSWORD, password=PASSWORD)
277        cursor = connection.cursor()
278        for table in self._tables:
279            cursor.execute(f'DROP TABLE IF EXISTS {table};')
280            cursor.execute('COMMIT;')
281        self._tables = []
282        connection.close()
283
284    def stop(self) -> bool:
285        """Stop MySQL
286        Clears all tables and stops the MySQL container.
287
288        Returns
289        -------
290        success : bool
291            Whether the execution was successfull or not.
292        """
293        try:
294            self._clear_tables()
295        except TimeoutError:
296            self._logger.warning('Clearing MySQL tables timed out after '
297                                 f'{CLEAR_TABLES_TIMEOUT}s!')
298
299        return super().stop()
300
301
302if __name__ == '__main__':
303    print(f'ℹ️  Starting up MySQL v{VERSION}...')
304    m = MySQL('data', 'config', 'log', True)
305    m.wait_until_ready()
306    input('ℹ️  Press any key to stop')
307    m.stop()
308    print('ℹ️  Stopped')
class MySQL(bench_executor.container.Container):
 30class MySQL(Container):
 31    """MySQL container for executing SQL queries."""
 32    def __init__(self, data_path: str, config_path: str, directory: str,
 33                 verbose: bool):
 34        """Creates an instance of the MySQL class.
 35
 36        Parameters
 37        ----------
 38        data_path : str
 39            Path to the data directory of the case.
 40        config_path : str
 41            Path to the config directory of the case.
 42        directory : str
 43            Path to the directory to store logs.
 44        verbose : bool
 45            Enable verbose logs.
 46        """
 47        self._data_path = os.path.abspath(data_path)
 48        self._config_path = os.path.abspath(config_path)
 49        self._logger = Logger(__name__, directory, verbose)
 50        self._tables: List[str] = []
 51        tmp_dir = os.path.join(tempfile.gettempdir(), 'mysql')
 52        os.umask(0)
 53        os.makedirs(tmp_dir, exist_ok=True)
 54        os.makedirs(os.path.join(self._data_path, 'mysql'), exist_ok=True)
 55
 56        super().__init__(f'blindreviewing/mysql:v{VERSION}', 'MySQL',
 57                         self._logger,
 58                         ports={PORT: PORT},
 59                         environment={'MYSQL_ROOT_PASSWORD': 'root',
 60                                      'MYSQL_DATABASE': 'db'},
 61                         volumes=[f'{self._data_path}/shared/:/data/shared',
 62                                  f'{self._config_path}/mysql/'
 63                                  f'mysql-secure-file-prive.cnf:'
 64                                  f'/etc/mysql/conf.d/'
 65                                  f'mysql-secure-file-prive.cnf',
 66                                  f'{tmp_dir}:/var/lib/mysql'])
 67
 68    def initialization(self) -> bool:
 69        """Initialize MySQL's database.
 70
 71        Returns
 72        -------
 73        success : bool
 74            Whether the initialization was successfull or not.
 75        """
 76        # MySQL should start with a initialized database, start MySQL
 77        # if not initialized to avoid the pre-run start during benchmark
 78        # execution
 79        success = self.wait_until_ready()
 80        if not success:
 81            return False
 82        success = self.stop()
 83
 84        return success
 85
 86    @property
 87    def root_mount_directory(self) -> str:
 88        """Subdirectory in the root directory of the case for MySQL.
 89
 90        Returns
 91        -------
 92        subdirectory : str
 93            Subdirectory of the root directory for MySQL.
 94        """
 95        return __name__.lower()
 96
 97    def wait_until_ready(self, command: str = '') -> bool:
 98        """Wait until MySQL is ready to execute SQL queries.
 99
100        Parameters
101        ----------
102        command : str
103            Command to execute in the MySQL container, optionally, defaults to
104            no command.
105
106        Returns
107        -------
108        success : bool
109            Whether the MySQL was initialized successfull or not.
110        """
111        log_line = f'port: {PORT}  MySQL Community Server - GPL.'
112        return self.run_and_wait_for_log(log_line, command=command)
113
114    def load(self, csv_file: str, table: str) -> bool:
115        """Load a single CSV file into MySQL.
116
117        Parameters
118        ----------
119        csv_file : str
120            Name of the CSV file.
121        table : str
122            Name of the table.
123
124        Returns
125        -------
126        success : bool
127            Whether the execution was successfull or not.
128        """
129        return self._load_csv(csv_file, table, True)
130
131    def load_multiple(self, csv_files: List[dict]) -> bool:
132        """Load multiple CSV files into MySQL.
133
134        Parameters
135        ----------
136        csv_files : list
137            List of CSV files to load. Each entry consist of a `file` and
138            `table` key.
139
140        Returns
141        -------
142        success : bool
143            Whether the execution was successfull or not.
144        """
145        for entry in csv_files:
146            if not self._load_csv(entry['file'], entry['table'], True):
147                return False
148        return True
149
150    def load_sql_schema(self, schema_file: str,
151                        csv_files: List[Tuple[str, str]]) -> bool:
152        """Execute SQL schema with MySQL.
153
154        Executes a .sql file with MySQL.
155        If the data is not loaded by the .sql file but only the schema is
156        provided through the .sql file, a list of CSV files can be provided to
157        load them as well.
158
159        Parameters
160        ----------
161        schema_file : str
162            Name of the .sql file.
163        csv_files : List[Tuple[str, str]]
164            List of CSV file names to load in the tables created with the .sql
165            file, may also be an empty list. Each entry contains a Tuple with
166            the name of the CSV file and the table name.
167
168        Returns
169        -------
170        success : bool
171            Whether the execution was successfull or not.
172        """
173        success = True
174
175        # Load SQL schema
176        cmd = f'/bin/sh -c \'mysql --host={HOST} --port={PORT} ' + \
177              f'--user={USER} --password={PASSWORD} --database={DB} ' + \
178              f'< /data/shared/{schema_file}\''
179        success, output = self.exec(cmd)
180
181        # Load CSVs
182        if success:
183            for csv_file, table in csv_files:
184                success = self._load_csv(csv_file, table, False)
185                if not success:
186                    break
187
188        return success
189
190    def _load_csv(self, csv_file: str, table: str, create: bool) -> bool:
191        """Load a single CSV file into MySQL.
192
193        Parameters
194        ----------
195        csv_file : str
196            Name of the CSV file.
197        table : str
198            Name of the table to store the data in.
199        create : bool
200            Whether to drop and create the table or re-use it
201
202        Returns
203        -------
204        success : bool
205            Whether the execution was successfull or not.
206        """
207        success = True
208        columns = None
209        table = table.lower()
210        path = os.path.join(self._data_path, 'shared', csv_file)
211        path2 = os.path.join(self._data_path, 'shared', f'tmp_{csv_file}')
212
213        self._tables.append(table)
214
215        # Analyze CSV for loading
216        if not os.path.exists(path):
217            self._logger.error(f'CSV file "{path}" does not exist')
218            return False
219
220        with open(path, 'r') as f:
221            csv_reader = reader(f)
222            columns = next(csv_reader)
223            columns = [x.lower() for x in columns]
224
225        # MySQL cannot set NULL as NULL keyword, use their own specific syntax
226        # for this: \N
227        with open(path, 'r') as f:
228            data = f.read()
229            data = data.replace('NULL', '\\N')
230
231            with open(path2, 'w') as f2:
232                f2.write(data)
233
234        # Load CSV
235        connection = pymysql.connect(host=HOST, user=USER, password=PASSWORD,
236                                     db=DB)
237        try:
238            cursor = connection.cursor()
239
240            if create:
241                cursor.execute(f'DROP TABLE IF EXISTS {table};')
242                c = ' TEXT , '.join(columns) + ' TEXT'
243                cursor.execute(f'CREATE TABLE {table} (k INT ZEROFILL '
244                               f'NOT NULL AUTO_INCREMENT, {c}, '
245                               f'PRIMARY KEY(k));')
246            c = ','.join(columns)
247            cursor.execute(f'LOAD DATA INFILE \'/data/shared/tmp_{csv_file}\' '
248                           f'INTO TABLE {table} FIELDS TERMINATED BY \',\' '
249                           f'ENCLOSED BY \'\\"\' LINES TERMINATED BY \'\\n\' '
250                           f'IGNORE 1 ROWS ({c});')
251            cursor.execute('COMMIT;')
252
253            header = '| ID | ' + ' | '.join(columns) + ' |'
254            self._logger.debug(header)
255            self._logger.debug('-' * len(header))
256
257            cursor.execute(f'SELECT * FROM {table};')
258            number_of_records = 0
259            for record in cursor:
260                number_of_records += 1
261                self._logger.debug(record)
262
263            if number_of_records == 0:
264                success = False
265        except Exception as e:
266            self._logger.error(f'Failed to load CSV: "{e}"')
267            success = False
268        finally:
269            connection.close()
270
271        return success
272
273    @timeout(CLEAR_TABLES_TIMEOUT)
274    def _clear_tables(self):
275        """Clears all tables with a provided timeout."""
276        connection = pymysql.connect(host=HOST, database=DB,
277                                     user=PASSWORD, password=PASSWORD)
278        cursor = connection.cursor()
279        for table in self._tables:
280            cursor.execute(f'DROP TABLE IF EXISTS {table};')
281            cursor.execute('COMMIT;')
282        self._tables = []
283        connection.close()
284
285    def stop(self) -> bool:
286        """Stop MySQL
287        Clears all tables and stops the MySQL container.
288
289        Returns
290        -------
291        success : bool
292            Whether the execution was successfull or not.
293        """
294        try:
295            self._clear_tables()
296        except TimeoutError:
297            self._logger.warning('Clearing MySQL tables timed out after '
298                                 f'{CLEAR_TABLES_TIMEOUT}s!')
299
300        return super().stop()

MySQL container for executing SQL queries.

MySQL(data_path: str, config_path: str, directory: str, verbose: bool)
32    def __init__(self, data_path: str, config_path: str, directory: str,
33                 verbose: bool):
34        """Creates an instance of the MySQL class.
35
36        Parameters
37        ----------
38        data_path : str
39            Path to the data directory of the case.
40        config_path : str
41            Path to the config directory of the case.
42        directory : str
43            Path to the directory to store logs.
44        verbose : bool
45            Enable verbose logs.
46        """
47        self._data_path = os.path.abspath(data_path)
48        self._config_path = os.path.abspath(config_path)
49        self._logger = Logger(__name__, directory, verbose)
50        self._tables: List[str] = []
51        tmp_dir = os.path.join(tempfile.gettempdir(), 'mysql')
52        os.umask(0)
53        os.makedirs(tmp_dir, exist_ok=True)
54        os.makedirs(os.path.join(self._data_path, 'mysql'), exist_ok=True)
55
56        super().__init__(f'blindreviewing/mysql:v{VERSION}', 'MySQL',
57                         self._logger,
58                         ports={PORT: PORT},
59                         environment={'MYSQL_ROOT_PASSWORD': 'root',
60                                      'MYSQL_DATABASE': 'db'},
61                         volumes=[f'{self._data_path}/shared/:/data/shared',
62                                  f'{self._config_path}/mysql/'
63                                  f'mysql-secure-file-prive.cnf:'
64                                  f'/etc/mysql/conf.d/'
65                                  f'mysql-secure-file-prive.cnf',
66                                  f'{tmp_dir}:/var/lib/mysql'])

Creates an instance of the MySQL class.

Parameters
  • data_path (str): Path to the data directory of the case.
  • config_path (str): Path to the config directory of the case.
  • directory (str): Path to the directory to store logs.
  • verbose (bool): Enable verbose logs.
def initialization(self) -> bool:
68    def initialization(self) -> bool:
69        """Initialize MySQL's database.
70
71        Returns
72        -------
73        success : bool
74            Whether the initialization was successfull or not.
75        """
76        # MySQL should start with a initialized database, start MySQL
77        # if not initialized to avoid the pre-run start during benchmark
78        # execution
79        success = self.wait_until_ready()
80        if not success:
81            return False
82        success = self.stop()
83
84        return success

Initialize MySQL's database.

Returns
  • success (bool): Whether the initialization was successfull or not.
root_mount_directory: str

Subdirectory in the root directory of the case for MySQL.

Returns
  • subdirectory (str): Subdirectory of the root directory for MySQL.
def wait_until_ready(self, command: str = '') -> bool:
 97    def wait_until_ready(self, command: str = '') -> bool:
 98        """Wait until MySQL is ready to execute SQL queries.
 99
100        Parameters
101        ----------
102        command : str
103            Command to execute in the MySQL container, optionally, defaults to
104            no command.
105
106        Returns
107        -------
108        success : bool
109            Whether the MySQL was initialized successfull or not.
110        """
111        log_line = f'port: {PORT}  MySQL Community Server - GPL.'
112        return self.run_and_wait_for_log(log_line, command=command)

Wait until MySQL is ready to execute SQL queries.

Parameters
  • command (str): Command to execute in the MySQL container, optionally, defaults to no command.
Returns
  • success (bool): Whether the MySQL was initialized successfull or not.
def load(self, csv_file: str, table: str) -> bool:
114    def load(self, csv_file: str, table: str) -> bool:
115        """Load a single CSV file into MySQL.
116
117        Parameters
118        ----------
119        csv_file : str
120            Name of the CSV file.
121        table : str
122            Name of the table.
123
124        Returns
125        -------
126        success : bool
127            Whether the execution was successfull or not.
128        """
129        return self._load_csv(csv_file, table, True)

Load a single CSV file into MySQL.

Parameters
  • csv_file (str): Name of the CSV file.
  • table (str): Name of the table.
Returns
  • success (bool): Whether the execution was successfull or not.
def load_multiple(self, csv_files: List[dict]) -> bool:
131    def load_multiple(self, csv_files: List[dict]) -> bool:
132        """Load multiple CSV files into MySQL.
133
134        Parameters
135        ----------
136        csv_files : list
137            List of CSV files to load. Each entry consist of a `file` and
138            `table` key.
139
140        Returns
141        -------
142        success : bool
143            Whether the execution was successfull or not.
144        """
145        for entry in csv_files:
146            if not self._load_csv(entry['file'], entry['table'], True):
147                return False
148        return True

Load multiple CSV files into MySQL.

Parameters
  • csv_files (list): List of CSV files to load. Each entry consist of a file and table key.
Returns
  • success (bool): Whether the execution was successfull or not.
def load_sql_schema(self, schema_file: str, csv_files: List[Tuple[str, str]]) -> bool:
150    def load_sql_schema(self, schema_file: str,
151                        csv_files: List[Tuple[str, str]]) -> bool:
152        """Execute SQL schema with MySQL.
153
154        Executes a .sql file with MySQL.
155        If the data is not loaded by the .sql file but only the schema is
156        provided through the .sql file, a list of CSV files can be provided to
157        load them as well.
158
159        Parameters
160        ----------
161        schema_file : str
162            Name of the .sql file.
163        csv_files : List[Tuple[str, str]]
164            List of CSV file names to load in the tables created with the .sql
165            file, may also be an empty list. Each entry contains a Tuple with
166            the name of the CSV file and the table name.
167
168        Returns
169        -------
170        success : bool
171            Whether the execution was successfull or not.
172        """
173        success = True
174
175        # Load SQL schema
176        cmd = f'/bin/sh -c \'mysql --host={HOST} --port={PORT} ' + \
177              f'--user={USER} --password={PASSWORD} --database={DB} ' + \
178              f'< /data/shared/{schema_file}\''
179        success, output = self.exec(cmd)
180
181        # Load CSVs
182        if success:
183            for csv_file, table in csv_files:
184                success = self._load_csv(csv_file, table, False)
185                if not success:
186                    break
187
188        return success

Execute SQL schema with MySQL.

Executes a .sql file with MySQL. If the data is not loaded by the .sql file but only the schema is provided through the .sql file, a list of CSV files can be provided to load them as well.

Parameters
  • schema_file (str): Name of the .sql file.
  • csv_files (List[Tuple[str, str]]): List of CSV file names to load in the tables created with the .sql file, may also be an empty list. Each entry contains a Tuple with the name of the CSV file and the table name.
Returns
  • success (bool): Whether the execution was successfull or not.
def stop(self) -> bool:
285    def stop(self) -> bool:
286        """Stop MySQL
287        Clears all tables and stops the MySQL container.
288
289        Returns
290        -------
291        success : bool
292            Whether the execution was successfull or not.
293        """
294        try:
295            self._clear_tables()
296        except TimeoutError:
297            self._logger.warning('Clearing MySQL tables timed out after '
298                                 f'{CLEAR_TABLES_TIMEOUT}s!')
299
300        return super().stop()

Stop MySQL Clears all tables and stops the MySQL container.

Returns
  • success (bool): Whether the execution was successfull or not.