Skip to content

Commit 5f156f2

Browse files
feat: PostgreSQL dialect databases (#1673)
* feat: PostgreSQL dialect databases * 🦉 Updates from OwlBot See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md * fix: add clirr differences * fix: do not join type names for every invocation * 🦉 Updates from OwlBot See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md * fix: revert to showing column index * test: add parameterization for ITBatchReadTest * test: parameterize BatchReadTest and skip unsupported tests Co-authored-by: Owl Bot <gcf-owl-bot[bot]@users.noreply.github.com>
1 parent 8ea2220 commit 5f156f2

File tree

91 files changed

+11073
-6824
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

91 files changed

+11073
-6824
lines changed
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<!-- see http://www.mojohaus.org/clirr-maven-plugin/examples/ignored-differences.html -->
3+
<differences>
4+
<difference>
5+
<differenceType>7012</differenceType>
6+
<className>com/google/cloud/spanner/connection/Connection</className>
7+
<method>com.google.cloud.spanner.Dialect getDialect()</method>
8+
</difference>
9+
<difference>
10+
<differenceType>8001</differenceType>
11+
<className>com/google/cloud/spanner/connection/StatementParser</className>
12+
</difference>
13+
</differences>

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

Lines changed: 25 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -375,6 +375,9 @@ private Object writeReplace() {
375375
case NUMERIC:
376376
builder.set(fieldName).to((BigDecimal) value);
377377
break;
378+
case PG_NUMERIC:
379+
builder.set(fieldName).to((String) value);
380+
break;
378381
case STRING:
379382
builder.set(fieldName).to((String) value);
380383
break;
@@ -391,7 +394,8 @@ private Object writeReplace() {
391394
builder.set(fieldName).to((Date) value);
392395
break;
393396
case ARRAY:
394-
switch (fieldType.getArrayElementType().getCode()) {
397+
final Type elementType = fieldType.getArrayElementType();
398+
switch (elementType.getCode()) {
395399
case BOOL:
396400
builder.set(fieldName).toBoolArray((Iterable<Boolean>) value);
397401
break;
@@ -404,6 +408,9 @@ private Object writeReplace() {
404408
case NUMERIC:
405409
builder.set(fieldName).toNumericArray((Iterable<BigDecimal>) value);
406410
break;
411+
case PG_NUMERIC:
412+
builder.set(fieldName).toPgNumericArray((Iterable<String>) value);
413+
break;
407414
case STRING:
408415
builder.set(fieldName).toStringArray((Iterable<String>) value);
409416
break;
@@ -420,13 +427,10 @@ private Object writeReplace() {
420427
builder.set(fieldName).toDateArray((Iterable<Date>) value);
421428
break;
422429
case STRUCT:
423-
builder
424-
.set(fieldName)
425-
.toStructArray(fieldType.getArrayElementType(), (Iterable<Struct>) value);
430+
builder.set(fieldName).toStructArray(elementType, (Iterable<Struct>) value);
426431
break;
427432
default:
428-
throw new AssertionError(
429-
"Unhandled array type code: " + fieldType.getArrayElementType());
433+
throw new AssertionError("Unhandled array type code: " + elementType);
430434
}
431435
break;
432436
case STRUCT:
@@ -484,7 +488,11 @@ private static Object decodeValue(Type fieldType, com.google.protobuf.Value prot
484488
case FLOAT64:
485489
return valueProtoToFloat64(proto);
486490
case NUMERIC:
491+
checkType(fieldType, proto, KindCase.STRING_VALUE);
487492
return new BigDecimal(proto.getStringValue());
493+
case PG_NUMERIC:
494+
checkType(fieldType, proto, KindCase.STRING_VALUE);
495+
return proto.getStringValue();
488496
case STRING:
489497
case JSON:
490498
checkType(fieldType, proto, KindCase.STRING_VALUE);
@@ -549,6 +557,10 @@ static Object decodeArrayValue(Type elementType, ListValue listValue) {
549557
}
550558
return list;
551559
}
560+
case PG_NUMERIC:
561+
return Lists.transform(
562+
listValue.getValuesList(),
563+
input -> input.getKindCase() == KindCase.NULL_VALUE ? null : input.getStringValue());
552564
case STRING:
553565
case JSON:
554566
return Lists.transform(
@@ -695,6 +707,8 @@ protected Value getValueInternal(int columnIndex) {
695707
return Value.int64(isNull ? null : getLongInternal(columnIndex));
696708
case NUMERIC:
697709
return Value.numeric(isNull ? null : getBigDecimalInternal(columnIndex));
710+
case PG_NUMERIC:
711+
return Value.pgNumeric(isNull ? null : getStringInternal(columnIndex));
698712
case FLOAT64:
699713
return Value.float64(isNull ? null : getDoubleInternal(columnIndex));
700714
case STRING:
@@ -708,13 +722,16 @@ protected Value getValueInternal(int columnIndex) {
708722
case STRUCT:
709723
return Value.struct(isNull ? null : getStructInternal(columnIndex));
710724
case ARRAY:
711-
switch (columnType.getArrayElementType().getCode()) {
725+
final Type elementType = columnType.getArrayElementType();
726+
switch (elementType.getCode()) {
712727
case BOOL:
713728
return Value.boolArray(isNull ? null : getBooleanListInternal(columnIndex));
714729
case INT64:
715730
return Value.int64Array(isNull ? null : getLongListInternal(columnIndex));
716731
case NUMERIC:
717732
return Value.numericArray(isNull ? null : getBigDecimalListInternal(columnIndex));
733+
case PG_NUMERIC:
734+
return Value.pgNumericArray(isNull ? null : getStringListInternal(columnIndex));
718735
case FLOAT64:
719736
return Value.float64Array(isNull ? null : getDoubleListInternal(columnIndex));
720737
case STRING:
@@ -727,8 +744,7 @@ protected Value getValueInternal(int columnIndex) {
727744
return Value.dateArray(isNull ? null : getDateListInternal(columnIndex));
728745
case STRUCT:
729746
return Value.structArray(
730-
columnType.getArrayElementType(),
731-
isNull ? null : getStructListInternal(columnIndex));
747+
elementType, isNull ? null : getStructListInternal(columnIndex));
732748
default:
733749
throw new IllegalArgumentException(
734750
"Invalid array value type " + this.type.getArrayElementType());

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

Lines changed: 33 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
import com.google.cloud.Date;
2323
import com.google.cloud.Timestamp;
2424
import java.math.BigDecimal;
25+
import java.util.Arrays;
2526
import java.util.List;
2627

2728
/**
@@ -159,14 +160,19 @@ public BigDecimal getBigDecimal(String columnName) {
159160

160161
@Override
161162
public String getString(int columnIndex) {
162-
checkNonNullOfType(columnIndex, Type.string(), columnIndex);
163+
checkNonNullOfTypes(
164+
columnIndex,
165+
Arrays.asList(Type.string(), Type.pgNumeric()),
166+
columnIndex,
167+
"STRING, NUMERIC");
163168
return getStringInternal(columnIndex);
164169
}
165170

166171
@Override
167172
public String getString(String columnName) {
168173
int columnIndex = getColumnIndex(columnName);
169-
checkNonNullOfType(columnIndex, Type.string(), columnName);
174+
checkNonNullOfTypes(
175+
columnIndex, Arrays.asList(Type.string(), Type.pgNumeric()), columnName, "STRING, NUMERIC");
170176
return getStringInternal(columnIndex);
171177
}
172178

@@ -327,14 +333,22 @@ public List<BigDecimal> getBigDecimalList(String columnName) {
327333

328334
@Override
329335
public List<String> getStringList(int columnIndex) {
330-
checkNonNullOfType(columnIndex, Type.array(Type.string()), columnIndex);
336+
checkNonNullOfTypes(
337+
columnIndex,
338+
Arrays.asList(Type.array(Type.string()), Type.array(Type.pgNumeric())),
339+
columnIndex,
340+
"ARRAY<STRING>, ARRAY<NUMERIC>");
331341
return getStringListInternal(columnIndex);
332342
}
333343

334344
@Override
335345
public List<String> getStringList(String columnName) {
336346
int columnIndex = getColumnIndex(columnName);
337-
checkNonNullOfType(columnIndex, Type.array(Type.string()), columnName);
347+
checkNonNullOfTypes(
348+
columnIndex,
349+
Arrays.asList(Type.array(Type.string()), Type.array(Type.pgNumeric())),
350+
columnName,
351+
"ARRAY<STRING>, ARRAY<NUMERIC>");
338352
return getStringListInternal(columnIndex);
339353
}
340354

@@ -429,6 +443,21 @@ private void checkNonNullOfType(int columnIndex, Type expectedType, Object colum
429443
checkNonNull(columnIndex, columnNameForError);
430444
}
431445

446+
private void checkNonNullOfTypes(
447+
int columnIndex,
448+
List<Type> expectedTypes,
449+
Object columnNameForError,
450+
String expectedTypeNames) {
451+
Type actualType = getColumnType(columnIndex);
452+
checkState(
453+
expectedTypes.contains(actualType),
454+
"Column %s is not of correct type: expected one of [%s] but was %s",
455+
columnNameForError,
456+
expectedTypeNames,
457+
actualType);
458+
checkNonNull(columnIndex, columnNameForError);
459+
}
460+
432461
private void checkNonNullArrayOfStruct(int columnIndex, Object columnNameForError) {
433462
Type actualType = getColumnType(columnIndex);
434463
checkState(

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,7 @@ static Database fromProto(
188188
.setEarliestVersionTime(Timestamp.fromProto(proto.getEarliestVersionTime()))
189189
.setEncryptionConfig(CustomerManagedEncryption.fromProtoOrNull(proto.getEncryptionConfig()))
190190
.setDefaultLeader(proto.getDefaultLeader())
191+
.setDialect(Dialect.fromProto(proto.getDatabaseDialect()))
191192
.setProto(proto)
192193
.build();
193194
}

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

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@
2323
import com.google.cloud.Policy;
2424
import com.google.cloud.Policy.DefaultMarshaller;
2525
import com.google.cloud.Timestamp;
26-
import com.google.cloud.spanner.DatabaseInfo.State;
2726
import com.google.cloud.spanner.Options.ListOption;
2827
import com.google.cloud.spanner.SpannerImpl.PageFetcher;
2928
import com.google.cloud.spanner.spi.v1.SpannerRpc;
@@ -281,14 +280,18 @@ public Backup fromProto(com.google.spanner.admin.database.v1.Backup proto) {
281280
public OperationFuture<Database, CreateDatabaseMetadata> createDatabase(
282281
String instanceId, String databaseId, Iterable<String> statements) throws SpannerException {
283282
return createDatabase(
284-
new Database(DatabaseId.of(projectId, instanceId, databaseId), State.UNSPECIFIED, this),
283+
newDatabaseBuilder(DatabaseId.of(projectId, instanceId, databaseId))
284+
.setDialect(Dialect.GOOGLE_STANDARD_SQL)
285+
.build(),
285286
statements);
286287
}
287288

288289
@Override
289290
public OperationFuture<Database, CreateDatabaseMetadata> createDatabase(
290291
Database database, Iterable<String> statements) throws SpannerException {
291-
String createStatement = "CREATE DATABASE `" + database.getId().getDatabase() + "`";
292+
final Dialect dialect = Preconditions.checkNotNull(database.getDialect());
293+
final String createStatement =
294+
dialect.createDatabaseStatementFor(database.getId().getDatabase());
292295
OperationFuture<com.google.spanner.admin.database.v1.Database, CreateDatabaseMetadata>
293296
rawOperationFuture =
294297
rpc.createDatabase(

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

Lines changed: 30 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,10 @@ public Builder setDefaultLeader(String defaultLeader) {
5454
throw new UnsupportedOperationException("Unimplemented");
5555
}
5656

57+
public Builder setDialect(Dialect dialect) {
58+
throw new UnsupportedOperationException("Unimplemented");
59+
}
60+
5761
abstract Builder setProto(com.google.spanner.admin.database.v1.Database proto);
5862

5963
/** Builds the database from this builder. */
@@ -69,6 +73,7 @@ abstract static class BuilderImpl extends Builder {
6973
private Timestamp earliestVersionTime;
7074
private CustomerManagedEncryption encryptionConfig;
7175
private String defaultLeader;
76+
private Dialect dialect = Dialect.GOOGLE_STANDARD_SQL;
7277
private com.google.spanner.admin.database.v1.Database proto;
7378

7479
BuilderImpl(DatabaseId id) {
@@ -84,6 +89,7 @@ abstract static class BuilderImpl extends Builder {
8489
this.earliestVersionTime = other.earliestVersionTime;
8590
this.encryptionConfig = other.encryptionConfig;
8691
this.defaultLeader = other.defaultLeader;
92+
this.dialect = other.dialect;
8793
this.proto = other.proto;
8894
}
8995

@@ -129,6 +135,12 @@ public Builder setDefaultLeader(String defaultLeader) {
129135
return this;
130136
}
131137

138+
@Override
139+
public Builder setDialect(Dialect dialect) {
140+
this.dialect = dialect;
141+
return this;
142+
}
143+
132144
@Override
133145
Builder setProto(@Nullable com.google.spanner.admin.database.v1.Database proto) {
134146
this.proto = proto;
@@ -156,6 +168,7 @@ public enum State {
156168
private final Timestamp earliestVersionTime;
157169
private final CustomerManagedEncryption encryptionConfig;
158170
private final String defaultLeader;
171+
private final Dialect dialect;
159172
private final com.google.spanner.admin.database.v1.Database proto;
160173

161174
public DatabaseInfo(DatabaseId id, State state) {
@@ -167,6 +180,7 @@ public DatabaseInfo(DatabaseId id, State state) {
167180
this.earliestVersionTime = null;
168181
this.encryptionConfig = null;
169182
this.defaultLeader = null;
183+
this.dialect = null;
170184
this.proto = null;
171185
}
172186

@@ -179,6 +193,7 @@ public DatabaseInfo(DatabaseId id, State state) {
179193
this.earliestVersionTime = builder.earliestVersionTime;
180194
this.encryptionConfig = builder.encryptionConfig;
181195
this.defaultLeader = builder.defaultLeader;
196+
this.dialect = builder.dialect;
182197
this.proto = builder.proto;
183198
}
184199

@@ -239,6 +254,14 @@ public Timestamp getEarliestVersionTime() {
239254
return defaultLeader;
240255
}
241256

257+
/**
258+
* The dialect that is used by the database. It can be one of the values as specified in {@link
259+
* Dialect#values()}.
260+
*/
261+
public @Nullable Dialect getDialect() {
262+
return dialect;
263+
}
264+
242265
/** Returns the raw proto instance that was used to construct this {@link Database}. */
243266
public @Nullable com.google.spanner.admin.database.v1.Database getProto() {
244267
return proto;
@@ -260,7 +283,8 @@ public boolean equals(Object o) {
260283
&& Objects.equals(versionRetentionPeriod, that.versionRetentionPeriod)
261284
&& Objects.equals(earliestVersionTime, that.earliestVersionTime)
262285
&& Objects.equals(encryptionConfig, that.encryptionConfig)
263-
&& Objects.equals(defaultLeader, that.defaultLeader);
286+
&& Objects.equals(defaultLeader, that.defaultLeader)
287+
&& Objects.equals(dialect, that.dialect);
264288
}
265289

266290
@Override
@@ -273,20 +297,22 @@ public int hashCode() {
273297
versionRetentionPeriod,
274298
earliestVersionTime,
275299
encryptionConfig,
276-
defaultLeader);
300+
defaultLeader,
301+
dialect);
277302
}
278303

279304
@Override
280305
public String toString() {
281306
return String.format(
282-
"Database[%s, %s, %s, %s, %s, %s, %s, %s]",
307+
"Database[%s, %s, %s, %s, %s, %s, %s, %s, %s]",
283308
id.getName(),
284309
state,
285310
createTime,
286311
restoreInfo,
287312
versionRetentionPeriod,
288313
earliestVersionTime,
289314
encryptionConfig,
290-
defaultLeader);
315+
defaultLeader,
316+
dialect);
291317
}
292318
}

0 commit comments

Comments
 (0)