# Copyright (c) 2014, 2019, Oracle and/or its affiliates. All rights reserved. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2.0, # as published by the Free Software Foundation. # # This program is also distributed with certain software (including # but not limited to OpenSSL) that is licensed under separate terms, as # designated in a particular file or component or in included license # documentation. The authors of MySQL hereby grant you an additional # permission to link the program and your derivative works with the # separately licensed software that they have included with MySQL. # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See # the GNU General Public License, version 2.0, for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA import os import re import grt import mforms from threading import Thread from queue import Queue, Empty from workbench.log import log_info, log_error from workbench.client_utils import MySQLScriptImporter class RunPanel(mforms.Table): def __init__(self, editor, log_callback): mforms.Table.__init__(self) self.set_managed() self.set_release_on_add() self.set_row_count(2) self.set_column_count(1) self.set_padding(-1) self.label = mforms.newLabel("Running script...") self.add(self.label, 0, 1, 0, 1, mforms.HFillFlag) self.progress = mforms.newProgressBar() self.add(self.progress, 0, 1, 1, 2, mforms.HFillFlag) self.progress.set_size(400, -1) self.log_callback = log_callback self.editor = editor self.importer = MySQLScriptImporter(editor.connection) self.importer.report_progress = self.report_progress self.importer.report_output = self.report_output self._worker_queue = Queue() self._worker = None self._progress_status = None self._progress_value = 0 self._update_timer = None def __del__(self): if self._update_timer: mforms.Utilities.cancel_timeout(self._update_timer) @property def is_busy(self): return self._worker != None def report_progress(self, status, current, total): self._progress_status = status if status: self._worker_queue.put(status) if total > 0: self._progress_value = float(current) / total def report_output(self, message): log_info("%s\n" % message) self._worker_queue.put(message) def update_ui(self): try: while True: data = self._worker_queue.get_nowait() if data is None: self._worker.join() self._worker = None self._update_timer = None self.progress.show(False) self.log_callback(None) return False if isinstance(data, Exception): self.log_callback(str(data)+"\n") if isinstance(data, grt.DBError) and data.args[1] == 1044: mforms.Utilities.show_error("Run SQL Script", "The current MySQL account does not have enough privileges to execute the script.", "OK", "", "") elif isinstance(data, grt.DBLoginError): username = self.editor.connection.parameterValues["userName"] host = self.editor.connection.hostIdentifier mforms.Utilities.forget_password(host, username) mforms.Utilities.show_error("Run SQL Script", "Error executing SQL script.\n"+str(data), "OK", "", "") else: mforms.Utilities.show_error("Run SQL Script", "Error executing SQL script.\n"+str(data), "OK", "", "") else: self.log_callback(data+"\n") if self._progress_status is not None: self.label.set_text(self._progress_status) self.progress.set_value(self._progress_value) except Empty: pass if self._progress_status is not None: self.label.set_text(self._progress_status) self.progress.set_value(self._progress_value) return True def work(self, file, schema, charset): try: log_info("Executing %s...\n" % file) self._progress_status = "Executing %s..." % file self._progress_value = 0 self.importer.import_script(file, default_schema = schema, default_charset = charset) log_info("Run script finished\n") except grt.DBLoginError as e: log_error("MySQL login error running script: %s\n" % e) self._worker_queue.put(e) except grt.DBError as e: log_error("MySQL error running script: %s\n" % e) self._worker_queue.put(e) except Exception as e: import traceback log_error("Unexpected exception running script: %s\n%s\n" % (e, traceback.format_exc())) self._worker_queue.put(e) self._worker_queue.put(None) def start(self, what, default_db, default_charset): parameterValues = self.editor.connection.parameterValues username = parameterValues["userName"] host = self.editor.connection.hostIdentifier ok, pwd = mforms.Utilities.find_cached_password(host, username) if not ok: accepted, pwd = mforms.Utilities.find_or_ask_for_password("Run SQL Script", host, username, False) if not accepted: return self.importer.set_password(pwd) self._worker = Thread(target = self.work, args = (what, default_db, default_charset)) self._worker.start() self._update_timer = mforms.Utilities.add_timeout(0.2, self.update_ui) class ParameterDialog(mforms.Form): def __init__(self, editor): mforms.Form.__init__(self, mforms.Form.main_form(), mforms.FormDialogFrame) self.editor = editor box = mforms.Box(False) box.set_padding(12) box.set_spacing(8) box.add(mforms.newLabel("Preview the first lines of the script below and click [Run] to start executing.\nNote: the preview below may display non-ASCII characters incorrectly, even if the MySQL server can treat them correctly."), False, True) self.file_info = mforms.newLabel("") box.add(self.file_info, False, True) self.text = mforms.newCodeEditor(None) self.text.set_language(mforms.LanguageMySQL) box.add(self.text, True, True) table = mforms.newTable() table.set_padding(20) table.set_row_count(2) table.set_column_count(3) table.set_row_spacing(8) table.set_column_spacing(4) table.add(mforms.newLabel("Default Schema Name:"), 0, 1, 0, 1, 0) self.schema = mforms.newSelector(mforms.SelectorCombobox) table.add(self.schema, 1, 2, 0, 1, mforms.HFillFlag|mforms.HExpandFlag) help = mforms.newLabel("Schema to be used unless explicitly specified in the script.\nLeave blank if the script already specified it,\npick a schema from the drop down or type a name to\ncreate a new one.") help.set_style(mforms.SmallHelpTextStyle) table.add(help, 2, 3, 0, 1, mforms.HFillFlag) table.add(mforms.newLabel("Default Character Set:"), 0, 1, 1, 2, 0) self.charset = mforms.newSelector() self.charset.add_changed_callback(self.update_preview) l = [""] for ch in grt.root.wb.rdbmsMgmt.rdbms[0].characterSets: l.append(ch.name) self.charset.add_items(sorted(l)) table.add(self.charset, 1, 2, 1, 2, mforms.HFillFlag|mforms.HExpandFlag) help = mforms.newLabel("Default character set to use when executing the script,\nunless specified in the script.") help.set_style(mforms.SmallHelpTextStyle) table.add(help, 2, 3, 1, 2, mforms.HFillFlag) box.add(table, False, True) self.ok = mforms.newButton() self.ok.set_text("Run") self.cancel = mforms.newButton() self.cancel.set_text("Cancel") hbox = mforms.Box(True) hbox.set_spacing(8) mforms.Utilities.add_end_ok_cancel_buttons(hbox, self.ok, self.cancel) box.add_end(hbox, False, True) self.set_content(box) def run(self, file): self.set_title("Run SQL Script - %s" % file) known_schemas = [""] result = self.editor.executeManagementQuery("SHOW SCHEMAS", 0) if result: while result.nextRow(): s = result.stringFieldValue(0) if s not in ["performance_schema", "mysql", "information_schema"]: known_schemas.append(s) self.schema.add_items(sorted(known_schemas)) try: self.preview_data = open(file).read(4098) self.detected_charset = None r = re.compile("/\*[^ ]* SET NAMES ([a-zA-Z0-9_]*) \*/") # try to detect the charset name from the SET NAMES line for line in self.preview_data.split("\n"): if line.startswith("/*"): g = r.match(line) if g: c = g.groups()[0] log_info("Character set of file %s detected to be %s\n" % (file, c)) self.detected_charset = c break self.file_info.set_text("%i total bytes in file, displaying first %i bytes" % (os.stat(file).st_size, len(self.preview_data))) self.update_preview() except Exception as e: mforms.Utilities.show_error("Run SQL Script", str(e), "OK", "", "") return self.set_size(800, 600) self.center() return self.run_modal(self.ok, self.cancel) def get_default_charset(self): c = self.charset.get_string_value().encode("utf8") if c == "Default": return "" return c def get_default_schema(self): return self.schema.get_string_value().encode("utf8") def update_preview(self): c = self.charset.get_string_value() data = self.preview_data if c and c != "utf8": tmp = data while tmp: try: data = tmp.decode(c).encode("utf8") break except (UnicodeDecodeError, LookupError): tmp = tmp[:-1] if not tmp: log_error("Could not convert file %s from %s to utf8\n" % (file, c)) self.text.set_features(mforms.FeatureReadOnly, False) self.text.set_text(data) self.text.set_features(mforms.FeatureReadOnly, True) class RunScriptForm(mforms.Form): def __init__(self, editor): mforms.Form.__init__(self, mforms.Form.main_form(), mforms.FormDialogFrame) self.editor = editor self.logbox = mforms.newTextBox(mforms.VerticalScrollBar) def report(self, text): if text is None: self.ok.set_enabled(True) else: self.logbox.append_text_and_scroll(text, True) def start_import(self, file, default_schema, default_charset): self.panel = RunPanel(self.editor, self.report) self.set_title("Run SQL Script") box = mforms.newBox(False) box.set_padding(12) box.set_spacing(12) box.add(self.panel, False, True) box.add(mforms.newLabel("Output:"), False, True) box.add(self.logbox, True, True) self.ok = mforms.newButton() self.ok.set_text("Close") self.ok.add_clicked_callback(self.close) #self.abort = mforms.newButton() #self.abort.set_text("Abort") hbox = mforms.Box(True) hbox.set_spacing(8) #hbox.add(self.abort, False, True) hbox.add_end(self.ok, False, True) box.add_end(hbox, False, True) self.set_content(box) self.set_size(800, 600) self.center() self.show() self.ok.set_enabled(False) self.panel.start(file, default_schema, default_charset) def run(self): chooser = mforms.FileChooser(mforms.OpenFile) chooser.set_title("Run SQL Script") chooser.set_extensions('SQL Scripts (*.sql)|*.sql', 'sql') if chooser.run_modal(): dlg = ParameterDialog(self.editor) if dlg.run(chooser.get_path()): schema = dlg.get_default_schema() if schema: self.editor.executeManagementCommand("CREATE SCHEMA IF NOT EXISTS `%s`" % schema, 1) self.start_import(chooser.get_path().encode("utf8"), dlg.get_default_schema(), dlg.get_default_charset()) return True return False def run_file(self, path): dlg = ParameterDialog(self.editor) if dlg.run(path): schema = dlg.get_default_schema() if schema: self.editor.executeManagementCommand("CREATE SCHEMA IF NOT EXISTS `%s`" % schema, 1) self.start_import(path, dlg.get_default_schema(), dlg.get_default_charset()) return True return False