Skip to content

Commit

Permalink
feat: add BIGNUMERIC support (#703)
Browse files Browse the repository at this point in the history
Expose BIGNUMERIC type in the client 

cc: @MingyuZhong
  • Loading branch information
stephaniewang526 committed Nov 19, 2020
1 parent f59241b commit 718ec2a
Show file tree
Hide file tree
Showing 7 changed files with 193 additions and 14 deletions.
Expand Up @@ -61,6 +61,11 @@ public LegacySQLTypeName apply(String constant) {
*/
public static final LegacySQLTypeName NUMERIC =
type.createAndRegister("NUMERIC").setStandardType(StandardSQLTypeName.NUMERIC);
/**
* A decimal value with 76+ digits of precision (the 77th digit is partial) and 38 digits of scale
*/
public static final LegacySQLTypeName BIGNUMERIC =
type.createAndRegister("BIGNUMERIC").setStandardType(StandardSQLTypeName.BIGNUMERIC);
/** A Boolean value (true or false). */
public static final LegacySQLTypeName BOOLEAN =
type.createAndRegister("BOOLEAN").setStandardType(StandardSQLTypeName.BOOL);
Expand Down
Expand Up @@ -60,6 +60,7 @@
* <li>Double: StandardSQLTypeName.FLOAT64
* <li>Float: StandardSQLTypeName.FLOAT64
* <li>BigDecimal: StandardSQLTypeName.NUMERIC
* <li>BigNumeric: StandardSQLTypeName.BIGNUMERIC
* </ul>
*
* <p>No other types are supported through that entry point. The other types can be created by
Expand Down Expand Up @@ -197,7 +198,10 @@ public Map<String, QueryParameterValue> getStructTypes() {
@Nullable
abstract Map<String, QueryParameterValue> getStructTypesInner();

/** Creates a {@code QueryParameterValue} object with the given value and type. */
/**
* Creates a {@code QueryParameterValue} object with the given value and type. Note: this does not
* support BigNumeric
*/
public static <T> QueryParameterValue of(T value, Class<T> type) {
return of(value, classToType(type));
}
Expand Down Expand Up @@ -240,6 +244,11 @@ 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);
Expand Down Expand Up @@ -363,6 +372,7 @@ private static <T> String valueToStringOrNull(T value, StandardSQLTypeName type)
}
break;
case NUMERIC:
case BIGNUMERIC:
if (value instanceof BigDecimal) {
return value.toString();
}
Expand Down
Expand Up @@ -32,6 +32,10 @@ public enum StandardSQLTypeName {
FLOAT64,
/** A decimal value with 38 digits of precision and 9 digits of scale. */
NUMERIC,
/**
* A decimal value with 76+ digits of precision (the 77th digit is partial) and 38 digits of scale
*/
BIGNUMERIC,
/** Variable-length character (Unicode) data. */
STRING,
/** Variable-length binary data. */
Expand Down
Expand Up @@ -49,7 +49,8 @@ public class FieldValueListTest {
LegacySQLTypeName.RECORD,
Field.of("first", LegacySQLTypeName.FLOAT),
Field.of("second", LegacySQLTypeName.TIMESTAMP)),
Field.of("tenth", LegacySQLTypeName.NUMERIC));
Field.of("tenth", LegacySQLTypeName.NUMERIC),
Field.of("eleventh", LegacySQLTypeName.BIGNUMERIC));

private final Map<String, String> integerPb = ImmutableMap.of("v", "1");
private final Map<String, String> floatPb = ImmutableMap.of("v", "1.5");
Expand All @@ -62,6 +63,9 @@ public class FieldValueListTest {
private final Map<String, Object> recordPb =
ImmutableMap.<String, Object>of("f", ImmutableList.<Object>of(floatPb, timestampPb));
private final Map<String, String> numericPb = ImmutableMap.of("v", "123456789.123456789");
private final Map<String, String> bigNumericPb =
ImmutableMap.of(
"v", "99999999999999999999999999999999999999.99999999999999999999999999999999999999");

private final FieldValue booleanFv = FieldValue.of(Attribute.PRIMITIVE, "false");
private final FieldValue integerFv = FieldValue.of(Attribute.PRIMITIVE, "1");
Expand All @@ -78,6 +82,10 @@ public class FieldValueListTest {
FieldValueList.of(
ImmutableList.of(floatFv, timestampFv), schema.get("ninth").getSubFields()));
private final FieldValue numericFv = FieldValue.of(Attribute.PRIMITIVE, "123456789.123456789");
private final FieldValue bigNumericFv =
FieldValue.of(
Attribute.PRIMITIVE,
"99999999999999999999999999999999999999.99999999999999999999999999999999999999");

private final List<?> fieldValuesPb =
ImmutableList.of(
Expand All @@ -90,7 +98,8 @@ public class FieldValueListTest {
nullPb,
repeatedPb,
recordPb,
numericPb);
numericPb,
bigNumericPb);

private final FieldValueList fieldValues =
FieldValueList.of(
Expand All @@ -104,7 +113,8 @@ public class FieldValueListTest {
nullFv,
repeatedFv,
recordFv,
numericFv),
numericFv,
bigNumericFv),
schema);

@Test
Expand All @@ -116,7 +126,7 @@ public void testFromPb() {

@Test
public void testGetByIndex() {
assertEquals(10, fieldValues.size());
assertEquals(11, fieldValues.size());
assertEquals(booleanFv, fieldValues.get(0));
assertEquals(integerFv, fieldValues.get(1));
assertEquals(floatFv, fieldValues.get(2));
Expand All @@ -133,11 +143,12 @@ public void testGetByIndex() {
assertEquals(floatFv, fieldValues.get(8).getRecordValue().get(0));
assertEquals(timestampFv, fieldValues.get(8).getRecordValue().get(1));
assertEquals(numericFv, fieldValues.get(9));
assertEquals(bigNumericFv, fieldValues.get(10));
}

@Test
public void testGetByName() {
assertEquals(10, fieldValues.size());
assertEquals(11, fieldValues.size());
assertEquals(booleanFv, fieldValues.get("first"));
assertEquals(integerFv, fieldValues.get("second"));
assertEquals(floatFv, fieldValues.get("third"));
Expand All @@ -154,6 +165,7 @@ public void testGetByName() {
assertEquals(floatFv, fieldValues.get("ninth").getRecordValue().get("first"));
assertEquals(timestampFv, fieldValues.get("ninth").getRecordValue().get("second"));
assertEquals(numericFv, fieldValues.get("tenth"));
assertEquals(bigNumericFv, fieldValues.get("eleventh"));
}

@Test
Expand All @@ -170,7 +182,8 @@ public void testNullSchema() {
nullFv,
repeatedFv,
recordFv,
numericFv));
numericFv,
bigNumericFv));

assertEquals(fieldValues, fieldValuesNoSchema);

Expand Down
Expand Up @@ -27,6 +27,7 @@
import com.google.cloud.bigquery.TimePartitioning.Type;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import java.math.BigDecimal;
import java.util.List;
import java.util.Map;
import org.junit.Test;
Expand Down Expand Up @@ -101,8 +102,10 @@ public class QueryJobConfigurationTest {
QueryParameterValue.string("stringValue");
private static final QueryParameterValue TIMESTAMP_PARAMETER =
QueryParameterValue.timestamp("2014-01-01 07:00:00.000000+00:00");
private static final QueryParameterValue BIGNUMERIC_PARAMETER =
QueryParameterValue.bigNumeric(new BigDecimal(1 / 3));
private static final List<QueryParameterValue> POSITIONAL_PARAMETER =
ImmutableList.of(STRING_PARAMETER, TIMESTAMP_PARAMETER);
ImmutableList.of(STRING_PARAMETER, TIMESTAMP_PARAMETER, BIGNUMERIC_PARAMETER);
private static final Map<String, QueryParameterValue> NAME_PARAMETER =
ImmutableMap.of("string", STRING_PARAMETER, "timestamp", TIMESTAMP_PARAMETER);
private static final QueryJobConfiguration QUERY_JOB_CONFIGURATION =
Expand Down
Expand Up @@ -141,6 +141,49 @@ public void testNumeric() {
assertThat(value.getArrayValues()).isNull();
}

@Test
public void testBigNumeric() {
QueryParameterValue value =
QueryParameterValue.bigNumeric(new BigDecimal("0.33333333333333333333333333333333333333"));
QueryParameterValue value1 =
QueryParameterValue.bigNumeric(new BigDecimal("0.50000000000000000000000000000000000000"));
QueryParameterValue value2 =
QueryParameterValue.bigNumeric(new BigDecimal("0.00000000500000000000000000000000000000"));
QueryParameterValue value3 =
QueryParameterValue.bigNumeric(new BigDecimal("-0.00000000500000000000000000000000000000"));
QueryParameterValue value4 =
QueryParameterValue.bigNumeric(
new BigDecimal("0.33333333333333333333333333333333333333888888888888888"));
QueryParameterValue value5 = QueryParameterValue.bigNumeric(new BigDecimal("1e-38"));
QueryParameterValue value6 = QueryParameterValue.bigNumeric(new BigDecimal("-1e38"));
QueryParameterValue value7 =
QueryParameterValue.bigNumeric(
new BigDecimal(
"578960446186580977117854925043439539266.34992332820282019728792003956564819967"));
QueryParameterValue value8 =
QueryParameterValue.bigNumeric(
new BigDecimal(
"-578960446186580977117854925043439539266.34992332820282019728792003956564819968"));

assertThat(value.getValue()).isEqualTo("0.33333333333333333333333333333333333333");
assertThat(value1.getValue()).isEqualTo("0.50000000000000000000000000000000000000");
assertThat(value2.getValue()).isEqualTo("5.00000000000000000000000000000E-9");
assertThat(value3.getValue()).isEqualTo("-5.00000000000000000000000000000E-9");
assertThat(value4.getValue())
.isEqualTo("0.33333333333333333333333333333333333333888888888888888");
assertThat(value5.getValue()).isEqualTo("1E-38");
assertThat(value6.getValue()).isEqualTo("-1E+38");
assertThat(value7.getValue())
.isEqualTo(
"578960446186580977117854925043439539266.34992332820282019728792003956564819967");
assertThat(value8.getValue())
.isEqualTo(
"-578960446186580977117854925043439539266.34992332820282019728792003956564819968");
assertThat(value.getType()).isEqualTo(StandardSQLTypeName.BIGNUMERIC);
assertThat(value.getArrayType()).isNull();
assertThat(value.getArrayValues()).isNull();
}

@Test
public void testString() {
QueryParameterValue value = QueryParameterValue.string("foo");
Expand Down

0 comments on commit 718ec2a

Please sign in to comment.