Skip to content

Commit 99fcacc

Browse files
authored
Add support for parameter binding to built queries (#1010)
1 parent d204b8f commit 99fcacc

File tree

9 files changed

+168
-125
lines changed

9 files changed

+168
-125
lines changed

QUERY_BUILDER.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -588,3 +588,19 @@ Query select = select().raw("an expression on select").from(dbName, "cpu").where
588588
```sqlite-psql
589589
SELECT an expression on select FROM h2o_feet WHERE an expression as condition;
590590
```
591+
592+
Binding parameters
593+
594+
If your Query is based on user input, it is good practice to use parameter binding to avoid [injection attacks](https://en.wikipedia.org/wiki/SQL_injection).
595+
You can create queries with parameter binding:
596+
597+
```java
598+
Query query = select().from(DATABASE,"h2o_feet").where(gt("water_level", FunctionFactory.placeholder("level")))
599+
.bindParameter("level", 8);
600+
```
601+
602+
```sqlite-psql
603+
SELECT * FROM h2o_feet WHERE water_level > $level;
604+
```
605+
606+
The values of bindParameter() calls are bound to the placeholders in the query (`level`).

src/main/java/org/influxdb/dto/BoundParameterQuery.java

Lines changed: 2 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -1,77 +1,9 @@
11
package org.influxdb.dto;
22

3-
import com.squareup.moshi.JsonWriter;
4-
import java.io.IOException;
5-
import java.nio.charset.Charset;
6-
import java.util.HashMap;
7-
import java.util.Map;
8-
import java.util.Map.Entry;
9-
10-
import org.influxdb.InfluxDBIOException;
11-
12-
import okio.Buffer;
13-
143
public final class BoundParameterQuery extends Query {
154

16-
private final Map<String, Object> params = new HashMap<>();
17-
185
private BoundParameterQuery(final String command, final String database) {
19-
super(command, database, true);
20-
}
21-
22-
public String getParameterJsonWithUrlEncoded() {
23-
try {
24-
String jsonParameterObject = createJsonObject(params);
25-
String urlEncodedJsonParameterObject = encode(jsonParameterObject);
26-
return urlEncodedJsonParameterObject;
27-
} catch (IOException e) {
28-
throw new InfluxDBIOException(e);
29-
}
30-
}
31-
32-
private String createJsonObject(final Map<String, Object> parameterMap) throws IOException {
33-
Buffer b = new Buffer();
34-
JsonWriter writer = JsonWriter.of(b);
35-
writer.beginObject();
36-
for (Entry<String, Object> pair : parameterMap.entrySet()) {
37-
String name = pair.getKey();
38-
Object value = pair.getValue();
39-
if (value instanceof Number) {
40-
Number number = (Number) value;
41-
writer.name(name).value(number);
42-
} else if (value instanceof String) {
43-
writer.name(name).value((String) value);
44-
} else if (value instanceof Boolean) {
45-
writer.name(name).value((Boolean) value);
46-
} else {
47-
writer.name(name).value(String.valueOf(value));
48-
}
49-
}
50-
writer.endObject();
51-
return b.readString(Charset.forName("utf-8"));
52-
}
53-
54-
@Override
55-
public int hashCode() {
56-
final int prime = 31;
57-
int result = super.hashCode();
58-
result = prime * result + params.hashCode();
59-
return result;
60-
}
61-
62-
@Override
63-
public boolean equals(final Object obj) {
64-
if (this == obj) {
65-
return true;
66-
}
67-
if (!super.equals(obj)) {
68-
return false;
69-
}
70-
BoundParameterQuery other = (BoundParameterQuery) obj;
71-
if (!params.equals(other.params)) {
72-
return false;
73-
}
74-
return true;
6+
super(command, database);
757
}
768

779
public static class QueryBuilder {
@@ -93,7 +25,7 @@ public QueryBuilder bind(final String placeholder, final Object value) {
9325
if (query == null) {
9426
query = new BoundParameterQuery(influxQL, null);
9527
}
96-
query.params.put(placeholder, value);
28+
query.bindParameter(placeholder, value);
9729
return this;
9830
}
9931

src/main/java/org/influxdb/dto/Query.java

Lines changed: 70 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,18 @@
11
package org.influxdb.dto;
22

3+
import com.squareup.moshi.JsonWriter;
4+
import okio.Buffer;
5+
import org.influxdb.InfluxDBIOException;
6+
import org.influxdb.querybuilder.Appendable;
7+
8+
import java.io.IOException;
39
import java.io.UnsupportedEncodingException;
410
import java.net.URLEncoder;
11+
import java.nio.charset.Charset;
512
import java.nio.charset.StandardCharsets;
13+
import java.util.HashMap;
14+
import java.util.Map;
15+
import java.util.Objects;
616

717
/**
818
* Represents a Query against Influxdb.
@@ -15,6 +25,7 @@ public class Query {
1525
private final String command;
1626
private final String database;
1727
private final boolean requiresPost;
28+
protected final Map<String, Object> params = new HashMap<>();
1829

1930
/**
2031
* @param command the query command
@@ -68,38 +79,43 @@ public boolean requiresPost() {
6879
return requiresPost;
6980
}
7081

71-
@SuppressWarnings("checkstyle:avoidinlineconditionals")
72-
@Override
73-
public int hashCode() {
74-
final int prime = 31;
75-
int result = 1;
76-
result = prime * result + ((command == null) ? 0 : command.hashCode());
77-
result = prime * result
78-
+ ((database == null) ? 0 : database.hashCode());
79-
return result;
82+
public Query bindParameter(final String placeholder, final Object value) {
83+
params.put(placeholder, value);
84+
return this;
85+
}
86+
87+
public boolean hasBoundParameters() {
88+
return !params.isEmpty();
89+
}
90+
91+
public String getParameterJsonWithUrlEncoded() {
92+
try {
93+
String jsonParameterObject = createJsonObject(params);
94+
String urlEncodedJsonParameterObject = encode(jsonParameterObject);
95+
return urlEncodedJsonParameterObject;
96+
} catch (IOException e) {
97+
throw new InfluxDBIOException(e);
98+
}
8099
}
81100

82-
@SuppressWarnings("checkstyle:needbraces")
83101
@Override
84-
public boolean equals(final Object obj) {
85-
if (this == obj)
86-
return true;
87-
if (obj == null)
88-
return false;
89-
if (getClass() != obj.getClass())
90-
return false;
91-
Query other = (Query) obj;
92-
if (command == null) {
93-
if (other.command != null)
94-
return false;
95-
} else if (!command.equals(other.command))
102+
public boolean equals(final Object o) {
103+
if (o == null || getClass() != o.getClass()) {
96104
return false;
97-
if (database == null) {
98-
if (other.database != null)
99-
return false;
100-
} else if (!database.equals(other.database))
101-
return false;
102-
return true;
105+
}
106+
107+
Query query = (Query) o;
108+
return Objects.equals(command, query.command) && Objects.equals(database, query.database) && params.equals(
109+
query.params);
110+
}
111+
112+
@Override
113+
public int hashCode() {
114+
final int prime = 31;
115+
int result = Objects.hashCode(command);
116+
result = prime * result + Objects.hashCode(database);
117+
result = prime * result + params.hashCode();
118+
return result;
103119
}
104120

105121
/**
@@ -115,4 +131,30 @@ public static String encode(final String command) {
115131
throw new IllegalStateException("Every JRE must support UTF-8", e);
116132
}
117133
}
134+
135+
private String createJsonObject(final Map<String, Object> parameterMap) throws IOException {
136+
Buffer b = new Buffer();
137+
JsonWriter writer = JsonWriter.of(b);
138+
writer.beginObject();
139+
for (Map.Entry<String, Object> pair : parameterMap.entrySet()) {
140+
String name = pair.getKey();
141+
Object value = pair.getValue();
142+
if (value instanceof Number) {
143+
Number number = (Number) value;
144+
writer.name(name).value(number);
145+
} else if (value instanceof String) {
146+
writer.name(name).value((String) value);
147+
} else if (value instanceof Boolean) {
148+
writer.name(name).value((Boolean) value);
149+
} else if (value instanceof Appendable) {
150+
StringBuilder stringBuilder = new StringBuilder();
151+
((Appendable) value).appendTo(stringBuilder);
152+
writer.name(name).value(stringBuilder.toString());
153+
} else {
154+
writer.name(name).value(String.valueOf(value));
155+
}
156+
}
157+
writer.endObject();
158+
return b.readString(Charset.forName("utf-8"));
159+
}
118160
}

src/main/java/org/influxdb/impl/InfluxDBImpl.java

Lines changed: 27 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@
1616
import org.influxdb.InfluxDBException;
1717
import org.influxdb.InfluxDBIOException;
1818
import org.influxdb.dto.BatchPoints;
19-
import org.influxdb.dto.BoundParameterQuery;
2019
import org.influxdb.dto.Point;
2120
import org.influxdb.dto.Pong;
2221
import org.influxdb.dto.Query;
@@ -637,13 +636,17 @@ public void query(final Query query, final int chunkSize, final BiConsumer<Cance
637636
public void query(final Query query, final int chunkSize, final BiConsumer<Cancellable, QueryResult> onNext,
638637
final Runnable onComplete, final Consumer<Throwable> onFailure) {
639638
Call<ResponseBody> call;
640-
if (query instanceof BoundParameterQuery) {
641-
BoundParameterQuery boundParameterQuery = (BoundParameterQuery) query;
642-
call = this.influxDBService.query(getDatabase(query), query.getCommandWithUrlEncoded(), chunkSize,
643-
boundParameterQuery.getParameterJsonWithUrlEncoded());
639+
if (query.hasBoundParameters()) {
640+
if (query.requiresPost()) {
641+
call = this.influxDBService.postQuery(getDatabase(query), query.getCommandWithUrlEncoded(), chunkSize,
642+
query.getParameterJsonWithUrlEncoded());
643+
} else {
644+
call = this.influxDBService.query(getDatabase(query), query.getCommandWithUrlEncoded(), chunkSize,
645+
query.getParameterJsonWithUrlEncoded());
646+
}
644647
} else {
645648
if (query.requiresPost()) {
646-
call = this.influxDBService.query(getDatabase(query), query.getCommandWithUrlEncoded(), chunkSize, null);
649+
call = this.influxDBService.postQuery(getDatabase(query), query.getCommandWithUrlEncoded(), chunkSize);
647650
} else {
648651
call = this.influxDBService.query(getDatabase(query), query.getCommandWithUrlEncoded(), chunkSize);
649652
}
@@ -716,18 +719,21 @@ public void onFailure(final Call<ResponseBody> call, final Throwable t) {
716719
@Override
717720
public QueryResult query(final Query query, final TimeUnit timeUnit) {
718721
Call<QueryResult> call;
719-
if (query instanceof BoundParameterQuery) {
720-
BoundParameterQuery boundParameterQuery = (BoundParameterQuery) query;
721-
call = this.influxDBService.query(getDatabase(query),
722-
TimeUtil.toTimePrecision(timeUnit), query.getCommandWithUrlEncoded(),
723-
boundParameterQuery.getParameterJsonWithUrlEncoded());
722+
if (query.hasBoundParameters()) {
723+
if (query.requiresPost()) {
724+
call = this.influxDBService.postQuery(getDatabase(query), TimeUtil.toTimePrecision(timeUnit),
725+
query.getCommandWithUrlEncoded(), query.getParameterJsonWithUrlEncoded());
726+
} else {
727+
call = this.influxDBService.query(getDatabase(query), TimeUtil.toTimePrecision(timeUnit),
728+
query.getCommandWithUrlEncoded(), query.getParameterJsonWithUrlEncoded());
729+
}
724730
} else {
725731
if (query.requiresPost()) {
726-
call = this.influxDBService.query(getDatabase(query),
727-
TimeUtil.toTimePrecision(timeUnit), query.getCommandWithUrlEncoded(), null);
732+
call = this.influxDBService.postQuery(getDatabase(query),
733+
TimeUtil.toTimePrecision(timeUnit), query.getCommandWithUrlEncoded());
728734
} else {
729735
call = this.influxDBService.query(getDatabase(query),
730-
TimeUtil.toTimePrecision(timeUnit), query.getCommandWithUrlEncoded());
736+
TimeUtil.toTimePrecision(timeUnit), query.getCommandWithUrlEncoded(), null);
731737
}
732738
}
733739
return executeQuery(call);
@@ -788,10 +794,14 @@ public boolean databaseExists(final String name) {
788794
*/
789795
private Call<QueryResult> callQuery(final Query query) {
790796
Call<QueryResult> call;
791-
if (query instanceof BoundParameterQuery) {
792-
BoundParameterQuery boundParameterQuery = (BoundParameterQuery) query;
797+
if (query.hasBoundParameters()) {
798+
if (query.requiresPost()) {
793799
call = this.influxDBService.postQuery(getDatabase(query), query.getCommandWithUrlEncoded(),
794-
boundParameterQuery.getParameterJsonWithUrlEncoded());
800+
query.getParameterJsonWithUrlEncoded());
801+
} else {
802+
call = this.influxDBService.query(getDatabase(query), null, query.getCommandWithUrlEncoded(),
803+
query.getParameterJsonWithUrlEncoded());
804+
}
795805
} else {
796806
if (query.requiresPost()) {
797807
call = this.influxDBService.postQuery(getDatabase(query), query.getCommandWithUrlEncoded());

src/main/java/org/influxdb/impl/InfluxDBService.java

Lines changed: 21 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -47,12 +47,7 @@ public Call<ResponseBody> writePoints(@Query(DB) String database,
4747

4848
@GET("query")
4949
public Call<QueryResult> query(@Query(DB) String db,
50-
@Query(EPOCH) String epoch, @Query(value = Q, encoded = true) String query);
51-
52-
@POST("query")
53-
@FormUrlEncoded
54-
public Call<QueryResult> query(@Query(DB) String db,
55-
@Query(EPOCH) String epoch, @Field(value = Q, encoded = true) String query,
50+
@Query(EPOCH) String epoch, @Query(value = Q, encoded = true) String query,
5651
@Query(value = PARAMS, encoded = true) String params);
5752

5853
@GET("query")
@@ -66,9 +61,26 @@ public Call<QueryResult> postQuery(@Query(DB) String db,
6661

6762
@POST("query")
6863
@FormUrlEncoded
69-
public Call<QueryResult> postQuery(@Query(DB) String db,
64+
public Call<QueryResult> postQuery(@Query(DB) String db, @Query(EPOCH) String epoch,
65+
@Field(value = Q, encoded = true) String query);
66+
67+
@POST("query")
68+
@FormUrlEncoded
69+
public Call<QueryResult> postQuery(@Query(DB) String db, @Query(EPOCH) String epoch,
7070
@Field(value = Q, encoded = true) String query, @Query(value = PARAMS, encoded = true) String params);
7171

72+
@Streaming
73+
@POST("query?chunked=true")
74+
@FormUrlEncoded
75+
public Call<ResponseBody> postQuery(@Query(DB) String db, @Field(value = Q, encoded = true) String query,
76+
@Query(CHUNK_SIZE) int chunkSize);
77+
78+
@Streaming
79+
@POST("query?chunked=true")
80+
@FormUrlEncoded
81+
public Call<ResponseBody> postQuery(@Query(DB) String db, @Field(value = Q, encoded = true) String query,
82+
@Query(CHUNK_SIZE) int chunkSize, @Query(value = PARAMS, encoded = true) String params);
83+
7284
@POST("query")
7385
@FormUrlEncoded
7486
public Call<QueryResult> postQuery(@Field(value = Q, encoded = true) String query);
@@ -79,8 +91,7 @@ public Call<ResponseBody> query(@Query(DB) String db, @Query(value = Q, encoded
7991
@Query(CHUNK_SIZE) int chunkSize);
8092

8193
@Streaming
82-
@POST("query?chunked=true")
83-
@FormUrlEncoded
84-
public Call<ResponseBody> query(@Query(DB) String db, @Field(value = Q, encoded = true) String query,
94+
@GET("query?chunked=true")
95+
public Call<ResponseBody> query(@Query(DB) String db, @Query(value = Q, encoded = true) String query,
8596
@Query(CHUNK_SIZE) int chunkSize, @Query(value = PARAMS, encoded = true) String params);
8697
}

src/main/java/org/influxdb/querybuilder/Appender.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,8 @@ public static StringBuilder appendValue(final Object value, final StringBuilder
6262
stringBuilder.append(')');
6363
} else if (value instanceof Column) {
6464
appendName(((Column) value).getName(), stringBuilder);
65+
} else if (value instanceof Placeholder) {
66+
stringBuilder.append('$').append(((Placeholder) value).getName());
6567
} else if (value instanceof String) {
6668
stringBuilder.append("'").append(value).append("'");
6769
} else if (value != null) {

src/main/java/org/influxdb/querybuilder/FunctionFactory.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,10 @@ public static Object column(final String name) {
6161
return new Column(name);
6262
}
6363

64+
public static Object placeholder(final String name) {
65+
return new Placeholder(name);
66+
}
67+
6468
private static void convertToColumns(final Object... arguments) {
6569
for (int i = 0; i < arguments.length; i++) {
6670
arguments[i] = convertToColumn(arguments[i]);

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy