SQL: move requests' parameters to requests JSON body#36149
SQL: move requests' parameters to requests JSON body#36149astefan merged 15 commits intoelastic:masterfrom
Conversation
|
Pinging @elastic/es-search |
matriv
left a comment
There was a problem hiding this comment.
Looks good overall, left some comments mostly about repetition of code.
...sql/sql-action/src/main/java/org/elasticsearch/xpack/sql/action/AbstractSqlQueryRequest.java
Outdated
Show resolved
Hide resolved
| import org.elasticsearch.common.ParseField; | ||
|
|
||
| final class SqlField { | ||
| public static final ParseField MODE = new ParseField("mode"); |
There was a problem hiding this comment.
There was a problem hiding this comment.
Indeed, overlooked this class. Will remove it and use the interface inside AbstractSqlQueryRequest. Thanks for catching.
|
|
||
| import org.elasticsearch.common.ParseField; | ||
|
|
||
| final class SqlField { |
There was a problem hiding this comment.
can be an interface, and also the name could be SqlRequestField
There was a problem hiding this comment.
Will use the interface inside AbstractSqlQueryRequest. Renamed to SqlRequestField.
| SqlTranslateRequest sqlRequest; | ||
| try (XContentParser parser = request.contentOrSourceParamParser()) { | ||
| sqlRequest = SqlTranslateRequest.fromXContent(parser, Mode.fromString(request.param("mode"))); | ||
| sqlRequest = SqlTranslateRequest.fromXContent(parser); |
There was a problem hiding this comment.
This block is repeated here: https://github.com/elastic/elasticsearch/pull/36149/files#diff-47894e3a968e76739b45d73c3405ab8eR61 and here: https://github.com/elastic/elasticsearch/pull/36149/files#diff-33543baa55df79bc3941f885b6fcfa61R47 so maybe extract it to a method.
| if (query != null) { | ||
| builder.field("query", query); | ||
| } | ||
| if (requestInfo() != null) { |
There was a problem hiding this comment.
code is repeated here: https://github.com/elastic/elasticsearch/pull/36149/files#diff-7e3b87bb842194e7f25b2a712cb821e6R46 please extract to a method.
There was a problem hiding this comment.
Has this been resolved because it still shows up?
| builder.endArray(); | ||
|
|
||
| if (cursor.equals("") == false) { | ||
| builder.field(SqlQueryRequest.CURSOR.getPreferredName(), cursor); |
There was a problem hiding this comment.
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 { |
There was a problem hiding this comment.
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.
costin
left a comment
There was a problem hiding this comment.
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"); |
There was a problem hiding this comment.
Should be client_id not client.id - helps with JSON and it is consistent with the other params.
There was a problem hiding this comment.
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) { |
There was a problem hiding this comment.
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) { |
| (String) objects[0] | ||
| )); | ||
| private static final ConstructingObjectParser<SqlClearCursorRequest, RequestInfo> PARSER = | ||
| // here the position in "objects" is the same as the fields parser declarations below |
There was a problem hiding this comment.
Why is the comment misaligned with the object below?
|
|
||
| // TODO: Simplify cursor handling | ||
| private String cursor; | ||
| private Mode mode; |
There was a problem hiding this comment.
Why do we need the mode in the response? Do clients consume it in any way?
There was a problem hiding this comment.
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; |
There was a problem hiding this comment.
Why is there a clientId and RequestInfo; the former is contained in the latter.
There was a problem hiding this comment.
Missed this one. Will fix.
| if (mode == null) { | ||
| try { | ||
| return Mode.valueOf(mode.toUpperCase(Locale.ROOT)); | ||
| } catch (Exception e) { |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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) { |
There was a problem hiding this comment.
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.
| if (mode == null) { | ||
| try { | ||
| return Mode.valueOf(mode.toUpperCase(Locale.ROOT)); | ||
| } catch (Exception e) { |
There was a problem hiding this comment.
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.
| public void clientId(String clientId) { | ||
| if (clientId != null) { | ||
| clientId = clientId.toLowerCase(Locale.ROOT); | ||
| if (!clientId.equals(CLI) && !clientId.equals(CANVAS)) { |
There was a problem hiding this comment.
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.
| @Override | ||
| public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { | ||
| builder.field("cursor", cursor); | ||
| if (requestInfo() != null) { |
There was a problem hiding this comment.
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?
| if (query != null) { | ||
| builder.field("query", query); | ||
| } | ||
| if (requestInfo() != null) { |
There was a problem hiding this comment.
Has this been resolved because it still shows up?
| public RequestInfo(Mode mode, String clientId) { | ||
| this.mode = mode; | ||
| this.clientId = clientId; | ||
| mode(mode); |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
The ObjectParser here needs access to individual methods to set mode and client_id respectively.
| return Objects.hash(super.hashCode(), query, timeZone, fetchSize, requestTimeout, pageTimeout, filter); | ||
| } | ||
|
|
||
| public interface SqlRequestField { |
There was a problem hiding this comment.
Let's move these fields inside the class itself, make them private/default and remove the interface.
| } | ||
|
|
||
| public static String mode(String mode) { | ||
| return mode.isEmpty() ? "" : ",\"mode\":\"" + mode + "\""; |
There was a problem hiding this comment.
Strings.isEmpty is a better choice as it checks if it's also null.
|
run the gradle build tests 2 |
|
run the gradle build tests 2 |
|
run the gradle build tests 2 |
| import org.apache.http.HttpEntity; | ||
| import org.apache.http.entity.ContentType; | ||
| import org.apache.http.entity.StringEntity; | ||
| import org.apache.logging.log4j.util.Strings; |
| response = runSql(mode, new StringEntity("{\"cursor\":\"" + cursor + "\"}", | ||
| ContentType.APPLICATION_JSON)); | ||
| response = runSql(new StringEntity("{\"cursor\":\"" + cursor + "\"" + mode(mode) + "}", | ||
| ContentType.APPLICATION_JSON), ""); |
There was a problem hiding this comment.
"" could be StringUtils/Strings.EMPTY
There was a problem hiding this comment.
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.
|
|
||
| public SqlClearCursorRequest() { | ||
|
|
||
| super(); |
There was a problem hiding this comment.
There's no value in adding super as this constructor is called implicitly anyway.
|
|
||
| public static Mode fromString(String mode) { | ||
| if (mode == null) { | ||
| if (mode == null || mode.isEmpty()) { |
There was a problem hiding this comment.
There is no utility class visible to this class that has an isEmpty() method useful here. Or I missed it... which is very likely.
|
run the gradle build tests 1 |
(cherry picked from commit eead8a1)
* 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 ...
Fixes #35992.