提交 e5e89883 authored 作者: Thomas Mueller's avatar Thomas Mueller

Improved EXPLAIN plan formatting and documentation.

上级 c808b054
......@@ -188,9 +188,12 @@ public class Insert extends Prepared implements ResultTarget {
if (list.size() > 0) {
buff.append("VALUES ");
int row = 0;
if (list.size() > 1) {
buff.append("\n");
}
for (Expression[] expr : list) {
if (row++ > 0) {
buff.append(", ");
buff.append(",\n");
}
buff.append('(');
buff.resetCount();
......
......@@ -140,14 +140,14 @@ public class Update extends Prepared {
public String getPlanSQL() {
StatementBuilder buff = new StatementBuilder("UPDATE ");
buff.append(tableFilter.getPlanSQL(false)).append("\nSET ");
buff.append(tableFilter.getPlanSQL(false)).append("\nSET\n ");
Table table = tableFilter.getTable();
int columnCount = table.getColumns().length;
for (int i = 0; i < columnCount; i++) {
Expression newExpr = expressions[i];
if (newExpr != null) {
Column column = table.getColumn(i);
buff.appendExceptFirst(",\n");
buff.appendExceptFirst(",\n ");
buff.append(column.getName()).append(" = ").append(newExpr.getSQL());
}
}
......
......@@ -46,10 +46,10 @@ public class ConditionAndOr extends Condition {
String sql;
switch (andOrType) {
case AND:
sql = left.getSQL() + " AND " + right.getSQL();
sql = left.getSQL() + "\n AND " + right.getSQL();
break;
case OR:
sql = left.getSQL() + " OR " + right.getSQL();
sql = left.getSQL() + "\n OR " + right.getSQL();
break;
default:
throw DbException.throwInternalError("andOrType=" + andOrType);
......
......@@ -15,6 +15,7 @@ import org.h2.message.DbException;
import org.h2.result.ResultInterface;
import org.h2.table.ColumnResolver;
import org.h2.table.TableFilter;
import org.h2.util.StringUtils;
import org.h2.value.Value;
import org.h2.value.ValueBoolean;
import org.h2.value.ValueNull;
......@@ -101,7 +102,7 @@ public class ConditionInSelect extends Condition {
}
public String getSQL() {
return "(" + left.getSQL() + " IN(" + query.getPlanSQL() + "))";
return "(" + left.getSQL() + " IN(\n" + StringUtils.indent(query.getPlanSQL(), 4, false) + "))";
}
public void updateAggregate(Session session) {
......
......@@ -91,4 +91,8 @@ public class FunctionIndex extends BaseIndex {
return functionTable.getRowCountApproximation();
}
public String getPlanSQL() {
return "function";
}
}
......@@ -97,4 +97,8 @@ public class MetaIndex extends BaseIndex {
return MetaTable.ROW_COUNT_APPROXIMATION;
}
public String getPlanSQL() {
return "meta";
}
}
......@@ -616,13 +616,16 @@ public class TableFilter implements ColumnResolver {
}
}
if (nestedJoin != null) {
buff.append("(\n");
StringBuffer buffNested = new StringBuffer();
TableFilter n = nestedJoin;
do {
buff.append(n.getPlanSQL(n != nestedJoin));
buff.append("\n");
buffNested.append(n.getPlanSQL(n != nestedJoin));
buffNested.append("\n");
n = (TableFilter) n.getJoin();
} while (n != null);
buff.append("(\n");
buff.append(StringUtils.indent(buffNested.toString(), 4, false));
buff.append(") ON ");
if (joinCondition == null) {
// need to have a ON expression,
......@@ -638,21 +641,24 @@ public class TableFilter implements ColumnResolver {
buff.append(' ').append(Parser.quoteIdentifier(alias));
}
if (index != null) {
buff.append(" /* ");
buff.append("\n");
StatementBuilder planBuff = new StatementBuilder();
planBuff.append(index.getPlanSQL());
if (indexConditions.size() > 0) {
planBuff.append(": ");
for (IndexCondition condition : indexConditions) {
planBuff.appendExceptFirst(" AND ");
planBuff.appendExceptFirst("\n AND ");
planBuff.append(condition.getSQL());
}
}
String plan = StringUtils.quoteRemarkSQL(planBuff.toString());
buff.append(plan).append(" */");
if (plan.indexOf('\n') >= 0) {
plan += "\n";
}
buff.append(StringUtils.indent("/* " + plan + " */", 4, false));
}
if (isJoin) {
buff.append(" ON ");
buff.append("\n ON ");
if (joinCondition == null) {
// need to have a ON expression, otherwise the nesting is
// unclear
......@@ -662,13 +668,13 @@ public class TableFilter implements ColumnResolver {
}
}
if (filterCondition != null) {
buff.append(" /* WHERE ");
buff.append("\n");
String condition = StringUtils.unEnclose(filterCondition.getSQL());
condition = StringUtils.quoteRemarkSQL(condition);
buff.append(condition).append(" */");
condition = "/* WHERE " + StringUtils.quoteRemarkSQL(condition) + "\n*/";
buff.append(StringUtils.indent(condition, 4, false));
}
if (scanCount > 0) {
buff.append(" /* scanCount: ").append(scanCount).append(" */");
buff.append("\n /* scanCount: ").append(scanCount).append(" */");
}
return buff.toString();
}
......
......@@ -269,7 +269,7 @@ public class TableView extends Table {
public String getSQL() {
if (isTemporary()) {
return "(" + querySQL + ")";
return "(\n" + StringUtils.indent(querySQL) + ")";
}
return super.getSQL();
}
......
......@@ -582,20 +582,23 @@ public class StringUtils {
/**
* Indents a string with 4 spaces.
*
* @param s the string
* @return the indented string
*/
private static String indent(String s) {
return indent(s, 4);
public static String indent(String s) {
return indent(s, 4, true);
}
/**
* Indents a string with spaces.
*
* @param s the string
* @param spaces the number of spaces
* @param newline append a newline if there is none
* @return the indented string
*/
private static String indent(String s, int spaces) {
public static String indent(String s, int spaces, boolean newline) {
StringBuilder buff = new StringBuilder(s.length() + spaces);
for (int i = 0; i < s.length();) {
for (int j = 0; j < spaces; j++) {
......@@ -606,7 +609,7 @@ public class StringUtils {
buff.append(s.substring(i, n));
i = n;
}
if (!s.endsWith("\n")) {
if (newline && !s.endsWith("\n")) {
buff.append('\n');
}
return buff.toString();
......
......@@ -21,8 +21,10 @@ SELECT COUNT(*) FROM TEST;
-- Display the query plan - 'direct lookup' means the index is used
EXPLAIN SELECT COUNT(*) FROM TEST;
--> SELECT COUNT(*)
--> FROM PUBLIC.TEST /* PUBLIC.TEST.tableScan */
--> SELECT
--> COUNT(*)
--> FROM PUBLIC.TEST
--> /* PUBLIC.TEST.tableScan */
--> /* direct lookup */
;
......@@ -56,8 +58,10 @@ SELECT DISTINCT TYPE FROM TEST ORDER BY TYPE LIMIT 3;
-- Display the query plan - 'index sorted' means the index is used to order
EXPLAIN SELECT DISTINCT TYPE FROM TEST ORDER BY TYPE LIMIT 3;
--> SELECT DISTINCT TYPE
--> FROM PUBLIC.TEST /* PUBLIC.IDX_TEST_TYPE */
--> SELECT DISTINCT
--> TYPE
--> FROM PUBLIC.TEST
--> /* PUBLIC.IDX_TEST_TYPE */
--> ORDER BY 1
--> LIMIT 3
--> /* distinct */
......@@ -89,8 +93,11 @@ SELECT MIN(VALUE), MAX(VALUE) FROM TEST;
-- Display the query plan - 'direct lookup' means it's optimized
EXPLAIN SELECT MIN(VALUE), MAX(VALUE) FROM TEST;
--> SELECT MIN(VALUE), MAX(VALUE)
--> FROM PUBLIC.TEST /* PUBLIC.TEST.tableScan */
--> SELECT
--> MIN(VALUE),
--> MAX(VALUE)
--> FROM PUBLIC.TEST
--> /* PUBLIC.TEST.tableScan */
--> /* direct lookup */
;
......@@ -130,17 +137,29 @@ FROM (SELECT DISTINCT TYPE FROM TEST) T ORDER BY TYPE;
EXPLAIN SELECT TYPE, (SELECT VALUE FROM TEST T2 WHERE T.TYPE = T2.TYPE
ORDER BY TYPE, VALUE LIMIT 1) MIN
FROM (SELECT DISTINCT TYPE FROM TEST) T ORDER BY TYPE;
--> SELECT TYPE, (SELECT VALUE
--> FROM PUBLIC.TEST T2 /* PUBLIC.IDX_TEST_TYPE_VALUE: TYPE = T.TYPE */
--> WHERE T.TYPE = T2.TYPE
--> ORDER BY =TYPE, 1
--> LIMIT 1
--> /* index sorted */) AS MIN
--> FROM (SELECT DISTINCT TYPE
--> FROM PUBLIC.TEST /* PUBLIC.IDX_TEST_TYPE_VALUE */
--> /* distinct */) T /* SELECT DISTINCT TYPE
--> FROM PUBLIC.TEST /++ PUBLIC.IDX_TEST_TYPE_VALUE ++/
--> /++ distinct ++/ */
--> SELECT
--> TYPE,
--> (SELECT
--> VALUE
--> FROM PUBLIC.TEST T2
--> /* PUBLIC.IDX_TEST_TYPE_VALUE: TYPE = T.TYPE */
--> WHERE T.TYPE = T2.TYPE
--> ORDER BY =TYPE, 1
--> LIMIT 1
--> /* index sorted */) AS MIN
--> FROM (
--> SELECT DISTINCT
--> TYPE
--> FROM PUBLIC.TEST
--> /* PUBLIC.IDX_TEST_TYPE_VALUE */
--> /* distinct */
--> ) T
--> /* SELECT DISTINCT
--> TYPE
--> FROM PUBLIC.TEST
--> /++ PUBLIC.IDX_TEST_TYPE_VALUE ++/
--> /++ distinct ++/
--> */
--> ORDER BY 1
;
......@@ -171,8 +190,10 @@ SELECT VALUE FROM TEST ORDER BY VALUE LIMIT 3;
-- Display the query plan - 'index sorted' means the index is used
EXPLAIN SELECT VALUE FROM TEST ORDER BY VALUE LIMIT 10;
--> SELECT VALUE
--> FROM PUBLIC.TEST /* PUBLIC.IDX_TEST_VALUE */
--> SELECT
--> VALUE
--> FROM PUBLIC.TEST
--> /* PUBLIC.IDX_TEST_VALUE */
--> ORDER BY 1
--> LIMIT 10
--> /* index sorted */
......@@ -190,8 +211,10 @@ SELECT VALUE FROM TEST ORDER BY VALUE DESC LIMIT 3;
-- Display the query plan - 'index sorted' means the index is used
EXPLAIN SELECT VALUE FROM TEST ORDER BY VALUE DESC LIMIT 10;
--> SELECT VALUE
--> FROM PUBLIC.TEST /* PUBLIC.IDX_TEST_VALUE_D */
--> SELECT
--> VALUE
--> FROM PUBLIC.TEST
--> /* PUBLIC.IDX_TEST_VALUE_D */
--> ORDER BY 1 DESC
--> LIMIT 10
--> /* index sorted */
......@@ -216,8 +239,10 @@ SELECT * FROM TEST WHERE ID IN(1, 1000);
-- Display the query plan
EXPLAIN SELECT * FROM TEST WHERE ID IN(1, 1000);
--> SELECT TEST.ID
--> FROM PUBLIC.TEST /* PUBLIC.PRIMARY_KEY_2: ID IN(1, 1000) */
--> SELECT
--> TEST.ID
--> FROM PUBLIC.TEST
--> /* PUBLIC.PRIMARY_KEY_2: ID IN(1, 1000) */
--> WHERE ID IN(1, 1000)
;
......@@ -236,9 +261,13 @@ INSERT INTO TEST SELECT X, MOD(X, 10) FROM SYSTEM_RANGE(1, 1000);
-- Display the query plan
EXPLAIN SELECT * FROM TEST WHERE ID IN (10, 20) AND DATA IN (1, 2);
--> SELECT TEST.ID, TEST.DATA
--> FROM PUBLIC.TEST /* PUBLIC.PRIMARY_KEY_2: ID IN(10, 20) */
--> WHERE (ID IN(10, 20)) AND (DATA IN(1, 2))
--> SELECT
--> TEST.ID,
--> TEST.DATA
--> FROM PUBLIC.TEST
--> /* PUBLIC.PRIMARY_KEY_2: ID IN(10, 20) */
--> WHERE (ID IN(10, 20))
--> AND (DATA IN(1, 2))
;
DROP TABLE TEST;
......@@ -259,7 +259,16 @@ public class TestScript extends TestBase {
if (s == null) {
return "null";
}
return s.replace('\n', ' ');
s = s.replace('\n', ' ');
s = StringUtils.replaceAll(s, " ", " ");
while (true) {
String s2 = StringUtils.replaceAll(s, " ", " ");
if (s2.length() == s.length()) {
break;
}
s = s2;
}
return s;
}
private void writeResultSet(String sql, ResultSet rs) throws Exception {
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论