Skip to content

Commit b38a91d

Browse files
authored
feat: add support for NUMERIC data type (#193)
* feat: add support for NUMERIC type * feat: add support for NUMERIC in keys * tests: add integration test for numeric * fix: uncomment code * test: remove integration tests from PR * chore: remove whitespaces * chore: solve merge conflicts
1 parent d67f108 commit b38a91d

29 files changed

+990
-36
lines changed

google-cloud-spanner/clirr-ignored-differences.xml

+43-1
Original file line numberDiff line numberDiff line change
@@ -256,11 +256,53 @@
256256
<className>com/google/cloud/spanner/spi/v1/SpannerRpc</className>
257257
<method>com.google.api.gax.retrying.RetrySettings getPartitionedDmlRetrySettings()</method>
258258
</difference>
259-
259+
260260
<!-- Streaming PDML -->
261261
<difference>
262262
<differenceType>7012</differenceType>
263263
<className>com/google/cloud/spanner/spi/v1/SpannerRpc</className>
264264
<method>com.google.api.gax.rpc.ServerStream executeStreamingPartitionedDml(com.google.spanner.v1.ExecuteSqlRequest, java.util.Map, org.threeten.bp.Duration)</method>
265265
</difference>
266+
267+
<!-- Add support for NUMERIC data type -->
268+
<difference>
269+
<differenceType>7013</differenceType>
270+
<className>com/google/cloud/spanner/AbstractStructReader</className>
271+
<method>java.math.BigDecimal getBigDecimalInternal(int)</method>
272+
</difference>
273+
<difference>
274+
<differenceType>7013</differenceType>
275+
<className>com/google/cloud/spanner/AbstractStructReader</className>
276+
<method>java.util.List getBigDecimalListInternal(int)</method>
277+
</difference>
278+
<difference>
279+
<differenceType>7012</differenceType>
280+
<className>com/google/cloud/spanner/StructReader</className>
281+
<method>java.math.BigDecimal getBigDecimal(int)</method>
282+
</difference>
283+
<difference>
284+
<differenceType>7012</differenceType>
285+
<className>com/google/cloud/spanner/StructReader</className>
286+
<method>java.math.BigDecimal getBigDecimal(java.lang.String)</method>
287+
</difference>
288+
<difference>
289+
<differenceType>7012</differenceType>
290+
<className>com/google/cloud/spanner/StructReader</className>
291+
<method>java.util.List getBigDecimalList(int)</method>
292+
</difference>
293+
<difference>
294+
<differenceType>7012</differenceType>
295+
<className>com/google/cloud/spanner/StructReader</className>
296+
<method>java.util.List getBigDecimalList(java.lang.String)</method>
297+
</difference>
298+
<difference>
299+
<differenceType>7013</differenceType>
300+
<className>com/google/cloud/spanner/Value</className>
301+
<method>java.math.BigDecimal getNumeric()</method>
302+
</difference>
303+
<difference>
304+
<differenceType>7013</differenceType>
305+
<className>com/google/cloud/spanner/Value</className>
306+
<method>java.util.List getNumericArray()</method>
307+
</difference>
266308
</differences>

google-cloud-spanner/src/main/java/com/google/cloud/spanner/AbstractResultSet.java

+42
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@
5252
import io.opencensus.trace.Tracing;
5353
import java.io.IOException;
5454
import java.io.Serializable;
55+
import java.math.BigDecimal;
5556
import java.util.AbstractList;
5657
import java.util.ArrayList;
5758
import java.util.BitSet;
@@ -358,6 +359,9 @@ private Object writeReplace() {
358359
case FLOAT64:
359360
builder.set(fieldName).to((Double) value);
360361
break;
362+
case NUMERIC:
363+
builder.set(fieldName).to((BigDecimal) value);
364+
break;
361365
case STRING:
362366
builder.set(fieldName).to((String) value);
363367
break;
@@ -381,6 +385,9 @@ private Object writeReplace() {
381385
case FLOAT64:
382386
builder.set(fieldName).toFloat64Array((Iterable<Double>) value);
383387
break;
388+
case NUMERIC:
389+
builder.set(fieldName).toNumericArray((Iterable<BigDecimal>) value);
390+
break;
384391
case STRING:
385392
builder.set(fieldName).toStringArray((Iterable<String>) value);
386393
break;
@@ -457,6 +464,8 @@ private static Object decodeValue(Type fieldType, com.google.protobuf.Value prot
457464
return Long.parseLong(proto.getStringValue());
458465
case FLOAT64:
459466
return valueProtoToFloat64(proto);
467+
case NUMERIC:
468+
return new BigDecimal(proto.getStringValue());
460469
case STRING:
461470
checkType(fieldType, proto, KindCase.STRING_VALUE);
462471
return proto.getStringValue();
@@ -513,6 +522,18 @@ public Boolean apply(com.google.protobuf.Value input) {
513522
return new Int64Array(listValue);
514523
case FLOAT64:
515524
return new Float64Array(listValue);
525+
case NUMERIC:
526+
{
527+
// Materialize list: element conversion is expensive and should happen only once.
528+
ArrayList<Object> list = new ArrayList<>(listValue.getValuesCount());
529+
for (com.google.protobuf.Value value : listValue.getValuesList()) {
530+
list.add(
531+
value.getKindCase() == KindCase.NULL_VALUE
532+
? null
533+
: new BigDecimal(value.getStringValue()));
534+
}
535+
return list;
536+
}
516537
case STRING:
517538
return Lists.transform(
518539
listValue.getValuesList(),
@@ -620,6 +641,11 @@ protected double getDoubleInternal(int columnIndex) {
620641
return (Double) rowData.get(columnIndex);
621642
}
622643

644+
@Override
645+
protected BigDecimal getBigDecimalInternal(int columnIndex) {
646+
return (BigDecimal) rowData.get(columnIndex);
647+
}
648+
623649
@Override
624650
protected String getStringInternal(int columnIndex) {
625651
return (String) rowData.get(columnIndex);
@@ -685,6 +711,12 @@ protected Float64Array getDoubleListInternal(int columnIndex) {
685711
return (Float64Array) rowData.get(columnIndex);
686712
}
687713

714+
@Override
715+
@SuppressWarnings("unchecked") // We know ARRAY<NUMERIC> produces a List<BigDecimal>.
716+
protected List<BigDecimal> getBigDecimalListInternal(int columnIndex) {
717+
return (List<BigDecimal>) rowData.get(columnIndex);
718+
}
719+
688720
@Override
689721
@SuppressWarnings("unchecked") // We know ARRAY<STRING> produces a List<String>.
690722
protected List<String> getStringListInternal(int columnIndex) {
@@ -1176,6 +1208,11 @@ protected double getDoubleInternal(int columnIndex) {
11761208
return currRow().getDoubleInternal(columnIndex);
11771209
}
11781210

1211+
@Override
1212+
protected BigDecimal getBigDecimalInternal(int columnIndex) {
1213+
return currRow().getBigDecimalInternal(columnIndex);
1214+
}
1215+
11791216
@Override
11801217
protected String getStringInternal(int columnIndex) {
11811218
return currRow().getStringInternal(columnIndex);
@@ -1226,6 +1263,11 @@ protected List<Double> getDoubleListInternal(int columnIndex) {
12261263
return currRow().getDoubleListInternal(columnIndex);
12271264
}
12281265

1266+
@Override
1267+
protected List<BigDecimal> getBigDecimalListInternal(int columnIndex) {
1268+
return currRow().getBigDecimalListInternal(columnIndex);
1269+
}
1270+
12291271
@Override
12301272
protected List<String> getStringListInternal(int columnIndex) {
12311273
return currRow().getStringListInternal(columnIndex);

google-cloud-spanner/src/main/java/com/google/cloud/spanner/AbstractStructReader.java

+31
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
import com.google.cloud.ByteArray;
2222
import com.google.cloud.Date;
2323
import com.google.cloud.Timestamp;
24+
import java.math.BigDecimal;
2425
import java.util.List;
2526

2627
/**
@@ -38,6 +39,8 @@ public abstract class AbstractStructReader implements StructReader {
3839

3940
protected abstract double getDoubleInternal(int columnIndex);
4041

42+
protected abstract BigDecimal getBigDecimalInternal(int columnIndex);
43+
4144
protected abstract String getStringInternal(int columnIndex);
4245

4346
protected abstract ByteArray getBytesInternal(int columnIndex);
@@ -58,6 +61,8 @@ public abstract class AbstractStructReader implements StructReader {
5861

5962
protected abstract List<Double> getDoubleListInternal(int columnIndex);
6063

64+
protected abstract List<BigDecimal> getBigDecimalListInternal(int columnIndex);
65+
6166
protected abstract List<String> getStringListInternal(int columnIndex);
6267

6368
protected abstract List<ByteArray> getBytesListInternal(int columnIndex);
@@ -127,6 +132,19 @@ public double getDouble(String columnName) {
127132
return getDoubleInternal(columnIndex);
128133
}
129134

135+
@Override
136+
public BigDecimal getBigDecimal(int columnIndex) {
137+
checkNonNullOfType(columnIndex, Type.numeric(), columnIndex);
138+
return getBigDecimalInternal(columnIndex);
139+
}
140+
141+
@Override
142+
public BigDecimal getBigDecimal(String columnName) {
143+
int columnIndex = getColumnIndex(columnName);
144+
checkNonNullOfType(columnIndex, Type.numeric(), columnName);
145+
return getBigDecimalInternal(columnIndex);
146+
}
147+
130148
@Override
131149
public String getString(int columnIndex) {
132150
checkNonNullOfType(columnIndex, Type.string(), columnIndex);
@@ -257,6 +275,19 @@ public List<Double> getDoubleList(String columnName) {
257275
return getDoubleListInternal(columnIndex);
258276
}
259277

278+
@Override
279+
public List<BigDecimal> getBigDecimalList(int columnIndex) {
280+
checkNonNullOfType(columnIndex, Type.array(Type.numeric()), columnIndex);
281+
return getBigDecimalListInternal(columnIndex);
282+
}
283+
284+
@Override
285+
public List<BigDecimal> getBigDecimalList(String columnName) {
286+
int columnIndex = getColumnIndex(columnName);
287+
checkNonNullOfType(columnIndex, Type.array(Type.numeric()), columnName);
288+
return getBigDecimalListInternal(columnIndex);
289+
}
290+
260291
@Override
261292
public List<String> getStringList(int columnIndex) {
262293
checkNonNullOfType(columnIndex, Type.array(Type.string()), columnIndex);

google-cloud-spanner/src/main/java/com/google/cloud/spanner/ForwardingStructReader.java

+21
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
import com.google.common.base.Preconditions;
2323
import com.google.common.base.Supplier;
2424
import com.google.common.base.Suppliers;
25+
import java.math.BigDecimal;
2526
import java.util.List;
2627

2728
/** Forwarding implements of StructReader */
@@ -133,6 +134,16 @@ public double getDouble(String columnName) {
133134
return delegate.get().getDouble(columnName);
134135
}
135136

137+
@Override
138+
public BigDecimal getBigDecimal(int columnIndex) {
139+
return delegate.get().getBigDecimal(columnIndex);
140+
}
141+
142+
@Override
143+
public BigDecimal getBigDecimal(String columnName) {
144+
return delegate.get().getBigDecimal(columnName);
145+
}
146+
136147
@Override
137148
public String getString(int columnIndex) {
138149
checkValidState();
@@ -253,6 +264,16 @@ public List<Double> getDoubleList(String columnName) {
253264
return delegate.get().getDoubleList(columnName);
254265
}
255266

267+
@Override
268+
public List<BigDecimal> getBigDecimalList(int columnIndex) {
269+
return delegate.get().getBigDecimalList(columnIndex);
270+
}
271+
272+
@Override
273+
public List<BigDecimal> getBigDecimalList(String columnName) {
274+
return delegate.get().getBigDecimalList(columnName);
275+
}
276+
256277
@Override
257278
public List<String> getStringList(int columnIndex) {
258279
checkValidState();

google-cloud-spanner/src/main/java/com/google/cloud/spanner/Key.java

+11
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
import com.google.protobuf.NullValue;
2727
import com.google.protobuf.Value;
2828
import java.io.Serializable;
29+
import java.math.BigDecimal;
2930
import java.util.ArrayList;
3031
import java.util.Collections;
3132
import java.util.List;
@@ -131,6 +132,11 @@ public Builder append(@Nullable Double value) {
131132
buffer.add(value);
132133
return this;
133134
}
135+
/** Appends a {@code NUMERIC} value to the key. */
136+
public Builder append(@Nullable BigDecimal value) {
137+
buffer.add(value);
138+
return this;
139+
}
134140
/** Appends a {@code STRING} value to the key. */
135141
public Builder append(@Nullable String value) {
136142
buffer.add(value);
@@ -172,6 +178,8 @@ public Builder appendObject(@Nullable Object value) {
172178
append((Float) value);
173179
} else if (value instanceof Double) {
174180
append((Double) value);
181+
} else if (value instanceof BigDecimal) {
182+
append((BigDecimal) value);
175183
} else if (value instanceof String) {
176184
append((String) value);
177185
} else if (value instanceof ByteArray) {
@@ -215,6 +223,7 @@ public int size() {
215223
* <li>{@code BOOL} is represented by {@code Boolean}
216224
* <li>{@code INT64} is represented by {@code Long}
217225
* <li>{@code FLOAT64} is represented by {@code Double}
226+
* <li>{@code NUMERIC} is represented by {@code BigDecimal}
218227
* <li>{@code STRING} is represented by {@code String}
219228
* <li>{@code BYTES} is represented by {@link ByteArray}
220229
* <li>{@code TIMESTAMP} is represented by {@link Timestamp}
@@ -276,6 +285,8 @@ ListValue toProto() {
276285
builder.addValuesBuilder().setStringValue(part.toString());
277286
} else if (part instanceof Double) {
278287
builder.addValuesBuilder().setNumberValue((Double) part);
288+
} else if (part instanceof BigDecimal) {
289+
builder.addValuesBuilder().setStringValue(part.toString());
279290
} else if (part instanceof String) {
280291
builder.addValuesBuilder().setStringValue((String) part);
281292
} else if (part instanceof ByteArray) {

google-cloud-spanner/src/main/java/com/google/cloud/spanner/ResultSets.java

+21
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
import com.google.common.collect.Lists;
2828
import com.google.common.util.concurrent.ThreadFactoryBuilder;
2929
import com.google.spanner.v1.ResultSetStats;
30+
import java.math.BigDecimal;
3031
import java.util.List;
3132

3233
/** Utility methods for working with {@link com.google.cloud.spanner.ResultSet}. */
@@ -186,6 +187,16 @@ public double getDouble(String columnName) {
186187
return getCurrentRowAsStruct().getDouble(columnName);
187188
}
188189

190+
@Override
191+
public BigDecimal getBigDecimal(int columnIndex) {
192+
return getCurrentRowAsStruct().getBigDecimal(columnIndex);
193+
}
194+
195+
@Override
196+
public BigDecimal getBigDecimal(String columnName) {
197+
return getCurrentRowAsStruct().getBigDecimal(columnName);
198+
}
199+
189200
@Override
190201
public String getString(int columnIndex) {
191202
return getCurrentRowAsStruct().getString(columnIndex);
@@ -286,6 +297,16 @@ public List<Double> getDoubleList(String columnName) {
286297
return getCurrentRowAsStruct().getDoubleList(columnName);
287298
}
288299

300+
@Override
301+
public List<BigDecimal> getBigDecimalList(int columnIndex) {
302+
return getCurrentRowAsStruct().getBigDecimalList(columnIndex);
303+
}
304+
305+
@Override
306+
public List<BigDecimal> getBigDecimalList(String columnName) {
307+
return getCurrentRowAsStruct().getBigDecimalList(columnName);
308+
}
309+
289310
@Override
290311
public List<String> getStringList(int columnIndex) {
291312
return getCurrentRowAsStruct().getStringList(columnIndex);

0 commit comments

Comments
 (0)