|  | @@ -5,8 +5,10 @@
 | 
	
		
			
				|  |  |   */
 | 
	
		
			
				|  |  |  package org.elasticsearch.xpack.sql.plugin;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +import org.elasticsearch.Version;
 | 
	
		
			
				|  |  |  import org.elasticsearch.common.Strings;
 | 
	
		
			
				|  |  |  import org.elasticsearch.rest.RestRequest;
 | 
	
		
			
				|  |  | +import org.elasticsearch.xpack.sql.SqlIllegalArgumentException;
 | 
	
		
			
				|  |  |  import org.elasticsearch.xpack.sql.action.BasicFormatter;
 | 
	
		
			
				|  |  |  import org.elasticsearch.xpack.sql.action.SqlQueryResponse;
 | 
	
		
			
				|  |  |  import org.elasticsearch.xpack.sql.proto.ColumnInfo;
 | 
	
	
		
			
				|  | @@ -26,9 +28,6 @@ import static org.elasticsearch.xpack.sql.action.BasicFormatter.FormatOption.TEX
 | 
	
		
			
				|  |  |  /**
 | 
	
		
			
				|  |  |   * Templating class for displaying SQL responses in text formats.
 | 
	
		
			
				|  |  |   */
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -// TODO are we sure toString is correct here? What about dates that come back as longs.
 | 
	
		
			
				|  |  | -// Tracked by https://github.com/elastic/x-pack-elasticsearch/issues/3081
 | 
	
		
			
				|  |  |  enum TextFormat {
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |      /**
 | 
	
	
		
			
				|  | @@ -41,22 +40,38 @@ enum TextFormat {
 | 
	
		
			
				|  |  |       */
 | 
	
		
			
				|  |  |      PLAIN_TEXT() {
 | 
	
		
			
				|  |  |          @Override
 | 
	
		
			
				|  |  | -        String format(Cursor cursor, RestRequest request, SqlQueryResponse response) {
 | 
	
		
			
				|  |  | -            final BasicFormatter formatter;
 | 
	
		
			
				|  |  | -            if (cursor instanceof TextFormatterCursor) {
 | 
	
		
			
				|  |  | -                formatter = ((TextFormatterCursor) cursor).getFormatter();
 | 
	
		
			
				|  |  | -                return formatter.formatWithoutHeader(response.rows());
 | 
	
		
			
				|  |  | -            } else {
 | 
	
		
			
				|  |  | +        String format(RestRequest request, SqlQueryResponse response) {
 | 
	
		
			
				|  |  | +            BasicFormatter formatter = null;
 | 
	
		
			
				|  |  | +            Cursor cursor = null;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +            // check if the cursor is already wrapped first
 | 
	
		
			
				|  |  | +            if (response.hasCursor()) {
 | 
	
		
			
				|  |  | +                cursor = Cursors.decodeFromString(response.cursor());
 | 
	
		
			
				|  |  | +                if (cursor instanceof TextFormatterCursor) {
 | 
	
		
			
				|  |  | +                    formatter = ((TextFormatterCursor) cursor).getFormatter();
 | 
	
		
			
				|  |  | +                }
 | 
	
		
			
				|  |  | +            }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +            // if there are headers available, it means it's the first request
 | 
	
		
			
				|  |  | +            // so initialize the underlying formatter and wrap it in the cursor
 | 
	
		
			
				|  |  | +            if (response.columns() != null) {
 | 
	
		
			
				|  |  |                  formatter = new BasicFormatter(response.columns(), response.rows(), TEXT);
 | 
	
		
			
				|  |  | +                // if there's a cursor, wrap the formatter in it
 | 
	
		
			
				|  |  | +                if (cursor != null) {
 | 
	
		
			
				|  |  | +                    response.cursor(Cursors.encodeToString(Version.CURRENT, new TextFormatterCursor(cursor, formatter)));
 | 
	
		
			
				|  |  | +                }
 | 
	
		
			
				|  |  | +                // format with header
 | 
	
		
			
				|  |  |                  return formatter.formatWithHeader(response.columns(), response.rows());
 | 
	
		
			
				|  |  |              }
 | 
	
		
			
				|  |  | -        }
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -        @Override
 | 
	
		
			
				|  |  | -        Cursor wrapCursor(Cursor oldCursor, SqlQueryResponse response) {
 | 
	
		
			
				|  |  | -            BasicFormatter formatter = (oldCursor instanceof TextFormatterCursor) ?
 | 
	
		
			
				|  |  | -                    ((TextFormatterCursor) oldCursor).getFormatter() : new BasicFormatter(response.columns(), response.rows(), TEXT);
 | 
	
		
			
				|  |  | -            return TextFormatterCursor.wrap(super.wrapCursor(oldCursor, response), formatter);
 | 
	
		
			
				|  |  | +            else {
 | 
	
		
			
				|  |  | +                // should be initialized (wrapped by the cursor)
 | 
	
		
			
				|  |  | +                if (formatter != null) {
 | 
	
		
			
				|  |  | +                    // format without header
 | 
	
		
			
				|  |  | +                    return formatter.formatWithoutHeader(response.rows());
 | 
	
		
			
				|  |  | +                }
 | 
	
		
			
				|  |  | +            }
 | 
	
		
			
				|  |  | +            // if this code is reached, it means it's a next page without cursor wrapping
 | 
	
		
			
				|  |  | +            throw new SqlIllegalArgumentException("Cannot find text formatter - this is likely a bug");
 | 
	
		
			
				|  |  |          }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |          @Override
 | 
	
	
		
			
				|  | @@ -219,12 +234,11 @@ enum TextFormat {
 | 
	
		
			
				|  |  |      };
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    String format(Cursor cursor, RestRequest request, SqlQueryResponse response) {
 | 
	
		
			
				|  |  | +    String format(RestRequest request, SqlQueryResponse response) {
 | 
	
		
			
				|  |  |          StringBuilder sb = new StringBuilder();
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -        boolean header = hasHeader(request);
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -        if (header && (cursor == null || cursor == Cursor.EMPTY)) {
 | 
	
		
			
				|  |  | +        // if the header is requested (and the column info is present - namely it's the first page) return the info
 | 
	
		
			
				|  |  | +        if (hasHeader(request) && response.columns() != null) {
 | 
	
		
			
				|  |  |              row(sb, response.columns(), ColumnInfo::name);
 | 
	
		
			
				|  |  |          }
 | 
	
		
			
				|  |  |  
 | 
	
	
		
			
				|  | @@ -239,10 +253,6 @@ enum TextFormat {
 | 
	
		
			
				|  |  |          return true;
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    Cursor wrapCursor(Cursor oldCursor, SqlQueryResponse response) {
 | 
	
		
			
				|  |  | -        return Cursors.decodeFromString(response.cursor());
 | 
	
		
			
				|  |  | -    }
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  |      static TextFormat fromMediaTypeOrFormat(String accept) {
 | 
	
		
			
				|  |  |          for (TextFormat text : values()) {
 | 
	
		
			
				|  |  |              String contentType = text.contentType();
 |