-- ---------------------------------------------------------------------------------------- -- Copyright (C) 2004 MySQL AB -- -- This program is free software; you can redistribute it and/or modify -- it under the terms of the GNU General Public License as published by -- the Free Software Foundation; either version 2 of the License, or -- (at your option) any later version. -- -- 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 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -- ---------------------------------------------------------------------------------------- -- ---------------------------------------------------------------------------------------- -- @file Workbench.lua -- @brief Module that contains program logic for MySQL Workbench -- ---------------------------------------------------------------------------------------- -- ---------------------------------------------------------------------------------------- -- @brief Returns the information about this module -- -- Every Grt module has to implement this function to return information about the -- module. Note that new functions that should be exposed to the Grt have to be listed -- here. Function that are not exposed should start with a underscore. -- -- @return A dict that contains the name and the function names of the module -- ---------------------------------------------------------------------------------------- function getModuleInfo() local moduleInfo= { name= "Workbench", functions= { -- workbench commands "initWorkbench::", "shutdownWorkbench::", "registerEditors::", "registerPlugins::", "newDocument::", "loadDocumentData::", "clearDocument::", "createModelForSchema::", -- element placement "placeNewTableElement::", "placeNewViewElement::", "placeNewRoutineGroupElement::", "placeNewNoteElement::", "placeNewImageElement::", "placeNewLayerElement::", "placeElementFromObject::", "relationshipStart::", "relationshipClickedPoint::", "relationshipCancel::", "getColorPresets::", -- layouting "verticalAlign::", "horizontalAlign::", "autoSpaceElements::", "centerElementsAt::", -- others "arrangeObjectsOnCurrentView::", "arrangeSchemaOnNewView::", "arrangeSchemaOnCurrentView::", "loadAndAutoArrange::", "loadAndAutoArrangeInNewView::", "addView", "deleteObjectAtPoint::", "deleteSelection::", "deleteRelationship::", "selectLayer::", "addRoutine::", "createTrigger::", "raiseObjectAtPoint::", "selectAll::", "bringSelectionToFront::", "sendSelectionToBack::", "collapseElements::", "expandElements::", "deleteFKsWithTable::", "createRelationshipsFromFKs::", "addPluginToAppPluginGroups::", -- synchronization aux "collectSqlScript::", -- export "generateSqlCreateScript::" }, extends= '' } return moduleInfo end -- ---------------------------------------------------------------------------------------- -- @brief Initalizes the application -- -- Creates -- -- @return success -- ---------------------------------------------------------------------------------------- function initWorkbench(args) -- get infos about all available RDBMS local rdbmsMgmt= RdbmsManagement:getManagementInfo() grtV.setGlobal("/rdbmsMgmt", rdbmsMgmt) -- create main workbench object local workbench= grtV.newObj("db.workbench.Workbench", "Workbench", "", "") grtV.setGlobal("/workbench", workbench) -- set the model version workbench.version= 2 -- register plugins registerPlugins({{"database", "workbench"}}) -- free cached GRT values on Lua side collectgarbage() return grt.success() end -- ---------------------------------------------------------------------------------------- -- @brief Performs actions when the Workbench is shut down -- -- Performs several actions shortly before the application is shut down -- -- @return 1 on success or an error -- ---------------------------------------------------------------------------------------- function shutdownWorkbench(args) RdbmsManagement:storeConns({grtV.getGlobal("/rdbmsMgmt/storedConns")}) return grt.success() end -- ---------------------------------------------------------------------------------------- -- @brief Registers GUI editors for objects -- -- Registers several editors that will be used when Base:editObj() is called -- -- @return 1 on success or an error -- ---------------------------------------------------------------------------------------- function registerEditors(args) local app= grtV.getGlobal("/app") local EditorList= app.editors local Editor local EDIT_ACTION=1001 local LOCK_ACTION=1002 local PLUGINS_ACTION=1003 if EditorList == nil then EditorList= grtV.newList("dict", "base.ObjectEditor") grtV.setGlobal("/app/editors", EditorList) end -- db.Schema Editor= grtV.newObj("base.ObjectEditor", "SchemaEditor", "editor://com.mysql.grt.db.Schema", "") Editor.moduleName= "WorkbenchUi" Editor.moduleFunctionName= "editSchema" Editor.objectStructName= "db.Schema" Editor.rating= 1 Editor.actionId= EDIT_ACTION grtV.insert(EditorList, Editor) -- db.Table Editor= grtV.newObj("base.ObjectEditor", "TableEditor", "editor://com.mysql.grt.db.Table", "") Editor.moduleName= "WorkbenchUi" Editor.moduleFunctionName= "editTable" Editor.objectStructName= "db.Table" Editor.rating= 1 Editor.actionId= EDIT_ACTION grtV.insert(EditorList, Editor) -- db.View Editor= grtV.newObj("base.ObjectEditor", "ViewEditor", "editor://com.mysql.grt.db.View", "") Editor.moduleName= "WorkbenchUi" Editor.moduleFunctionName= "editView" Editor.objectStructName= "db.View" Editor.rating= 1 Editor.actionId= EDIT_ACTION grtV.insert(EditorList, Editor) -- db.Routine Editor= grtV.newObj("base.ObjectEditor", "RoutineEditor", "editor://com.mysql.grt.db.Routine", "") Editor.moduleName= "WorkbenchUi" Editor.moduleFunctionName= "editRoutine" Editor.objectStructName= "db.Routine" Editor.rating= 1 Editor.actionId= EDIT_ACTION grtV.insert(EditorList, Editor) -- db.RoutineGroup Editor= grtV.newObj("base.ObjectEditor", "RoutineGroupEditor", "editor://com.mysql.grt.db.RoutineGroup", "") Editor.moduleName= "WorkbenchUi" Editor.moduleFunctionName= "editRoutineGroup" Editor.objectStructName= "db.RoutineGroup" Editor.rating= 1 Editor.actionId= EDIT_ACTION grtV.insert(EditorList, Editor) Editor= grtV.newObj("base.ObjectEditor", "PluginsMenuHandler", "editor://com.mysql.grt.PluginsMenu", "") Editor.moduleName= "WorkbenchUi" Editor.moduleFunctionName= "popupPluginsMenu" Editor.objectStructName= "db.DatabaseObject" Editor.rating= 1 Editor.actionId= PLUGINS_ACTION grtV.insert(EditorList, Editor) return grt.success(EditorList) end -- ---------------------------------------------------------------------------------------- -- @brief -- -- @param path -- @param @pluginId -- -- @return 1 on success or an error -- ---------------------------------------------------------------------------------------- function addPluginToAppPluginGroups(args) local currentGroupList local currentGroup local path local i local p local app= grtV.getGlobal("/app") path= grtV.toLua(args[1]) id= grtV.toLua(args[2]) -- if app.pluginHierachy == nil then -- grtV.setGlobal("/app/pluginHierarchy", grtV.newList("dict", "base.PluginGroup")) -- end currentGroupList= grtV.getGlobal("/app/pluginGroups") currentGroup= nil -- ensure the subtree for the path exists p = 1 for i= 1, string.len(path) do if ((string.sub(path, i, i) == "/") or (i == string.len(path))) and (i > p) then local subGroupName if(string.sub(path, i, i) == "/") then subGroupName = string.sub(path, p, i-1) else subGroupName = string.sub(path, p, i) end local subGroup= grtV.getListItemByObjName(currentGroupList, subGroupName) if(subGroup == nil) then subGroup= grtV.newObj("base.PluginGroup", subGroupName, "", ""); grtV.insert(currentGroupList, subGroup) -- grtV.insert(app.pluginGroups, subGroup) end currentGroup= subGroup currentGroupList= subGroup.subGroups p = i + 1 end end if currentGroup ~= nil then if currentGroup.plugins == nil then currentGroup.plugins= grtV.newList("string", "") end -- add ref to the plugin grtV.insert(currentGroup.plugins, id) return grt.success() end return grt.error("addPluginToAppPluginGroups failed") end -- ---------------------------------------------------------------------------------------- -- @brief Scans for and registers plugins -- -- Gets plugin information from all modules that implement "PluginInfo". The -- categoryList parameter is used to filter the plugins so only plugins written for this -- application are registerd. -- -- @param categoryList a list of plugin categories that should be registerd -- -- @return 1 on success or an error -- ---------------------------------------------------------------------------------------- function registerPlugins(args) local categoryList= args[1] local categories local app= grtV.getGlobal("/app") if grtV.typeOf(categoryList) == "list" then categories= grtV.toLua(categoryList) else categories= categoryList end if app.pluginGroups == nil then grtV.setGlobal("/app/pluginGroups", grtV.newList("dict", "base.PluginGroup")) end if app.plugins == nil then grtV.setGlobal("/app/plugins", grtV.newList("dict", "base.Plugin")) end -- scan all modules that implement PluginInfo local modules= grtM.get("PluginInfo") local i -- Call the getPluginInfo function of all modules that extend PluginInfo for i= 1, table.getn(modules) do local plugins= grtM.callFunction(modules[i], "getPluginInfo") local j for j= 1, grtV.getn(plugins) do local plugin= plugins[j] local k local found= false -- check if one category of the plugin is in the categoryList for k= 1, table.getn(categories) do local pluginCategories= plugin.categories local l for l= 1, grtV.getn(pluginCategories) do if categories[l] == grtV.toLua(pluginCategories[l]) then found= true break end end if found then break end end -- add plugin if found then grtV.insert(app.plugins, plugin) addPluginToAppPluginGroups({plugin.groupPath, plugin._id}) end end end collectgarbage() return grt.success() end -- ---------------------------------------------------------------------------------------- -- @brief Sets up the workbench model with a new, empty document. -- -- -- @param rdbmsName name of the db.mgmt.Rdbms to use -- -- ---------------------------------------------------------------------------------------- function newDocument(args) local rdbmsName= (args[1] ~= nil) and grtV.toLua(args[1]) or "" if rdbmsName == "" then rdbmsName= "Mysql" end local rdbmsVersion= (args[2] ~= nil) and grtV.toLua(args[2]) or "5.1.6" -- Find model RDBMS local rdbmsMgmt= grtV.getGlobal("/rdbmsMgmt") local rdbms= nil local i for i= 1, grtV.getn(rdbmsMgmt.rdbms) do local rdbmsItem= rdbmsMgmt.rdbms[i] if (rdbmsItem.name ~= nil) and (grtV.toLua(rdbmsItem.name) == rdbmsName) then rdbms= rdbmsItem break end end if rdbms == nil then return grt.error(string.format( _("There is no RDBMS with the name %s in the /rdbmsMgmt/rdbms list."), rdbmsName)) end -- get workbench and set RDBMS local workbench= grtV.getGlobal("/workbench") workbench.rdbms= rdbms -- remove old views if present if workbench.model.views ~= nil then for i= grtV.getn(workbench.model.views), 1, -1 do grtV.remove(workbench.model.views, i) end end -- initalize the catalog local catalog -- build struct names local databaseObjectPackage= grtV.toLua(rdbms.databaseObjectPackage) local catalogStructName= databaseObjectPackage .. ".Catalog" local schemaStructName= databaseObjectPackage .. ".Schema" -- check if structs exists if ((not grtS.exists(catalogStructName)) or (not grtS.exists(schemaStructName))) then return grt.error(string.format(_("The struct definition for %s or %s cannot be found."), catalogStructName, schemaStructName)) end catalog= grtV.newObj(catalogStructName, "default", "", grtV.toLua(workbench._id)) catalog.oldName= "default" -- set catalog version local versionSplit= grt.split(rdbmsVersion, "%.") catalog.version= grtV.newObj("db.Version", "Version", "", grtV.toLua(catalog._id)) if table.getn(versionSplit) >= 1 then catalog.version.major= tonumber(versionSplit[1]) end if table.getn(versionSplit) >= 2 then catalog.version.minor= tonumber(versionSplit[2]) end if table.getn(versionSplit) >= 3 then catalog.version.release= tonumber(versionSplit[3]) end -- fill reference lists for i= 1, grtV.getn(rdbms.simpleDatatypes) do grtV.insert(catalog.simpleDatatypes, rdbms.simpleDatatypes[i]._id) end for i= 1, grtV.getn(rdbms.characterSets) do grtV.insert(catalog.characterSets, rdbms.characterSets[i]._id) end -- create a schema for the catalog and add it to the model local schema= grtV.newObj(schemaStructName, "test", "", grtV.toLua(catalog._id)) schema.oldName= "test" schema.defaultCharacterSetName= "latin1" -- schema.defaultCollationName= "latin1_swedish_ci" grtV.insert(catalog.schemata, schema) workbench.catalog= catalog -- initialize properties struct local info= grtV.newObj("db.workbench.ModelInfo", "Properties", "", grtV.toLua(workbench.model._id)) info.modelName= "New Model" info.version= "1.0" info.project= "Name of the project" info.dateCreated= os.date("%b %d, %Y") info.dateChanged= os.date("%b %d, %Y") workbench.model.properties= info -- initalize the model by creating a view (the creation of the view automatically triggers -- the creation of the view's rootLayer) local view= grtV.newObj("db.workbench.View", "Main View", "", grtV.toLua(workbench.model._id)) -- view.rootLayer.description= _("The root layer of this view that contains all other layers.") -- initialize workbench data dict workbench.customData= grtV.newDict("", "") collectgarbage() return grt.success() end function recreateElementDefaultProperties(soure, target) target.top= grtV.toLua(soure.top) target.left= grtV.toLua(soure.left) target.width= grtV.toLua(soure.width) target.height= grtV.toLua(soure.height) target.visible= grtV.toLua(soure.visible) target.enabled= grtV.toLua(soure.enabled) target.locked= grtV.toLua(soure.locked) target.expanded= grtV.toLua(soure.expanded) end function recreateElement(wb, info) local type= grtS.get(info) local elem= nil if type == "db.workbench.TableElement" then elem= grtV.newObj("db.workbench.TableElement", grtV.toLua(info.name), "", "") recreateElementDefaultProperties(info, elem) elem.columnsExpanded= grtV.toLua(info.columnsExpanded) elem.indicesExpanded= grtV.toLua(info.indicesExpanded) elem.foreignKeysExpanded= grtV.toLua(info.foreignKeysExpanded) elem.triggersExpanded= grtV.toLua(info.triggersExpanded) elem.color= grtV.toLua(info.color) elem.table= info.table elseif type == "db.workbench.ViewElement" then elem= grtV.newObj("db.workbench.ViewElement", grtV.toLua(info.name), "", "") recreateElementDefaultProperties(info, elem) elem.view= info.view elseif type == "db.workbench.RoutinesElement" then elem= grtV.newObj("db.workbench.RoutinesElement", grtV.toLua(info.name), "", "") recreateElementDefaultProperties(info, elem) elem.routineGroup= info.routineGroup elseif type == "db.workbench.NoteElement" then elem= grtV.newObj("db.workbench.NoteElement", grtV.toLua(info.name), "", "") recreateElementDefaultProperties(info, elem) elem.color= grtV.toLua(info.color) elem.text= grtV.toLua(info.text) else print("Unknown element type: "..type) return nil end if elem ~= nil then end return elem end function migrateDoc(doc) -- version 2 if (doc.version == nil) or (grtV.toLua(doc.version) < 2) then -- table's primaryKeys have changed to indices local i for i= 1, grtV.getn(doc.catalog.schemata) do local tbls= doc.catalog.schemata[i].tables local j for j= 1, grtV.getn(tbls) do local tbl= tbls[j] local idxStructName= grtS.getMemberContentStruct(grtS.get(tbl), "indices") local idx= grtV.newObj(idxStructName, "PRIMARY", "", grtV.toLua(tbl._id)) local idxColStructName= grtS.getMemberContentStruct(grtS.get(tbl), "columns") local k idx.isPrimary= 1 idx.unique= 1 for k= 1, grtV.getn(tbl.primaryKey.columns) do local col_id= grtV.toLua(tbl.primaryKey.columns[k]) local l local col= nil for l=1, grtV.getn(tbl.columns) do if col_id == grtV.toLua(tbl.columns[l]._id) then col= tbl.columns[l] break end end if col ~= nil then local idxCol= grtV.newObj(idxColStructName, grtV.toLua(col.name), "", grtV.toLua(idx._id)) idxCol.referedColumn= col_id grtV.insert(idx.columns, idxCol) end end tbl.primaryKey= idx grtV.insert(tbl.indices, idx) end end doc.version= 2 end end function fixupDocument(wb) if wb.model.properties == nil then -- check properties struct local info= grtV.newObj("db.workbench.ModelInfo", "Properties", "", grtV.toLua(wb.model._id)) info.modelName= "New Model" info.version= "1.0" info.project= "Name of the project" wb.model.properties= info end end function clearDocument(args) local wb= grtV.getGlobal("/workbench") -- remove old views if present if wb.model.views ~= nil then for i= grtV.getn(wb.model.views), 1, -1 do grtV.remove(wb.model.views, i) end end collectgarbage() return grt.success() end function loadDocumentData(args) local wb= grtV.getGlobal("/workbench") local doc= args[1] local view, vinfo local layer, linfo local elem, einfo local rel, rinfo local model local i, vc local j, lc local r, rc local ec local tableMap= {} -- fix any incompatibilities with files saved with older versions fixupDocument(wb) -- remove old views if present if wb.model.views ~= nil then for i= grtV.getn(wb.model.views), 1, -1 do grtV.remove(wb.model.views, i) end end collectgarbage() -- migrate catalog migrateDoc(doc) -- set the catalog subtree to the loaded one wb.catalog= doc.catalog -- set plugin data if doc.customData ~= nil then wb.customData= doc.customData else wb.customData= grtV.newDict("", "") end -- set the rdbms subtree to the loaded one wb.rdbms= doc.rdbms -- load diagram stuff model= doc.model vc= grtV.getn(model.views) for i=1, vc do -- recreate the view vinfo= model.views[i] view= grtV.newObj("db.workbench.View", grtV.toLua(vinfo.name), "", grtV.toLua(wb.model._id)) -- set attributes if vinfo.elementLayoutClass ~= nil then view.elementLayoutClass= grtV.toLua(vinfo.elementLayoutClass) end if vinfo.connectionLayoutClass ~= nil then view.connectionLayoutClass= grtV.toLua(vinfo.connectionLayoutClass) end -- recreate layers in the view -- dont create the root layer because it's auto-created by the view lc= grtV.getn(vinfo.layers) for j=2,lc do linfo= vinfo.layers[j] layer= grtV.newObj("db.workbench.Layer", grtV.toLua(linfo.name), "", grtV.toLua(wb.model._id)) layer.top= grtV.toLua(linfo.top) layer.left= grtV.toLua(linfo.left) layer.width= grtV.toLua(linfo.width) layer.height= grtV.toLua(linfo.height) if linfo.color ~= nil then layer.color= grtV.toLua(linfo.color) end layer.description= grtV.toLua(linfo.description) grtV.insert(view.layers, layer) end -- recreate elements in the view ec= grtV.getn(vinfo.elements) for j=1,ec do einfo= vinfo.elements[j] elem = recreateElement(wb, einfo) if elem ~= nil then grtV.insert(view.elements, elem) elem.visible= einfo.visible if grtS.get(einfo) == "db.workbench.TableElement" then tableMap[grtV.toLua(einfo._id)] = elem end end end -- update recreated relationships rc= grtV.getn(vinfo.relationships) for r=1, rc do local startTableElem, endTableElem rinfo= vinfo.relationships[r] startTableElem= tableMap[grtV.toLua(rinfo.startTable)] endTableElem= tableMap[grtV.toLua(rinfo.endTable)] if (startTableElem == nil) or (endTableElem == nil) then print("Ignoring invalid relationship in loaded document: "..grtV.toLua(rinfo.name)) else rel= grtV.newObj("db.workbench.Relationship", grtV.toLua(rinfo.name), "", grtV.toLua(wb.model._id)) rel.startTable= startTableElem rel.endTable= endTableElem rel.startMany= grtV.toLua(rinfo.startMany) rel.endMany= grtV.toLua(rinfo.endMany) rel.startMandatory= grtV.toLua(rinfo.startMandatory) rel.endMandatory= grtV.toLua(rinfo.endMandatory) rel.startCaption= grtV.toLua(rinfo.startCaption) rel.startCaptionXOffs= grtV.toLua(rinfo.startCaptionXOffs) rel.startCaptionYOffs= grtV.toLua(rinfo.startCaptionYOffs) rel.endCaption= grtV.toLua(rinfo.endCaption) rel.endCaptionXOffs= grtV.toLua(rinfo.endCaptionXOffs) rel.endCaptionYOffs= grtV.toLua(rinfo.endCaptionYOffs) rel.caption= grtV.toLua(rinfo.caption) rel.captionXOffs= grtV.toLua(rinfo.captionXOffs) rel.captionYOffs= grtV.toLua(rinfo.captionYOffs) rel.comment= grtV.toLua(rinfo.comment) grtV.insert(view.relationships, rel) end end end -- createRelationshipsFromFKs({view}) return grt.success() end function createModelForSchema(args) local schema= args[1] local view= grtV.getGlobal("/workbench/model/currentView") local i for i,table in schema.tables do local elem elem= grtV.newObj("db.workbench.TableElement", grtV.toLua(table.name), "", "") elem.left= math.mod(i, 10) * 200 elem.top= (i/10) * 200 elem.table= table grtV.insert(view.elements, elem) elem.visible= 1 end return grt.success() end function createPrintPreview(args) local view end -- ======================================================================================== function getCharsetFromCollation(collation) if collation == "binary" then return "binary" else return string.sub(collation, 1, string.find(collation, "_", 1, true) - 1) end end -- ---------------------------------------------------------------------------------------- -- @brief Creates a new table and places it in the indicated position in the current -- canvas view. -- -- @param top Coordinate for the top of the table -- @param left Coordinate for the left of the table -- @param name Initial name of the table to be created -- @param schema the GRT schema value the table belongs to -- @param engine the name of the engine to use, is optional -- @param collation the name of thecollation to use, is optional -- @param color the name of style color, is optional -- -- @return the table object on success or an error -- ---------------------------------------------------------------------------------------- function placeNewTableElement(args) local left= args[1] local top= args[2] local name= args[3] local schema= args[4] -- optional engine parameter local engine= (grtV.getn(args) >= 5) and args[5] or nil -- optional collation parameter local collation= (grtV.getn(args) >= 6) and args[6] or nil local color= (grtV.getn(args) >= 7) and args[7] or "blue" local tableStructName local tbl tableStructName= grtV.toLua(grtV.getGlobal("/workbench/rdbms/databaseObjectPackage")) .. ".Table" tbl= grtV.newObj(tableStructName, grtV.toLua(name), "", grtV.toLua(schema._id)) tbl.oldName= grtV.toLua(name) if engine ~= nil then tbl.tableEngine= engine end if collation ~= nil then tbl.defaultCharacterSetName= getCharsetFromCollation(grtV.toLua(collation)) tbl.defaultCollationName= collation end grtV.insert(schema.tables, tbl) -- place element local elem= placeElementFromObject({tbl, left, top}).value elem.color= color elem.visible= 1 return grt.success(elem) end function placeNewViewElement(args) local left= args[1] local top= args[2] local name= args[3] local schema= args[4] local view local viewStructName viewStructName= grtV.toLua(grtV.getGlobal("/workbench/rdbms/databaseObjectPackage")) .. ".View" view= grtV.newObj(viewStructName, grtV.toLua(name), "", grtV.toLua(schema._id)) grtV.insert(schema.views, view) -- place element local elem= placeElementFromObject({view, left, top}).value elem.visible= 1 return grt.success(elem) end function placeNewRoutineGroupElement(args) local left= args[1] local top= args[2] local name= args[3] local schema= args[4] local routineGroup local routineStructName routineStructName= grtV.toLua(grtV.getGlobal("/workbench/rdbms/databaseObjectPackage")) .. ".RoutineGroup" routineGroup= grtV.newObj(routineStructName, grtV.toLua(name), "", grtV.toLua(schema._id)) grtV.insert(schema.routineGroups, routineGroup) -- place element local elem= placeElementFromObject({routineGroup, left, top}).value elem.visible= 1 return grt.success(elem) end function formatSourceDestTableIdentifier(fmt, stable, dtable) local stableName= grtV.toLua(stable.name) local dtableName= grtV.toLua(dtable.name) return grtU.replace(grtU.replace(fmt, "%stable%", stableName), "%dtable%", dtableName) end function formatTableIdentifier(fmt, table) local tableName= grtV.toLua(table.name) local schemaName= grtV.toLua(table.owner.name) return grtU.replace(grtU.replace(fmt, "%table%", tableName), "%schema%", schemaName) end function formatColumnIdentifier(fmt, table, column) local tableName= grtV.toLua(table.name) local schemaName= grtV.toLua(table.owner.name) local columnName= grtV.toLua(column.name) return grtU.replace(grtU.replace(grtU.replace(fmt, "%table%", tableName), "%schema%", schemaName), "%column%", columnName) end -- ---------------------------------------------------------------------------------------- -- @brief Initiates relationship creation, creating a new context to be used during it. -- -- @param relType specifies the relationship type, "1n", "11" or "nm" -- @param relSourceOptional specifies wether the relationship source is optional -- @param relTargetOptional specifies wether the relationship target is optional -- -- @return returns a context object which should be used in the other related functions -- ---------------------------------------------------------------------------------------- function relationshipStart(args) local relType= grtV.toLua(args[1]) local relSourceOptional= grtV.toLua(args[2]) local relTargetOptional= grtV.toLua(args[3]) return grt.success({ relType= relType, relSourceOptional= relSourceOptional, relTargetOptional= relTargetOptional, step= 0, mapping= grtV.newList("list"), pkcount= {} }) end function createFKFromTablePKs(sourceTable, destTable) local i, c local newFK local pk= sourceTable.primaryKey local opts= grtV.getGlobal("/app/options") local fkNameFormat= grtV.toLua(opts.FKNameTemplate) or "FK%table%" local fkColumnNameFormat= grtV.toLua(opts.FKColumnNameTemplate) or "FK%table%%column%" -- check if there is a PK if (pk == nil) or (grtV.toLua(pk) == "") then return -1 end -- create a new FK newFK= grtV.newObj("db.ForeignKey", formatTableIdentifier(fkNameFormat, sourceTable), "", grtV.toLua(destTable._id)) newFK.deleteRule = grtV.toLua(opts.FKDeleteRule) or "NO ACTION" newFK.updateRule = grtV.toLua(opts.FKUpdateRule) or "NO ACTION" newFK.referedTable= sourceTable newFK.referedTableName= sourceTable.name c= grtV.getn(pk.columns) for i=1,c do local pkCol= pk.columns[i] local column= pkCol.referedColumn local newColumn newColumn= grtV.newObj(grtS.get(column), formatColumnIdentifier(fkColumnNameFormat, sourceTable, column), "", grtV.toLua(destTable._id)) -- copy column stuff DbUtils:copyColumnType({newColumn, column}) -- add to the table grtV.insert(destTable.columns, newColumn) -- add the new column to the FK grtV.insert(newFK.columns, newColumn._id) grtV.insert(newFK.referedColumns, column._id) grtV.insert(newFK.referedColumnNames, grtV.toLua(column.name)) end -- add the FK to the table if destTable.foreignKeys == nil then local fkList= grtV.newList("dict", grtS.get(newFK)) destTable.foreignKeys= fkList end grtV.insert(destTable.foreignKeys, newFK) return 1 end function createNMRelationshipFromTablePKs(sourceTableElem, destTableElem) local i, c local sfk, dfk local sourceTable= sourceTableElem.table local destTable= destTableElem.table local spk= sourceTable.primaryKey local dpk= destTable.primaryKey local opts= grtV.getGlobal("/app/options") local auxTableFormat= grtV.toLua(opts.AuxTableTemplate) or "%stable%_has_%dtable%" local fkNameFormat= grtV.toLua(opts.FKNameTemplate) or "FK%table%" local fkColumnNameFormat= grtV.toLua(opts.FKColumnNameTemplate) or "FK%table%%column%" local auxTable -- check if there is a PK if (spk == nil) or (grtV.toLua(spk) == "") then return -1 end if (dpk == nil) or (grtV.toLua(dpk) == "") then return -1 end -- create the aux. table auxTable= grtV.newObj(grtS.get(sourceTable), formatSourceDestTableIdentifier(auxTableFormat, sourceTable, destTable), "", grtV.toLua(sourceTable.owner._id)) auxTable.tableEngine= sourceTable.tableEngine auxTable.oldName= auxTable.name grtV.insert(sourceTable.owner.tables, auxTable) -- create a new FK pointing to source table and one to the dest table sfk= grtV.newObj("db.ForeignKey", formatTableIdentifier(fkNameFormat, sourceTable), "", grtV.toLua(auxTable._id)) sfk.deleteRule = grtV.toLua(opts.FKDeleteRule) or "NO ACTION" sfk.updateRule = grtV.toLua(opts.FKUpdateRule) or "NO ACTION" sfk.referedTable= sourceTable sfk.referedTableName= sourceTable.name c= grtV.getn(spk.columns) for i=1,c do local pkCol= spk.columns[i] local column= pkCol.referedColumn local newColumn newColumn= grtV.newObj(grtS.get(column), formatColumnIdentifier(fkColumnNameFormat, sourceTable, column), "", grtV.toLua(auxTable._id)) -- copy column stuff DbUtils:copyColumnType({newColumn, column}) -- add to the table grtV.insert(auxTable.columns, newColumn) -- add the new column to the FK grtV.insert(sfk.columns, newColumn._id) grtV.insert(sfk.referedColumns, column._id) grtV.insert(sfk.referedColumnNames, grtV.toLua(column.name)) end dfk= grtV.newObj("db.ForeignKey", formatTableIdentifier(fkNameFormat, destTable), "", grtV.toLua(auxTable._id)) dfk.deleteRule = grtV.toLua(opts.FKDeleteRule) or "NO ACTION" dfk.updateRule = grtV.toLua(opts.FKUpdateRule) or "NO ACTION" dfk.referedTable= destTable dfk.referedTableName= destTable.name c= grtV.getn(dpk.columns) for i=1,c do local pkCol= dpk.columns[i] local column= pkCol.referedColumn local newColumn newColumn= grtV.newObj(grtS.get(column), formatColumnIdentifier(fkColumnNameFormat, destTable, column), "", grtV.toLua(auxTable._id)) -- copy column stuff DbUtils:copyColumnType({newColumn, column}) -- add to the table grtV.insert(auxTable.columns, newColumn) -- add the new column to the FK grtV.insert(dfk.columns, newColumn._id) grtV.insert(dfk.referedColumns, column._id) grtV.insert(dfk.referedColumnNames, grtV.toLua(column.name)) end -- add the FKs to the table if auxTable.foreignKeys == nil then local fkList= grtV.newList("dict", grtS.get(sfk)) auxTable.foreignKeys= fkList end grtV.insert(auxTable.foreignKeys, sfk) grtV.insert(auxTable.foreignKeys, dfk) -- create a figure element for the aux table and place it between the -- source and dest tables local tableElem local top, left top= (grtV.toLua(sourceTableElem.top) + grtV.toLua(destTableElem.top))/2 left= (grtV.toLua(sourceTableElem.left) + grtV.toLua(destTableElem.left))/2 tableElem= placeElementFromObject({auxTable, left, top}).value tableElem.visible= 1 return 1 end function createFKFromMapping(context) local i, c local newFK local opts= grtV.getGlobal("/app/options") local fkNameFormat= grtV.toLua(opts.FKNameTemplate) or "FK%table%" local fkColumnNameFormat= grtV.toLua(opts.FKColumnNameTemplate) or "FK%table%%column%" local sourceTable= context.sourceTable local destTable= context.destTable -- create a new FK newFK= grtV.newObj("db.ForeignKey", formatTableIdentifier(fkNameFormat, sourceTable), "", grtV.toLua(destTable._id)) newFK.deleteRule = grtV.toLua(opts.FKDeleteRule) or "NO ACTION" newFK.updateRule = grtV.toLua(opts.FKUpdateRule) or "NO ACTION" newFK.referedTable= sourceTable newFK.referedTableName= sourceTable.name c= grtV.getn(context.mapping) for i=1,c do local srcColumn= context.mapping[i][1] local dstColumn= context.mapping[i][2] if (dstColumn == nil) or (grtV.toLua(dstColumn) == "") then dstColumn= grtV.newObj(grtS.get(srcColumn), formatColumnIdentifier(fkColumnNameFormat, sourceTable, srcColumn), "", grtV.toLua(destTable._id)) -- copy column stuff dstColumn.datatypeName= srcColumn.datatypeName dstColumn.precision= srcColumn.precision dstColumn.scale= srcColumn.scale dstColumn.length= srcColumn.length dstColumn.datatypeExplicitParams= srcColumn.datatypeExplicitParams -- add to the table grtV.insert(destTable.columns, dstColumn) end grtV.insert(newFK.columns, dstColumn._id) grtV.insert(newFK.referedColumns, srcColumn._id) grtV.insert(newFK.referedColumnNames, grtV.toLua(srcColumn.name)) end -- add the FK to the table if destTable.foreignKeys == nil then local fkList= grtV.newList("dict", grtS.get(newFK)) destTable.foreignKeys= fkList end grtV.insert(destTable.foreignKeys, newFK) end function tableColumnIsPK(table, column) local i, c if table.primaryKey == nil then return 0 end c= grtV.getn(table.primaryKey.columns) for i=1,c do if grtV.toLua(table.primaryKey.columns[i]) == grtV.toLua(column._id) then return 1 end end return 0 end function compareColumnTypes(column1, column2) if grtV.toLua(column1.datatypeName) ~= grtV.toLua(column2.datatypeName) then return 0 end if grtV.toLua(column1.datatypeExplicitParams) ~= grtV.toLua(column2.datatypeExplicitParams) then return 0 end if grtV.toLua(column1.length) ~= grtV.toLua(column2.length) then return 0 end if grtV.toLua(column1.precision) ~= grtV.toLua(column2.precision) then return 0 end if grtV.toLua(column1.scale) ~= grtV.toLua(column2.scale) then return 0 end return 1 end -- ---------------------------------------------------------------------------------------- -- @brief Must be called to handle click events in the canvas during relationship creations. -- -- @param context the relationship creation context -- @param x the X coordinate where the click occurred -- @param y the Y coordinate where the click occurred -- -- @return returns a status message which should be displayed to the user on success -- ---------------------------------------------------------------------------------------- function relationshipClickedPoint(args) local context= args[1] local x= grtV.toLua(args[2]) local y= grtV.toLua(args[3]) local result local msg= "" local error= 0 local view= grtV.lookupId(grtV.getGlobal("/workbench/model/currentView")) result= WorkbenchController:objectAtPoint({x,y}) if (result ~= nil) and (result.element ~= nil) and (grtS.get(result.element) == "db.workbench.TableElement") then local table= result.element.table local tableElem= result.element if grtV.toLua(context.step) == 0 then -- select source object local column= nil if result.detail ~= nil then local columnStructName= grtS.get(result.detail) if grtS.isOrInheritsFrom(columnStructName, "db.Column") then column= result.detail end end if column == nil then -- a table was selected, there must be a PK defined if table.primaryKey == nil then msg= "Selected table has no PK, please select the column to add as FK." error= 2 else msg= "Source table selected. " context.sourceTableElem= tableElem context.sourceTable= table context.step= 1 -- select dest table -- start rubberband WorkbenchController:rubberbandStart({ grtV.toLua(result.element.left) + grtV.toLua(result.element.width)/2, grtV.toLua(result.element.top) + 10}) end elseif grtV.toLua(context.relType) == "nm" then -- a column was selected but we're creating a n:m relationship msg= _("Please select a table for the n:m relationship.") error= 2 else -- check if the column is part of PK if tableColumnIsPK(table, column) then local bounds msg= "Source column selected. Select matching column for "..grtV.toLua(column.name) context.sourceColumn= column context.sourceTable= table context.step= 2 -- select dest column bounds= WorkbenchController:detailFrame({result.element, result.detail}) WorkbenchController:rubberbandStart({ grtV.toLua(bounds[1]) + grtV.toLua(bounds[3])/2, grtV.toLua(bounds[2]) + grtV.toLua(bounds[4])/2}) else msg= _("Source column must be a PK.") error= 2 end end elseif grtV.toLua(context.step) == 1 then -- select dest table, after having src table selected local res if grtV.toLua(context.relType) ~= "nm" then -- add all PK columns from the source table as FK of the dest table res= createFKFromTablePKs(context.sourceTable, table) else -- create an aux. table with FKs pointing to both tables res= createNMRelationshipFromTablePKs(context.sourceTableElem, tableElem) end context.step= -1 context.sourceTable= nil context.sourceTableElem= nil context.mapping= grtV.newList("") if res ~= -1 then msg= string.format(_("Foreign Key created in table %s."), grtV.toLua(table.name)) createRelationshipsFromFKs({view}) else if grtV.toLua(context.relType) ~= "nm" then msg= _("The source table must have a primary key.") else msg= _("The source and destination tables must have primary keys.") end end -- stop the rubberband WorkbenchController:rubberbandStop() -- notify about task completion WorkbenchController:taskComplete() elseif grtV.toLua(context.step) == 2 then -- select dest column or table -- add the selected column of the source table as FK of the dest table -- if column as selected in dest table, then turn that into FK local column= nil if result.detail ~= nil then local columnStructName= grtS.get(result.detail) if (columnStructName == "db.Column") or (grtS.inheritsFrom(columnStructName, "db.Column")) then column= result.detail end end if (context.destTable ~= nil) and (grtV.toLua(context.destTable._id) ~= grtV.toLua(table._id)) then msg= "Selected column in invalid table, please select next column from table "..grtV.toLua(context.destTable.name) error= 2 else if context.destTable == nil then context.destTable= table end if column == nil then -- a table was selected, add a new FK column to the dest table grtV.insert(context.mapping, {context.sourceColumn, ""}) msg= "Added new FK column. " context.step= 2 -- select another source column else -- check if the dest column has the same type as the source column if compareColumnTypes(context.sourceColumn, column) == 1 then grtV.insert(context.mapping, {context.sourceColumn, column}) msg= "Added FK column. " context.step= 2 else msg= "Selected destination column does not match type. " error= 2 WorkbenchController:rubberbandStop() end end if error == 0 then -- auto-select next source PK local i1,c1,i2,c2 local stable= context.sourceColumn.owner context.sourceColumn= nil c1= grtV.getn(stable.primaryKey.columns) for i1=1,c1 do local col= stable.primaryKey.columns[i1]["referedColumn"] local found= 0 c2= grtV.getn(context.mapping) for i2=1,c2 do if grtV.toLua(context.mapping[i2][1]._id) == grtV.toLua(col._id) then found= 1 break end end if found == 0 then context.sourceColumn= col msg= msg .. "Please select the matching column or table for column "..grtV.toLua(context.sourceColumn.name) break end end if context.sourceColumn == nil then -- end of operation msg= "Foreign Key created in table "..grtV.toLua(table.name)..". " createFKFromMapping(context) context.step= -1 context.sourceColumn= nil context.mapping= grtV.newList() createRelationshipsFromFKs({view}) -- stop the rubberband WorkbenchController:rubberbandStop() -- notify about task completion WorkbenchController:taskComplete() end end -- error == 0 end else print("BAD STEP") end else error= 1 end if error > 0 then -- notify about task abortion WorkbenchController:taskCanceled() end if error == 1 then msg= "Invalid object selected. " -- stop the rubberband WorkbenchController:rubberbandStop() end if grtV.toLua(context.step) == 0 then if error ~= 2 then if created then msg= msg .. "Please select source table or column." else msg= msg .. "Please select source table or column for next FK." end end elseif grtV.toLua(context.step) == 1 then msg= msg .. "Please select the destination table." elseif grtV.toLua(context.step) == 2 then msg= msg end return grt.success(msg) end -- ---------------------------------------------------------------------------------------- -- @brief Cancels an ongoing relationship creation. -- -- @param context the relationship creation context -- -- @return returns 1 on success or and error -- ---------------------------------------------------------------------------------------- function relationshipCancel(args) -- notify about task abortion WorkbenchController:rubberbandStop() WorkbenchController:taskCanceled() return grt.success() end -- ---------------------------------------------------------------------------------------- -- @brief Creates a new layer and places it in the indicated position in the current -- canvas view. -- -- @param top Coordinate for the top of the layer -- @param left Coordinate for the left of the layer -- @param width Coordinate for the top of the layer -- @param height Coordinate for the left of the table -- @param name Initial name of the layer to be created -- @param color Initial color of the layer -- -- @return the created layer on success or an error -- ---------------------------------------------------------------------------------------- function placeNewLayerElement(args) local left= grtV.toLua(args[1]) local top= grtV.toLua(args[2]) local width= grtV.toLua(args[3]) local height= grtV.toLua(args[4]) local name= grtV.toLua(args[5]) local color= (grtV.getn(args) >= 6) and grtV.toLua(args[6]) or "blue" local model= grtV.getGlobal("/workbench/model") local layer layer= grtV.newObj("db.workbench.Layer", name, "", grtV.toLua(model._id)) layer.description= name layer.color= color if width < 0 then layer.left= left + width layer.width= -width else layer.left= left layer.width= width end if height < 0 then layer.top= top + height layer.height= -height else layer.top= top layer.height= height end grtV.insert(model.currentView.layers, layer) return grt.success(layer) end function placeNewImageElement(args) local x= grtV.toLua(args[1]) local y= grtV.toLua(args[2]) local file= grtV.toLua(args[3]) local model= grtV.getGlobal("/workbench/model") local image image= grtV.newObj("db.workbench.ImageElement", "", "", grtV.toLua(model._id)) image.left= x image.top= y image.filename= file grtV.insert(model.currentView.elements, image) image.visible= 1 return grt.success(image) end -- ---------------------------------------------------------------------------------------- -- @brief Creates a new note and places it in the indicated position in the current -- canvas view. -- -- @param top Coordinate for the top of the note -- @param left Coordinate for the left of the note -- @param width Width of the note -- @param height height of the note -- @param color the name of style color -- -- @return the note object on success or an error -- ---------------------------------------------------------------------------------------- function placeNewNoteElement(args) local x= grtV.toLua(args[1]) local y= grtV.toLua(args[2]) local width= grtV.toLua(args[3]) local height= grtV.toLua(args[4]) local color= grtV.toLua(args[5]) local text= (grtV.getn(args) >= 6) and grtV.toLua(args[6]) or "Note" local model= grtV.getGlobal("/workbench/model") local note note= grtV.newObj("db.workbench.NoteElement", "note", "", grtV.toLua(model._id)) note.left= x note.top= y note.width= width note.height= height note.text= text note.color= color grtV.insert(model.currentView.elements, note) note.visible= 1 return grt.success(note) end -- ======================================================================================== function verticalAlign(args) local model= grtV.getGlobal("/workbench/model") local how= grtV.toLua(args[1]) local elem local i, c local p c= grtV.getn(model.currentView.selection) if how == "T" then p= 100000000 for i=1,c do elem= model.currentView.selection[i] if p > grtV.toLua(elem.top) then p= grtV.toLua(elem.top) end end for i=1,c do elem= model.currentView.selection[i] elem.top= p end elseif how == "C" then p= 0 for i=1,c do elem= model.currentView.selection[i] p= p + grtV.toLua(elem.top)+grtV.toLua(elem.height) end p = (p / c) for i=1,c do elem= model.currentView.selection[i] elem.top= p - grtV.toLua(elem.height)/2 end else p= 0 for i=1,c do elem= model.currentView.selection[i] if p < grtV.toLua(elem.top)+grtV.toLua(elem.height) then p= grtV.toLua(elem.top)+grtV.toLua(elem.height) end end for i=1,c do elem= model.currentView.selection[i] elem.top= p - grtV.toLua(elem.height) end end return grt.success() end function horizontalAlign(args) local model= grtV.getGlobal("/workbench/model") local how= grtV.toLua(args[1]) local elem local i, c local p c= grtV.getn(model.currentView.selection) if how == "L" then p= 100000000 for i=1,c do elem= model.currentView.selection[i] if p > grtV.toLua(elem.left) then p= grtV.toLua(elem.left) end end for i=1,c do elem= model.currentView.selection[i] elem.left= p end elseif how == "C" then p= 0 for i=1,c do elem= model.currentView.selection[i] p= p + grtV.toLua(elem.left)+grtV.toLua(elem.width) end p = p / c for i=1,c do elem= model.currentView.selection[i] elem.left= p - grtV.toLua(elem.width)/2 end else p= 0 for i=1,c do elem= model.currentView.selection[i] if p < grtV.toLua(elem.left)+grtV.toLua(elem.width) then p= grtV.toLua(elem.left)+grtV.toLua(elem.width) end end for i=1,c do elem= model.currentView.selection[i] elem.left= p - grtV.toLua(elem.width) end end return grt.success() end function autoSpaceElements(args) local vert= grtV.toLua(args[1]) local spacing= grtV.toLua(args[2]) local model= grtV.getGlobal("/workbench/model") local elem local i, j, c local x, y local elements= {} local found c= grtV.getn(model.currentView.selection) if vert == 0 then for i=1,c do found= 0 for j=1,i-1 do if grtV.toLua(model.currentView.selection[i].left) < grtV.toLua(elements[j].left) then table.insert(elements, j, model.currentView.selection[i]) found= 1 end end if found == 0 then table.insert(elements, model.currentView.selection[i]) end end for i=1,c do elem= elements[i] if i == 1 then x= grtV.toLua(elem.left) y= grtV.toLua(elem.top) end elem.left= x elem.top= y x= x + grtV.toLua(elem.width) + spacing end else for i=1,c do found= 0 for j=1,i-1 do if grtV.toLua(model.currentView.selection[i].top) < grtV.toLua(elements[j].top) then table.insert(elements, j, model.currentView.selection[i]) found= 1 end end if found == 0 then table.insert(elements, model.currentView.selection[i]) end end for i=1,c do elem= elements[i] if i == 1 then x= grtV.toLua(elem.left) y= grtV.toLua(elem.top) end elem.left= x elem.top= y y= y + grtV.toLua(elem.height) + spacing end end return grt.success() end function centerElementsAt(args) local x= grtV.toLua(args[1]) local y= grtV.toLua(args[2]) local curView= grtV.getGlobal("/workbench/model/currentView") local lowestX= 9999999999.0 local lowestY= 9999999999.0 local highestX= 0.0 local highestY= 0.0 local i, c c= grtV.getn(curView.elements) for i= 1, c do local elem= curView.elements[i] lowestX= math.min(lowestX, grtV.toLua(elem.x)) lowestY= math.min(lowestY, grtV.toLua(elem.y)) highestX= math.max(highestX, grtV.toLua(elem.x)+grtV.toLua(elem.width)) highestY= math.max(highestY, grtV.toLua(elem.y)+grtV.toLua(elem.height)) end end -- ======================================================================================== function copySelection(args) local data local model= grtV.getGlobal("/workbench/model") local selection= model.currentView.selection local i, c c= grtV.getn(selection) --for i=1, c do --end return grt.success(data) end function cutSelection(args) local data= copySelection(args) deleteSelection(args) return data end function pasteSelection(args) local data= args[1] return grt.success() end function countElementsWithObject(object) local v, vc, e, ec local view local count= 0 local model= grtV.getGlobal("/workbench/model") vc= grtV.getn(model.views) for v=1,vc do view= model.views[v] ec= grtV.getn(view.elements) for e=1,ec do local elem= view.elements[e] if grtS.get(elem) == "db.workbench.RoutinesElement" then local r, rc local routines= elem.routineGroup.routines rc= grtV.getn(routines) for r=1,rc do if grtV.toLua(object.id) == grtV.toLua(routines[r]) then count= count+1 end end else if grtS.get(elem) == "db.workbenchTableElement" then if grtV.toLua(object.id) == grtV.toLua(elem.table.id) then count= count+1 end elseif grtS.get(elem) == "db.workbench.ViewElement" then if grtV.toLua(object.id) == grtV.toLua(elem.view.id) then count= count+1 end end end end end return count end function deleteObject(obj) local model= grtV.getGlobal("/workbench/model") if grtS.get(obj) == "db.workbench.TableElement" then if countElementsWithObject(obj.table) <= 1 then local table= obj.table grtV.removeObj(table.owner.tables, table) end elseif grtS.get(obj) == "db.workbench.RoutinesElement" then local routines= obj.routineGroup.routines local j if countElementsWithObject(obj.routineGroup) <= 1 then grtV.removeObj(obj.routineGroup.owner.routineGroups, obj.routineGroup) end for j=1,grtV.getn(routines) do local rout= grtV.lookupId(routines[j]) if countElementsWithObject(rout) <= 1 then grtV.removeObj(rout.owner.routines, rout) end end elseif grtS.get(obj) == "db.workbench.ViewElement" then if countElementsWithObject(obj.view) <= 1 then local view= obj.view grtV.removeObj(view.owner.views, view) end end WorkbenchController:deleteElement({obj}) end function deleteObjectAtPoint(args) local x= grtV.toLua(args[1]) local y= grtV.toLua(args[2]) local result= WorkbenchController:objectAtPoint({x,y}) if result ~= nil then deleteObject(result.element) end collectgarbage() return grt.success() end -------------------------------------------------------------------------------------- -- @brief Deletes selected elements from the model -------------------------------------------------------------------------------------- function deleteSelection(args) local model= grtV.getGlobal("/workbench/model") local selection= model.currentView.selection local i for i=grtV.getn(selection),1,-1 do deleteObject(selection[i]) end selection= nil collectgarbage() return grt.success() end -- ======================================================================================== function selectLayer(args) local layer= args[1] local i, c c= grtV.getn(layer.elements) for i=1, c do WorkbenchController:selectObject({layer.elements[i]}) end c= grtV.getn(layer.subLayers) for i=1, c do selectLayer({layer.subLayers[i]}) end return grt.success() end function raiseLayer(args) end function lowerLayer(args) end -- ---------------------------------------------------------------------------------------- -- @brief Creates a new element based on the object struct and places it in the indicated -- position in the current canvas view. -- -- @param obj Object to place or a list of objects for collection objects -- @param top Coordinate for the top of the table -- @param left Coordinate for the left of the table -- -- @return the table element on success or an error -- ---------------------------------------------------------------------------------------- function placeElementFromObject(args) local obj= args[1] local left= args[2] local top= args[3] local model= grtV.getGlobal("/workbench/model") local elementList= model.currentView.elements local elem= nil local objType= grtV.typeOf(obj) if objType == "dict" then local objStructName= grtS.get(obj) local objName= grtV.toLua(obj.name) -- create Table element if grtS.isOrInheritsFrom(objStructName, "db.Table") then elem= grtV.newObj("db.workbench.TableElement", objName, "", "") elem.top= top elem.left= left elem.width= 160 elem.table= obj grtV.insert(elementList, elem) -- create View element elseif grtS.isOrInheritsFrom(objStructName, "db.View") then elem= grtV.newObj("db.workbench.ViewElement", objName, "", "") elem.top= top elem.left= left elem.width= 320 elem.height= 120 elem.view= obj grtV.insert(elementList, elem) elseif grtS.isOrInheritsFrom(objStructName, "db.RoutineGroup") then elem= grtV.newObj("db.workbench.RoutinesElement", objName, "", "") elem.top= top elem.left= left elem.routineGroup= obj grtV.insert(elementList, elem) end elseif (objType == "list") and (grtV.getn(obj) > 0) then local objStructName= grtS.get(obj[1]) local schema= obj[1].owner -- create Routine Group element if grtS.isOrInheritsFrom(objStructName, "db.Routine") and (schema ~= nil) then local i local routineGroup routineGroup= grtV.newObj("db.RoutineGroup", "Routine Group", "", grtV.toLua(schema._id)) grtV.insert(schema.routineGroups, routineGroup) for i= 1, grtV.getn(obj) do grtV.insert(routineGroup.routines, obj[i]._id) end elem= placeElementFromObject({routineGroup, left, top}).value -- elem.visible= 1 end end return grt.success(elem) end -- ---------------------------------------------------------------------------------------- -- @brief Arranges the given objects on the current view in a grid -- -- @param objs list of objects to arrange -- @param offsetX x offset of the area to place the objects -- @param offsetY y offset of the area to place the objects -- @param cols number of columns to use, optional -- @param space the space between the objects, optional -- -- @return returns 1 on success or and error -- ---------------------------------------------------------------------------------------- function arrangeObjectsOnCurrentView(args) local objs= args[1] local offsetX= grtV.toLua(args[2]) local offsetY= grtV.toLua(args[3]) local cols= (grtV.getn(args) >= 4) and grtV.toLua(args[4]) or 10 local space= (grtV.getn(args) >= 5) and grtV.toLua(args[5]) or 50 local i local x= 0 local y= 0 local spaceX= space * 3 local spaceY= space local maxRowHeight= 200 local routineList= grtV.newList("dict", "db.Routine") local elementList= {} local currentCol= 1 for i= 1, grtV.getn(objs) do local objStructName= grtS.get(objs[i]) -- collect db.Routine and make a single RoutinesElement if grtS.isOrInheritsFrom(objStructName, "db.Routine") then grtV.insert(routineList, objs[i]) -- place all other objects else local elem elem= placeElementFromObject({objs[i], offsetX + x, offsetY + y}).value table.insert(elementList, elem) x= x + grtV.toLua(elem.width) + spaceX if maxRowHeight < grtV.toLua(elem.height) then maxRowHeight= grtV.toLua(elem.height) end end currentCol= currentCol + 1 -- only do the specified number of columns if currentCol > cols then currentCol= 1 x= 0 y= y + maxRowHeight + spaceY maxRowHeight= 200 end end -- if there have been routines, places the RoutinesElement if grtV.getn(routineList) > 0 then local elem -- print("Place " .. grtV.getn(routineList) .. " routines") elem= placeElementFromObject({routineList, offsetX + x, offsetY + y}).value table.insert(elementList, elem) end -- set all elements visible for i= 1, table.getn(elementList) do elementList[i].expanded= 0 elementList[i].visible= 1 end createRelationshipsFromFKs({grtV.lookupId(grtV.getGlobal("/workbench/model/currentView"))}) -- free cached GRT values on Lua side collectgarbage() return grt.success() end -- ---------------------------------------------------------------------------------------- -- @brief Arranges the given objects on a new view in a grid -- -- @param model -- @param name the name of the new view -- @param schema the schema object that should be arranged -- @param offsetX x offset of the area to place the objects -- @param offsetY y offset of the area to place the objects -- @param cols number of columns to use, optional -- @param space the space between the objects, optional -- -- @return returns 1 on success or and error -- ---------------------------------------------------------------------------------------- function arrangeSchemaOnNewView(args) addView({args[1], args[2]}) return arrangeSchemaOnCurrentView({args[3],args[4],args[5],args[6],args[7]}) end -- ---------------------------------------------------------------------------------------- -- @brief Arranges the given objects on the current view in a grid -- -- @param schema the schema object that should be arranged -- @param offsetX x offset of the area to place the objects -- @param offsetY y offset of the area to place the objects -- @param cols number of columns to use, optional -- @param space the space between the objects, optional -- -- @return returns 1 on success or and error -- ---------------------------------------------------------------------------------------- function arrangeSchemaOnCurrentView(args) local schema= args[1] local offsetX= grtV.toLua(args[2]) local offsetY= grtV.toLua(args[3]) local cols= (grtV.getn(args) >= 4) and grtV.toLua(args[4]) or nil local space= (grtV.getn(args) >= 5) and grtV.toLua(args[5]) or nil local i local schemaStructName= grtS.get(schema) local schemaObjList= grtV.newList("dict") -- check all schemata member variables local members= grtS.getMembers(schemaStructName) for i= 1, grtV.getn(members) do -- if the member variable is a list if (grtS.getMemberType(schemaStructName, members[i]) == "list") then local memberContentType= grtS.getMemberContentStruct(schemaStructName, members[i]) -- check content type of the list if ((memberContentType ~= nil) and grtS.inheritsFrom(memberContentType, "db.DatabaseObject")) then local objList= schema[members[i]] local j if objList ~= nil then -- build schemaObjList for j= 1, grtV.getn(objList) do grtV.insert(schemaObjList, objList[j]) end end end end end if grtV.getn(schemaObjList) > 0 then arrangeObjectsOnCurrentView({schemaObjList, offsetX, offsetY, cols, space}) end end function loadAndAutoArrangeInNewView(args) local schema= args[1] local viewName= args[2] addView({grtV.getGlobal("/workbench/model"), viewName}) return loadAndAutoArrange({schema, grtV.getGlobal("/workbench/model/currentView")}) end -- ---------------------------------------------------------------------------------------- -- @brief Creates GC objects and arranges them objects on the current view using a more sophisticated algo. -- -- @param schema the schema object that should be arranged -- @param view The view whose objects must be loaded and arranged. -- -- @return returns 1 on success or and error -- ---------------------------------------------------------------------------------------- function loadAndAutoArrange(args) local schema= args[1] local view= args[2] local i local schemaStructName= grtS.get(schema) local schemaObjList= grtV.newList("dict") -- check all schemata member variables local members= grtS.getMembers(schemaStructName) for i= 1, grtV.getn(members) do -- if the member variable is a list if (grtS.getMemberType(schemaStructName, members[i]) == "list") then local memberContentType= grtS.getMemberContentStruct(schemaStructName, members[i]) -- check content type of the list if ((memberContentType ~= nil) and grtS.inheritsFrom(memberContentType, "db.DatabaseObject")) then local objList= schema[members[i]] local j if objList ~= nil then -- build schemaObjList for j= 1, grtV.getn(objList) do grtV.insert(schemaObjList, objList[j]) end end end end end if grtV.getn(schemaObjList) > 0 then arrangeObjectsOnCurrentView({schemaObjList, 0, 0}) WorkbenchController:arrangeObjects({view, 0}) end return grt.success() end -- ---------------------------------------------------------------------------------------- -- @brief Adds a view to the canvas and makes it the new current view -- -- @param model the model object -- @param name the name of the new view -- -- @return returns 1 on success or and error -- ---------------------------------------------------------------------------------------- function addView(args) local model= args[1] local viewName= grtV.toLua(args[2]) local view view= grtV.newObj("db.workbench.View", viewName, "", grtV.toLua(model._id)) model.currentView= view return grt.success(view) end function relationshipExists(view, table1, table2) local i, rel local rc rc= grtV.getn(view.relationships) for i=1,rc do rel= view.relationships[i] if (grtV.toLua(rel.startTable) == "") or (grtV.toLua(rel.endTable) == "") then print("Invalid relationship "..grtV.toLua(rel.name)) elseif (rel.startTable.table == table1) and (rel.endTable.table == table2) then return 1 end end return 0 end function tableElementForTable(view, table) local i, t local c c= grtV.getn(view.elements) for i=1,c do t= view.elements[i] if grtS.get(t) == "db.workbench.TableElement" then if grtV.toLua(t.table._id) == grtV.toLua(table._id) then return t end end end return nil end function createRelationshipElement(view, sourceTable, sourceType, destTable, destType) local wb= grtV.getGlobal("/workbench") local rel rel= grtV.newObj("db.workbench.Relationship", grtV.toLua(sourceTable.name).."_"..grtV.toLua(destTable.name), "", grtV.toLua(wb.model._id)) if sourceType == "1" then rel.startMany=0 rel.startMandatory=1 elseif sourceType == "0" then rel.startMany=0 rel.startMandatory=0 elseif sourceType == "*" then rel.startMany= 1 rel.startMandatory= 0 else rel.startMany=1 rel.startMandatory=1 end if destType == "1" then rel.endMany=0 rel.endMandatory=1 elseif destType == "0" then rel.endMany=0 rel.endMandatory=0 elseif destType == "*" then rel.endMany= 1 rel.endMandatory= 0 else rel.endMany=1 rel.endMandatory=1 end rel.startTable= sourceTable rel.endTable= destTable rel.captionYOffs= grtV.toLua(rel.captionYOffs) + 30 grtV.insert(view.relationships, rel) return rel end -- ---------------------------------------------------------------------------------------- -- @brief Processes tables in the view, turning Foreign Keys into relationships -- -- @param view the view to process -- -- @return returns 1 on success or and error -- ---------------------------------------------------------------------------------------- function createRelationshipsFromFKs(args) local view= args[1] local i, ec, elem local j, fc, fk local table local rel local refTable ec= grtV.getn(view.elements) for i=1,ec do elem= view.elements[i] if grtS.get(elem) == "db.workbench.TableElement" then table= elem.table if (table ~= nil) and (table.foreignKeys ~= nil) then fc= grtV.getn(table.foreignKeys) for j=1,fc do fk= table.foreignKeys[j] refTable= fk.referedTable if refTable ~= nil then if relationshipExists(view, refTable, table) == 0 then local stype, dtype local stable, dtable stype= "1" dtype= "+" -- TODO: try to detect n:m relationships? stable= tableElementForTable(view, refTable) dtable= tableElementForTable(view, table) if (stable ~= nil) and (dtable ~= nil) then rel= createRelationshipElement(view, stable, stype, dtable, dtype) rel.caption= fk.name end end end end end end end return grt.success() end -- ---------------------------------------------------------------------------------------- -- @brief Returns a list of available colors for different elements -- -- @return returns the list of colors -- ---------------------------------------------------------------------------------------- function getColorPresets(args) return grt.success({ table= { {colorName="blue", color="#98BFDA"}, {colorName="red", color="#EEBFBF"}, {colorName="green", color="#BFEAA8"}, }, layer= { {colorName="white", color="#F8F8F8", border="#AAAAAA"}, {colorName="red", color="#FDE1DD", border="#FFFFFF"}, {colorName="blue", color="#D2E7F6", border="#FFFFFF"}, {colorName="green", color="#E0FCD2", border="#FFFFFF"}, {colorName="yellow", color="#FFFEE6", border="#AAAAAA"}, {colorName="cyan", color="#E2FFFF", border="#FFFFFF"}, {colorName="magenta", color="#FFE1F7", border="#FFFFFF"} }, note= { {colorName="white", color="#FFFFFF"}, {colorName="yellow", color="#FFEEA9"}, }, view= { {colorName="orange", color="#FEDE58"} }, package= { {colorName="green", color="#98D8A5"} }}) end -- ---------------------------------------------------------------------------------------- -- @brief Returns a list of available colors for different elements -- -- @return returns the list of colors -- ---------------------------------------------------------------------------------------- function generateSqlCreateScript(args) local sql local catalog= grtV.getGlobal("/workbench/catalog") TransformationMysql:generateSqlCreateStatements({catalog}) sql= TransformationMysql:getSqlScript({catalog}) return grt.success(sql) end -- ---------------------------------------------------------------------------------------- -- @brief Returns a new created routine -- -- @param schema -- @param name -- -- @return returns the list of colors -- ---------------------------------------------------------------------------------------- function addRoutine(args) local schema= args[1] local name= grtV.toLua(args[2]) local routine local structName structName= grtV.toLua(grtV.getGlobal("/workbench/rdbms/databaseObjectPackage")) .. ".Routine" routine= grtV.newObj(structName, grtV.toLua(name), "", grtV.toLua(schema._id)) routine.routineType= "PROCEDURE" grtV.insert(schema.routines, routine) return grt.success(routine) end -- ---------------------------------------------------------------------------------------- -- @brief Returns a new created trigger -- -- @param table -- @param name -- -- @return returns the newly created trigger -- ---------------------------------------------------------------------------------------- function createTrigger(args) local table= args[1] local name= grtV.toLua(args[2]) local trigger local structName structName= grtV.toLua(grtV.getGlobal("/workbench/rdbms/databaseObjectPackage")) .. ".Trigger" trigger= grtV.newObj(structName, grtV.toLua(name), "", grtV.toLua(table._id)) -- grtV.insert(table.triggers, trigger) return grt.success(trigger) end function raiseObjectAtPoint(args) local x= grtV.toLua(args[1]) local y= grtV.toLua(args[2]) local result result= WorkbenchController:objectAtPoint({x,y}) if (result ~= nil) and (result.element ~= nil) then WorkbenchController:bringToFront(result.element) end return grt.success() end -- ---------------------------------------------------------------------------------------- -- @brief Selects all elements on a layer -- -- @param view can be ommited, in that case the current view is chosen -- -- @return success -- ---------------------------------------------------------------------------------------- function selectAll(args) local view= (grtV.getn(args) >= 1) and args[1] or grtV.getGlobal("/workbench/model").currentView local i if view ~= nil then for i= grtV.getn(view.selection), 1, -1 do WorkbenchController:unselectObject({view.selection[i]}) end for i= 1, grtV.getn(view.elements) do WorkbenchController:selectObject({view.elements[i]}) end end return grt.success() end -- ---------------------------------------------------------------------------------------- -- @brief Selects all elements on a layer -- -- @param view can be ommited, in that case the current view is chosen -- -- @return success -- ---------------------------------------------------------------------------------------- function bringSelectionToFront(args) local view= (grtV.getn(args) >= 1) and args[1] or grtV.getGlobal("/workbench/model").currentView local i if view ~= nil then for i= 1, grtV.getn(view.selection) do WorkbenchController:bringToFront({view.selection[i]}) end end end -- ---------------------------------------------------------------------------------------- -- @brief Selects all elements on a layer -- -- @param view can be ommited, in that case the current view is chosen -- -- @return success -- ---------------------------------------------------------------------------------------- function sendSelectionToBack(args) local view= (grtV.getn(args) >= 1) and args[1] or grtV.getGlobal("/workbench/model").currentView local i if view ~= nil then for i= 1, grtV.getn(view.selection) do WorkbenchController:sendToBack({view.selection[i]}) end end end function removeFKToTable(fromTable, toTable) local j, k for j= grtV.getn(fromTable.foreignKeys), 1, -1 do local fk= fromTable.foreignKeys[j] if (fk ~= nil) and grtV.toLua(fk.referedTable._id) == grtV.toLua(toTable._id) then grtV.remove(fromTable.foreignKeys, j) for k= grtV.getn(fromTable.columns), 1, -1 do local column= fromTable.columns[k] local l for l= 1, grtV.getn(fk.referedColumns) do if grtV.toLua(fk.columns[l]) == grtV.toLua(column._id) then grtV.remove(fromTable.columns, k) break end end end end end end -- ---------------------------------------------------------------------------------------- -- @brief Deletes all foreign keys that refer to the given table. Must be -- called when a table is deleted. -- -- @param table that foreign keys point to. -- -- @return success -- ---------------------------------------------------------------------------------------- function deleteFKsWithTable(args) local delTable= args[1] local schemata local s, i, j, k local id= grtV.toLua(delTable.table._id) local tableName= grtV.toLua(delTable.table.name) schemata= delTable.table.owner.owner.schemata for s= 1, grtV.getn(schemata) do local schema= schemata[s] local schemaId= grtV.toLua(schema._id) local schemaName= grtV.toLua(schema.name) for i= 1, grtV.getn(schema.tables) do local tbl= schema.tables[i] for j= grtV.getn(tbl.foreignKeys), 1, -1 do local fk= tbl.foreignKeys[j] if (fk ~= nil) and ((grtV.toLua(fk.referedTableSchemaName) == schemaName) and (grtV.toLua(fk.referedTableName) == tableName) or grtV.toLua(fk.referedTable._id) == id) then grtV.remove(tbl.foreignKeys, j) for k= grtV.getn(tbl.columns), 1, -1 do local column= tbl.columns[k] local l for l= 1, grtV.getn(fk.referedColumns) do if grtV.toLua(fk.columns[l]) == grtV.toLua(column._id) then grtV.remove(tbl.columns, k) break end end end end end end end collectgarbage() return grt.success() end function deleteRelationship(args) local rel= args[1] -- when logical view is enabled, check if this is a n:m removeFKToTable(rel.startTable.table, rel.endTable.table) removeFKToTable(rel.endTable.table, rel.startTable.table) rel.startTable= nil rel.endTable= nil grtV.removeObj(rel.owner.currentView.relationships, rel) collectgarbage() return grt.success() end -- ---------------------------------------------------------------------------------------- -- @brief Traverses a SQL changes tree from the synchronization module and creates a -- single script string with all the snippets -- -- @param tree the changes tree -- -- @return script string -- ---------------------------------------------------------------------------------------- function collectSqlScriptFromTree(tree) local script= nil local obj, text local children local i, c obj= tree["dbObject"] if obj == nil then obj= tree["modelObject"] end if obj ~= nil then text= obj["sql"] if text ~= nil then text= grtV.toLua(text) end if text ~= nil then script= text end children= tree["children"] if children ~= nil then c= grtV.getn(children) for i= 1, c do text= collectSqlScriptFromTree(children[i]) if text ~= nil then if script ~= nil then script= script .. ";\n" .. text else script= text end end end end end return script end function collectSqlScript(args) local script= collectSqlScriptFromTree(args[1]) return grt.success(script) end function collapseExpandElements(elementList, flag, compartments) local i, c c= grtV.getn(elementList) for i= 1, c do local elem= elementList[i] local type= grtS.get(elem) if type == "db.workbench.TableElement" then if compartments ~= 0 then elem.columnsExpanded= flag elem.indicesExpanded= flag elem.foreignKeysExpanded= flag elem.triggersExpanded= flag else elem.expanded= flag end elseif type == "db.workbench.ViewElement" then if compartments == 0 then elem.expanded= flag end elseif type == "db.workbench.RoutineGroupElement" then if compartments == 0 then elem.expanded= flag end end end end function collapseElements(args) local view= grtV.lookupId(grtV.getGlobal("/workbench/model/currentView")) local compartments= grtV.toLua(args[1]) if grtV.getn(view.selection) > 0 then return collapseExpandElements(view.selection, 0, compartments) else return collapseExpandElements(view.elements, 0, compartments) end return grt.success() end function expandElements(args) local view= grtV.lookupId(grtV.getGlobal("/workbench/model/currentView")) local compartments= grtV.toLua(args[1]) if grtV.getn(view.selection) > 0 then return collapseExpandElements(view.selection, 1, compartments) else return collapseExpandElements(view.elements, 1, compartments) end return grt.success() end