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 support for passing query parameters in REST API calls #51029

Merged
merged 8 commits into from
Jan 20, 2020

Conversation

astefan
Copy link
Contributor

@astefan astefan commented Jan 15, 2020

Add support for PreparedStatement-like of passing values to a sql query.
The query string will use question mark placeholders (?) for any values that will be passed in through a new params request parameter:

POST /_sql?format=txt
{
	"query": "SELECT YEAR(release_date) AS year FROM library WHERE page_count > ? AND author = ? GROUP BY year HAVING COUNT(*) > ?",
	"params": [300, "Frank Herbert", 0]
}

Addresses #42916.

@elasticmachine
Copy link
Collaborator

Pinging @elastic/es-search (:Search/SQL)

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

--------------------------------------------------
// TEST[setup:library]

or it can be done by extracting the values in a separate list of parameters and using question mark placeholders (`?`) in the query string:
Copy link
Member

Choose a reason for hiding this comment

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

I would add a statement to mention that this is the recommended way of passing parameters to avoid hacking or SQL injection (which does not apply to us but nevertheless).

@polyfractal polyfractal added v7.7.0 and removed v7.6.0 labels Jan 15, 2020
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. Left 2 comments

@@ -61,8 +61,8 @@ public SqlQueryRequest() {
}

public SqlQueryRequest(String query, List<SqlTypedParamValue> params, QueryBuilder filter, ZoneId zoneId,
int fetchSize, TimeValue requestTimeout, TimeValue pageTimeout, Boolean columnar,
String cursor, RequestInfo requestInfo, boolean fieldMultiValueLeniency, boolean indexIncludeFrozen) {
int fetchSize, TimeValue requestTimeout, TimeValue pageTimeout, Boolean columnar,
Copy link
Contributor

Choose a reason for hiding this comment

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

I think this formatting change is wrong?

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'll change it.

return Objects.equals(value, that.value) && Objects.equals(type, that.type);
return Objects.equals(value, that.value)
&& Objects.equals(type, that.type)
&& Objects.equals(hasExplicitType, that.hasExplicitType);
Copy link
Contributor

Choose a reason for hiding this comment

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

tokenLocation shouldn't be part of equals and hashCode?

parser.declareString((request, zoneId) -> request.zoneId(ZoneId.of(zoneId)), TIME_ZONE);
parser.declareInt(AbstractSqlQueryRequest::fetchSize, FETCH_SIZE);
parser.declareString((request, timeout) -> request.requestTimeout(TimeValue.parseTimeValue(timeout, Protocol.REQUEST_TIMEOUT,
"request_timeout")), REQUEST_TIMEOUT);
"request_timeout")), REQUEST_TIMEOUT);
Copy link
Contributor

@bpintea bpintea Jan 16, 2020

Choose a reason for hiding this comment

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

was the extra space intended?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes, to have a uniform formatting for the field parsers' declaration. See here the initial formatting.

// we are at the start of a value/type pair... hopefully
SqlTypedParamValue s = SqlTypedParamValue.fromXContent(p);
/*
* Set the xcontentlocation for the first param just in case the first one doesn't meet the parsing rules
Copy link
Contributor

Choose a reason for hiding this comment

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

Was wondering if this was meant as ... for the first param that doesn't meet ..., since an object could occur at any point in the array.

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 rephrased this section for, hopefully, better clarity.

Comment on lines 147 to 151
if ((previousParam != null && previousParam.hasExplicitType() == false) || result.isEmpty()) {
s.tokenLocation(loc);
}
result.add(s);
previousParam = s;
Copy link
Contributor

Choose a reason for hiding this comment

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

This code executes in both if/else branches, it might be worth considering taking it out.
Another nit: a switch with all the top enums (object, string, number, bool, null) might also arguably simplify the code.

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 extracted the common code, but I kept the if/else approach. I find it a bit more clear than a switch.

@@ -75,11 +81,11 @@ public AbstractSqlQueryRequest(String query, List<SqlTypedParamValue> params, Qu
parser.declareString(AbstractSqlQueryRequest::query, QUERY);
parser.declareString((request, mode) -> request.mode(Mode.fromString(mode)), MODE);
parser.declareString((request, clientId) -> request.clientId(clientId), CLIENT_ID);
parser.declareObjectArray(AbstractSqlQueryRequest::params, (p, c) -> SqlTypedParamValue.fromXContent(p), PARAMS);
parser.declareField(AbstractSqlQueryRequest::params, p -> AbstractSqlQueryRequest.parseParams(p), PARAMS, ValueType.VALUE_ARRAY);
Copy link
Contributor

Choose a reason for hiding this comment

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

Could the second arg also be a method reference?

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

Copy link
Contributor

@bpintea bpintea left a comment

Choose a reason for hiding this comment

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

Looks great, only left a few questions and suggestions.

@astefan astefan merged commit 45b8bf6 into elastic:master Jan 20, 2020
astefan added a commit to astefan/elasticsearch that referenced this pull request Jan 20, 2020
…tic#51029)

* REST PreparedStatement-like query parameters are now supported in the form of an array of non-object, non-array values where ES SQL parser will try to infer the data type of the value being passed as parameter.

(cherry picked from commit 45b8bf6)
astefan added a commit that referenced this pull request Jan 20, 2020
…) (#51222)

* REST PreparedStatement-like query parameters are now supported in the form of an array of non-object, non-array values where ES SQL parser will try to infer the data type of the value being passed as parameter.

(cherry picked from commit 45b8bf6)
@astefan astefan deleted the 42916_impl branch January 20, 2020 14:45
SivagurunathanV pushed a commit to SivagurunathanV/elasticsearch that referenced this pull request Jan 23, 2020
…tic#51029)

* REST PreparedStatement-like query parameters are now supported in the form of an array of non-object, non-array values where ES SQL parser will try to infer the data type of the value being passed as parameter.
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.

7 participants