# Copyright (c) 2014, 2021, 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 grt import mforms import sys class TemplateEditor(mforms.Form): def __init__(self, owner): mforms.Form.__init__(self, None, mforms.FormDialogFrame|mforms.FormResizable|mforms.FormMinimizable) self.owner = owner # We need to have some default catalog and schema so parser is happy self.tmpCatalog = grt.classes.db_mysql_Catalog() self.tmpCatalog.name = 'tmpCatalog' self.tmpCatalog.oldName = self.tmpCatalog.name self.tmpCatalog.simpleDatatypes.extend(grt.root.wb.rdbmsMgmt.rdbms[0].simpleDatatypes) self.tmpSchema= grt.classes.db_mysql_Schema() self.tmpSchema.name = "tmpSchema" self.tmpSchema.oldName = self.tmpSchema.name self.tmpSchema.owner = self.tmpCatalog self.tmpCatalog.schemata.append(self.tmpSchema) self.tables_by_id = {} self.set_title("Table Templates") box = mforms.newBox(False) box.set_padding(12) box.set_spacing(12) label = mforms.newLabel("Manage templates of tables with pre-defined columns, for frequently used table structures.") box.add(label, False, True) top = mforms.newBox(True) box.add(top, False, True) #top.set_padding(12) top.set_spacing(12) self.template_list = mforms.newTreeView(mforms.TreeFlatList) self.template_list.add_column(mforms.IconStringColumnType, "Table Template", 200, True) self.template_list.end_columns() self.template_list.add_changed_callback(self.table_selected) self.template_list.set_cell_edited_callback(self.table_edited) top.add(self.template_list, True, True) if sys.platform.lower() != "darwin": self.template_list.set_size(-1, 150) bbox = mforms.newBox(False) bbox.set_spacing(8) top.add(bbox, False, True) self.add = mforms.newButton() self.add.set_text("New Template") self.add.add_clicked_callback(self.add_templ) bbox.add(self.add, False, True) self.duplicate = mforms.newButton() self.duplicate.set_text("Duplicate") self.duplicate.add_clicked_callback(self.dup_templ) self.duplicate.set_enabled(False) bbox.add(self.duplicate, False, True) self.delete = mforms.newButton() self.delete.set_text("Delete") self.delete.add_clicked_callback(self.del_templ) self.delete.set_enabled(False) bbox.add(self.delete, False, True) hbox = mforms.newBox(True) hbox.set_spacing(12) self.column_list = mforms.newTreeView(mforms.TreeFlatList) self.column_list.add_column(mforms.IconStringColumnType, "Column", 100, True) self.column_list.add_column(mforms.StringColumnType, "Datatype", 100, True) self.column_list.add_column(mforms.StringColumnType, "Default", 150, True) self.column_list.add_column(mforms.CheckColumnType, "PK", 25, True) self.column_list.add_column(mforms.CheckColumnType, "NN", 25, True) self.column_list.add_column(mforms.CheckColumnType, "UQ", 25, True) self.column_list.add_column(mforms.CheckColumnType, "AI", 25, True) self.column_list.end_columns() self.column_list.set_cell_edited_callback(self.column_edited) self.column_list.add_changed_callback(self.column_selected) hbox.add(self.column_list, True, True) vbox = mforms.newBox(False) vbox.set_spacing(8) vbox.add(mforms.newLabel("Column Collation:"), False, True) self.charset = mforms.newSelector(mforms.SelectorPopup) self.charset.add_changed_callback(self.collation_changed) collations = ["Table Default"] for ch in grt.root.wb.rdbmsMgmt.rdbms[0].characterSets: collations += ch.collations self.charset.add_items(collations) vbox.add(self.charset, False, True) vbox.add(mforms.newLabel("Additional Flags:"), False, True) self.flag_checkboxes = [] hbox.add(vbox, False, True) self.column_details = vbox box.add(hbox, True, True) self.column_menu = mforms.newContextMenu() self.column_menu.add_item_with_title("Delete", self.delete_column) self.column_list.set_context_menu(self.column_menu) bbox = mforms.newBox(True) self.ok = mforms.newButton() self.ok.set_text("Close") bbox.add_end(self.ok, False, True) box.add(bbox, False, True) self.set_content(box) self.set_size(800, 500) self.center() self.refresh_tables() def add_templ(self): table = grt.classes.db_mysql_Table() table.name = "template %i" % (len(self.owner.templates)+1) table.owner = self.tmpSchema self.tmpSchema.tables.append(table) self.tables_by_id[table.__id__] = table self.owner.templates.append(table) node = self.template_list.add_node() node.set_icon_path(0, mforms.App.get().get_resource_path("db.Table.16x16.png")) node.set_string(0, table.name) node.set_tag(table.__id__) self.template_list.select_node(node) self.table_selected() def del_templ(self): node = self.template_list.get_selected_node() if node: table = self.tables_by_id[node.get_tag()] del self.tables_by_id[node.get_tag()] self.owner.templates.remove(table) self.tmpSchema.tables.remove(table) node.remove_from_parent() def dup_templ(self): orig = self.selected_table() table = self.owner.copy_table(orig) table.owner = self.tmpSchema self.tmpSchema.tables.append(table) self.tables_by_id[table.__id__] = table self.owner.templates.append(table) node = self.template_list.add_node() node.set_icon_path(0, mforms.App.get().get_resource_path("db.Table.16x16.png")) node.set_string(0, table.name) node.set_tag(table.__id__) self.template_list.select_node(node) self.table_selected() def delete_column(self): table = self.selected_table() node = self.column_list.get_selected_node() if node and table: i = self.column_list.row_for_node(node) if i < len(table.columns): del table.columns[i] node.remove_from_parent() def select_template(self, name): for i in range(self.template_list.count()): node = self.template_list.node_at_row(i) if node.get_string(0) == name: self.template_list.select_node(node) self.table_selected() break def table_edited(self, node, column, new_value): table = self.selected_table() if table: node.set_string(column, new_value) table.name = new_value def table_selected(self): self.refresh_columns() self.duplicate.set_enabled(self.template_list.get_selected_node()) self.delete.set_enabled(self.template_list.get_selected_node()) def refresh_tables(self): self.tables_by_id = {} icon = mforms.App.get().get_resource_path("db.Table.16x16.png") for table in self.owner.templates: node = self.template_list.add_node() node.set_icon_path(0, icon) node.set_string(0, table.name) node.set_tag(table.__id__) table.owner = self.tmpSchema self.tmpSchema.tables.append(table) self.tables_by_id[table.__id__] = table def selected_table(self): node = self.template_list.get_selected_node() if node: return self.tables_by_id[node.get_tag()] return None def selected_column(self): node = self.column_list.get_selected_node() table = self.selected_table() if node and table: row = self.column_list.row_for_node(node) if row < len(table.columns): return table.columns[row] return None def show_column_node(self, node, column): node.set_icon_path(0, mforms.App.get().get_resource_path("db.Column.16x16.png")) node.set_string(0, column.name) node.set_string(1, column.formattedType) node.set_string(2, "NULL" if column.defaultValue is None else column.defaultValue) node.set_int(3, column.owner.isPrimaryKeyColumn(column)) node.set_int(4, column.isNotNull) node.set_int(5, "UNIQUE" in column.flags) node.set_int(6, column.autoIncrement) return node def refresh_columns(self): self.column_list.clear() table = self.selected_table() if table: for column in table.columns: node = self.column_list.add_node() self.show_column_node(node, column) node = self.column_list.add_node() node.set_string(0, "Click to add") node.set_tag("placeholder") def column_selected(self): column = self.selected_column() if column: self.column_details.set_enabled(True) for c in self.flag_checkboxes: self.column_details.remove(c) self.flag_checkboxes = [] if column.simpleType: for flag in column.simpleType.flags: check = mforms.newCheckBox() check.set_text(flag) check.set_active(flag in column.flags) self.column_details.add(check, False, True) self.flag_checkboxes.append(check) check.add_clicked_callback(lambda check=check, flag=flag:self.flag_checked(check, flag)) if column.simpleType.group.name != "string" and not column.simpleType.name.lower().endswith("text"): self.charset.set_selected(0) self.charset.set_enabled(False) else: self.charset.set_enabled(True) if not column.collationName: self.charset.set_selected(0) else: self.charset.set_value(column.collationName) else: self.charset.set_selected(0) self.column_details.set_enabled(False) for c in self.flag_checkboxes: self.column_details.remove(c) self.flag_checkboxes = [] def flag_checked(self, check, flag): column = self.selected_column() if column: if check.get_active(): if flag not in column.flags: column.flags.append(flag) else: if flag in column.flags: column.flags.remove(flag) def collation_changed(self): column = self.selected_column() if column: if self.charset.get_selected_index() < 1: column.characterSetName = "" column.collationName = "" else: collation = self.charset.get_string_value() column.collationName = collation column.characterSetName = collation.partition("_")[0] def column_edited(self, node, tree_column, new_value): table = self.selected_table() if not table or node.get_string(0) == new_value: return if node.get_tag() == "placeholder": node.set_tag("") node.set_icon_path(0, mforms.App.get().get_resource_path("db.Column.16x16.png")) child = self.column_list.add_node() child.set_string(0, "Click to add") child.set_tag("placeholder") self.column_list.select_node(node) column = grt.classes.db_mysql_Column() column.owner = table table.columns.append(column) self.show_column_node(node, column) else: row = self.column_list.row_for_node(node) if row < len(table.columns): column = table.columns[row] else: return if tree_column == 0: column.name = new_value elif tree_column == 1: column.setParseType(new_value, grt.root.wb.rdbmsMgmt.rdbms[0].simpleDatatypes) elif tree_column == 2: if new_value == 'NULL': column.defaultValueIsNull = True column.defaultValue = None else: column.defaultValueIsNull = False column.defaultValue = new_value elif tree_column == 3: if new_value == '1': table.addPrimaryKeyColumn(column) else: table.removePrimaryKeyColumn(column) elif tree_column == 4: column.isNotNull = new_value == '1' elif tree_column == 5: if new_value == '1' and "UNIQUE" not in column.flags: column.flags.append("UNIQUE") elif new_value != '1' and "UNIQUE" in column.flags: column.flags.remove("UNIQUE") elif tree_column == 6: column.autoIncrement = new_value == '1' node.set_string(tree_column, new_value) def run(self): self.run_modal(None, self.ok) class TableTemplateManager: @property def templates(self): tlist = grt.root.wb.options.options.get("TableTemplates", None) if not tlist: tlist = grt.List() grt.root.wb.options.options["TableTemplates"] = tlist return tlist def export_templates(self): dlg = mforms.FileChooser(mforms.SaveFile) dlg.set_title("Export Table Templates") if dlg.run_modal(): grt.serialize(self.templates, dlg.get_path()) def copy_table(self, orig): table = grt.classes.db_mysql_Table() table.name = orig.name+"copy" for col in orig.columns: colcopy = col.shallow_copy() colcopy.owner = table table.columns.append(colcopy) if orig.isPrimaryKeyColumn(col): table.addPrimaryKeyColumn(colcopy) return table def edit_templates(self): ed = TemplateEditor(self) ed.run() def edit_template(self, name): ed = TemplateEditor(self) ed.select_template(name) ed.run() def create_table_like_template_in_schema(self, schema, template_name): template = None for t in self.templates: if t.name == template_name: template = t break if template: copy = self.copy_table(template) new_name = template_name i = 1 while any(t.name == new_name for t in schema.tables): new_name = "%s_%i" % (template_name, i) i += 1 copy.name = new_name copy.owner = schema schema.tables.append(copy) return copy return None def create_table_like_template(self, editor, schema_name, template): copy = self.copy_table(template) copy.name = template.name ocatalog = grt.classes.db_mysql_Catalog() ocatalog.name = 'default' ocatalog.oldName = ocatalog.name ocatalog.simpleDatatypes.extend(grt.root.wb.rdbmsMgmt.rdbms[0].simpleDatatypes) oschema = grt.classes.db_mysql_Schema() oschema.name = schema_name oschema.oldName = oschema.name oschema.owner = ocatalog ocatalog.schemata.append(oschema) catalog = grt.classes.db_mysql_Catalog() catalog.name = 'default' catalog.oldName = catalog.name catalog.simpleDatatypes.extend(grt.root.wb.rdbmsMgmt.rdbms[0].simpleDatatypes) schema = grt.classes.db_mysql_Schema() schema.name = schema_name schema.oldName = schema.name schema.owner = catalog catalog.schemata.append(schema) copy.owner = schema schema.tables.append(copy) if editor: editor.editLiveObject(copy, ocatalog) def create_table_like(self, editor, schema_name, table_name): pass