com.google.cloud.bigquery.QueryParameterValue Maven / Gradle / Ivy
Show all versions of google-cloud-bigquery Show documentation
/*
* Copyright 2016 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.cloud.bigquery;
import static org.threeten.bp.temporal.ChronoField.HOUR_OF_DAY;
import static org.threeten.bp.temporal.ChronoField.MINUTE_OF_HOUR;
import static org.threeten.bp.temporal.ChronoField.NANO_OF_SECOND;
import static org.threeten.bp.temporal.ChronoField.SECOND_OF_MINUTE;
import com.google.api.services.bigquery.model.QueryParameterType;
import com.google.api.services.bigquery.model.RangeValue;
import com.google.auto.value.AutoValue;
import com.google.cloud.Timestamp;
import com.google.common.base.Function;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Lists;
import com.google.common.io.BaseEncoding;
import com.google.gson.JsonObject;
import java.io.Serializable;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.annotation.Nullable;
import org.threeten.bp.Instant;
import org.threeten.bp.ZoneOffset;
import org.threeten.bp.format.DateTimeFormatter;
import org.threeten.bp.format.DateTimeFormatterBuilder;
import org.threeten.bp.format.DateTimeParseException;
import org.threeten.extra.PeriodDuration;
/**
* A value for a QueryParameter along with its type.
*
* A static factory method is provided for each of the possible types (e.g. {@link #int64(Long)}
* for StandardSQLTypeName.INT64). Alternatively, an instance can be constructed by calling {@link
* #of(Object, Class)} with the value and a Class object, which will use these mappings:
*
*
* - Boolean: StandardSQLTypeName.BOOL
*
- String: StandardSQLTypeName.STRING
*
- Integer: StandardSQLTypeName.INT64
*
- Long: StandardSQLTypeName.INT64
*
- Double: StandardSQLTypeName.FLOAT64
*
- Float: StandardSQLTypeName.FLOAT64
*
- BigDecimal: StandardSQLTypeName.NUMERIC
*
- BigNumeric: StandardSQLTypeName.BIGNUMERIC
*
- JSON: StandardSQLTypeName.JSON
*
- INTERVAL: StandardSQLTypeName.INTERVAL
*
*
* No other types are supported through that entry point. The other types can be created by
* calling {@link #of(Object, StandardSQLTypeName)} with the value and a particular
* StandardSQLTypeName enum value.
*
*
Struct parameters are currently not supported.
*/
@AutoValue
public abstract class QueryParameterValue implements Serializable {
private static final DateTimeFormatter timestampFormatter =
new DateTimeFormatterBuilder()
.parseLenient()
.append(DateTimeFormatter.ISO_LOCAL_DATE)
.appendLiteral(' ')
.appendValue(HOUR_OF_DAY, 2)
.appendLiteral(':')
.appendValue(MINUTE_OF_HOUR, 2)
.optionalStart()
.appendLiteral(':')
.appendValue(SECOND_OF_MINUTE, 2)
.optionalStart()
.appendFraction(NANO_OF_SECOND, 6, 9, true)
.optionalStart()
.appendOffset("+HHMM", "+00:00")
.optionalEnd()
.toFormatter()
.withZone(ZoneOffset.UTC);
private static final DateTimeFormatter timestampValidator =
new DateTimeFormatterBuilder()
.parseLenient()
.append(timestampFormatter)
.optionalStart()
.appendOffsetId()
.optionalEnd()
.toFormatter()
.withZone(ZoneOffset.UTC);
private static final DateTimeFormatter dateFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
private static final DateTimeFormatter timeFormatter =
DateTimeFormatter.ofPattern("HH:mm:ss.SSSSSS");
private static final DateTimeFormatter datetimeFormatter =
DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSSSSS");
static final Function<
QueryParameterValue, com.google.api.services.bigquery.model.QueryParameterValue>
TO_VALUE_PB_FUNCTION =
new Function<
QueryParameterValue, com.google.api.services.bigquery.model.QueryParameterValue>() {
@Override
public com.google.api.services.bigquery.model.QueryParameterValue apply(
QueryParameterValue value) {
return value.toValuePb();
}
};
private static final long serialVersionUID = -5620695863123562896L;
@AutoValue.Builder
public abstract static class Builder {
/** Sets the value to the given scalar value. */
public abstract Builder setValue(String value);
/** Sets array values. The type must set to ARRAY. */
public Builder setArrayValues(List arrayValues) {
return setArrayValuesInner(ImmutableList.copyOf(arrayValues));
}
abstract Builder setArrayValuesInner(ImmutableList arrayValues);
/** Sets struct values. The type must set to STRUCT. */
public Builder setStructValues(Map structValues) {
setStructTypes(ImmutableMap.copyOf(structValues));
return setStructValuesInner(ImmutableMap.copyOf(structValues));
}
abstract Builder setStructValuesInner(Map structValues);
/** Sets range values. The type must set to RANGE. */
public Builder setRangeValues(Range range) {
return setRangeValuesInner(range);
}
abstract Builder setRangeValuesInner(Range range);
/** Sets the parameter data type. */
public abstract Builder setType(StandardSQLTypeName type);
/** Sets the data type of the array elements. The type must set to ARRAY. */
public abstract Builder setArrayType(StandardSQLTypeName arrayType);
/** Sets the data type of the struct elements. The type must set to STRUCT. */
public Builder setStructTypes(Map structTypes) {
return setStructTypesInner(structTypes);
}
abstract Builder setStructTypesInner(Map structTypes);
/** Creates a {@code QueryParameterValue} object. */
public abstract QueryParameterValue build();
}
QueryParameterValue() {
// Package-private so it's extensible by AutoValue but not users.
}
/** Returns the value of this parameter. */
@Nullable
public abstract String getValue();
/** Returns the array values of this parameter. The returned list, if not null, is immutable. */
@Nullable
public List getArrayValues() {
return getArrayValuesInner();
}
@Nullable
abstract ImmutableList getArrayValuesInner();
/** Returns the struct values of this parameter. The returned map, if not null, is immutable. */
@Nullable
public Map getStructValues() {
return getStructValuesInner();
}
@Nullable
abstract Map getStructValuesInner();
/** Returns the struct values of this parameter. The returned map, if not null, is immutable. */
@Nullable
public Range getRangeValues() {
return getRangeValuesInner();
}
@Nullable
abstract Range getRangeValuesInner();
/** Returns the data type of this parameter. */
public abstract StandardSQLTypeName getType();
/** Returns the data type of the array elements. */
@Nullable
public abstract StandardSQLTypeName getArrayType();
/** Returns the data type of the struct elements. */
@Nullable
public Map getStructTypes() {
return getStructTypesInner();
}
@Nullable
abstract Map getStructTypesInner();
/**
* Creates a {@code QueryParameterValue} object with the given value and type. Note: this does not
* support BigNumeric
*/
public static QueryParameterValue of(T value, Class type) {
return of(value, classToType(type));
}
/** Creates a {@code QueryParameterValue} object with the given value and type. */
public static QueryParameterValue of(T value, StandardSQLTypeName type) {
return QueryParameterValue.newBuilder()
.setValue(valueToStringOrNull(value, type))
.setType(type)
.build();
}
/** Creates a {@code QueryParameterValue} object with a type of BOOL. */
public static QueryParameterValue bool(Boolean value) {
return of(value, StandardSQLTypeName.BOOL);
}
/** Creates a {@code QueryParameterValue} object with a type of INT64. */
public static QueryParameterValue int64(Long value) {
return of(value, StandardSQLTypeName.INT64);
}
/** Creates a {@code QueryParameterValue} object with a type of INT64. */
public static QueryParameterValue int64(Integer value) {
return of(value, StandardSQLTypeName.INT64);
}
/** Creates a {@code QueryParameterValue} object with a type of FLOAT64. */
public static QueryParameterValue float64(Double value) {
return of(value, StandardSQLTypeName.FLOAT64);
}
/** Creates a {@code QueryParameterValue} object with a type of FLOAT64. */
public static QueryParameterValue float64(Float value) {
return of(value, StandardSQLTypeName.FLOAT64);
}
/** Creates a {@code QueryParameterValue} object with a type of NUMERIC. */
public static QueryParameterValue numeric(BigDecimal value) {
return of(value, StandardSQLTypeName.NUMERIC);
}
/** Creates a {@code QueryParameterValue} object with a type of BIGNUMERIC. */
public static QueryParameterValue bigNumeric(BigDecimal value) {
return of(value, StandardSQLTypeName.BIGNUMERIC);
}
/** Creates a {@code QueryParameterValue} object with a type of STRING. */
public static QueryParameterValue string(String value) {
return of(value, StandardSQLTypeName.STRING);
}
/** Creates a {@code QueryParameterValue} object with a type of GEOGRAPHY. */
public static QueryParameterValue geography(String value) {
return of(value, StandardSQLTypeName.GEOGRAPHY);
}
/**
* Creates a {@code QueryParameterValue} object with a type of JSON. Currently, this is only
* supported in INSERT, not in query as a filter
*/
public static QueryParameterValue json(String value) {
return of(value, StandardSQLTypeName.JSON);
}
/**
* Creates a {@code QueryParameterValue} object with a type of JSON. Currently, this is only
* supported in INSERT, not in query as a filter
*/
public static QueryParameterValue json(JsonObject value) {
return of(value, StandardSQLTypeName.JSON);
}
/** Creates a {@code QueryParameterValue} object with a type of BYTES. */
public static QueryParameterValue bytes(byte[] value) {
return of(value, StandardSQLTypeName.BYTES);
}
/** Creates a {@code QueryParameterValue} object with a type of TIMESTAMP. */
public static QueryParameterValue timestamp(Long value) {
return of(value, StandardSQLTypeName.TIMESTAMP);
}
/**
* Creates a {@code QueryParameterValue} object with a type of TIMESTAMP. Must be in the format
* "yyyy-MM-dd HH:mm:ss.SSSSSSZZ", e.g. "2014-08-19 12:41:35.220000+00:00".
*/
public static QueryParameterValue timestamp(String value) {
return of(value, StandardSQLTypeName.TIMESTAMP);
}
/**
* Creates a {@code QueryParameterValue} object with a type of DATE. Must be in the format
* "yyyy-MM-dd", e.g. "2014-08-19".
*/
public static QueryParameterValue date(String value) {
return of(value, StandardSQLTypeName.DATE);
}
/**
* Creates a {@code QueryParameterValue} object with a type of TIME. Must be in the format
* "HH:mm:ss.SSSSSS", e.g. "12:41:35.220000".
*/
public static QueryParameterValue time(String value) {
return of(value, StandardSQLTypeName.TIME);
}
/**
* Creates a {@code QueryParameterValue} object with a type of DATETIME. Must be in the format
* "yyyy-MM-dd HH:mm:ss.SSSSSS", e.g. "2014-08-19 12:41:35.220000".
*/
public static QueryParameterValue dateTime(String value) {
return of(value, StandardSQLTypeName.DATETIME);
}
/**
* Creates a {@code QueryParameterValue} object with a type of INTERVAL. Must be in the canonical
* format "[sign]Y-M [sign]D [sign]H:M:S[.F]", e.g. "123-7 -19 0:24:12.000006" or ISO 8601
* duration format, e.g. "P123Y7M-19DT0H24M12.000006S"
*/
public static QueryParameterValue interval(String value) {
return of(value, StandardSQLTypeName.INTERVAL);
}
/** Creates a {@code QueryParameterValue} object with a type of INTERVAL. */
public static QueryParameterValue interval(PeriodDuration value) {
return of(value, StandardSQLTypeName.INTERVAL);
}
/** Creates a {@code QueryParameterValue} object with a type of RANGE. */
public static QueryParameterValue range(Range value) {
return QueryParameterValue.newBuilder()
.setRangeValues(value)
.setType(StandardSQLTypeName.RANGE)
.build();
}
/**
* Creates a {@code QueryParameterValue} object with a type of ARRAY, and an array element type
* based on the given class.
*/
public static QueryParameterValue array(T[] array, Class clazz) {
return array(array, classToType(clazz));
}
/**
* Creates a {@code QueryParameterValue} object with a type of ARRAY the given array element type.
*/
public static QueryParameterValue array(T[] array, StandardSQLTypeName type) {
List listValues = new ArrayList<>();
for (T obj : array) {
if (type == StandardSQLTypeName.STRUCT) {
listValues.add((QueryParameterValue) obj);
} else {
listValues.add(QueryParameterValue.of(obj, type));
}
}
return QueryParameterValue.newBuilder()
.setArrayValues(listValues)
.setType(StandardSQLTypeName.ARRAY)
.setArrayType(type)
.build();
}
/**
* Creates a map with {@code QueryParameterValue} object and a type of STRUCT the given struct
* element type.
*/
public static QueryParameterValue struct(Map struct) {
return QueryParameterValue.newBuilder()
.setStructValues(struct)
.setType(StandardSQLTypeName.STRUCT)
.build();
}
private static StandardSQLTypeName classToType(Class type) {
if (Boolean.class.isAssignableFrom(type)) {
return StandardSQLTypeName.BOOL;
} else if (String.class.isAssignableFrom(type)) {
return StandardSQLTypeName.STRING;
} else if (String.class.isAssignableFrom(type)) {
return StandardSQLTypeName.GEOGRAPHY;
} else if (Integer.class.isAssignableFrom(type)) {
return StandardSQLTypeName.INT64;
} else if (Long.class.isAssignableFrom(type)) {
return StandardSQLTypeName.INT64;
} else if (Double.class.isAssignableFrom(type)) {
return StandardSQLTypeName.FLOAT64;
} else if (Float.class.isAssignableFrom(type)) {
return StandardSQLTypeName.FLOAT64;
} else if (BigDecimal.class.isAssignableFrom(type)) {
return StandardSQLTypeName.NUMERIC;
} else if (Date.class.isAssignableFrom(type)) {
return StandardSQLTypeName.DATE;
} else if (String.class.isAssignableFrom(type)) {
return StandardSQLTypeName.JSON;
} else if (JsonObject.class.isAssignableFrom(type)) {
return StandardSQLTypeName.JSON;
}
throw new IllegalArgumentException("Unsupported object type for QueryParameter: " + type);
}
private static String valueToStringOrNull(T value, StandardSQLTypeName type) {
if (value == null) {
return null;
}
switch (type) {
case BOOL:
if (value instanceof Boolean) {
return value.toString();
}
break;
case INT64:
if (value instanceof Integer || value instanceof Long) {
return value.toString();
}
break;
case FLOAT64:
if (value instanceof Double || value instanceof Float) {
return value.toString();
}
break;
case NUMERIC:
case BIGNUMERIC:
if (value instanceof BigDecimal) {
return value.toString();
}
break;
case BYTES:
if (value instanceof byte[]) {
return BaseEncoding.base64().encode((byte[]) value);
}
break;
case STRING:
return value.toString();
case GEOGRAPHY:
return value.toString();
case JSON:
if (value instanceof String || value instanceof JsonObject) return value.toString();
case INTERVAL:
if (value instanceof String || value instanceof PeriodDuration) return value.toString();
break;
case STRUCT:
throw new IllegalArgumentException("Cannot convert STRUCT to String value");
case ARRAY:
throw new IllegalArgumentException("Cannot convert ARRAY to String value");
case RANGE:
throw new IllegalArgumentException("Cannot convert RANGE to String value");
case TIMESTAMP:
if (value instanceof Long) {
Timestamp timestamp = Timestamp.ofTimeMicroseconds((Long) value);
return timestampFormatter.format(
Instant.ofEpochSecond(timestamp.getSeconds(), timestamp.getNanos()));
} else if (value instanceof String) {
// verify that the String is in the right format
checkFormat(value, timestampValidator);
return (String) value;
}
break;
case DATE:
if (value instanceof String) {
// verify that the String is in the right format
checkFormat(value, dateFormatter);
return (String) value;
} else if (value instanceof Date) {
com.google.cloud.Date date = com.google.cloud.Date.fromJavaUtilDate((Date) value);
return date.toString();
}
break;
case TIME:
if (value instanceof String) {
// verify that the String is in the right format
checkFormat(value, timeFormatter);
return (String) value;
}
break;
case DATETIME:
if (value instanceof String) {
// verify that the String is in the right format
checkFormat(value, datetimeFormatter);
return (String) value;
}
break;
default:
throw new UnsupportedOperationException("Implementation error - Unsupported type: " + type);
}
throw new IllegalArgumentException(
"Type " + type + " incompatible with " + value.getClass().getCanonicalName());
}
private static void checkFormat(Object value, DateTimeFormatter formatter) {
try {
formatter.parse((String) value);
} catch (DateTimeParseException e) {
throw new IllegalArgumentException(e.getMessage(), e);
}
}
/** Returns a builder for a QueryParameterValue object with given value. */
public abstract Builder toBuilder();
/** Returns a builder for the {@code QueryParameterValue} object. */
public static Builder newBuilder() {
return new AutoValue_QueryParameterValue.Builder();
}
com.google.api.services.bigquery.model.QueryParameterValue toValuePb() {
com.google.api.services.bigquery.model.QueryParameterValue valuePb =
new com.google.api.services.bigquery.model.QueryParameterValue();
valuePb.setValue(getValue());
if (getArrayValues() != null) {
valuePb.setArrayValues(
Lists.transform(getArrayValues(), QueryParameterValue.TO_VALUE_PB_FUNCTION));
}
if (getStructValues() != null) {
Map structValues =
new HashMap<>();
for (Map.Entry structValue : getStructValues().entrySet()) {
structValues.put(structValue.getKey(), structValue.getValue().toValuePb());
}
valuePb.setStructValues(structValues);
}
if (getType() == StandardSQLTypeName.RANGE) {
RangeValue rangeValue = new RangeValue();
if (!getRangeValues().getStart().isNull()) {
com.google.api.services.bigquery.model.QueryParameterValue startValue =
new com.google.api.services.bigquery.model.QueryParameterValue();
startValue.setValue(getRangeValues().getStart().getStringValue());
rangeValue.setStart(startValue);
}
if (!getRangeValues().getEnd().isNull()) {
com.google.api.services.bigquery.model.QueryParameterValue endValue =
new com.google.api.services.bigquery.model.QueryParameterValue();
endValue.setValue(getRangeValues().getEnd().getStringValue());
rangeValue.setEnd(endValue);
}
valuePb.setRangeValue(rangeValue);
}
return valuePb;
}
QueryParameterType toTypePb() {
QueryParameterType typePb = new QueryParameterType();
typePb.setType(getType().toString());
if (getArrayType() != null) {
List values = getArrayValues();
if (getArrayType() == StandardSQLTypeName.STRUCT && values != null && values.size() != 0) {
QueryParameterType structType = values.get(0).toTypePb();
typePb.setArrayType(structType);
} else {
QueryParameterType arrayTypePb = new QueryParameterType();
arrayTypePb.setType(getArrayType().toString());
typePb.setArrayType(arrayTypePb);
}
}
if (getStructTypes() != null) {
List structTypes = new ArrayList<>();
for (Map.Entry entry : getStructTypes().entrySet()) {
QueryParameterType.StructTypes structType = new QueryParameterType.StructTypes();
structType.setName(entry.getKey());
structType.setType(entry.getValue().toTypePb());
structTypes.add(structType);
}
typePb.setStructTypes(structTypes);
}
if (getType() == StandardSQLTypeName.RANGE
&& getRangeValues() != null
&& getRangeValues().getType() != null) {
QueryParameterType rangeTypePb = new QueryParameterType();
rangeTypePb.setType(getRangeValues().getType().getType());
typePb.setRangeElementType(rangeTypePb);
}
return typePb;
}
static QueryParameterValue fromPb(
com.google.api.services.bigquery.model.QueryParameterValue valuePb,
QueryParameterType typePb) {
Builder valueBuilder = newBuilder();
Map parameterTypes = new HashMap<>();
StandardSQLTypeName type = StandardSQLTypeName.valueOf(typePb.getType());
valueBuilder.setType(type);
if (type == StandardSQLTypeName.ARRAY) {
valueBuilder.setArrayType(StandardSQLTypeName.valueOf(typePb.getArrayType().getType()));
if (valuePb == null || valuePb.getArrayValues() == null) {
valueBuilder.setArrayValues(ImmutableList.of());
} else {
ImmutableList.Builder arrayValues = ImmutableList.builder();
for (com.google.api.services.bigquery.model.QueryParameterValue elementValuePb :
valuePb.getArrayValues()) {
arrayValues.add(fromPb(elementValuePb, typePb.getArrayType()));
}
valueBuilder.setArrayValues(arrayValues.build());
}
} else if (type == StandardSQLTypeName.STRUCT) {
Map structTypes = new HashMap<>();
for (QueryParameterType.StructTypes types : typePb.getStructTypes()) {
structTypes.put(
types.getName(),
QueryParameterValue.newBuilder()
.setType(StandardSQLTypeName.valueOf(types.getType().getType()))
.build());
}
valueBuilder.setStructTypes(structTypes);
if (valuePb == null || valuePb.getStructValues() == null) {
valueBuilder.setStructValues(ImmutableMap.of());
} else {
Map structValues = new HashMap<>();
for (QueryParameterType.StructTypes structType : typePb.getStructTypes()) {
parameterTypes.put(structType.getName(), structType.getType());
}
for (Map.Entry
structValue : valuePb.getStructValues().entrySet()) {
structValues.put(
structValue.getKey(),
QueryParameterValue.fromPb(
structValue.getValue(), parameterTypes.get(structValue.getKey())));
}
valueBuilder.setStructValues(structValues);
}
} else if (type == StandardSQLTypeName.RANGE) {
Range.Builder range = Range.newBuilder();
if (valuePb.getRangeValue() != null) {
com.google.api.services.bigquery.model.RangeValue rangeValuePb = valuePb.getRangeValue();
if (rangeValuePb.getStart() != null && rangeValuePb.getStart().getValue() != null) {
range.setStart(valuePb.getRangeValue().getStart().getValue());
}
if (rangeValuePb.getEnd() != null && rangeValuePb.getEnd().getValue() != null) {
range.setEnd(valuePb.getRangeValue().getEnd().getValue());
}
}
if (typePb.getRangeElementType() != null && typePb.getRangeElementType().getType() != null) {
range.setType(FieldElementType.fromPb(typePb));
}
valueBuilder.setRangeValues(range.build());
} else {
valueBuilder.setValue(valuePb == null ? "" : valuePb.getValue());
}
return valueBuilder.build();
}
}