diff --git a/google-cloud-clients/google-cloud-bigquery/src/main/java/com/google/cloud/bigquery/FieldValue.java b/google-cloud-clients/google-cloud-bigquery/src/main/java/com/google/cloud/bigquery/FieldValue.java
index 42d1993900a5..4d6d303aae2c 100644
--- a/google-cloud-clients/google-cloud-bigquery/src/main/java/com/google/cloud/bigquery/FieldValue.java
+++ b/google-cloud-clients/google-cloud-bigquery/src/main/java/com/google/cloud/bigquery/FieldValue.java
@@ -24,6 +24,7 @@
import com.google.common.base.MoreObjects;
import com.google.common.io.BaseEncoding;
import java.io.Serializable;
+import java.math.BigDecimal;
import java.util.List;
import java.util.Map;
import java.util.Objects;
@@ -48,7 +49,8 @@ public enum Attribute {
* A primitive field value. A {@code FieldValue} is primitive when the corresponding field has
* type {@link LegacySQLTypeName#BYTES}, {@link LegacySQLTypeName#BOOLEAN},
* {@link LegacySQLTypeName#STRING}, {@link LegacySQLTypeName#FLOAT},
- * {@link LegacySQLTypeName#INTEGER}, {@link LegacySQLTypeName#TIMESTAMP} or the value is set to
+ * {@link LegacySQLTypeName#INTEGER}, {@link LegacySQLTypeName#NUMERIC},
+ * {@link LegacySQLTypeName#TIMESTAMP}, or the value is set to
* {@code null}.
*/
PRIMITIVE,
@@ -76,7 +78,8 @@ private FieldValue(Attribute attribute, Object value) {
* @return {@link Attribute#PRIMITIVE} if the field is a primitive type
* ({@link LegacySQLTypeName#BYTES}, {@link LegacySQLTypeName#BOOLEAN}, {@link LegacySQLTypeName#STRING},
* {@link LegacySQLTypeName#FLOAT}, {@link LegacySQLTypeName#INTEGER},
- * {@link LegacySQLTypeName#TIMESTAMP}) or is {@code null}. Returns {@link Attribute#REPEATED} if
+ * {@link LegacySQLTypeName#NUMERIC}, {@link LegacySQLTypeName#TIMESTAMP})
+ * or is {@code null}. Returns {@link Attribute#REPEATED} if
* the corresponding field has ({@link Field.Mode#REPEATED}) mode. Returns
* {@link Attribute#RECORD} if the corresponding field is a
* {@link LegacySQLTypeName#RECORD} type.
@@ -107,7 +110,7 @@ public Object getValue() {
* corresponding field has primitive type ({@link LegacySQLTypeName#BYTES},
* {@link LegacySQLTypeName#BOOLEAN}, {@link LegacySQLTypeName#STRING},
* {@link LegacySQLTypeName#FLOAT}, {@link LegacySQLTypeName#INTEGER},
- * {@link LegacySQLTypeName#TIMESTAMP}).
+ * {@link LegacySQLTypeName#NUMERIC} {@link LegacySQLTypeName#TIMESTAMP}).
*
* @throws ClassCastException if the field is not a primitive type
* @throws NullPointerException if {@link #isNull()} returns {@code true}
@@ -198,6 +201,21 @@ public long getTimestampValue() {
}
+ /**
+ * Returns this field's value as a {@link java.math.BigDecimal}. This method should only be used if the
+ * corresponding field has {@link LegacySQLTypeName#NUMERIC} type.
+ *
+ * @throws ClassCastException if the field is not a primitive type
+ * @throws NumberFormatException if the field's value could not be converted to
+ * {@link java.math.BigDecimal}
+ * @throws NullPointerException if {@link #isNull()} returns {@code true}
+ */
+ @SuppressWarnings("unchecked")
+ public BigDecimal getNumericValue() {
+ return new BigDecimal(getStringValue());
+ }
+
+
/**
* Returns this field's value as a list of {@link FieldValue}. This method should only be used if
* the corresponding field has {@link Field.Mode#REPEATED} mode (i.e. {@link #getAttribute()} is
diff --git a/google-cloud-clients/google-cloud-bigquery/src/main/java/com/google/cloud/bigquery/LegacySQLTypeName.java b/google-cloud-clients/google-cloud-bigquery/src/main/java/com/google/cloud/bigquery/LegacySQLTypeName.java
index e99fbe21438b..a0f6da6c992e 100644
--- a/google-cloud-clients/google-cloud-bigquery/src/main/java/com/google/cloud/bigquery/LegacySQLTypeName.java
+++ b/google-cloud-clients/google-cloud-bigquery/src/main/java/com/google/cloud/bigquery/LegacySQLTypeName.java
@@ -49,6 +49,11 @@ public LegacySQLTypeName apply(String constant) {
public static final LegacySQLTypeName INTEGER = type.createAndRegister("INTEGER").setStandardType(StandardSQLTypeName.INT64);
/** A 64-bit IEEE binary floating-point value. */
public static final LegacySQLTypeName FLOAT = type.createAndRegister("FLOAT").setStandardType(StandardSQLTypeName.FLOAT64);
+ /**
+ * A decimal value with 38 digits of precision and 9 digits of scale.
+ * Note, support for this type is limited in legacy SQL.
+ */
+ public static final LegacySQLTypeName NUMERIC = type.createAndRegister("NUMERIC").setStandardType(StandardSQLTypeName.NUMERIC);
/** A Boolean value (true or false). */
public static final LegacySQLTypeName BOOLEAN = type.createAndRegister("BOOLEAN").setStandardType(StandardSQLTypeName.BOOL);
/** Represents an absolute point in time, with microsecond precision. */
diff --git a/google-cloud-clients/google-cloud-bigquery/src/main/java/com/google/cloud/bigquery/QueryParameterValue.java b/google-cloud-clients/google-cloud-bigquery/src/main/java/com/google/cloud/bigquery/QueryParameterValue.java
index 3038db96e724..56b62cd45b6e 100644
--- a/google-cloud-clients/google-cloud-bigquery/src/main/java/com/google/cloud/bigquery/QueryParameterValue.java
+++ b/google-cloud-clients/google-cloud-bigquery/src/main/java/com/google/cloud/bigquery/QueryParameterValue.java
@@ -24,6 +24,7 @@
import com.google.common.collect.Lists;
import com.google.common.io.BaseEncoding;
import java.io.Serializable;
+import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import javax.annotation.Nullable;
@@ -47,6 +48,7 @@
*
Long: StandardSQLTypeName.INT64
*
Double: StandardSQLTypeName.FLOAT64
*
Float: StandardSQLTypeName.FLOAT64
+ *
BigDecimal: StandardSQLTypeName.NUMERIC
*
*
*
No other types are supported through that entry point. The other types can be created by
@@ -164,6 +166,11 @@ 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 STRING. */
public static QueryParameterValue string(String value) {
return of(value, StandardSQLTypeName.STRING);
@@ -245,6 +252,8 @@ private static StandardSQLTypeName classToType(Class type) {
return StandardSQLTypeName.FLOAT64;
} else if (Float.class.isAssignableFrom(type)) {
return StandardSQLTypeName.FLOAT64;
+ } else if (BigDecimal.class.isAssignableFrom(type)) {
+ return StandardSQLTypeName.NUMERIC;
}
throw new IllegalArgumentException("Unsupported object type for QueryParameter: " + type);
}
@@ -269,6 +278,11 @@ private static String valueToStringOrNull(T value, StandardSQLTypeName type)
return value.toString();
}
break;
+ case NUMERIC:
+ if (value instanceof BigDecimal) {
+ return value.toString();
+ }
+ break;
case BYTES:
if (value instanceof byte[]) {
return BaseEncoding.base64().encode((byte[]) value);
diff --git a/google-cloud-clients/google-cloud-bigquery/src/main/java/com/google/cloud/bigquery/StandardSQLTypeName.java b/google-cloud-clients/google-cloud-bigquery/src/main/java/com/google/cloud/bigquery/StandardSQLTypeName.java
index a250c1fc1509..e6f43b0b776f 100644
--- a/google-cloud-clients/google-cloud-bigquery/src/main/java/com/google/cloud/bigquery/StandardSQLTypeName.java
+++ b/google-cloud-clients/google-cloud-bigquery/src/main/java/com/google/cloud/bigquery/StandardSQLTypeName.java
@@ -29,6 +29,8 @@ public enum StandardSQLTypeName {
INT64,
/** A 64-bit IEEE binary floating-point value. */
FLOAT64,
+ /** A decimal value with 38 digits of precision and 9 digits of scale. */
+ NUMERIC,
/** Variable-length character (Unicode) data. */
STRING,
/** Variable-length binary data. */
diff --git a/google-cloud-clients/google-cloud-bigquery/src/test/java/com/google/cloud/bigquery/FieldValueListTest.java b/google-cloud-clients/google-cloud-bigquery/src/test/java/com/google/cloud/bigquery/FieldValueListTest.java
index c59f5c94a5c4..e0f8e0ef65a3 100644
--- a/google-cloud-clients/google-cloud-bigquery/src/test/java/com/google/cloud/bigquery/FieldValueListTest.java
+++ b/google-cloud-clients/google-cloud-bigquery/src/test/java/com/google/cloud/bigquery/FieldValueListTest.java
@@ -48,7 +48,8 @@ public class FieldValueListTest {
"ninth",
LegacySQLTypeName.RECORD,
Field.of("first", LegacySQLTypeName.FLOAT),
- Field.of("second", LegacySQLTypeName.TIMESTAMP)));
+ Field.of("second", LegacySQLTypeName.TIMESTAMP)),
+ Field.of("tenth", LegacySQLTypeName.NUMERIC));
private final Map integerPb = ImmutableMap.of("v", "1");
private final Map floatPb = ImmutableMap.of("v", "1.5");
@@ -60,6 +61,7 @@ public class FieldValueListTest {
ImmutableMap.of("v", ImmutableList.