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: move requests' parameters to requests JSON body #36149

Merged
merged 15 commits into from
Dec 11, 2018

Conversation

astefan
Copy link
Contributor

@astefan astefan commented Dec 3, 2018

Fixes #35992.

@elasticmachine
Copy link
Collaborator

Pinging @elastic/es-search

@astefan astefan requested a review from matriv December 3, 2018 10:05
Copy link
Contributor

@matriv matriv left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good overall, left some comments mostly about repetition of code.

import org.elasticsearch.common.ParseField;

final class SqlField {
public static final ParseField MODE = new ParseField("mode");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Indeed, overlooked this class. Will remove it and use the interface inside AbstractSqlQueryRequest. Thanks for catching.


import org.elasticsearch.common.ParseField;

final class SqlField {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can be an interface, and also the name could be SqlRequestField

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will use the interface inside AbstractSqlQueryRequest. Renamed to SqlRequestField.

@@ -47,8 +51,22 @@ public RestSqlTranslateAction(Settings settings, RestController controller) {
protected RestChannelConsumer prepareRequest(RestRequest request, NodeClient client) throws IOException {
SqlTranslateRequest sqlRequest;
try (XContentParser parser = request.contentOrSourceParamParser()) {
sqlRequest = SqlTranslateRequest.fromXContent(parser, Mode.fromString(request.param("mode")));
sqlRequest = SqlTranslateRequest.fromXContent(parser);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@@ -140,6 +140,14 @@ public XContentBuilder toXContent(XContentBuilder builder, Params params) throws
if (query != null) {
builder.field("query", query);
}
if (requestInfo() != null) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Has this been resolved because it still shows up?

@@ -157,7 +158,7 @@ public XContentBuilder toXContent(XContentBuilder builder, Params params) throws
builder.endArray();

if (cursor.equals("") == false) {
builder.field(SqlQueryRequest.CURSOR.getPreferredName(), cursor);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we still need the CURSOR and FILTER in org.elasticsearch.xpack.sql.action.SqlQueryRequest?

assertThat(e.getMessage(), containsString("unknown field [key]"));
}

public void testClearCursorRequestParser() throws IOException {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please also check for missing completely client.id and mode.
If you extract the parsing in a method then you could also unit test the method so we can avoid checking for all different types of requests.

Copy link
Member

@costin costin left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Left some comments mainly around encapsulation.

ParseField PAGE_TIMEOUT = new ParseField("page_timeout");
ParseField FILTER = new ParseField("filter");
ParseField MODE = new ParseField("mode");
ParseField CLIENT_ID = new ParseField("client.id");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should be client_id not client.id - helps with JSON and it is consistent with the other params.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I planned to change the parameter name after this PR, but I can do it now, as well.

parser.declareString(AbstractSqlQueryRequest::query, SqlRequestField.QUERY);
parser.declareString((request, mode) -> {
Mode m = Mode.fromString(mode);
if (request.requestInfo() != null) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This would be better handled inside the request object so there are no null checks.
Either there's a default requestInfo so one can just call request.info().clientId() or do request.clientId(..) and internally set that up.

}
}, SqlRequestField.MODE);
parser.declareString((request, clientId) -> {
if (request.requestInfo() != null) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See above.

(String) objects[0]
));
private static final ConstructingObjectParser<SqlClearCursorRequest, RequestInfo> PARSER =
// here the position in "objects" is the same as the fields parser declarations below
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is the comment misaligned with the object below?

@@ -31,15 +32,17 @@

// TODO: Simplify cursor handling
private String cursor;
private Mode mode;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we need the mode in the response? Do clients consume it in any way?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We use it to output dates in different formats, depending on mode (jdbc/odbc VS. plain). See

public Mode testMode;

public RequestInfo requestInfo;
public String clientId;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is there a clientId and RequestInfo; the former is contained in the latter.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missed this one. Will fix.

if (mode == null) {
try {
return Mode.valueOf(mode.toUpperCase(Locale.ROOT));
} catch (Exception e) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should just thrown an exception stating the mode is invalid. I think it's good to be lenient but at the same time simply don't set up a mode instead of setting an incorrect one.

Copy link
Contributor Author

@astefan astefan Dec 5, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've done this way because mode is a required parameter, even if a client can choose not to send one or send the wrong one. It's just required everywhere in the code and I kept the same behavior.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If it's a required param let's use a default value for it so in case the client doesn't send one, we default to PLAIN which is what we do anyway.
However if the client sends an incorrect value we should report this not silently ignore it.

AbstractSqlRequest sqlRequest = initializeSqlRequest(request);

// no mode specified, default to "PLAIN"
if (sqlRequest.requestInfo() == null) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This constant checking of requestInfo is trappy - simply set a default one for PLAIN and no client id so there's no risk of getting a NPE.

Copy link
Member

@costin costin left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some leftovers.

if (mode == null) {
try {
return Mode.valueOf(mode.toUpperCase(Locale.ROOT));
} catch (Exception e) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If it's a required param let's use a default value for it so in case the client doesn't send one, we default to PLAIN which is what we do anyway.
However if the client sends an incorrect value we should report this not silently ignore it.

@@ -37,6 +38,12 @@ public String clientId() {
}

public void clientId(String clientId) {
if (clientId != null) {
clientId = clientId.toLowerCase(Locale.ROOT);
if (!clientId.equals(CLI) && !clientId.equals(CANVAS)) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's make this pluggable by checking clientId against a list of known values/whitelist.
I mention this since for odbc we're looking into potentially sending extra information (32 vs 64 bit, maybe some OS info) and this would make it easier - just add the item to the whitelist.

@@ -43,6 +43,14 @@ public int hashCode() {
@Override
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
builder.field("cursor", cursor);
if (requestInfo() != null) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This makes reading inconsistent since a field might appear or not depending on its value. How about using nullValue?
Also mode is always required so the check should not be performed for it no?

@@ -140,6 +140,14 @@ public XContentBuilder toXContent(XContentBuilder builder, Params params) throws
if (query != null) {
builder.field("query", query);
}
if (requestInfo() != null) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Has this been resolved because it still shows up?

@@ -20,8 +21,8 @@ public RequestInfo(Mode mode) {
}

public RequestInfo(Mode mode, String clientId) {
this.mode = mode;
this.clientId = clientId;
mode(mode);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we expect the requestInfo to be modified after the data is passed through the constructor?
If not, the setters are not needed and the fields can be made final.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The ObjectParser here needs access to individual methods to set mode and client_id respectively.

@@ -235,4 +238,17 @@ public boolean equals(Object o) {
public int hashCode() {
return Objects.hash(super.hashCode(), query, timeZone, fetchSize, requestTimeout, pageTimeout, filter);
}

public interface SqlRequestField {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's move these fields inside the class itself, make them private/default and remove the interface.

@@ -718,6 +711,10 @@ private static int getOpenContexts(Map<String, Object> stats, String index) {
public static String randomMode() {
return randomFrom("", "jdbc", "plain");
}

public static String mode(String mode) {
return mode.isEmpty() ? "" : ",\"mode\":\"" + mode + "\"";
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Strings.isEmpty is a better choice as it checks if it's also null.

@astefan
Copy link
Contributor Author

astefan commented Dec 5, 2018

run the gradle build tests 2

@astefan
Copy link
Contributor Author

astefan commented Dec 6, 2018

run the gradle build tests 2

@astefan
Copy link
Contributor Author

astefan commented Dec 6, 2018

run the gradle build tests 2

Copy link
Member

@costin costin left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@@ -10,6 +10,7 @@
import org.apache.http.HttpEntity;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.logging.log4j.util.Strings;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like an incorrect import.

response = runSql(mode, new StringEntity("{\"cursor\":\"" + cursor + "\"}",
ContentType.APPLICATION_JSON));
response = runSql(new StringEntity("{\"cursor\":\"" + cursor + "\"" + mode(mode) + "}",
ContentType.APPLICATION_JSON), "");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"" could be StringUtils/Strings.EMPTY

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've added an EMPTY constant to org.elasticsearch.xpack.sql.proto.StringUtils. It seems there is no other class visible to qa that has this constant defined.

}

private String cursor;

public SqlClearCursorRequest() {

super();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's no value in adding super as this constructor is called implicitly anyway.

@@ -17,7 +17,7 @@
ODBC;

public static Mode fromString(String mode) {
if (mode == null) {
if (mode == null || mode.isEmpty()) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Potentially Strings.isEmpty

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is no utility class visible to this class that has an isEmpty() method useful here. Or I missed it... which is very likely.

Copy link
Contributor

@matriv matriv left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@astefan
Copy link
Contributor Author

astefan commented Dec 11, 2018

run the gradle build tests 1

@astefan astefan merged commit eead8a1 into elastic:master Dec 11, 2018
astefan added a commit that referenced this pull request Dec 11, 2018
jasontedor added a commit to dnhatn/elasticsearch that referenced this pull request Dec 11, 2018
* elastic/master: (36 commits)
  Add check for minimum required Docker version (elastic#36497)
  Minor search controller changes (elastic#36479)
  Add default methods to DocValueFormat (elastic#36480)
  Fix the mixed cluster REST test explain/11_basic_with_types.
  Modify `BigArrays` to take name of circuit breaker (elastic#36461)
  Move LoggedExec to minimumRuntime source set (elastic#36453)
  added 6.5.4 version
  Add test logging for elastic#35644
  Tests- added helper methods to ESRestTestCase for checking warnings (elastic#36443)
  SQL: move requests' parameters to requests JSON body (elastic#36149)
  [Zen2] Respect the no_master_block setting (elastic#36478)
  Require soft-deletes when access changes snapshot (elastic#36446)
  Switch more tests to zen2 (elastic#36367)
  [Painless] Add extensive tests for def to primitive casts (elastic#36455)
  Add setting to bypass Rollover action (elastic#36235)
  Try running CI against Zulu (elastic#36391)
  [DOCS] Reworked the shard allocation filtering info.  (elastic#36456)
  Log [initial_master_nodes] on formation failure (elastic#36466)
  converting ForbiddenPatternsTask to .java (elastic#36194)
  fixed typo
  ...
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants