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]
 | 
			
		||||
'ssh' -p <port> <host> 'gerrit gsql'
 | 
			
		||||
  [--format {PRETTY | JSON}]
 | 
			
		||||
  [--format {PRETTY | JSON | JSON_SINGLE}]
 | 
			
		||||
  [-c QUERY]
 | 
			
		||||
 | 
			
		||||
DESCRIPTION
 | 
			
		||||
@@ -26,6 +26,8 @@ OPTIONS
 | 
			
		||||
	for reading by a human on a sufficiently wide terminal.
 | 
			
		||||
	In JSON mode records are output as JSON objects using the
 | 
			
		||||
	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::
 | 
			
		||||
	Execute the single query statement supplied, and then exit.
 | 
			
		||||
@@ -38,7 +40,8 @@ global capability.
 | 
			
		||||
 | 
			
		||||
SCRIPTING
 | 
			
		||||
---------
 | 
			
		||||
Intended for interactive use only, unless format is JSON.
 | 
			
		||||
Intended for interactive use only, unless format is JSON, or
 | 
			
		||||
JSON_SINGLE.
 | 
			
		||||
 | 
			
		||||
EXAMPLES
 | 
			
		||||
--------
 | 
			
		||||
 
 | 
			
		||||
@@ -16,7 +16,11 @@ package com.google.gerrit.sshd.commands;
 | 
			
		||||
 | 
			
		||||
import com.google.gerrit.common.Version;
 | 
			
		||||
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.JsonParser;
 | 
			
		||||
import com.google.gwtorm.jdbc.JdbcSchema;
 | 
			
		||||
import com.google.gwtorm.server.OrmException;
 | 
			
		||||
import com.google.gwtorm.server.SchemaFactory;
 | 
			
		||||
@@ -49,7 +53,7 @@ public class QueryShell {
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public static enum OutputFormat {
 | 
			
		||||
    PRETTY, JSON;
 | 
			
		||||
    PRETTY, JSON, JSON_SINGLE;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private final BufferedReader in;
 | 
			
		||||
@@ -178,6 +182,7 @@ public class QueryShell {
 | 
			
		||||
        } else {
 | 
			
		||||
          final String msg = "'\\" + line + "' not supported";
 | 
			
		||||
          switch (outputFormat) {
 | 
			
		||||
            case JSON_SINGLE:
 | 
			
		||||
            case JSON: {
 | 
			
		||||
              final JsonObject err = new JsonObject();
 | 
			
		||||
              err.addProperty("type", "error");
 | 
			
		||||
@@ -228,7 +233,7 @@ public class QueryShell {
 | 
			
		||||
        if (outputFormat == OutputFormat.PRETTY) {
 | 
			
		||||
          println("                     List of relations");
 | 
			
		||||
        }
 | 
			
		||||
        showResultSet(rs, false,
 | 
			
		||||
        showResultSet(rs, false, 0,
 | 
			
		||||
            Identity.create(rs, "TABLE_SCHEM"),
 | 
			
		||||
            Identity.create(rs, "TABLE_NAME"),
 | 
			
		||||
            Identity.create(rs, "TABLE_TYPE"));
 | 
			
		||||
@@ -267,7 +272,7 @@ public class QueryShell {
 | 
			
		||||
        if (outputFormat == OutputFormat.PRETTY) {
 | 
			
		||||
          println("                     Table " + tableName);
 | 
			
		||||
        }
 | 
			
		||||
        showResultSet(rs, true,
 | 
			
		||||
        showResultSet(rs, true, 0,
 | 
			
		||||
            Identity.create(rs, "COLUMN_NAME"),
 | 
			
		||||
            new Function("TYPE") {
 | 
			
		||||
              @Override
 | 
			
		||||
@@ -365,24 +370,7 @@ public class QueryShell {
 | 
			
		||||
      if (hasResultSet) {
 | 
			
		||||
        final ResultSet rs = statement.getResultSet();
 | 
			
		||||
        try {
 | 
			
		||||
          final int rowCount = showResultSet(rs, false);
 | 
			
		||||
          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;
 | 
			
		||||
          }
 | 
			
		||||
          showResultSet(rs, false, start);
 | 
			
		||||
        } finally {
 | 
			
		||||
          rs.close();
 | 
			
		||||
        }
 | 
			
		||||
@@ -391,6 +379,7 @@ public class QueryShell {
 | 
			
		||||
        final int updateCount = statement.getUpdateCount();
 | 
			
		||||
        final long ms = System.currentTimeMillis() - start;
 | 
			
		||||
        switch (outputFormat) {
 | 
			
		||||
          case JSON_SINGLE:
 | 
			
		||||
          case JSON: {
 | 
			
		||||
            final JsonObject tail = new JsonObject();
 | 
			
		||||
            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) {
 | 
			
		||||
      case JSON_SINGLE:
 | 
			
		||||
      case JSON:
 | 
			
		||||
        return showResultSetJson(rs, alreadyOnRow, show);
 | 
			
		||||
        showResultSetJson(rs, alreadyOnRow,  start, show);
 | 
			
		||||
        break;
 | 
			
		||||
      case PRETTY:
 | 
			
		||||
      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 Function[] columnMap;
 | 
			
		||||
    if (show != null && 0 < show.length) {
 | 
			
		||||
@@ -453,15 +470,68 @@ public class QueryShell {
 | 
			
		||||
      }
 | 
			
		||||
      row.addProperty("type", "row");
 | 
			
		||||
      row.add("columns", cols);
 | 
			
		||||
      println(row.toString());
 | 
			
		||||
      switch (outputFormat) {
 | 
			
		||||
        case JSON:
 | 
			
		||||
          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;
 | 
			
		||||
      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);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    switch (outputFormat) {
 | 
			
		||||
      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;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private int showResultSetPretty(final ResultSet rs, boolean alreadyOnRow,
 | 
			
		||||
      Function... show) throws SQLException {
 | 
			
		||||
  /**
 | 
			
		||||
   * 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 Function[] columnMap;
 | 
			
		||||
@@ -559,11 +629,18 @@ public class QueryShell {
 | 
			
		||||
    if (dataTruncated) {
 | 
			
		||||
      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) {
 | 
			
		||||
    switch (outputFormat) {
 | 
			
		||||
      case JSON_SINGLE:
 | 
			
		||||
      case JSON: {
 | 
			
		||||
        final JsonObject obj = new JsonObject();
 | 
			
		||||
        obj.addProperty("type", "warning");
 | 
			
		||||
@@ -581,6 +658,7 @@ public class QueryShell {
 | 
			
		||||
 | 
			
		||||
  private void error(final SQLException err) {
 | 
			
		||||
    switch (outputFormat) {
 | 
			
		||||
      case JSON_SINGLE:
 | 
			
		||||
      case JSON: {
 | 
			
		||||
        final JsonObject obj = new JsonObject();
 | 
			
		||||
        obj.addProperty("type", "error");
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user