Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SQL: Add option to provide the delimiter for the CSV format #59907

Merged
merged 9 commits into from
Jul 29, 2020
45 changes: 42 additions & 3 deletions docs/reference/sql/endpoints/rest.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
=== Overview

The SQL REST API accepts SQL in a JSON document, executes it,
and returns the results.
and returns the results.
For example:

[source,console]
Expand Down Expand Up @@ -106,6 +106,21 @@ s|Description

|===

The `CSV` format accepts the following additional formatting URL query attributes:

* `header`
+
Controls if the CSV header should be included in the response or not. It can take one of the following values:
+
- `present` (default): include the header;
- `absent`: exclude the header.
bpintea marked this conversation as resolved.
Show resolved Hide resolved
+
* `delimiter` (default: `,`)
+
Indicates which character should be used to separate the CSV values. The following values cannot be used for this attribute:
double quote (`"`), carriage-return (`\r`) and new-line (`\n`). The tab (`\t`) can also not be used, the `tsv` format needs to be used
instead.

Here are some examples for the human readable formats:

==== CSV
Expand All @@ -120,7 +135,7 @@ POST /_sql?format=csv
--------------------------------------------------
// TEST[setup:library]

Which returns:
which returns:

[source,text]
--------------------------------------------------
Expand All @@ -133,6 +148,30 @@ James S.A. Corey,Leviathan Wakes,561,2011-06-02T00:00:00.000Z
--------------------------------------------------
// TESTRESPONSE[non_json]

or:

[source,console]
--------------------------------------------------
POST /_sql?format=csv&header=absent&delimiter=%3b
{
"query": "SELECT * FROM library ORDER BY page_count DESC",
"fetch_size": 5
}
--------------------------------------------------
// TEST[setup:library]

which returns:

[source,text]
--------------------------------------------------
Peter F. Hamilton;Pandora's Star;768;2004-03-02T00:00:00.000Z
Vernor Vinge;A Fire Upon the Deep;613;1992-06-01T00:00:00.000Z
Frank Herbert;Dune;604;1965-06-01T00:00:00.000Z
Alastair Reynolds;Revelation Space;585;2000-03-15T00:00:00.000Z
James S.A. Corey;Leviathan Wakes;561;2011-06-02T00:00:00.000Z
--------------------------------------------------
// TESTRESPONSE[non_json]

==== JSON

[source,console]
Expand Down Expand Up @@ -210,7 +249,7 @@ Which returns:

[source,text]
--------------------------------------------------
author | name | page_count | release_date
author | name | page_count | release_date
-----------------+--------------------+---------------+------------------------
Peter F. Hamilton|Pandora's Star |768 |2004-03-02T00:00:00.000Z
Vernor Vinge |A Fire Upon the Deep|613 |1992-06-01T00:00:00.000Z
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,15 +50,22 @@ public final class Protocol {
public static final TimeValue PAGE_TIMEOUT = TimeValue.timeValueSeconds(45);
public static final boolean FIELD_MULTI_VALUE_LENIENCY = false;
public static final boolean INDEX_INCLUDE_FROZEN = false;

/*
* Using the Boolean object here so that SqlTranslateRequest to set this to null (since it doesn't need a "columnar" or
* Using the Boolean object here so that SqlTranslateRequest to set this to null (since it doesn't need a "columnar" or
* binary parameter).
* See {@code SqlTranslateRequest.toXContent}
*/
public static final Boolean COLUMNAR = Boolean.FALSE;
public static final Boolean BINARY_COMMUNICATION = null;

/*
* URL parameters
*/
public static final String URL_PARAM_FORMAT = "format";
public static final String URL_PARAM_HEADER = "header";
public static final String URL_PARAM_DELIMITER = "delimiter";

/**
* SQL-related endpoints
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,20 @@

import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.Collections;
import java.util.List;
import java.util.Set;

import static org.elasticsearch.rest.RestRequest.Method.GET;
import static org.elasticsearch.rest.RestRequest.Method.POST;
import static org.elasticsearch.xpack.sql.proto.Protocol.URL_PARAM_DELIMITER;
import static org.elasticsearch.xpack.sql.proto.Protocol.URL_PARAM_FORMAT;
import static org.elasticsearch.xpack.sql.proto.Protocol.URL_PARAM_HEADER;

public class RestSqlQueryAction extends BaseRestHandler {

TextFormat textFormat;

@Override
public List<Route> routes() {
return List.of(
Expand Down Expand Up @@ -65,7 +72,7 @@ protected RestChannelConsumer prepareRequest(RestRequest request, NodeClient cli
// enforce CBOR response for drivers and CLI (unless instructed differently through the config param)
accept = XContentType.CBOR.name();
} else {
accept = request.param("format");
accept = request.param(URL_PARAM_FORMAT);
}
if (accept == null) {
accept = request.header("Accept");
Expand All @@ -87,7 +94,7 @@ protected RestChannelConsumer prepareRequest(RestRequest request, NodeClient cli
* which we turn into a 400 error.
*/
XContentType xContentType = accept == null ? XContentType.JSON : XContentType.fromMediaTypeOrFormat(accept);
TextFormat textFormat = xContentType == null ? TextFormat.fromMediaTypeOrFormat(accept) : null;
textFormat = xContentType == null ? TextFormat.fromMediaTypeOrFormat(accept) : null;

if (xContentType == null && sqlRequest.columnar()) {
throw new IllegalArgumentException("Invalid use of [columnar] argument: cannot be used in combination with "
Expand Down Expand Up @@ -124,6 +131,11 @@ public RestResponse buildResponse(SqlQueryResponse response) throws Exception {
});
}

@Override
protected Set<String> responseParams() {
return textFormat == TextFormat.CSV ? Set.of(URL_PARAM_DELIMITER, URL_PARAM_HEADER) : Collections.emptySet();
}

@Override
public String getName() {
return "sql_query";
Expand Down
Loading