Add gsql format that returns result set as single Json object
Change-Id: I16d93147d4c800bbfa568ce8402fc008f8eb69cd
This commit is contained in:
@@ -9,7 +9,7 @@ SYNOPSIS
|
|||||||
--------
|
--------
|
||||||
[verse]
|
[verse]
|
||||||
'ssh' -p <port> <host> 'gerrit gsql'
|
'ssh' -p <port> <host> 'gerrit gsql'
|
||||||
[--format {PRETTY | JSON}]
|
[--format {PRETTY | JSON | JSON_SINGLE}]
|
||||||
[-c QUERY]
|
[-c QUERY]
|
||||||
|
|
||||||
DESCRIPTION
|
DESCRIPTION
|
||||||
@@ -26,6 +26,8 @@ OPTIONS
|
|||||||
for reading by a human on a sufficiently wide terminal.
|
for reading by a human on a sufficiently wide terminal.
|
||||||
In JSON mode records are output as JSON objects using the
|
In JSON mode records are output as JSON objects using the
|
||||||
column names as the property names, one object per line.
|
column names as the property names, one object per line.
|
||||||
|
In JSON_SINGLE mode the whole result set is output as a
|
||||||
|
single JSON object.
|
||||||
|
|
||||||
-c::
|
-c::
|
||||||
Execute the single query statement supplied, and then exit.
|
Execute the single query statement supplied, and then exit.
|
||||||
@@ -38,7 +40,8 @@ global capability.
|
|||||||
|
|
||||||
SCRIPTING
|
SCRIPTING
|
||||||
---------
|
---------
|
||||||
Intended for interactive use only, unless format is JSON.
|
Intended for interactive use only, unless format is JSON, or
|
||||||
|
JSON_SINGLE.
|
||||||
|
|
||||||
EXAMPLES
|
EXAMPLES
|
||||||
--------
|
--------
|
||||||
|
@@ -16,7 +16,11 @@ package com.google.gerrit.sshd.commands;
|
|||||||
|
|
||||||
import com.google.gerrit.common.Version;
|
import com.google.gerrit.common.Version;
|
||||||
import com.google.gerrit.reviewdb.server.ReviewDb;
|
import com.google.gerrit.reviewdb.server.ReviewDb;
|
||||||
|
import com.google.gson.Gson;
|
||||||
|
import com.google.gson.JsonArray;
|
||||||
|
import com.google.gson.JsonElement;
|
||||||
import com.google.gson.JsonObject;
|
import com.google.gson.JsonObject;
|
||||||
|
import com.google.gson.JsonParser;
|
||||||
import com.google.gwtorm.jdbc.JdbcSchema;
|
import com.google.gwtorm.jdbc.JdbcSchema;
|
||||||
import com.google.gwtorm.server.OrmException;
|
import com.google.gwtorm.server.OrmException;
|
||||||
import com.google.gwtorm.server.SchemaFactory;
|
import com.google.gwtorm.server.SchemaFactory;
|
||||||
@@ -49,7 +53,7 @@ public class QueryShell {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static enum OutputFormat {
|
public static enum OutputFormat {
|
||||||
PRETTY, JSON;
|
PRETTY, JSON, JSON_SINGLE;
|
||||||
}
|
}
|
||||||
|
|
||||||
private final BufferedReader in;
|
private final BufferedReader in;
|
||||||
@@ -178,6 +182,7 @@ public class QueryShell {
|
|||||||
} else {
|
} else {
|
||||||
final String msg = "'\\" + line + "' not supported";
|
final String msg = "'\\" + line + "' not supported";
|
||||||
switch (outputFormat) {
|
switch (outputFormat) {
|
||||||
|
case JSON_SINGLE:
|
||||||
case JSON: {
|
case JSON: {
|
||||||
final JsonObject err = new JsonObject();
|
final JsonObject err = new JsonObject();
|
||||||
err.addProperty("type", "error");
|
err.addProperty("type", "error");
|
||||||
@@ -228,7 +233,7 @@ public class QueryShell {
|
|||||||
if (outputFormat == OutputFormat.PRETTY) {
|
if (outputFormat == OutputFormat.PRETTY) {
|
||||||
println(" List of relations");
|
println(" List of relations");
|
||||||
}
|
}
|
||||||
showResultSet(rs, false,
|
showResultSet(rs, false, 0,
|
||||||
Identity.create(rs, "TABLE_SCHEM"),
|
Identity.create(rs, "TABLE_SCHEM"),
|
||||||
Identity.create(rs, "TABLE_NAME"),
|
Identity.create(rs, "TABLE_NAME"),
|
||||||
Identity.create(rs, "TABLE_TYPE"));
|
Identity.create(rs, "TABLE_TYPE"));
|
||||||
@@ -267,7 +272,7 @@ public class QueryShell {
|
|||||||
if (outputFormat == OutputFormat.PRETTY) {
|
if (outputFormat == OutputFormat.PRETTY) {
|
||||||
println(" Table " + tableName);
|
println(" Table " + tableName);
|
||||||
}
|
}
|
||||||
showResultSet(rs, true,
|
showResultSet(rs, true, 0,
|
||||||
Identity.create(rs, "COLUMN_NAME"),
|
Identity.create(rs, "COLUMN_NAME"),
|
||||||
new Function("TYPE") {
|
new Function("TYPE") {
|
||||||
@Override
|
@Override
|
||||||
@@ -365,24 +370,7 @@ public class QueryShell {
|
|||||||
if (hasResultSet) {
|
if (hasResultSet) {
|
||||||
final ResultSet rs = statement.getResultSet();
|
final ResultSet rs = statement.getResultSet();
|
||||||
try {
|
try {
|
||||||
final int rowCount = showResultSet(rs, false);
|
showResultSet(rs, false, start);
|
||||||
final long ms = System.currentTimeMillis() - start;
|
|
||||||
switch (outputFormat) {
|
|
||||||
case JSON: {
|
|
||||||
final JsonObject tail = new JsonObject();
|
|
||||||
tail.addProperty("type", "query-stats");
|
|
||||||
tail.addProperty("rowCount", rowCount);
|
|
||||||
tail.addProperty("runTimeMilliseconds", ms);
|
|
||||||
println(tail.toString());
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case PRETTY:
|
|
||||||
default:
|
|
||||||
println("(" + rowCount + (rowCount == 1 ? " row" : " rows")
|
|
||||||
+ "; " + ms + " ms)");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
} finally {
|
} finally {
|
||||||
rs.close();
|
rs.close();
|
||||||
}
|
}
|
||||||
@@ -391,6 +379,7 @@ public class QueryShell {
|
|||||||
final int updateCount = statement.getUpdateCount();
|
final int updateCount = statement.getUpdateCount();
|
||||||
final long ms = System.currentTimeMillis() - start;
|
final long ms = System.currentTimeMillis() - start;
|
||||||
switch (outputFormat) {
|
switch (outputFormat) {
|
||||||
|
case JSON_SINGLE:
|
||||||
case JSON: {
|
case JSON: {
|
||||||
final JsonObject tail = new JsonObject();
|
final JsonObject tail = new JsonObject();
|
||||||
tail.addProperty("type", "update-stats");
|
tail.addProperty("type", "update-stats");
|
||||||
@@ -411,19 +400,47 @@ public class QueryShell {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private int showResultSet(final ResultSet rs, boolean alreadyOnRow,
|
/**
|
||||||
Function... show) throws SQLException {
|
* Outputs a result set to stdout.
|
||||||
|
*
|
||||||
|
* @param rs ResultSet to show.
|
||||||
|
* @param alreadyOnRow true if rs is already on the first row. false
|
||||||
|
* otherwise.
|
||||||
|
* @param start Timestamp in milliseconds when executing the statement
|
||||||
|
* started. This timestamp is used to compute statistics about the
|
||||||
|
* statement. If no statistics should be shown, set it to 0.
|
||||||
|
* @param show Functions to map columns
|
||||||
|
* @throws SQLException
|
||||||
|
*/
|
||||||
|
private void showResultSet(final ResultSet rs, boolean alreadyOnRow,
|
||||||
|
long start, Function... show) throws SQLException {
|
||||||
switch (outputFormat) {
|
switch (outputFormat) {
|
||||||
|
case JSON_SINGLE:
|
||||||
case JSON:
|
case JSON:
|
||||||
return showResultSetJson(rs, alreadyOnRow, show);
|
showResultSetJson(rs, alreadyOnRow, start, show);
|
||||||
|
break;
|
||||||
case PRETTY:
|
case PRETTY:
|
||||||
default:
|
default:
|
||||||
return showResultSetPretty(rs, alreadyOnRow, show);
|
showResultSetPretty(rs, alreadyOnRow, start, show);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private int showResultSetJson(final ResultSet rs, boolean alreadyOnRow,
|
/**
|
||||||
Function... show) throws SQLException {
|
* Outputs a result set to stdout in Json format.
|
||||||
|
*
|
||||||
|
* @param rs ResultSet to show.
|
||||||
|
* @param alreadyOnRow true if rs is already on the first row. false
|
||||||
|
* otherwise.
|
||||||
|
* @param start Timestamp in milliseconds when executing the statement
|
||||||
|
* started. This timestamp is used to compute statistics about the
|
||||||
|
* statement. If no statistics should be shown, set it to 0.
|
||||||
|
* @param show Functions to map columns
|
||||||
|
* @throws SQLException
|
||||||
|
*/
|
||||||
|
private void showResultSetJson(final ResultSet rs, boolean alreadyOnRow,
|
||||||
|
long start, Function... show) throws SQLException {
|
||||||
|
JsonArray collector = new JsonArray();
|
||||||
final ResultSetMetaData meta = rs.getMetaData();
|
final ResultSetMetaData meta = rs.getMetaData();
|
||||||
final Function[] columnMap;
|
final Function[] columnMap;
|
||||||
if (show != null && 0 < show.length) {
|
if (show != null && 0 < show.length) {
|
||||||
@@ -453,15 +470,68 @@ public class QueryShell {
|
|||||||
}
|
}
|
||||||
row.addProperty("type", "row");
|
row.addProperty("type", "row");
|
||||||
row.add("columns", cols);
|
row.add("columns", cols);
|
||||||
|
switch (outputFormat) {
|
||||||
|
case JSON:
|
||||||
println(row.toString());
|
println(row.toString());
|
||||||
|
break;
|
||||||
|
case JSON_SINGLE:
|
||||||
|
collector.add(row);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
final JsonObject obj = new JsonObject();
|
||||||
|
obj.addProperty("type", "error");
|
||||||
|
obj.addProperty("message", "Unsupported Json variant");
|
||||||
|
println(obj.toString());
|
||||||
|
return;
|
||||||
|
}
|
||||||
alreadyOnRow = false;
|
alreadyOnRow = false;
|
||||||
rowCnt++;
|
rowCnt++;
|
||||||
}
|
}
|
||||||
return rowCnt;
|
|
||||||
|
JsonObject tail = null;
|
||||||
|
if (start != 0) {
|
||||||
|
tail = new JsonObject();
|
||||||
|
tail.addProperty("type", "query-stats");
|
||||||
|
tail.addProperty("rowCount", rowCnt);
|
||||||
|
final long ms = System.currentTimeMillis() - start;
|
||||||
|
tail.addProperty("runTimeMilliseconds", ms);
|
||||||
}
|
}
|
||||||
|
|
||||||
private int showResultSetPretty(final ResultSet rs, boolean alreadyOnRow,
|
switch (outputFormat) {
|
||||||
Function... show) throws SQLException {
|
case JSON:
|
||||||
|
if (tail != null) {
|
||||||
|
println(tail.toString());
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case JSON_SINGLE:
|
||||||
|
if (tail != null) {
|
||||||
|
collector.add(tail);
|
||||||
|
}
|
||||||
|
println(collector.toString());
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
final JsonObject obj = new JsonObject();
|
||||||
|
obj.addProperty("type", "error");
|
||||||
|
obj.addProperty("message", "Unsupported Json variant");
|
||||||
|
println(obj.toString());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Outputs a result set to stdout in plain text format.
|
||||||
|
*
|
||||||
|
* @param rs ResultSet to show.
|
||||||
|
* @param alreadyOnRow true if rs is already on the first row. false
|
||||||
|
* otherwise.
|
||||||
|
* @param start Timestamp in milliseconds when executing the statement
|
||||||
|
* started. This timestamp is used to compute statistics about the
|
||||||
|
* statement. If no statistics should be shown, set it to 0.
|
||||||
|
* @param show Functions to map columns
|
||||||
|
* @throws SQLException
|
||||||
|
*/
|
||||||
|
private void showResultSetPretty(final ResultSet rs, boolean alreadyOnRow,
|
||||||
|
long start, Function... show) throws SQLException {
|
||||||
final ResultSetMetaData meta = rs.getMetaData();
|
final ResultSetMetaData meta = rs.getMetaData();
|
||||||
|
|
||||||
final Function[] columnMap;
|
final Function[] columnMap;
|
||||||
@@ -559,11 +629,18 @@ public class QueryShell {
|
|||||||
if (dataTruncated) {
|
if (dataTruncated) {
|
||||||
warning("some column data was truncated");
|
warning("some column data was truncated");
|
||||||
}
|
}
|
||||||
return rows.size();
|
|
||||||
|
if (start != 0) {
|
||||||
|
final int rowCount = rows.size();
|
||||||
|
final long ms = System.currentTimeMillis() - start;
|
||||||
|
println("(" + rowCount + (rowCount == 1 ? " row" : " rows")
|
||||||
|
+ "; " + ms + " ms)");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void warning(final String msg) {
|
private void warning(final String msg) {
|
||||||
switch (outputFormat) {
|
switch (outputFormat) {
|
||||||
|
case JSON_SINGLE:
|
||||||
case JSON: {
|
case JSON: {
|
||||||
final JsonObject obj = new JsonObject();
|
final JsonObject obj = new JsonObject();
|
||||||
obj.addProperty("type", "warning");
|
obj.addProperty("type", "warning");
|
||||||
@@ -581,6 +658,7 @@ public class QueryShell {
|
|||||||
|
|
||||||
private void error(final SQLException err) {
|
private void error(final SQLException err) {
|
||||||
switch (outputFormat) {
|
switch (outputFormat) {
|
||||||
|
case JSON_SINGLE:
|
||||||
case JSON: {
|
case JSON: {
|
||||||
final JsonObject obj = new JsonObject();
|
final JsonObject obj = new JsonObject();
|
||||||
obj.addProperty("type", "error");
|
obj.addProperty("type", "error");
|
||||||
|
Reference in New Issue
Block a user