Skip to content

Commit d7ff940

Browse files
authored
feat: add support for JSON data type (#872)
Allow users to read and write to Cloud Spanner databases using the JSON type through the client libraries. Integration tests here: zoercai#1
1 parent c6c9304 commit d7ff940

30 files changed

+927
-57
lines changed

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

+42
Original file line numberDiff line numberDiff line change
@@ -659,4 +659,46 @@
659659
<method>void setOptimizerStatisticsPackage(java.lang.String)</method>
660660
</difference>
661661

662+
<!-- Add support for JSON data type -->
663+
<difference>
664+
<differenceType>7013</differenceType>
665+
<className>com/google/cloud/spanner/AbstractStructReader</className>
666+
<method>java.lang.String getJsonInternal(int)</method>
667+
</difference>
668+
<difference>
669+
<differenceType>7013</differenceType>
670+
<className>com/google/cloud/spanner/AbstractStructReader</className>
671+
<method>java.util.List getJsonListInternal(int)</method>
672+
</difference>
673+
<difference>
674+
<differenceType>7012</differenceType>
675+
<className>com/google/cloud/spanner/StructReader</className>
676+
<method>java.lang.String getJson(int)</method>
677+
</difference>
678+
<difference>
679+
<differenceType>7012</differenceType>
680+
<className>com/google/cloud/spanner/StructReader</className>
681+
<method>java.lang.String getJson(java.lang.String)</method>
682+
</difference>
683+
<difference>
684+
<differenceType>7012</differenceType>
685+
<className>com/google/cloud/spanner/StructReader</className>
686+
<method>java.util.List getJsonList(int)</method>
687+
</difference>
688+
<difference>
689+
<differenceType>7012</differenceType>
690+
<className>com/google/cloud/spanner/StructReader</className>
691+
<method>java.util.List getJsonList(java.lang.String)</method>
692+
</difference>
693+
<difference>
694+
<differenceType>7013</differenceType>
695+
<className>com/google/cloud/spanner/Value</className>
696+
<method>java.lang.String getJson()</method>
697+
</difference>
698+
<difference>
699+
<differenceType>7013</differenceType>
700+
<className>com/google/cloud/spanner/Value</className>
701+
<method>java.util.List getJsonArray()</method>
702+
</difference>
703+
662704
</differences>

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

+29
Original file line numberDiff line numberDiff line change
@@ -378,6 +378,9 @@ private Object writeReplace() {
378378
case STRING:
379379
builder.set(fieldName).to((String) value);
380380
break;
381+
case JSON:
382+
builder.set(fieldName).to(Value.json((String) value));
383+
break;
381384
case BYTES:
382385
builder.set(fieldName).to((ByteArray) value);
383386
break;
@@ -404,6 +407,9 @@ private Object writeReplace() {
404407
case STRING:
405408
builder.set(fieldName).toStringArray((Iterable<String>) value);
406409
break;
410+
case JSON:
411+
builder.set(fieldName).toJsonArray((Iterable<String>) value);
412+
break;
407413
case BYTES:
408414
builder.set(fieldName).toBytesArray((Iterable<ByteArray>) value);
409415
break;
@@ -480,6 +486,7 @@ private static Object decodeValue(Type fieldType, com.google.protobuf.Value prot
480486
case NUMERIC:
481487
return new BigDecimal(proto.getStringValue());
482488
case STRING:
489+
case JSON:
483490
checkType(fieldType, proto, KindCase.STRING_VALUE);
484491
return proto.getStringValue();
485492
case BYTES:
@@ -543,6 +550,7 @@ static Object decodeArrayValue(Type elementType, ListValue listValue) {
543550
return list;
544551
}
545552
case STRING:
553+
case JSON:
546554
return Lists.transform(
547555
listValue.getValuesList(),
548556
input -> input.getKindCase() == KindCase.NULL_VALUE ? null : input.getStringValue());
@@ -654,6 +662,11 @@ protected String getStringInternal(int columnIndex) {
654662
return (String) rowData.get(columnIndex);
655663
}
656664

665+
@Override
666+
protected String getJsonInternal(int columnIndex) {
667+
return (String) rowData.get(columnIndex);
668+
}
669+
657670
@Override
658671
protected ByteArray getBytesInternal(int columnIndex) {
659672
return (ByteArray) rowData.get(columnIndex);
@@ -782,6 +795,12 @@ protected List<String> getStringListInternal(int columnIndex) {
782795
return Collections.unmodifiableList((List<String>) rowData.get(columnIndex));
783796
}
784797

798+
@Override
799+
@SuppressWarnings("unchecked") // We know ARRAY<String> produces a List<String>.
800+
protected List<String> getJsonListInternal(int columnIndex) {
801+
return Collections.unmodifiableList((List<String>) rowData.get(columnIndex));
802+
}
803+
785804
@Override
786805
@SuppressWarnings("unchecked") // We know ARRAY<BYTES> produces a List<ByteArray>.
787806
protected List<ByteArray> getBytesListInternal(int columnIndex) {
@@ -1308,6 +1327,11 @@ protected String getStringInternal(int columnIndex) {
13081327
return currRow().getStringInternal(columnIndex);
13091328
}
13101329

1330+
@Override
1331+
protected String getJsonInternal(int columnIndex) {
1332+
return currRow().getJsonInternal(columnIndex);
1333+
}
1334+
13111335
@Override
13121336
protected ByteArray getBytesInternal(int columnIndex) {
13131337
return currRow().getBytesInternal(columnIndex);
@@ -1368,6 +1392,11 @@ protected List<String> getStringListInternal(int columnIndex) {
13681392
return currRow().getStringListInternal(columnIndex);
13691393
}
13701394

1395+
@Override
1396+
protected List<String> getJsonListInternal(int columnIndex) {
1397+
return currRow().getJsonListInternal(columnIndex);
1398+
}
1399+
13711400
@Override
13721401
protected List<ByteArray> getBytesListInternal(int columnIndex) {
13731402
return currRow().getBytesListInternal(columnIndex);

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

+34
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,10 @@ public abstract class AbstractStructReader implements StructReader {
4343

4444
protected abstract String getStringInternal(int columnIndex);
4545

46+
protected String getJsonInternal(int columnIndex) {
47+
throw new UnsupportedOperationException("Not implemented");
48+
}
49+
4650
protected abstract ByteArray getBytesInternal(int columnIndex);
4751

4852
protected abstract Timestamp getTimestampInternal(int columnIndex);
@@ -69,6 +73,10 @@ protected Value getValueInternal(int columnIndex) {
6973

7074
protected abstract List<String> getStringListInternal(int columnIndex);
7175

76+
protected List<String> getJsonListInternal(int columnIndex) {
77+
throw new UnsupportedOperationException("Not implemented");
78+
}
79+
7280
protected abstract List<ByteArray> getBytesListInternal(int columnIndex);
7381

7482
protected abstract List<Timestamp> getTimestampListInternal(int columnIndex);
@@ -162,6 +170,19 @@ public String getString(String columnName) {
162170
return getStringInternal(columnIndex);
163171
}
164172

173+
@Override
174+
public String getJson(int columnIndex) {
175+
checkNonNullOfType(columnIndex, Type.json(), columnIndex);
176+
return getJsonInternal(columnIndex);
177+
}
178+
179+
@Override
180+
public String getJson(String columnName) {
181+
int columnIndex = getColumnIndex(columnName);
182+
checkNonNullOfType(columnIndex, Type.json(), columnName);
183+
return getJsonInternal(columnIndex);
184+
}
185+
165186
@Override
166187
public ByteArray getBytes(int columnIndex) {
167188
checkNonNullOfType(columnIndex, Type.bytes(), columnIndex);
@@ -317,6 +338,19 @@ public List<String> getStringList(String columnName) {
317338
return getStringListInternal(columnIndex);
318339
}
319340

341+
@Override
342+
public List<String> getJsonList(int columnIndex) {
343+
checkNonNullOfType(columnIndex, Type.array(Type.json()), columnIndex);
344+
return getJsonListInternal(columnIndex);
345+
}
346+
347+
@Override
348+
public List<String> getJsonList(String columnName) {
349+
int columnIndex = getColumnIndex(columnName);
350+
checkNonNullOfType(columnIndex, Type.array(Type.json()), columnName);
351+
return getJsonListInternal(columnIndex);
352+
}
353+
320354
@Override
321355
public List<ByteArray> getBytesList(int columnIndex) {
322356
checkNonNullOfType(columnIndex, Type.array(Type.bytes()), columnIndex);

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

+24
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,18 @@ public String getString(String columnName) {
156156
return delegate.get().getString(columnName);
157157
}
158158

159+
@Override
160+
public String getJson(int columnIndex) {
161+
checkValidState();
162+
return delegate.get().getJson(columnIndex);
163+
}
164+
165+
@Override
166+
public String getJson(String columnName) {
167+
checkValidState();
168+
return delegate.get().getJson(columnName);
169+
}
170+
159171
@Override
160172
public ByteArray getBytes(int columnIndex) {
161173
checkValidState();
@@ -286,6 +298,18 @@ public List<String> getStringList(String columnName) {
286298
return delegate.get().getStringList(columnName);
287299
}
288300

301+
@Override
302+
public List<String> getJsonList(int columnIndex) {
303+
checkValidState();
304+
return delegate.get().getJsonList(columnIndex);
305+
}
306+
307+
@Override
308+
public List<String> getJsonList(String columnName) {
309+
checkValidState();
310+
return delegate.get().getJsonList(columnName);
311+
}
312+
289313
@Override
290314
public List<ByteArray> getBytesList(int columnIndex) {
291315
checkValidState();

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

+2
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ private Key(List<Object> parts) {
6565
* <li>{@code Float}, {@code Double} for the {@code FLOAT64} Cloud Spanner type
6666
* <li>{@code BigDecimal} for the {@code NUMERIC} Cloud Spanner type
6767
* <li>{@code String} for the {@code STRING} Cloud Spanner type
68+
* <li>{@code String} for the {@code JSON} Cloud Spanner type
6869
* <li>{@link ByteArray} for the {@code BYTES} Cloud Spanner type
6970
* <li>{@link Timestamp} for the {@code TIMESTAMP} Cloud Spanner type
7071
* <li>{@link Date} for the {@code DATE} Cloud Spanner type
@@ -228,6 +229,7 @@ public int size() {
228229
* <li>{@code FLOAT64} is represented by {@code Double}
229230
* <li>{@code NUMERIC} is represented by {@code BigDecimal}
230231
* <li>{@code STRING} is represented by {@code String}
232+
* <li>{@code JSON} is represented by {@code String}
231233
* <li>{@code BYTES} is represented by {@link ByteArray}
232234
* <li>{@code TIMESTAMP} is represented by {@link Timestamp}
233235
* <li>{@code DATE} is represented by {@link Date}

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

+20
Original file line numberDiff line numberDiff line change
@@ -243,6 +243,16 @@ public String getString(String columnName) {
243243
return getCurrentRowAsStruct().getString(columnName);
244244
}
245245

246+
@Override
247+
public String getJson(int columnIndex) {
248+
return getCurrentRowAsStruct().getJson(columnIndex);
249+
}
250+
251+
@Override
252+
public String getJson(String columnName) {
253+
return getCurrentRowAsStruct().getJson(columnName);
254+
}
255+
246256
@Override
247257
public ByteArray getBytes(int columnIndex) {
248258
return getCurrentRowAsStruct().getBytes(columnIndex);
@@ -363,6 +373,16 @@ public List<String> getStringList(String columnName) {
363373
return getCurrentRowAsStruct().getStringList(columnName);
364374
}
365375

376+
@Override
377+
public List<String> getJsonList(int columnIndex) {
378+
return getCurrentRowAsStruct().getJsonList(columnIndex);
379+
}
380+
381+
@Override
382+
public List<String> getJsonList(String columnName) {
383+
return getCurrentRowAsStruct().getJsonList(columnName);
384+
}
385+
366386
@Override
367387
public List<ByteArray> getBytesList(int columnIndex) {
368388
return getCurrentRowAsStruct().getBytesList(columnIndex);

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

+14
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,11 @@ protected String getStringInternal(int columnIndex) {
192192
return values.get(columnIndex).getString();
193193
}
194194

195+
@Override
196+
protected String getJsonInternal(int columnIndex) {
197+
return values.get(columnIndex).getJson();
198+
}
199+
195200
@Override
196201
protected ByteArray getBytesInternal(int columnIndex) {
197202
return values.get(columnIndex).getBytes();
@@ -257,6 +262,11 @@ protected List<String> getStringListInternal(int columnIndex) {
257262
return values.get(columnIndex).getStringArray();
258263
}
259264

265+
@Override
266+
protected List<String> getJsonListInternal(int columnIndex) {
267+
return values.get(columnIndex).getJsonArray();
268+
}
269+
260270
@Override
261271
protected List<ByteArray> getBytesListInternal(int columnIndex) {
262272
return values.get(columnIndex).getBytesArray();
@@ -341,6 +351,8 @@ private Object getAsObject(int columnIndex) {
341351
return getBigDecimalInternal(columnIndex);
342352
case STRING:
343353
return getStringInternal(columnIndex);
354+
case JSON:
355+
return getJsonInternal(columnIndex);
344356
case BYTES:
345357
return getBytesInternal(columnIndex);
346358
case TIMESTAMP:
@@ -361,6 +373,8 @@ private Object getAsObject(int columnIndex) {
361373
return getBigDecimalListInternal(columnIndex);
362374
case STRING:
363375
return getStringListInternal(columnIndex);
376+
case JSON:
377+
return getJsonListInternal(columnIndex);
364378
case BYTES:
365379
return getBytesListInternal(columnIndex);
366380
case TIMESTAMP:

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

+20
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,16 @@ public interface StructReader {
114114
/** Returns the value of a non-{@code NULL} column with type {@link Type#string()}. */
115115
String getString(String columnName);
116116

117+
/** Returns the value of a non-{@code NULL} column with type {@link Type#string()}. */
118+
default String getJson(int columnIndex) {
119+
throw new UnsupportedOperationException("method should be overwritten");
120+
}
121+
122+
/** Returns the value of a non-{@code NULL} column with type {@link Type#string()}. */
123+
default String getJson(String columnName) {
124+
throw new UnsupportedOperationException("method should be overwritten");
125+
}
126+
117127
/** Returns the value of a non-{@code NULL} column with type {@link Type#bytes()}. */
118128
ByteArray getBytes(int columnIndex);
119129

@@ -228,6 +238,16 @@ default Value getValue(String columnName) {
228238
/** Returns the value of a non-{@code NULL} column with type {@code Type.array(Type.string())}. */
229239
List<String> getStringList(String columnName);
230240

241+
/** Returns the value of a non-{@code NULL} column with type {@code Type.array(Type.string())}. */
242+
default List<String> getJsonList(int columnIndex) {
243+
throw new UnsupportedOperationException("method should be overwritten");
244+
};
245+
246+
/** Returns the value of a non-{@code NULL} column with type {@code Type.array(Type.string())}. */
247+
default List<String> getJsonList(String columnName) {
248+
throw new UnsupportedOperationException("method should be overwritten");
249+
};
250+
231251
/** Returns the value of a non-{@code NULL} column with type {@code Type.array(Type.bytes())}. */
232252
List<ByteArray> getBytesList(int columnIndex);
233253

0 commit comments

Comments
 (0)