diff --git a/plugins/org.jkiss.dbeaver.ext.mysql/src/org/jkiss/dbeaver/ext/mysql/model/plan/MySQLPlanAnalyser.java b/plugins/org.jkiss.dbeaver.ext.mysql/src/org/jkiss/dbeaver/ext/mysql/model/plan/MySQLPlanAnalyser.java index 627d63ab9ee8f42db202c4def333bceffbf13319..4552766df10455a13fc8c09b39ead16996f13b24 100644 --- a/plugins/org.jkiss.dbeaver.ext.mysql/src/org/jkiss/dbeaver/ext/mysql/model/plan/MySQLPlanAnalyser.java +++ b/plugins/org.jkiss.dbeaver.ext.mysql/src/org/jkiss/dbeaver/ext/mysql/model/plan/MySQLPlanAnalyser.java @@ -16,21 +16,33 @@ */ package org.jkiss.dbeaver.ext.mysql.model.plan; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; +import com.google.gson.JsonPrimitive; import org.jkiss.code.NotNull; -import org.jkiss.dbeaver.DBException; import org.jkiss.dbeaver.ext.mysql.model.MySQLDataSource; import org.jkiss.dbeaver.model.DBPDataSource; import org.jkiss.dbeaver.model.exec.DBCException; import org.jkiss.dbeaver.model.exec.DBCSession; import org.jkiss.dbeaver.model.exec.jdbc.JDBCSession; -import org.jkiss.dbeaver.model.exec.plan.DBCPlan; -import org.jkiss.dbeaver.model.exec.plan.DBCPlanStyle; -import org.jkiss.dbeaver.model.exec.plan.DBCQueryPlanner; +import org.jkiss.dbeaver.model.exec.plan.*; +import org.jkiss.dbeaver.model.impl.plan.AbstractExecutionPlanSerializer; +import org.jkiss.dbeaver.model.impl.plan.ExecutionPlanDeserializer; +import org.jkiss.utils.CommonUtils; + +import java.io.IOException; +import java.io.Reader; +import java.io.Writer; +import java.lang.reflect.InvocationTargetException; +import java.util.HashMap; +import java.util.List; +import java.util.Map; /** * MySQL execution plan analyser */ -public class MySQLPlanAnalyser implements DBCQueryPlanner { +public class MySQLPlanAnalyser extends AbstractExecutionPlanSerializer implements DBCQueryPlanner { private MySQLDataSource dataSource; @@ -71,4 +83,83 @@ public class MySQLPlanAnalyser implements DBCQueryPlanner { return DBCPlanStyle.PLAN; } + @Override + public void serialize(@NotNull Writer writer, @NotNull DBCPlan plan) throws IOException, InvocationTargetException { + + serializeJson(writer, plan, dataSource.getInfo().getDriverName(), new DBCQueryPlannerSerialInfo() { + + @Override + public String version() { + return plan instanceof MySQLPlanClassic ? "classic" : "json"; + } + + @Override + public void addNodeProperties(DBCPlanNode node, JsonObject nodeJson) { + + JsonObject attributes = new JsonObject(); + if (node instanceof MySQLPlanNodePlain) { + MySQLPlanNodePlain plainNode = (MySQLPlanNodePlain) node; + attributes.add("id", new JsonPrimitive(plainNode.getId())); + attributes.add("select_type", new JsonPrimitive(plainNode.getSelectType())); + attributes.add("table", new JsonPrimitive(plainNode.getTable())); + attributes.add("type", new JsonPrimitive(plainNode.getNodeType())); + attributes.add("possible_keys", new JsonPrimitive(plainNode.getPossibleKeys())); + attributes.add("key", new JsonPrimitive(plainNode.getKey())); + attributes.add("key_len", new JsonPrimitive(plainNode.getKeyLength())); + attributes.add("ref", new JsonPrimitive(plainNode.getRef())); + attributes.add("rows", new JsonPrimitive(plainNode.getRowCount())); + attributes.add("filtered", new JsonPrimitive(plainNode.getFiltered())); + attributes.add("extra", new JsonPrimitive(plainNode.getExtra())); + } else if (node instanceof MySQLPlanNodeJSON) { + MySQLPlanNodeJSON jsNode = (MySQLPlanNodeJSON) node; + for(Map.Entry e : jsNode.getNodeProps().entrySet()) { + attributes.add(e.getKey(), new JsonPrimitive(CommonUtils.notEmpty(e.getValue()))); + } + } + nodeJson.add(PROP_ATTRIBUTES, attributes); + } + }); + +/* + if (plan instanceof MySQLPlanClassic) { + serializeJson(planData, plan,dataSource.getInfo().getDriverName(),(MySQLPlanClassic) plan); + } else if (plan instanceof MySQLPlanJSON) { + serializeJson(planData, plan,dataSource.getInfo().getDriverName(),(MySQLPlanJSON) plan); + } +*/ + + } + + private static Map getNodeAttributes(JsonObject nodeObject){ + Map attributes = new HashMap<>(); + + JsonObject attrs = nodeObject.getAsJsonObject(PROP_ATTRIBUTES); + for(Map.Entry attr : attrs.entrySet()) { + attributes.put(attr.getKey(), attr.getValue().getAsString()); + } + + return attributes; + } + + @Override + public DBCPlan deserialize(@NotNull Reader planData) throws IOException, InvocationTargetException { + + JsonObject jo = new JsonParser().parse(planData).getAsJsonObject(); + String savedVersion = jo.get(AbstractExecutionPlanSerializer.PROP_VERSION).getAsString(); + String query = jo.get(AbstractExecutionPlanSerializer.PROP_SQL).getAsString(); + + if (savedVersion.equals("classic")) { + ExecutionPlanDeserializer loader = new ExecutionPlanDeserializer<>(); + List rootNodes = loader.loadRoot(dataSource, jo, + (datasource, node, parent) -> new MySQLPlanNodePlain(parent, getNodeAttributes(node))); + return new MySQLPlanClassic(dataSource, query, rootNodes); + } else { + ExecutionPlanDeserializer loader = new ExecutionPlanDeserializer<>(); + List rootNodes = loader.loadRoot(dataSource, jo, + (datasource, node, parent) -> new MySQLPlanNodeJSON(parent, getNodeAttributes(node))); + return new MySQLPlanJSON(dataSource,query,rootNodes); + } + + } + } diff --git a/plugins/org.jkiss.dbeaver.ext.mysql/src/org/jkiss/dbeaver/ext/mysql/model/plan/MySQLPlanClassic.java b/plugins/org.jkiss.dbeaver.ext.mysql/src/org/jkiss/dbeaver/ext/mysql/model/plan/MySQLPlanClassic.java index 7e6446eca490a5dd857229e22dd6d918078b1478..158e02fe541adc4955cd07b1c4b0286d0ce0a7de 100644 --- a/plugins/org.jkiss.dbeaver.ext.mysql/src/org/jkiss/dbeaver/ext/mysql/model/plan/MySQLPlanClassic.java +++ b/plugins/org.jkiss.dbeaver.ext.mysql/src/org/jkiss/dbeaver/ext/mysql/model/plan/MySQLPlanClassic.java @@ -62,6 +62,11 @@ public class MySQLPlanClassic extends MySQLPlanAbstract { } } + public MySQLPlanClassic(MySQLDataSource dataSource, String query, List rootNodes) { + super(dataSource, query); + this.rootNodes = rootNodes; + } + @Override public Object getPlanFeature(String feature) { if (DBCPlanCostNode.FEATURE_PLAN_ROWS.equals(feature)) { diff --git a/plugins/org.jkiss.dbeaver.ext.mysql/src/org/jkiss/dbeaver/ext/mysql/model/plan/MySQLPlanJSON.java b/plugins/org.jkiss.dbeaver.ext.mysql/src/org/jkiss/dbeaver/ext/mysql/model/plan/MySQLPlanJSON.java index f6c45eb348b95729c5c46023ac36495d48c3bf68..53a322b8940cc81c303662a41551d283c7f06fe7 100644 --- a/plugins/org.jkiss.dbeaver.ext.mysql/src/org/jkiss/dbeaver/ext/mysql/model/plan/MySQLPlanJSON.java +++ b/plugins/org.jkiss.dbeaver.ext.mysql/src/org/jkiss/dbeaver/ext/mysql/model/plan/MySQLPlanJSON.java @@ -98,6 +98,11 @@ public class MySQLPlanJSON extends MySQLPlanAbstract { } } + public MySQLPlanJSON(MySQLDataSource dataSource, String query, List rootNodes) { + super(dataSource, query); + this.rootNodes = rootNodes; + } + @Override public Object getPlanFeature(String feature) { if (dataSource.isMariaDB()) { diff --git a/plugins/org.jkiss.dbeaver.ext.mysql/src/org/jkiss/dbeaver/ext/mysql/model/plan/MySQLPlanNodeJSON.java b/plugins/org.jkiss.dbeaver.ext.mysql/src/org/jkiss/dbeaver/ext/mysql/model/plan/MySQLPlanNodeJSON.java index 929cc75524c5d0fcd492c9ec905f02b6860694f0..865bf5b595e665697fcf27f8555ed3bb025a29e1 100644 --- a/plugins/org.jkiss.dbeaver.ext.mysql/src/org/jkiss/dbeaver/ext/mysql/model/plan/MySQLPlanNodeJSON.java +++ b/plugins/org.jkiss.dbeaver.ext.mysql/src/org/jkiss/dbeaver/ext/mysql/model/plan/MySQLPlanNodeJSON.java @@ -37,8 +37,8 @@ public class MySQLPlanNodeJSON extends MySQLPlanNode implements DBPPropertySourc private MySQLPlanNodeJSON parent; private String name; private JsonObject object; - private Map nodeProps = new LinkedHashMap<>(); - private List nested; + private Map nodeProps = new LinkedHashMap<>(); + private List nested = new ArrayList<>(); public MySQLPlanNodeJSON(MySQLPlanNodeJSON parent, String name, JsonObject object) { this.parent = parent; @@ -48,6 +48,15 @@ public class MySQLPlanNodeJSON extends MySQLPlanNode implements DBPPropertySourc parseObject(name, object); } + public MySQLPlanNodeJSON(MySQLPlanNodeJSON parent, Map attributes) { + this.parent = parent; + this.nodeProps.putAll(attributes); + } + + public Map getNodeProps() { + return nodeProps; + } + private void parseObject(String objName, JsonObject object) { for (Map.Entry prop : object.entrySet()) { String propName = prop.getKey(); @@ -186,7 +195,7 @@ public class MySQLPlanNodeJSON extends MySQLPlanNode implements DBPPropertySourc @Override public String toString() { - return object.toString(); + return object == null ? nodeProps.toString() : object.toString(); } ////////////////////////////////////////////////////////// @@ -201,7 +210,7 @@ public class MySQLPlanNodeJSON extends MySQLPlanNode implements DBPPropertySourc public DBPPropertyDescriptor[] getPropertyDescriptors2() { DBPPropertyDescriptor[] props = new DBPPropertyDescriptor[nodeProps.size()]; int index = 0; - for (Map.Entry attr : nodeProps.entrySet()) { + for (Map.Entry attr : nodeProps.entrySet()) { props[index++] = new PropertyDescriptor( "Details", attr.getKey(), diff --git a/plugins/org.jkiss.dbeaver.ext.mysql/src/org/jkiss/dbeaver/ext/mysql/model/plan/MySQLPlanNodePlain.java b/plugins/org.jkiss.dbeaver.ext.mysql/src/org/jkiss/dbeaver/ext/mysql/model/plan/MySQLPlanNodePlain.java index 159c60625d8c8312946da383b2af2dab50859673..ad05723037a6cf7244fe47e7805af8e0e0e5ced9 100644 --- a/plugins/org.jkiss.dbeaver.ext.mysql/src/org/jkiss/dbeaver/ext/mysql/model/plan/MySQLPlanNodePlain.java +++ b/plugins/org.jkiss.dbeaver.ext.mysql/src/org/jkiss/dbeaver/ext/mysql/model/plan/MySQLPlanNodePlain.java @@ -26,6 +26,7 @@ import org.jkiss.utils.CommonUtils; import java.sql.ResultSet; import java.util.ArrayList; import java.util.List; +import java.util.Map; /** * MySQL execution plan node. @@ -84,6 +85,21 @@ public class MySQLPlanNodePlain extends MySQLPlanNode { this.extra = JDBCUtils.safeGetString(dbResult, "extra"); } + public MySQLPlanNodePlain(MySQLPlanNodePlain parent, Map props) { + this.parent = parent; + this.id = props.containsKey("id") ? CommonUtils.toInt(props.get("id")) : null; + this.selectType = props.get("select_type"); + this.table = props.get("table"); + this.type = props.get("type"); + this.possibleKeys = props.get("possible_keys"); + this.key = props.get("key"); + this.keyLength = props.get("key_len"); + this.ref = props.get("ref"); + this.rowCount = props.containsKey("rows") ? CommonUtils.toLong(props.get("rows")) : null; + this.filtered = props.containsKey("filtered") ? CommonUtils.toLong(props.get("filtered")) : null; + this.extra = props.get("extra"); + } + public MySQLPlanNodePlain(MySQLPlanNodePlain parent, String type) { this.parent = parent; this.selectType = type; diff --git a/plugins/org.jkiss.dbeaver.ext.postgresql/src/org/jkiss/dbeaver/ext/postgresql/model/plan/PostgreQueryPlaner.java b/plugins/org.jkiss.dbeaver.ext.postgresql/src/org/jkiss/dbeaver/ext/postgresql/model/plan/PostgreQueryPlaner.java index 2402781dd399bdd74090d0e35daf6447306c9897..93882b8944e2f2a150f480e976ccea4e31ce83bb 100644 --- a/plugins/org.jkiss.dbeaver.ext.postgresql/src/org/jkiss/dbeaver/ext/postgresql/model/plan/PostgreQueryPlaner.java +++ b/plugins/org.jkiss.dbeaver.ext.postgresql/src/org/jkiss/dbeaver/ext/postgresql/model/plan/PostgreQueryPlaner.java @@ -101,9 +101,7 @@ public class PostgreQueryPlaner extends AbstractExecutionPlanSerializer implemen @Override public DBCPlan deserialize(@NotNull Reader planData) throws IOException, InvocationTargetException { - try { - JsonObject jo = new JsonParser().parse(planData).getAsJsonObject(); String query = jo.get(AbstractExecutionPlanSerializer.PROP_SQL).getAsString(); diff --git a/plugins/org.jkiss.dbeaver.model/src/org/jkiss/dbeaver/model/exec/plan/DBCQueryPlannerSerialInfo.java b/plugins/org.jkiss.dbeaver.model/src/org/jkiss/dbeaver/model/exec/plan/DBCQueryPlannerSerialInfo.java index 64c7c73e04ce260600c7b19ebdb144847ba4666d..b06b9c4f871ec67ba0e835d4d78b2104ccd783ba 100644 --- a/plugins/org.jkiss.dbeaver.model/src/org/jkiss/dbeaver/model/exec/plan/DBCQueryPlannerSerialInfo.java +++ b/plugins/org.jkiss.dbeaver.model/src/org/jkiss/dbeaver/model/exec/plan/DBCQueryPlannerSerialInfo.java @@ -22,9 +22,9 @@ import com.google.gson.JsonObject; public interface DBCQueryPlannerSerialInfo { - public String version(); + String version(); - public void addNodeProperties(DBCPlanNode node,JsonObject nodeJson); + void addNodeProperties(DBCPlanNode node,JsonObject nodeJson); } diff --git a/plugins/org.jkiss.dbeaver.model/src/org/jkiss/dbeaver/model/impl/plan/AbstractExecutionPlanSerializer.java b/plugins/org.jkiss.dbeaver.model/src/org/jkiss/dbeaver/model/impl/plan/AbstractExecutionPlanSerializer.java index f9a5b32844531694f02e842bfc837c3bff2c0f74..f55f55419d8654a31d053d112f667e13c74f2ec6 100644 --- a/plugins/org.jkiss.dbeaver.model/src/org/jkiss/dbeaver/model/impl/plan/AbstractExecutionPlanSerializer.java +++ b/plugins/org.jkiss.dbeaver.model/src/org/jkiss/dbeaver/model/impl/plan/AbstractExecutionPlanSerializer.java @@ -64,16 +64,15 @@ public abstract class AbstractExecutionPlanSerializer implements DBCQueryPlanne info.addNodeProperties(node,nodeJson); - JsonArray nodes = new JsonArray(); - - if (node.getNested() != null) { + if (!CommonUtils.isEmpty(node.getNested())) { + JsonArray nodes = new JsonArray(); for(DBCPlanNode childNode : node.getNested()) { nodes.add(serializeNode(childNode,info)); } + nodeJson.add(PROP_CHILD, nodes); } - nodeJson.add(PROP_CHILD, nodes); return nodeJson; }