提交 6e271d45 authored 作者: Thomas Mueller's avatar Thomas Mueller

Shell tool: the built-in command "distinct" has been removed (use the SQL…

Shell tool: the built-in command "distinct" has been removed (use the SQL statements "show tables" / "show columns from tableName" instead). The result set formatting has been improved.
上级 8e266637
...@@ -14,7 +14,6 @@ import java.io.PrintStream; ...@@ -14,7 +14,6 @@ import java.io.PrintStream;
import java.io.StringReader; import java.io.StringReader;
import java.sql.Connection; import java.sql.Connection;
import java.sql.DriverManager; import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet; import java.sql.ResultSet;
import java.sql.ResultSetMetaData; import java.sql.ResultSetMetaData;
import java.sql.SQLException; import java.sql.SQLException;
...@@ -37,6 +36,7 @@ import org.h2.util.Utils; ...@@ -37,6 +36,7 @@ import org.h2.util.Utils;
*/ */
public class Shell extends Tool implements Runnable { public class Shell extends Tool implements Runnable {
private static final int MAX_ROW_BUFFER = 5000;
private static final int HISTORY_COUNT = 20; private static final int HISTORY_COUNT = 20;
private PrintStream err = System.err; private PrintStream err = System.err;
...@@ -163,7 +163,6 @@ public class Shell extends Tool implements Runnable { ...@@ -163,7 +163,6 @@ public class Shell extends Tool implements Runnable {
println("help or ? Display this help"); println("help or ? Display this help");
println("list Toggle result list / stack trace mode"); println("list Toggle result list / stack trace mode");
println("maxwidth Set maximum column width (default is 100)"); println("maxwidth Set maximum column width (default is 100)");
println("describe Describe a table");
println("autocommit Enable or disable autocommit"); println("autocommit Enable or disable autocommit");
println("history Show the last 20 statements"); println("history Show the last 20 statements");
println("quit or exit Close the connection and exit"); println("quit or exit Close the connection and exit");
...@@ -205,15 +204,15 @@ public class Shell extends Tool implements Runnable { ...@@ -205,15 +204,15 @@ public class Shell extends Tool implements Runnable {
line = line.substring(0, line.lastIndexOf(';')); line = line.substring(0, line.lastIndexOf(';'));
trimmed = trimmed.substring(0, trimmed.length() - 1); trimmed = trimmed.substring(0, trimmed.length() - 1);
} }
String upper = trimmed.toUpperCase(); String lower = StringUtils.toLowerEnglish(trimmed);
if ("EXIT".equals(upper) || "QUIT".equals(upper)) { if ("exit".equals(lower) || "quit".equals(lower)) {
break; break;
} else if ("HELP".equals(upper) || "?".equals(upper)) { } else if ("help".equals(lower) || "?".equals(lower)) {
showHelp(); showHelp();
} else if ("LIST".equals(upper)) { } else if ("list".equals(lower)) {
listMode = !listMode; listMode = !listMode;
println("Result list mode is now " + (listMode ? "on" : "off")); println("Result list mode is now " + (listMode ? "on" : "off"));
} else if ("HISTORY".equals(upper)) { } else if ("history".equals(lower)) {
for (int i = 0, size = history.size(); i < size; i++) { for (int i = 0, size = history.size(); i < size; i++) {
String s = history.get(i); String s = history.get(i);
s = s.replace('\n', ' ').replace('\r', ' '); s = s.replace('\n', ' ').replace('\r', ' ');
...@@ -224,66 +223,20 @@ public class Shell extends Tool implements Runnable { ...@@ -224,66 +223,20 @@ public class Shell extends Tool implements Runnable {
} else { } else {
println("No history"); println("No history");
} }
} else if (upper.startsWith("DESCRIBE")) { } else if (lower.startsWith("autocommit")) {
String tableName = upper.substring("DESCRIBE".length()).trim(); lower = lower.substring("autocommit".length()).trim();
if (tableName.length() == 0) { if ("true".equals(lower)) {
println("Usage: describe [<schema name>.]<table name>");
ResultSet rs = null;
try {
rs = stat.executeQuery(
"SELECT CAST(TABLE_SCHEMA AS VARCHAR(32)) AS \"Schema\", TABLE_NAME AS \"Table Name\" " +
"FROM INFORMATION_SCHEMA.TABLES ORDER BY TABLE_SCHEMA, TABLE_NAME");
printResult(rs, false);
} finally {
JdbcUtils.closeSilently(rs);
}
} else {
String schemaName = null;
int dot = tableName.indexOf('.');
if (dot >= 0) {
schemaName = tableName.substring(0, dot);
tableName = tableName.substring(dot + 1);
}
PreparedStatement prep = null;
ResultSet rs = null;
try {
String sql = "SELECT CAST(COLUMN_NAME AS VARCHAR(32)) AS \"Column Name\", " +
"CAST(TYPE_NAME AS VARCHAR(14)) AS \"Type\", " +
"NUMERIC_PRECISION AS \"Precision\", " +
"CAST(IS_NULLABLE AS VARCHAR(8)) AS \"Nullable\", " +
"CAST(COLUMN_DEFAULT AS VARCHAR(20)) AS \"Default\" " +
"FROM INFORMATION_SCHEMA.COLUMNS " +
"WHERE UPPER(TABLE_NAME)=?";
if (schemaName != null) {
sql += " AND UPPER(TABLE_SCHEMA)=?";
}
sql += " ORDER BY ORDINAL_POSITION";
prep = conn.prepareStatement(sql);
prep.setString(1, tableName.toUpperCase());
if (schemaName != null) {
prep.setString(2, schemaName.toUpperCase());
}
rs = prep.executeQuery();
printResult(rs, false);
} finally {
JdbcUtils.closeSilently(rs);
JdbcUtils.closeSilently(prep);
}
}
} else if (upper.startsWith("AUTOCOMMIT")) {
upper = upper.substring("AUTOCOMMIT".length()).trim();
if ("TRUE".equals(upper)) {
conn.setAutoCommit(true); conn.setAutoCommit(true);
} else if ("FALSE".equals(upper)) { } else if ("false".equals(lower)) {
conn.setAutoCommit(false); conn.setAutoCommit(false);
} else { } else {
println("Usage: autocommit [true|false]"); println("Usage: autocommit [true|false]");
} }
println("Autocommit is now " + conn.getAutoCommit()); println("Autocommit is now " + conn.getAutoCommit());
} else if (upper.startsWith("MAXWIDTH")) { } else if (lower.startsWith("maxwidth")) {
upper = upper.substring("MAXWIDTH".length()).trim(); lower = lower.substring("maxwidth".length()).trim();
try { try {
maxColumnSize = Integer.parseInt(upper); maxColumnSize = Integer.parseInt(lower);
} catch (NumberFormatException e) { } catch (NumberFormatException e) {
println("Usage: maxwidth <integer value>"); println("Usage: maxwidth <integer value>");
} }
...@@ -493,32 +446,79 @@ public class Shell extends Tool implements Runnable { ...@@ -493,32 +446,79 @@ public class Shell extends Tool implements Runnable {
} }
private int printResult(ResultSet rs, boolean asList) throws SQLException { private int printResult(ResultSet rs, boolean asList) throws SQLException {
if (asList) {
return printResultAsList(rs);
}
return printResultAsTable(rs);
}
private int printResultAsTable(ResultSet rs) throws SQLException {
ResultSetMetaData meta = rs.getMetaData(); ResultSetMetaData meta = rs.getMetaData();
int longest = 0;
int len = meta.getColumnCount(); int len = meta.getColumnCount();
boolean truncated = false;
ArrayList<String[]> rows = New.arrayList();
// buffer the header
String[] columns = new String[len]; String[] columns = new String[len];
int[] columnSizes = new int[len];
for (int i = 0; i < len; i++) { for (int i = 0; i < len; i++) {
String s = meta.getColumnLabel(i + 1); String s = meta.getColumnLabel(i + 1);
int l = s.length(); columns[i] = s == null ? "" : s;
if (!asList) {
l = Math.max(l, meta.getColumnDisplaySize(i + 1));
l = Math.min(maxColumnSize, l);
} }
if (s.length() > l) { rows.add(columns);
s = s.substring(0, l); int rowCount = 0;
while (rs.next()) {
rowCount++;
truncated |= loadRow(rs, len, rows);
if (rowCount > MAX_ROW_BUFFER) {
printRows(rows, len);
rows.clear();
} }
columns[i] = s;
columnSizes[i] = l;
longest = Math.max(longest, l);
} }
printRows(rows, len);
rows.clear();
if (truncated) {
println("(data is partially truncated)");
}
return rowCount;
}
private boolean loadRow(ResultSet rs, int len, ArrayList<String[]> rows) throws SQLException {
boolean truncated = false;
String[] row = new String[len];
for (int i = 0; i < len; i++) {
String s = rs.getString(i + 1);
if (s == null) {
s = "null";
}
// only truncate if more than one column
if (len > 1 && s.length() > maxColumnSize) {
s = s.substring(0, maxColumnSize);
truncated = true;
}
row[i] = s;
}
rows.add(row);
return truncated;
}
private int[] printRows(ArrayList<String[]> rows, int len) {
int[] columnSizes = new int[len];
for (int i = 0; i < len; i++) {
int max = 0;
for (String[] row : rows) {
max = Math.max(max, row[i].length());
}
if (len > 1) {
Math.min(maxColumnSize, max);
}
columnSizes[i] = max;
}
for (String[] row : rows) {
StringBuilder buff = new StringBuilder(); StringBuilder buff = new StringBuilder();
if (!asList) {
for (int i = 0; i < len; i++) { for (int i = 0; i < len; i++) {
if (i > 0) { if (i > 0) {
buff.append(boxVertical); buff.append(' ').append(boxVertical).append(' ');
} }
String s = columns[i]; String s = row[i];
buff.append(s); buff.append(s);
if (i < len - 1) { if (i < len - 1) {
for (int j = s.length(); j < columnSizes[i]; j++) { for (int j = s.length(); j < columnSizes[i]; j++) {
...@@ -528,12 +528,24 @@ public class Shell extends Tool implements Runnable { ...@@ -528,12 +528,24 @@ public class Shell extends Tool implements Runnable {
} }
println(buff.toString()); println(buff.toString());
} }
boolean truncated = false; return columnSizes;
}
private int printResultAsList(ResultSet rs) throws SQLException {
ResultSetMetaData meta = rs.getMetaData();
int longestLabel = 0;
int len = meta.getColumnCount();
String[] columns = new String[len];
for (int i = 0; i < len; i++) {
String s = meta.getColumnLabel(i + 1);
columns[i] = s;
longestLabel = Math.max(longestLabel, s.length());
}
StringBuilder buff = new StringBuilder();
int rowCount = 0; int rowCount = 0;
while (rs.next()) { while (rs.next()) {
rowCount++; rowCount++;
buff.setLength(0); buff.setLength(0);
if (asList) {
if (rowCount > 1) { if (rowCount > 1) {
println(""); println("");
} }
...@@ -543,45 +555,23 @@ public class Shell extends Tool implements Runnable { ...@@ -543,45 +555,23 @@ public class Shell extends Tool implements Runnable {
} }
String label = columns[i]; String label = columns[i];
buff.append(label); buff.append(label);
for (int j = label.length(); j < longest; j++) { for (int j = label.length(); j < longestLabel; j++) {
buff.append(' '); buff.append(' ');
} }
buff.append(": ").append(rs.getString(i + 1)); buff.append(": ").append(rs.getString(i + 1));
} }
} else { println(buff.toString());
}
if (rowCount == 0) {
for (int i = 0; i < len; i++) { for (int i = 0; i < len; i++) {
if (i > 0) { if (i > 0) {
buff.append(boxVertical); buff.append('\n');
}
String s = rs.getString(i + 1);
if (s == null) {
s = "null";
}
int m = columnSizes[i];
// only truncate if more than once column
if (len > 1 && !asList && s.length() > m) {
s = s.substring(0, m);
truncated = true;
}
buff.append(s);
if (i < len - 1) {
for (int j = s.length(); j < m; j++) {
buff.append(' ');
}
}
}
}
println(buff.toString());
} }
if (rowCount == 0 && asList) { String label = columns[i];
for (String label : columns) { buff.append(label);
buff.append(label).append('\n');
} }
println(buff.toString()); println(buff.toString());
} }
if (truncated) {
println("(data is partially truncated)");
}
return rowCount; return rowCount;
} }
......
...@@ -56,6 +56,14 @@ public class TestShell extends TestBase { ...@@ -56,6 +56,14 @@ public class TestShell extends TestBase {
assertContains(s, "HI"); assertContains(s, "HI");
assertContains(s, "Hello World"); assertContains(s, "Hello World");
assertContains(s, "(1 row, "); assertContains(s, "(1 row, ");
shell = new Shell();
buff = new ByteArrayOutputStream();
shell.setOut(new PrintStream(buff));
shell.runTool("-help");
s = new String(buff.toByteArray());
assertContains(s, "Interactive command line tool to access a database using JDBC.");
test(true); test(true);
test(false); test(false);
} }
...@@ -106,7 +114,6 @@ public class TestShell extends TestBase { ...@@ -106,7 +114,6 @@ public class TestShell extends TestBase {
read("help or ?"); read("help or ?");
read("list"); read("list");
read("maxwidth"); read("maxwidth");
read("describe");
read("autocommit"); read("autocommit");
read("history"); read("history");
read("quit or exit"); read("quit or exit");
...@@ -133,6 +140,17 @@ public class TestShell extends TestBase { ...@@ -133,6 +140,17 @@ public class TestShell extends TestBase {
read("x"); read("x");
read("(data is partially truncated)"); read("(data is partially truncated)");
read("(1 row,"); read("(1 row,");
testOut.println("select x, 's' s from system_range(0, 10001);");
read("sql> X | S");
for (int i = 0; i < 10000; i++) {
read((i + " ").substring(0, 4) + " | s");
}
for (int i = 10000; i <= 10001; i++) {
read((i + " ").substring(0, 5) + " | s");
}
read("(10002 rows,");
testOut.println("select error;"); testOut.println("select error;");
read("sql> Error:"); read("sql> Error:");
if (read("").startsWith("Column \"ERROR\" not found")) { if (read("").startsWith("Column \"ERROR\" not found")) {
...@@ -142,10 +160,24 @@ public class TestShell extends TestBase { ...@@ -142,10 +160,24 @@ public class TestShell extends TestBase {
read("sql> ...>"); read("sql> ...>");
testOut.println("insert into test values(1, 'Hello');"); testOut.println("insert into test values(1, 'Hello');");
read("sql>"); read("sql>");
testOut.println("select * from test;"); testOut.println("select null n, * from test;");
read("sql> N | ID | NAME");
read("null | 1 | Hello");
read("(1 row,");
// test history
for (int i = 0; i < 30; i++) {
testOut.println("select " + i + " ID from test;");
read("sql> ID"); read("sql> ID");
read("1 "); read("" + i);
read("(1 row,");
}
testOut.println("20");
read("sql> select 10 ID from test");
read("ID");
read("10");
read("(1 row,"); read("(1 row,");
testOut.println("maxwidth"); testOut.println("maxwidth");
read("sql> Usage: maxwidth <integer value>"); read("sql> Usage: maxwidth <integer value>");
read("Maximum column width is now 100"); read("Maximum column width is now 100");
...@@ -158,28 +190,29 @@ public class TestShell extends TestBase { ...@@ -158,28 +190,29 @@ public class TestShell extends TestBase {
read("sql> Autocommit is now false"); read("sql> Autocommit is now false");
testOut.println("autocommit true"); testOut.println("autocommit true");
read("sql> Autocommit is now true"); read("sql> Autocommit is now true");
testOut.println("describe");
read("sql> Usage: describe [<schema name>.]<table name>");
read("Schema");
while (read("").startsWith("INFORMATION_SCHEMA")) {
// ignore
}
testOut.println("describe test");
read("sql> Column Name");
read("ID");
read("NAME");
testOut.println("describe public.test");
read("sql> Column Name");
read("ID");
read("NAME");
testOut.println("\n;"); testOut.println("\n;");
read("sql>"); read("sql>");
testOut.println("list"); testOut.println("list");
read("sql> Result list mode is now on"); read("sql> Result list mode is now on");
testOut.println("select 1 a, 2 b;");
read("sql> A: 1"); testOut.println("select 1 first, 2 second;");
read("B: 2"); read("sql> FIRST : 1");
read("SECOND: 2");
read("(1 row, "); read("(1 row, ");
testOut.println("select x from system_range(1, 3);");
read("sql> X: 1");
read("");
read("X: 2");
read("");
read("X: 3");
read("(3 rows, ");
testOut.println("select x, 2 as y from system_range(1, 3) where 1 = 0;");
read("sql> X");
read("Y");
read("(0 rows, ");
testOut.println("list"); testOut.println("list");
read("sql> Result list mode is now off"); read("sql> Result list mode is now off");
testOut.println("help"); testOut.println("help");
...@@ -187,7 +220,6 @@ public class TestShell extends TestBase { ...@@ -187,7 +220,6 @@ public class TestShell extends TestBase {
read("help or ?"); read("help or ?");
read("list"); read("list");
read("maxwidth"); read("maxwidth");
read("describe");
read("autocommit"); read("autocommit");
read("history"); read("history");
read("quit or exit"); read("quit or exit");
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论