Skip to content

Commit d2b426f

Browse files
authored
feat: add support for PG JSONB data type (#1964)
* feat: add support for PG JSONB data type * chore: address review comments * test: temporarily disable integration test for JSONB
1 parent e93ab4c commit d2b426f

24 files changed

+1371
-24
lines changed

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

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,4 +131,25 @@
131131
<className>com/google/cloud/spanner/spi/v1/GapicSpannerRpc</className>
132132
<method>com.google.iam.v1.Policy getDatabaseAdminIAMPolicy(java.lang.String)</method>
133133
</difference>
134+
<!-- PG JSONB -->
135+
<difference>
136+
<differenceType>7012</differenceType>
137+
<className>com/google/cloud/spanner/StructReader</className>
138+
<method>java.lang.String getPgJsonb(int)</method>
139+
</difference>
140+
<difference>
141+
<differenceType>7012</differenceType>
142+
<className>com/google/cloud/spanner/StructReader</className>
143+
<method>java.lang.String getPgJsonb(java.lang.String)</method>
144+
</difference>
145+
<difference>
146+
<differenceType>7012</differenceType>
147+
<className>com/google/cloud/spanner/StructReader</className>
148+
<method>java.util.List getPgJsonbList(int)</method>
149+
</difference>
150+
<difference>
151+
<differenceType>7012</differenceType>
152+
<className>com/google/cloud/spanner/StructReader</className>
153+
<method>java.util.List getPgJsonbList(java.lang.String)</method>
154+
</difference>
134155
</differences>

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

Lines changed: 40 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@
6767
import java.util.concurrent.TimeUnit;
6868
import java.util.logging.Level;
6969
import java.util.logging.Logger;
70+
import java.util.stream.Collectors;
7071
import javax.annotation.Nullable;
7172

7273
/** Implementation of {@link ResultSet}. */
@@ -384,6 +385,9 @@ private Object writeReplace() {
384385
case JSON:
385386
builder.set(fieldName).to(Value.json((String) value));
386387
break;
388+
case PG_JSONB:
389+
builder.set(fieldName).to(Value.pgJsonb((String) value));
390+
break;
387391
case BYTES:
388392
builder.set(fieldName).to((ByteArray) value);
389393
break;
@@ -417,6 +421,9 @@ private Object writeReplace() {
417421
case JSON:
418422
builder.set(fieldName).toJsonArray((Iterable<String>) value);
419423
break;
424+
case PG_JSONB:
425+
builder.set(fieldName).toPgJsonbArray((Iterable<String>) value);
426+
break;
420427
case BYTES:
421428
builder.set(fieldName).toBytesArray((Iterable<ByteArray>) value);
422429
break;
@@ -491,10 +498,9 @@ private static Object decodeValue(Type fieldType, com.google.protobuf.Value prot
491498
checkType(fieldType, proto, KindCase.STRING_VALUE);
492499
return new BigDecimal(proto.getStringValue());
493500
case PG_NUMERIC:
494-
checkType(fieldType, proto, KindCase.STRING_VALUE);
495-
return proto.getStringValue();
496501
case STRING:
497502
case JSON:
503+
case PG_JSONB:
498504
checkType(fieldType, proto, KindCase.STRING_VALUE);
499505
return proto.getStringValue();
500506
case BYTES:
@@ -558,14 +564,14 @@ static Object decodeArrayValue(Type elementType, ListValue listValue) {
558564
return list;
559565
}
560566
case PG_NUMERIC:
561-
return Lists.transform(
562-
listValue.getValuesList(),
563-
input -> input.getKindCase() == KindCase.NULL_VALUE ? null : input.getStringValue());
564567
case STRING:
565568
case JSON:
566-
return Lists.transform(
567-
listValue.getValuesList(),
568-
input -> input.getKindCase() == KindCase.NULL_VALUE ? null : input.getStringValue());
569+
case PG_JSONB:
570+
return listValue.getValuesList().stream()
571+
.map(
572+
input ->
573+
input.getKindCase() == KindCase.NULL_VALUE ? null : input.getStringValue())
574+
.collect(Collectors.toList());
569575
case BYTES:
570576
{
571577
// Materialize list: element conversion is expensive and should happen only once.
@@ -679,6 +685,11 @@ protected String getJsonInternal(int columnIndex) {
679685
return (String) rowData.get(columnIndex);
680686
}
681687

688+
@Override
689+
protected String getPgJsonbInternal(int columnIndex) {
690+
return (String) rowData.get(columnIndex);
691+
}
692+
682693
@Override
683694
protected ByteArray getBytesInternal(int columnIndex) {
684695
return (ByteArray) rowData.get(columnIndex);
@@ -715,6 +726,8 @@ protected Value getValueInternal(int columnIndex) {
715726
return Value.string(isNull ? null : getStringInternal(columnIndex));
716727
case JSON:
717728
return Value.json(isNull ? null : getJsonInternal(columnIndex));
729+
case PG_JSONB:
730+
return Value.pgJsonb(isNull ? null : getPgJsonbInternal(columnIndex));
718731
case BYTES:
719732
return Value.bytes(isNull ? null : getBytesInternal(columnIndex));
720733
case TIMESTAMP:
@@ -740,6 +753,8 @@ protected Value getValueInternal(int columnIndex) {
740753
return Value.stringArray(isNull ? null : getStringListInternal(columnIndex));
741754
case JSON:
742755
return Value.jsonArray(isNull ? null : getJsonListInternal(columnIndex));
756+
case PG_JSONB:
757+
return Value.pgJsonbArray(isNull ? null : getPgJsonbListInternal(columnIndex));
743758
case BYTES:
744759
return Value.bytesArray(isNull ? null : getBytesListInternal(columnIndex));
745760
case TIMESTAMP:
@@ -816,11 +831,17 @@ protected List<String> getStringListInternal(int columnIndex) {
816831
}
817832

818833
@Override
819-
@SuppressWarnings("unchecked") // We know ARRAY<String> produces a List<String>.
834+
@SuppressWarnings("unchecked") // We know ARRAY<JSON> produces a List<String>.
820835
protected List<String> getJsonListInternal(int columnIndex) {
821836
return Collections.unmodifiableList((List<String>) rowData.get(columnIndex));
822837
}
823838

839+
@Override
840+
@SuppressWarnings("unchecked") // We know ARRAY<JSONB> produces a List<String>.
841+
protected List<String> getPgJsonbListInternal(int columnIndex) {
842+
return Collections.unmodifiableList((List<String>) rowData.get(columnIndex));
843+
}
844+
824845
@Override
825846
@SuppressWarnings("unchecked") // We know ARRAY<BYTES> produces a List<ByteArray>.
826847
protected List<ByteArray> getBytesListInternal(int columnIndex) {
@@ -1352,6 +1373,11 @@ protected String getJsonInternal(int columnIndex) {
13521373
return currRow().getJsonInternal(columnIndex);
13531374
}
13541375

1376+
@Override
1377+
protected String getPgJsonbInternal(int columnIndex) {
1378+
return currRow().getPgJsonbInternal(columnIndex);
1379+
}
1380+
13551381
@Override
13561382
protected ByteArray getBytesInternal(int columnIndex) {
13571383
return currRow().getBytesInternal(columnIndex);
@@ -1417,6 +1443,11 @@ protected List<String> getJsonListInternal(int columnIndex) {
14171443
return currRow().getJsonListInternal(columnIndex);
14181444
}
14191445

1446+
@Override
1447+
protected List<String> getPgJsonbListInternal(int columnIndex) {
1448+
return currRow().getJsonListInternal(columnIndex);
1449+
}
1450+
14201451
@Override
14211452
protected List<ByteArray> getBytesListInternal(int columnIndex) {
14221453
return currRow().getBytesListInternal(columnIndex);

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

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,10 @@ protected String getJsonInternal(int columnIndex) {
4848
throw new UnsupportedOperationException("Not implemented");
4949
}
5050

51+
protected String getPgJsonbInternal(int columnIndex) {
52+
throw new UnsupportedOperationException("Not implemented");
53+
}
54+
5155
protected abstract ByteArray getBytesInternal(int columnIndex);
5256

5357
protected abstract Timestamp getTimestampInternal(int columnIndex);
@@ -78,6 +82,10 @@ protected List<String> getJsonListInternal(int columnIndex) {
7882
throw new UnsupportedOperationException("Not implemented");
7983
}
8084

85+
protected List<String> getPgJsonbListInternal(int columnIndex) {
86+
throw new UnsupportedOperationException("Not implemented");
87+
}
88+
8189
protected abstract List<ByteArray> getBytesListInternal(int columnIndex);
8290

8391
protected abstract List<Timestamp> getTimestampListInternal(int columnIndex);
@@ -189,6 +197,19 @@ public String getJson(String columnName) {
189197
return getJsonInternal(columnIndex);
190198
}
191199

200+
@Override
201+
public String getPgJsonb(int columnIndex) {
202+
checkNonNullOfType(columnIndex, Type.pgJsonb(), columnIndex);
203+
return getPgJsonbInternal(columnIndex);
204+
}
205+
206+
@Override
207+
public String getPgJsonb(String columnName) {
208+
int columnIndex = getColumnIndex(columnName);
209+
checkNonNullOfType(columnIndex, Type.pgJsonb(), columnName);
210+
return getPgJsonbInternal(columnIndex);
211+
}
212+
192213
@Override
193214
public ByteArray getBytes(int columnIndex) {
194215
checkNonNullOfType(columnIndex, Type.bytes(), columnIndex);
@@ -365,6 +386,19 @@ public List<String> getJsonList(String columnName) {
365386
return getJsonListInternal(columnIndex);
366387
}
367388

389+
@Override
390+
public List<String> getPgJsonbList(int columnIndex) {
391+
checkNonNullOfType(columnIndex, Type.array(Type.pgJsonb()), columnIndex);
392+
return getPgJsonbListInternal(columnIndex);
393+
}
394+
395+
@Override
396+
public List<String> getPgJsonbList(String columnName) {
397+
int columnIndex = getColumnIndex(columnName);
398+
checkNonNullOfType(columnIndex, Type.array(Type.pgJsonb()), columnName);
399+
return getPgJsonbListInternal(columnIndex);
400+
}
401+
368402
@Override
369403
public List<ByteArray> getBytesList(int columnIndex) {
370404
checkNonNullOfType(columnIndex, Type.array(Type.bytes()), columnIndex);

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

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,18 @@ public String getJson(String columnName) {
168168
return delegate.get().getJson(columnName);
169169
}
170170

171+
@Override
172+
public String getPgJsonb(int columnIndex) {
173+
checkValidState();
174+
return delegate.get().getPgJsonb(columnIndex);
175+
}
176+
177+
@Override
178+
public String getPgJsonb(String columnName) {
179+
checkValidState();
180+
return delegate.get().getPgJsonb(columnName);
181+
}
182+
171183
@Override
172184
public ByteArray getBytes(int columnIndex) {
173185
checkValidState();
@@ -310,6 +322,18 @@ public List<String> getJsonList(String columnName) {
310322
return delegate.get().getJsonList(columnName);
311323
}
312324

325+
@Override
326+
public List<String> getPgJsonbList(int columnIndex) {
327+
checkValidState();
328+
return delegate.get().getPgJsonbList(columnIndex);
329+
}
330+
331+
@Override
332+
public List<String> getPgJsonbList(String columnName) {
333+
checkValidState();
334+
return delegate.get().getPgJsonbList(columnName);
335+
}
336+
313337
@Override
314338
public List<ByteArray> getBytesList(int columnIndex) {
315339
checkValidState();

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

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -253,6 +253,16 @@ public String getJson(String columnName) {
253253
return getCurrentRowAsStruct().getJson(columnName);
254254
}
255255

256+
@Override
257+
public String getPgJsonb(int columnIndex) {
258+
return getCurrentRowAsStruct().getPgJsonb(columnIndex);
259+
}
260+
261+
@Override
262+
public String getPgJsonb(String columnName) {
263+
return getCurrentRowAsStruct().getPgJsonb(columnName);
264+
}
265+
256266
@Override
257267
public ByteArray getBytes(int columnIndex) {
258268
return getCurrentRowAsStruct().getBytes(columnIndex);
@@ -383,6 +393,16 @@ public List<String> getJsonList(String columnName) {
383393
return getCurrentRowAsStruct().getJsonList(columnName);
384394
}
385395

396+
@Override
397+
public List<String> getPgJsonbList(int columnIndex) {
398+
return getCurrentRowAsStruct().getPgJsonbList(columnIndex);
399+
}
400+
401+
@Override
402+
public List<String> getPgJsonbList(String columnName) {
403+
return getCurrentRowAsStruct().getPgJsonbList(columnName);
404+
}
405+
386406
@Override
387407
public List<ByteArray> getBytesList(int columnIndex) {
388408
return getCurrentRowAsStruct().getBytesList(columnIndex);

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

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,11 @@ protected String getJsonInternal(int columnIndex) {
197197
return values.get(columnIndex).getJson();
198198
}
199199

200+
@Override
201+
protected String getPgJsonbInternal(int columnIndex) {
202+
return values.get(columnIndex).getPgJsonb();
203+
}
204+
200205
@Override
201206
protected ByteArray getBytesInternal(int columnIndex) {
202207
return values.get(columnIndex).getBytes();
@@ -267,6 +272,11 @@ protected List<String> getJsonListInternal(int columnIndex) {
267272
return values.get(columnIndex).getJsonArray();
268273
}
269274

275+
@Override
276+
protected List<String> getPgJsonbListInternal(int columnIndex) {
277+
return values.get(columnIndex).getPgJsonbArray();
278+
}
279+
270280
@Override
271281
protected List<ByteArray> getBytesListInternal(int columnIndex) {
272282
return values.get(columnIndex).getBytesArray();
@@ -355,6 +365,8 @@ private Object getAsObject(int columnIndex) {
355365
return getStringInternal(columnIndex);
356366
case JSON:
357367
return getJsonInternal(columnIndex);
368+
case PG_JSONB:
369+
return getPgJsonbInternal(columnIndex);
358370
case BYTES:
359371
return getBytesInternal(columnIndex);
360372
case TIMESTAMP:
@@ -379,6 +391,8 @@ private Object getAsObject(int columnIndex) {
379391
return getStringListInternal(columnIndex);
380392
case JSON:
381393
return getJsonListInternal(columnIndex);
394+
case PG_JSONB:
395+
return getPgJsonbListInternal(columnIndex);
382396
case BYTES:
383397
return getBytesListInternal(columnIndex);
384398
case TIMESTAMP:

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

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -114,16 +114,26 @@ 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()}. */
117+
/** Returns the value of a non-{@code NULL} column with type {@link Type#json()}. */
118118
default String getJson(int columnIndex) {
119119
throw new UnsupportedOperationException("method should be overwritten");
120120
}
121121

122-
/** Returns the value of a non-{@code NULL} column with type {@link Type#string()}. */
122+
/** Returns the value of a non-{@code NULL} column with type {@link Type#json()}. */
123123
default String getJson(String columnName) {
124124
throw new UnsupportedOperationException("method should be overwritten");
125125
}
126126

127+
/** Returns the value of a non-{@code NULL} column with type {@link Type#pgJsonb()}. */
128+
default String getPgJsonb(int columnIndex) {
129+
throw new UnsupportedOperationException("method should be overwritten");
130+
}
131+
132+
/** Returns the value of a non-{@code NULL} column with type {@link Type#pgJsonb()}. */
133+
default String getPgJsonb(String columnName) {
134+
throw new UnsupportedOperationException("method should be overwritten");
135+
}
136+
127137
/** Returns the value of a non-{@code NULL} column with type {@link Type#bytes()}. */
128138
ByteArray getBytes(int columnIndex);
129139

@@ -238,16 +248,30 @@ default Value getValue(String columnName) {
238248
/** Returns the value of a non-{@code NULL} column with type {@code Type.array(Type.string())}. */
239249
List<String> getStringList(String columnName);
240250

241-
/** Returns the value of a non-{@code NULL} column with type {@code Type.array(Type.string())}. */
251+
/** Returns the value of a non-{@code NULL} column with type {@code Type.array(Type.json())}. */
242252
default List<String> getJsonList(int columnIndex) {
243253
throw new UnsupportedOperationException("method should be overwritten");
244254
};
245255

246-
/** Returns the value of a non-{@code NULL} column with type {@code Type.array(Type.string())}. */
256+
/** Returns the value of a non-{@code NULL} column with type {@code Type.array(Type.json())}. */
247257
default List<String> getJsonList(String columnName) {
248258
throw new UnsupportedOperationException("method should be overwritten");
249259
};
250260

261+
/**
262+
* Returns the value of a non-{@code NULL} column with type {@code Type.array(Type.pgJsonb())}.
263+
*/
264+
default List<String> getPgJsonbList(int columnIndex) {
265+
throw new UnsupportedOperationException("method should be overwritten");
266+
};
267+
268+
/**
269+
* Returns the value of a non-{@code NULL} column with type {@code Type.array(Type.pgJsonb())}.
270+
*/
271+
default List<String> getPgJsonbList(String columnName) {
272+
throw new UnsupportedOperationException("method should be overwritten");
273+
};
274+
251275
/** Returns the value of a non-{@code NULL} column with type {@code Type.array(Type.bytes())}. */
252276
List<ByteArray> getBytesList(int columnIndex);
253277

0 commit comments

Comments
 (0)