Skip to content

Commit a65640e

Browse files
feat: Support float32, float64, and array type query params (#2297)
* feat: Support float32, float64, and array type query params Also fixes a bug with float32 lists where it was incorrectly converted to List<Double> where we expect List<Float> Change-Id: I4d4b32dcddef74711eeea8997b020c46eee8be3c * 🦉 Updates from OwlBot post-processor See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md --------- Co-authored-by: Owl Bot <gcf-owl-bot[bot]@users.noreply.github.com>
1 parent 46d95ca commit a65640e

File tree

4 files changed

+615
-12
lines changed

4 files changed

+615
-12
lines changed

google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/AbstractProtoStructReader.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -268,8 +268,10 @@ Object decodeValue(Value value, SqlType<?> type) {
268268
case INT64:
269269
return value.getIntValue();
270270
case FLOAT64:
271-
case FLOAT32:
272271
return value.getFloatValue();
272+
case FLOAT32:
273+
// cast to float so we produce List<Float>, etc
274+
return (float) value.getFloatValue();
273275
case BOOL:
274276
return value.getBoolValue();
275277
case TIMESTAMP:

google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/models/sql/Statement.java

Lines changed: 154 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717

1818
import com.google.api.core.BetaApi;
1919
import com.google.api.core.InternalApi;
20+
import com.google.bigtable.v2.ArrayValue;
2021
import com.google.bigtable.v2.ExecuteQueryRequest;
2122
import com.google.bigtable.v2.Type;
2223
import com.google.bigtable.v2.Value;
@@ -27,6 +28,7 @@
2728
import com.google.protobuf.ByteString;
2829
import com.google.protobuf.Timestamp;
2930
import java.util.HashMap;
31+
import java.util.List;
3032
import java.util.Map;
3133
import javax.annotation.Nullable;
3234
import org.threeten.bp.Instant;
@@ -65,6 +67,10 @@ public class Statement {
6567
Type.newBuilder().setBytesType(Type.Bytes.getDefaultInstance()).build();
6668
private static final Type INT64_TYPE =
6769
Type.newBuilder().setInt64Type(Type.Int64.getDefaultInstance()).build();
70+
private static final Type FLOAT32_TYPE =
71+
Type.newBuilder().setFloat32Type(Type.Float32.getDefaultInstance()).build();
72+
private static final Type FLOAT64_TYPE =
73+
Type.newBuilder().setFloat64Type(Type.Float64.getDefaultInstance()).build();
6874
private static final Type BOOL_TYPE =
6975
Type.newBuilder().setBoolType(Type.Bool.getDefaultInstance()).build();
7076
private static final Type TIMESTAMP_TYPE =
@@ -131,6 +137,24 @@ public Builder setLongParam(String paramName, @Nullable Long value) {
131137
return this;
132138
}
133139

140+
/**
141+
* Sets a query parameter with the name {@code paramName} and the FLOAT32 typed value {@code
142+
* value}
143+
*/
144+
public Builder setFloatParam(String paramName, @Nullable Float value) {
145+
params.put(paramName, float32ParamOf(value));
146+
return this;
147+
}
148+
149+
/**
150+
* Sets a query parameter with the name {@code paramName} and the FLOAT64 typed value {@code
151+
* value}
152+
*/
153+
public Builder setDoubleParam(String paramName, @Nullable Double value) {
154+
params.put(paramName, float64ParamOf(value));
155+
return this;
156+
}
157+
134158
/**
135159
* Sets a query parameter with the name {@code paramName} and the BOOL typed value {@code value}
136160
*/
@@ -156,6 +180,17 @@ public Builder setDateParam(String paramName, @Nullable Date value) {
156180
return this;
157181
}
158182

183+
/**
184+
* Sets a query parameter with the name {@code paramName} and the ARRAY typed value {@code
185+
* value}. The array element type is specified by {@code arrayType} and the List elements must
186+
* be of the corresponding Java type. Null array elements are valid.
187+
*/
188+
public <T> Builder setListParam(
189+
String paramName, @Nullable List<T> value, SqlType.Array<T> arrayType) {
190+
params.put(paramName, arrayParamOf(value, arrayType));
191+
return this;
192+
}
193+
159194
private static Value stringParamOf(@Nullable String value) {
160195
Value.Builder builder = nullValueWithType(STRING_TYPE);
161196
if (value != null) {
@@ -180,6 +215,22 @@ private static Value int64ParamOf(@Nullable Long value) {
180215
return builder.build();
181216
}
182217

218+
private static Value float32ParamOf(@Nullable Float value) {
219+
Value.Builder builder = nullValueWithType(FLOAT32_TYPE);
220+
if (value != null) {
221+
builder.setFloatValue(value);
222+
}
223+
return builder.build();
224+
}
225+
226+
private static Value float64ParamOf(@Nullable Double value) {
227+
Value.Builder builder = nullValueWithType(FLOAT64_TYPE);
228+
if (value != null) {
229+
builder.setFloatValue(value);
230+
}
231+
return builder.build();
232+
}
233+
183234
private static Value booleanParamOf(@Nullable Boolean value) {
184235
Value.Builder builder = nullValueWithType(BOOL_TYPE);
185236
if (value != null) {
@@ -191,28 +242,120 @@ private static Value booleanParamOf(@Nullable Boolean value) {
191242
private static Value timestampParamOf(@Nullable Instant value) {
192243
Value.Builder builder = nullValueWithType(TIMESTAMP_TYPE);
193244
if (value != null) {
194-
builder.setTimestampValue(
195-
Timestamp.newBuilder()
196-
.setSeconds(value.getEpochSecond())
197-
.setNanos(value.getNano())
198-
.build());
245+
builder.setTimestampValue(toTimestamp(value));
199246
}
200247
return builder.build();
201248
}
202249

203250
private static Value dateParamOf(@Nullable Date value) {
204251
Value.Builder builder = nullValueWithType(DATE_TYPE);
205252
if (value != null) {
206-
builder.setDateValue(
207-
com.google.type.Date.newBuilder()
208-
.setYear(value.getYear())
209-
.setMonth(value.getMonth())
210-
.setDay(value.getDayOfMonth())
211-
.build());
253+
builder.setDateValue(toProtoDate(value));
254+
}
255+
return builder.build();
256+
}
257+
258+
private static <T> Value arrayParamOf(@Nullable List<T> value, SqlType.Array<T> arrayType) {
259+
Type type =
260+
Type.newBuilder()
261+
.setArrayType(
262+
Type.Array.newBuilder().setElementType(getElementType(arrayType)).build())
263+
.build();
264+
Value.Builder builder = nullValueWithType(type);
265+
if (value != null) {
266+
builder.setArrayValue(arrayValueOf(value, arrayType));
212267
}
213268
return builder.build();
214269
}
215270

271+
private static Type getElementType(SqlType.Array<?> arrayType) {
272+
switch (arrayType.getElementType().getCode()) {
273+
case BYTES:
274+
return BYTES_TYPE;
275+
case STRING:
276+
return STRING_TYPE;
277+
case INT64:
278+
return INT64_TYPE;
279+
case FLOAT32:
280+
return FLOAT32_TYPE;
281+
case FLOAT64:
282+
return FLOAT64_TYPE;
283+
case BOOL:
284+
return BOOL_TYPE;
285+
case TIMESTAMP:
286+
return TIMESTAMP_TYPE;
287+
case DATE:
288+
return DATE_TYPE;
289+
default:
290+
throw new IllegalArgumentException(
291+
"Unsupported query parameter Array element type: " + arrayType.getElementType());
292+
}
293+
}
294+
295+
private static ArrayValue arrayValueOf(List<?> value, SqlType.Array<?> arrayType) {
296+
ArrayValue.Builder valueBuilder = ArrayValue.newBuilder();
297+
for (Object element : value) {
298+
if (element == null) {
299+
valueBuilder.addValues(Value.getDefaultInstance());
300+
continue;
301+
}
302+
switch (arrayType.getElementType().getCode()) {
303+
case BYTES:
304+
ByteString bytesElem = (ByteString) element;
305+
valueBuilder.addValues(Value.newBuilder().setBytesValue(bytesElem).build());
306+
break;
307+
case STRING:
308+
String stringElem = (String) element;
309+
valueBuilder.addValues(Value.newBuilder().setStringValue(stringElem).build());
310+
break;
311+
case INT64:
312+
Long longElem = (Long) element;
313+
valueBuilder.addValues(Value.newBuilder().setIntValue(longElem).build());
314+
break;
315+
case FLOAT32:
316+
Float floatElem = (Float) element;
317+
valueBuilder.addValues(Value.newBuilder().setFloatValue(floatElem).build());
318+
break;
319+
case FLOAT64:
320+
Double doubleElem = (Double) element;
321+
valueBuilder.addValues(Value.newBuilder().setFloatValue(doubleElem).build());
322+
break;
323+
case BOOL:
324+
Boolean boolElem = (Boolean) element;
325+
valueBuilder.addValues(Value.newBuilder().setBoolValue(boolElem).build());
326+
break;
327+
case TIMESTAMP:
328+
Instant timestampElem = (Instant) element;
329+
valueBuilder.addValues(
330+
Value.newBuilder().setTimestampValue(toTimestamp(timestampElem)).build());
331+
break;
332+
case DATE:
333+
Date dateElem = (Date) element;
334+
valueBuilder.addValues(Value.newBuilder().setDateValue(toProtoDate(dateElem)).build());
335+
break;
336+
default:
337+
throw new IllegalArgumentException(
338+
"Unsupported query parameter Array element type: " + arrayType.getElementType());
339+
}
340+
}
341+
return valueBuilder.build();
342+
}
343+
344+
private static Timestamp toTimestamp(Instant instant) {
345+
return Timestamp.newBuilder()
346+
.setSeconds(instant.getEpochSecond())
347+
.setNanos(instant.getNano())
348+
.build();
349+
}
350+
351+
private static com.google.type.Date toProtoDate(Date date) {
352+
return com.google.type.Date.newBuilder()
353+
.setYear(date.getYear())
354+
.setMonth(date.getMonth())
355+
.setDay(date.getDayOfMonth())
356+
.build();
357+
}
358+
216359
private static Value.Builder nullValueWithType(Type type) {
217360
return Value.newBuilder().setType(type);
218361
}

google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/internal/AbstractProtoStructReaderTest.java

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -221,6 +221,22 @@ public void arrayField_validatesType() {
221221
IllegalStateException.class,
222222
() -> structWithList.getList(0, SqlType.arrayOf(SqlType.bytes())));
223223
}
224+
225+
// Test this independently since it won't throw an exception until accessing an element if
226+
// float is converted to double incorrectly
227+
@Test
228+
public void arrayField_accessingFloat() {
229+
TestProtoStruct structWithList =
230+
TestProtoStruct.create(
231+
ProtoResultSetMetadata.fromProto(
232+
metadata(columnMetadata("testField", arrayType(float32Type()))).getMetadata()),
233+
Collections.singletonList(arrayValue(floatValue(1.1f), floatValue(1.2f))));
234+
235+
List<Float> floatList =
236+
structWithList.getList("testField", SqlType.arrayOf(SqlType.float32()));
237+
assertThat(floatList.get(0)).isEqualTo(1.1f);
238+
assertThat(floatList.get(1)).isEqualTo(1.2f);
239+
}
224240
}
225241

226242
@RunWith(Parameterized.class)
@@ -378,6 +394,32 @@ public static List<Object[]> parameters() {
378394
(row, index) -> row.getList(index, SqlType.arrayOf(SqlType.string())),
379395
Arrays.asList("foo", null, "baz")
380396
},
397+
// Float List
398+
{
399+
Collections.singletonList(columnMetadata("testField", arrayType(float32Type()))),
400+
Collections.singletonList(
401+
arrayValue(floatValue(1.1f), floatValue(1.2f), floatValue(1.3f))),
402+
0,
403+
"testField",
404+
(BiFunction<TestProtoStruct, String, List<Float>>)
405+
(row, field) -> row.getList(field, SqlType.arrayOf(SqlType.float32())),
406+
(BiFunction<TestProtoStruct, Integer, List<Float>>)
407+
(row, index) -> row.getList(index, SqlType.arrayOf(SqlType.float32())),
408+
Arrays.asList(1.1f, 1.2f, 1.3f)
409+
},
410+
// Double List
411+
{
412+
Collections.singletonList(columnMetadata("testField", arrayType(float64Type()))),
413+
Collections.singletonList(
414+
arrayValue(floatValue(1.11d), floatValue(1.22d), floatValue(1.33d))),
415+
0,
416+
"testField",
417+
(BiFunction<TestProtoStruct, String, List<Double>>)
418+
(row, field) -> row.getList(field, SqlType.arrayOf(SqlType.float64())),
419+
(BiFunction<TestProtoStruct, Integer, List<Double>>)
420+
(row, index) -> row.getList(index, SqlType.arrayOf(SqlType.float64())),
421+
Arrays.asList(1.11d, 1.22d, 1.33d)
422+
},
381423
// Simple Map
382424
{
383425
Collections.singletonList(

0 commit comments

Comments
 (0)