Repository: googleapis/java-spanner-jdbc
Pull request #2032 (2025-04-29 16:33:03)

deps: update dependency com.google.cloud:google-cloud-spanner-bom to v6.92.0
This PR contains the following updates:

| Package | Change | Age | Adoption | Passing | Confidence |
|---|---|---|---|---|---|
| [com.google.cloud:google-cloud-spanner-bom](https://redirect.github.com/googleapis/java-spanner) | `6.91.1` -> `6.92.0` | [![age](https://developer.mend.io/api/mc/badges/age/maven/com.google.cloud:google-cloud-spanner-bom/6.92.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![adoption](https://developer.mend.io/api/mc/badges/adoption/maven/com.google.cloud:google-cloud-spanner-bom/6.92.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![passing](https://developer.mend.io/api/mc/badges/compatibility/maven/com.google.cloud:google-cloud-spanner-bom/6.91.1/6.92.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![confidence](https://developer.mend.io/api/mc/badges/confidence/maven/com.google.cloud:google-cloud-spanner-bom/6.91.1/6.92.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) |

---

### Release Notes

<details>
<summary>googleapis/java-spanner (com.google.cloud:google-cloud-spanner-bom)</summary>

### [`v6.92.0`](https://redirect.github.com/googleapis/java-spanner/blob/HEAD/CHANGELOG.md#6920-2025-04-29)

[Compare Source](https://redirect.github.com/googleapis/java-spanner/compare/v6.91.1...v6.92.0)

##### Features

-   \[Internal] client-side metrics for afe latency and connectivity error ([#&#8203;3819](https://redirect.github.com/googleapis/java-spanner/issues/3819)) ([a8dba0a](https://redirect.github.com/googleapis/java-spanner/commit/a8dba0a83939fdbbc324f0a7aa6c44180462fa3a))
-   Support begin with AbortedException for manager interface ([#&#8203;3835](https://redirect.github.com/googleapis/java-spanner/issues/3835)) ([5783116](https://redirect.github.com/googleapis/java-spanner/commit/578311693bed836c8916f4b4ffa0782a468c1af3))

##### Bug Fixes

-   **deps:** Update the Java code generator (gapic-generator-java) to 2.56.2 ([11bfd90](https://redirect.github.com/googleapis/java-spanner/commit/11bfd90daa244dbd31a76bc5a1d2e694e43fa292))

##### Dependencies

-   Update dependency com.google.cloud:sdk-platform-java-config to v3.46.2 ([#&#8203;3836](https://redirect.github.com/googleapis/java-spanner/issues/3836)) ([2ee7f97](https://redirect.github.com/googleapis/java-spanner/commit/2ee7f971f3374b01d22e5a7f8f2483cf60c3363d))

</details>

---

### Configuration

📅 **Schedule**: Branch creation - At any time (no schedule defined), Automerge - At any time (no schedule defined).

🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update again.

---

 - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box

---

This PR was generated by [Mend Renovate](https://mend.io/renovate/). View the [repository job log](https://developer.mend.io/github/googleapis/java-spanner-jdbc).
<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiIzOS4yNTcuMyIsInVwZGF0ZWRJblZlciI6IjM5LjI1Ny4zIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6W119-->


dpebot: /gcbrun
dpebot: /gcbrun
forking-renovate[bot]: ### Edited/Blocked Notification

Renovate will not automatically rebase this PR, because it does not recognize the last commit author and assumes somebody else may have edited the PR.

You can manually request rebase by checking the rebase/retry box above.

 ⚠️ **Warning**: custom changes will be lost.
dpebot: /gcbrun

Patch:
diff --git a/pom.xml b/pom.xml
index 12a65bf..9e3398e 100644
--- a/pom.xml
+++ b/pom.xml
@@ -61,7 +61,7 @@
       <dependency>
         <groupId>com.google.cloud</groupId>
         <artifactId>google-cloud-spanner-bom</artifactId>
-        <version>6.91.1</version>
+        <version>6.92.0</version>
         <type>pom</type>
         <scope>import</scope>
       </dependency>
diff --git a/samples/spring-data-jdbc/googlesql/pom.xml b/samples/spring-data-jdbc/googlesql/pom.xml
index 57b0dea..a84f306 100644
--- a/samples/spring-data-jdbc/googlesql/pom.xml
+++ b/samples/spring-data-jdbc/googlesql/pom.xml
@@ -30,7 +30,7 @@
       <dependency>
         <groupId>com.google.cloud</groupId>
         <artifactId>google-cloud-spanner-bom</artifactId>
-        <version>6.91.1</version>
+        <version>6.92.0</version>
         <scope>import</scope>
         <type>pom</type>
       </dependency>
diff --git a/samples/spring-data-jdbc/postgresql/pom.xml b/samples/spring-data-jdbc/postgresql/pom.xml
index 5e04a25..7bedf6c 100644
--- a/samples/spring-data-jdbc/postgresql/pom.xml
+++ b/samples/spring-data-jdbc/postgresql/pom.xml
@@ -30,7 +30,7 @@
       <dependency>
         <groupId>com.google.cloud</groupId>
         <artifactId>google-cloud-spanner-bom</artifactId>
-        <version>6.91.1</version>
+        <version>6.92.0</version>
         <scope>import</scope>
         <type>pom</type>
       </dependency>
diff --git a/samples/spring-data-mybatis/googlesql/pom.xml b/samples/spring-data-mybatis/googlesql/pom.xml
index 1b79783..b4cc9e0 100644
--- a/samples/spring-data-mybatis/googlesql/pom.xml
+++ b/samples/spring-data-mybatis/googlesql/pom.xml
@@ -35,7 +35,7 @@
       <dependency>
         <groupId>com.google.cloud</groupId>
         <artifactId>google-cloud-spanner-bom</artifactId>
-        <version>6.91.1</version>
+        <version>6.92.0</version>
         <scope>import</scope>
         <type>pom</type>
       </dependency>
diff --git a/src/main/java/com/google/cloud/spanner/jdbc/JdbcDataType.java b/src/main/java/com/google/cloud/spanner/jdbc/JdbcDataType.java
index d73233c..6a77cec 100644
--- a/src/main/java/com/google/cloud/spanner/jdbc/JdbcDataType.java
+++ b/src/main/java/com/google/cloud/spanner/jdbc/JdbcDataType.java
@@ -29,6 +29,7 @@ import java.util.Collections;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Set;
+import java.util.UUID;
 
 /** Enum for mapping Cloud Spanner data types to Java classes and JDBC SQL {@link Types}. */
 enum JdbcDataType {
@@ -379,6 +380,32 @@ enum JdbcDataType {
       return Type.timestamp();
     }
   },
+  UUID {
+    @Override
+    public int getSqlType() {
+      return UuidType.VENDOR_TYPE_NUMBER;
+    }
+
+    @Override
+    public Class<UUID> getJavaClass() {
+      return UUID.class;
+    }
+
+    @Override
+    public Code getCode() {
+      return Code.UUID;
+    }
+
+    @Override
+    public List<UUID> getArrayElements(ResultSet rs, int columnIndex) {
+      return rs.getUuidList(columnIndex);
+    }
+
+    @Override
+    public Type getSpannerType() {
+      return Type.uuid();
+    }
+  },
   STRUCT {
     @Override
     public int getSqlType() {
diff --git a/src/main/java/com/google/cloud/spanner/jdbc/JdbcParameterStore.java b/src/main/java/com/google/cloud/spanner/jdbc/JdbcParameterStore.java
index 47d0962..0964db0 100644
--- a/src/main/java/com/google/cloud/spanner/jdbc/JdbcParameterStore.java
+++ b/src/main/java/com/google/cloud/spanner/jdbc/JdbcParameterStore.java
@@ -729,6 +729,12 @@ class JdbcParameterStore {
     } else if (Time.class.isAssignableFrom(value.getClass())) {
       Time timeValue = (Time) value;
       return binder.to(JdbcTypeConverter.toGoogleTimestamp(new Timestamp(timeValue.getTime())));
+    } else if (UUID.class.isAssignableFrom(value.getClass())) {
+      // Bind UUID values as untyped strings to allow them to be used with all types that support
+      // string values (e.g. STRING, UUID).
+      return binder.to(
+          Value.untyped(
+              com.google.protobuf.Value.newBuilder().setStringValue(value.toString()).build()));
     } else if (String.class.isAssignableFrom(value.getClass())) {
       String stringVal = (String) value;
       return binder.to(stringVal);
@@ -853,6 +859,9 @@ class JdbcParameterStore {
                   com.google.protobuf.Value.newBuilder()
                       .setNullValue(NullValue.NULL_VALUE)
                       .build()));
+        case UuidType.VENDOR_TYPE_NUMBER:
+        case UuidType.SHORT_VENDOR_TYPE_NUMBER:
+          return binder.toUuidArray(null);
         default:
           return binder.to(
               Value.untyped(
@@ -907,6 +916,8 @@ class JdbcParameterStore {
       return binder.toDateArray(JdbcTypeConverter.toGoogleDates((Date[]) value));
     } else if (Timestamp[].class.isAssignableFrom(value.getClass())) {
       return binder.toTimestampArray(JdbcTypeConverter.toGoogleTimestamps((Timestamp[]) value));
+    } else if (UUID[].class.isAssignableFrom(value.getClass())) {
+      return binder.toUuidArray(Arrays.asList((UUID[]) value));
     } else if (String[].class.isAssignableFrom(value.getClass())) {
       if (type == JsonType.VENDOR_TYPE_NUMBER || type == JsonType.SHORT_VENDOR_TYPE_NUMBER) {
         return binder.toJsonArray(Arrays.asList((String[]) value));
diff --git a/src/main/java/com/google/cloud/spanner/jdbc/JdbcResultSet.java b/src/main/java/com/google/cloud/spanner/jdbc/JdbcResultSet.java
index 00df251..d9bfe81 100644
--- a/src/main/java/com/google/cloud/spanner/jdbc/JdbcResultSet.java
+++ b/src/main/java/com/google/cloud/spanner/jdbc/JdbcResultSet.java
@@ -52,6 +52,7 @@ import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
 import java.util.NoSuchElementException;
+import java.util.UUID;
 import javax.annotation.Nonnull;
 
 /** Implementation of {@link ResultSet} for Cloud Spanner */
@@ -623,6 +624,38 @@ class JdbcResultSet extends AbstractJdbcResultSet {
     }
   }
 
+  public UUID getUUID(int columnIndex) throws SQLException {
+    checkClosedAndValidRow();
+    if (isNull(columnIndex)) {
+      return null;
+    }
+    int spannerIndex = columnIndex - 1;
+    Code type = getMainTypeCode(spanner.getColumnType(spannerIndex));
+    switch (type) {
+      case UUID:
+        return spanner.getUuid(spannerIndex);
+      case STRING:
+        return UUID.fromString(spanner.getString(spannerIndex));
+      case BYTES:
+      case DATE:
+      case TIMESTAMP:
+      case BOOL:
+      case FLOAT32:
+      case FLOAT64:
+      case INT64:
+      case NUMERIC:
+      case PG_NUMERIC:
+      case JSON:
+      case PG_JSONB:
+      case STRUCT:
+      case PROTO:
+      case ENUM:
+      case ARRAY:
+      default:
+        throw createInvalidToGetAs("uuid", type);
+    }
+  }
+
   private InputStream getInputStream(String val, Charset charset) {
     if (val == null) return null;
     byte[] b = val.getBytes(charset);
@@ -764,35 +797,46 @@ class JdbcResultSet extends AbstractJdbcResultSet {
   }
 
   private Object getObject(Type type, int columnIndex) throws SQLException {
-    // TODO: Refactor to check based on type code.
-    if (type == Type.bool()) return getBoolean(columnIndex);
-    if (type == Type.bytes()) return getBytes(columnIndex);
-    if (type == Type.date()) return getDate(columnIndex);
-    if (type == Type.float32()) {
-      return getFloat(columnIndex);
-    }
-    if (type == Type.float64()) return getDouble(columnIndex);
-    if (type == Type.int64() || type == Type.pgOid()) {
-      return getLong(columnIndex);
-    }
-    if (type == Type.numeric()) return getBigDecimal(columnIndex);
-    if (type == Type.pgNumeric()) {
-      final String value = getString(columnIndex);
-      try {
-        return parseBigDecimal(value);
-      } catch (Exception e) {
-        return parseDouble(value);
-      }
-    }
-    if (type == Type.string()) return getString(columnIndex);
-    if (type == Type.json() || type == Type.pgJsonb()) {
-      return getString(columnIndex);
+    JdbcPreconditions.checkArgument(type != null, "type is null");
+    switch (type.getCode()) {
+      case BOOL:
+        return getBoolean(columnIndex);
+      case BYTES:
+      case PROTO:
+        return getBytes(columnIndex);
+      case DATE:
+        return getDate(columnIndex);
+      case FLOAT32:
+        return getFloat(columnIndex);
+      case FLOAT64:
+        return getDouble(columnIndex);
+      case INT64:
+      case PG_OID:
+      case ENUM:
+        return getLong(columnIndex);
+      case NUMERIC:
+        return getBigDecimal(columnIndex);
+      case PG_NUMERIC:
+        final String value = getString(columnIndex);
+        try {
+          return parseBigDecimal(value);
+        } catch (Exception e) {
+          return parseDouble(value);
+        }
+      case STRING:
+      case JSON:
+      case PG_JSONB:
+        return getString(columnIndex);
+      case TIMESTAMP:
+        return getTimestamp(columnIndex);
+      case UUID:
+        return getUUID(columnIndex);
+      case ARRAY:
+        return getArray(columnIndex);
+      default:
+        throw JdbcSqlExceptionFactory.of(
+            "Unknown type: " + type, com.google.rpc.Code.INVALID_ARGUMENT);
     }
-    if (type == Type.timestamp()) return getTimestamp(columnIndex);
-    if (type.getCode() == Code.PROTO) return getBytes(columnIndex);
-    if (type.getCode() == Code.ENUM) return getLong(columnIndex);
-    if (type.getCode() == Code.ARRAY) return getArray(columnIndex);
-    throw JdbcSqlExceptionFactory.of("Unknown type: " + type, com.google.rpc.Code.INVALID_ARGUMENT);
   }
 
   @Override
diff --git a/src/main/java/com/google/cloud/spanner/jdbc/JdbcTypeConverter.java b/src/main/java/com/google/cloud/spanner/jdbc/JdbcTypeConverter.java
index b983403..942be15 100644
--- a/src/main/java/com/google/cloud/spanner/jdbc/JdbcTypeConverter.java
+++ b/src/main/java/com/google/cloud/spanner/jdbc/JdbcTypeConverter.java
@@ -82,6 +82,9 @@ class JdbcTypeConverter {
     if (value == null) {
       return null;
     }
+    if (value.getClass().equals(targetType)) {
+      return value;
+    }
     try {
       if (targetType.equals(Value.class)) {
         return convertToSpannerValue(value, type);
@@ -370,7 +373,9 @@ class JdbcTypeConverter {
 
   private static void checkValidTypeAndValueForConvert(Type type, Object value)
       throws SQLException {
-    if (value == null) return;
+    if (value == null) {
+      return;
+    }
     JdbcPreconditions.checkArgument(
         type.getCode() != Code.ARRAY || Array.class.isAssignableFrom(value.getClass()),
         "input type is array, but input value is not an instance of java.sql.Array");
diff --git a/src/main/java/com/google/cloud/spanner/jdbc/UuidType.java b/src/main/java/com/google/cloud/spanner/jdbc/UuidType.java
new file mode 100644
index 0000000..46317b4
--- /dev/null
+++ b/src/main/java/com/google/cloud/spanner/jdbc/UuidType.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2025 Google LLC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *       http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.cloud.spanner.jdbc;
+
+import com.google.spanner.v1.TypeCode;
+import java.sql.DatabaseMetaData;
+import java.sql.PreparedStatement;
+import java.sql.SQLType;
+
+/**
+ * Custom SQL type for Spanner UUID data type. This type (or the vendor type number) must be used
+ * when setting a UUID parameter using {@link PreparedStatement#setObject(int, Object, SQLType)}.
+ */
+public class UuidType implements SQLType {
+  public static final UuidType INSTANCE = new UuidType();
+  /**
+   * Spanner does not have any type numbers, but the code values are unique. Add 100,000 to avoid
+   * conflicts with the type numbers in java.sql.Types.
+   */
+  public static final int VENDOR_TYPE_NUMBER = 100_000 + TypeCode.UUID_VALUE;
+  /**
+   * Define a short type number as well, as this is what is expected to be returned in {@link
+   * DatabaseMetaData#getTypeInfo()}.
+   */
+  public static final short SHORT_VENDOR_TYPE_NUMBER = (short) VENDOR_TYPE_NUMBER;
+
+  private UuidType() {}
+
+  @Override
+  public String getName() {
+    return "UUID";
+  }
+
+  @Override
+  public String getVendor() {
+    return UuidType.class.getPackage().getName();
+  }
+
+  @Override
+  public Integer getVendorTypeNumber() {
+    return VENDOR_TYPE_NUMBER;
+  }
+
+  public String toString() {
+    return getName();
+  }
+}


Test patch:
diff --git a/src/test/java/com/google/cloud/spanner/jdbc/AllTypesMockServerTest.java b/src/test/java/com/google/cloud/spanner/jdbc/AllTypesMockServerTest.java
index c384893..7cb9729 100644
--- a/src/test/java/com/google/cloud/spanner/jdbc/AllTypesMockServerTest.java
+++ b/src/test/java/com/google/cloud/spanner/jdbc/AllTypesMockServerTest.java
@@ -40,6 +40,7 @@ import java.sql.Timestamp;
 import java.util.Arrays;
 import java.util.Base64;
 import java.util.Map;
+import java.util.UUID;
 import java.util.stream.Collectors;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -74,6 +75,7 @@ public class AllTypesMockServerTest
             new java.sql.Date(
                 DATE_VALUE.getYear() - 1900, DATE_VALUE.getMonth() - 1, DATE_VALUE.getDayOfMonth()),
             resultSet.getDate(++col));
+        assertEquals(UUID_VALUE, resultSet.getObject(++col, UUID.class));
         assertEquals(TIMESTAMP_VALUE.toSqlTimestamp(), resultSet.getTimestamp(++col));
         if (dialect == Dialect.POSTGRESQL) {
           assertEquals(PG_OID_VALUE, resultSet.getLong(++col));
@@ -120,6 +122,8 @@ public class AllTypesMockServerTest
                                 date.getYear() - 1900, date.getMonth() - 1, date.getDayOfMonth()))
                 .collect(Collectors.toList()),
             Arrays.asList((Date[]) resultSet.getArray(++col).getArray()));
+        assertEquals(
+            UUID_ARRAY_VALUE, Arrays.asList((UUID[]) resultSet.getArray(++col).getArray()));
         assertEquals(
             TIMESTAMP_ARRAY_VALUE.stream()
                 .map(timestamp -> timestamp == null ? null : timestamp.toSqlTimestamp())
@@ -140,13 +144,13 @@ public class AllTypesMockServerTest
   @Override
   @Test
   public void testInsertAllTypes() {
-    // TODO: Remove when PG_NUMERIC NaN is supported.
+    Statement insertStatement = createInsertStatement(dialect);
     if (dialect == Dialect.POSTGRESQL) {
-      Statement insertStatement = createInsertStatement(dialect);
+      // TODO: Remove when PG_NUMERIC NaN is supported.
       insertStatement =
           insertStatement.toBuilder()
               .replace(insertStatement.getSql().replaceAll("@p", "\\$"))
-              .bind("p16")
+              .bind("p17")
               .to(
                   com.google.cloud.spanner.Value.pgNumericArray(
                       NUMERIC_ARRAY_VALUE.stream()
@@ -155,8 +159,10 @@ public class AllTypesMockServerTest
                                   bigDecimal == null ? null : bigDecimal.toEngineeringString())
                           .collect(Collectors.toList())))
               .build();
-      mockSpanner.putStatementResult(StatementResult.update(insertStatement, 1L));
     }
+    // The JDBC driver binds UUID values as untyped strings, so we need to add it as 'partial'
+    // result, meaning that the match will only be made based on the SQL string.
+    mockSpanner.putPartialStatementResult(StatementResult.update(insertStatement, 1L));
     try (Connection connection = createJdbcConnection()) {
       try (PreparedStatement statement =
           connection.prepareStatement(
@@ -184,6 +190,7 @@ public class AllTypesMockServerTest
                 DATE_VALUE.getYear() - 1900,
                 DATE_VALUE.getMonth() - 1,
                 DATE_VALUE.getDayOfMonth()));
+        statement.setObject(++param, UUID_VALUE);
         statement.setTimestamp(++param, TIMESTAMP_VALUE.toSqlTimestamp());
         if (dialect == Dialect.POSTGRESQL) {
           statement.setLong(++param, PG_OID_VALUE);
@@ -243,6 +250,8 @@ public class AllTypesMockServerTest
                                     date.getMonth() - 1,
                                     date.getDayOfMonth()))
                     .toArray(Date[]::new)));
+        statement.setArray(
+            ++param, connection.createArrayOf("UUID", UUID_ARRAY_VALUE.toArray(new UUID[0])));
         statement.setArray(
             ++param,
             connection.createArrayOf(
@@ -262,8 +271,10 @@ public class AllTypesMockServerTest
       ExecuteSqlRequest request = mockSpanner.getRequestsOfType(ExecuteSqlRequest.class).get(0);
       Map<String, Type> paramTypes = request.getParamTypesMap();
       Map<String, Value> params = request.getParams().getFieldsMap();
-      assertEquals(dialect == Dialect.POSTGRESQL ? 22 : 20, paramTypes.size());
-      assertEquals(dialect == Dialect.POSTGRESQL ? 22 : 20, params.size());
+      // UUID is sent without any type information to allow it to be used with any type of column
+      // that accepts a STRING value.
+      assertEquals(dialect == Dialect.POSTGRESQL ? 23 : 21, paramTypes.size());
+      assertEquals(dialect == Dialect.POSTGRESQL ? 24 : 22, params.size());
 
       // Verify param types.
       ImmutableList<TypeCode> expectedTypes =
@@ -277,18 +288,25 @@ public class AllTypesMockServerTest
               TypeCode.JSON,
               TypeCode.BYTES,
               TypeCode.DATE,
+              TypeCode.TYPE_CODE_UNSPECIFIED, // UUID
               TypeCode.TIMESTAMP);
       if (dialect == Dialect.POSTGRESQL) {
         expectedTypes =
             ImmutableList.<TypeCode>builder().addAll(expectedTypes).add(TypeCode.INT64).build();
       }
       for (int col = 0; col < expectedTypes.size(); col++) {
-        assertEquals(expectedTypes.get(col), paramTypes.get("p" + (col + 1)).getCode());
+        TypeCode expectedType = expectedTypes.get(col);
+        if (expectedType == TypeCode.TYPE_CODE_UNSPECIFIED) {
+          assertFalse(paramTypes.containsKey("p" + (col + 1)));
+        } else {
+          assertEquals(expectedType, paramTypes.get("p" + (col + 1)).getCode());
+        }
         int arrayCol = col + expectedTypes.size();
         assertEquals(TypeCode.ARRAY, paramTypes.get("p" + (arrayCol + 1)).getCode());
-        assertEquals(
-            expectedTypes.get(col),
-            paramTypes.get("p" + (arrayCol + 1)).getArrayElementType().getCode());
+        if (expectedType != TypeCode.TYPE_CODE_UNSPECIFIED) {
+          assertEquals(
+              expectedType, paramTypes.get("p" + (arrayCol + 1)).getArrayElementType().getCode());
+        }
       }
 
       // Verify param values.
@@ -306,6 +324,7 @@ public class AllTypesMockServerTest
           Base64.getEncoder().encodeToString(BYTES_VALUE),
           params.get("p" + ++col).getStringValue());
       assertEquals(DATE_VALUE.toString(), params.get("p" + ++col).getStringValue());
+      assertEquals(UUID_VALUE.toString(), params.get("p" + ++col).getStringValue());
       assertEquals(TIMESTAMP_VALUE.toString(), params.get("p" + ++col).getStringValue());
       if (dialect == Dialect.POSTGRESQL) {
         assertEquals(String.valueOf(PG_OID_VALUE), params.get("p" + ++col).getStringValue());
@@ -376,6 +395,11 @@ public class AllTypesMockServerTest
                           ? null
                           : com.google.cloud.Date.parseDate(value.getStringValue()))
               .collect(Collectors.toList()));
+      assertEquals(
+          UUID_ARRAY_VALUE,
+          params.get("p" + ++col).getListValue().getValuesList().stream()
+              .map(value -> value.hasNullValue() ? null : UUID.fromString(value.getStringValue()))
+              .collect(Collectors.toList()));
       assertEquals(
           TIMESTAMP_ARRAY_VALUE,
           params.get("p" + ++col).getListValue().getValuesList().stream()
diff --git a/src/test/java/com/google/cloud/spanner/jdbc/JdbcParameterStoreTest.java b/src/test/java/com/google/cloud/spanner/jdbc/JdbcParameterStoreTest.java
index d77c932..699f36c 100644
--- a/src/test/java/com/google/cloud/spanner/jdbc/JdbcParameterStoreTest.java
+++ b/src/test/java/com/google/cloud/spanner/jdbc/JdbcParameterStoreTest.java
@@ -729,7 +729,12 @@ public class JdbcParameterStoreTest {
     verifyParameter(params, Value.string("test"));
     params.setParameter(1, UUID.fromString("83b988cf-1f4e-428a-be3d-cc712621942e"), (Integer) null);
     assertEquals(UUID.fromString("83b988cf-1f4e-428a-be3d-cc712621942e"), params.getParameter(1));
-    verifyParameter(params, Value.string("83b988cf-1f4e-428a-be3d-cc712621942e"));
+    verifyParameter(
+        params,
+        Value.untyped(
+            com.google.protobuf.Value.newBuilder()
+                .setStringValue("83b988cf-1f4e-428a-be3d-cc712621942e")
+                .build()));
 
     String jsonString = "{\"test\": \"value\"}";
     params.setParameter(1, Value.json(jsonString), (Integer) null);
diff --git a/src/test/java/com/google/cloud/spanner/jdbc/PartitionedQueryMockServerTest.java b/src/test/java/com/google/cloud/spanner/jdbc/PartitionedQueryMockServerTest.java
index 1ee8098..42c59b4 100644
--- a/src/test/java/com/google/cloud/spanner/jdbc/PartitionedQueryMockServerTest.java
+++ b/src/test/java/com/google/cloud/spanner/jdbc/PartitionedQueryMockServerTest.java
@@ -74,7 +74,7 @@ public class PartitionedQueryMockServerTest extends AbstractMockServerTest {
   private int getExpectedColumnCount(Dialect dialect) {
     // GoogleSQL also adds 4 PROTO columns.
     // PostgreSQL adds 2 OID columns.
-    return dialect == Dialect.GOOGLE_STANDARD_SQL ? 24 : 22;
+    return dialect == Dialect.GOOGLE_STANDARD_SQL ? 26 : 24;
   }
 
   private String createUrl() {
diff --git a/src/test/java/com/google/cloud/spanner/jdbc/RandomResultSetTest.java b/src/test/java/com/google/cloud/spanner/jdbc/RandomResultSetTest.java
index e00907e..a6b5855 100644
--- a/src/test/java/com/google/cloud/spanner/jdbc/RandomResultSetTest.java
+++ b/src/test/java/com/google/cloud/spanner/jdbc/RandomResultSetTest.java
@@ -28,6 +28,7 @@ import java.sql.ResultSet;
 import java.sql.ResultSetMetaData;
 import java.sql.SQLException;
 import java.sql.Types;
+import java.util.UUID;
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
@@ -77,6 +78,7 @@ public class RandomResultSetTest extends AbstractMockServerTest {
         assertEquals(Types.NVARCHAR, metadata.getColumnType(++col));
         assertEquals(Types.BINARY, metadata.getColumnType(++col));
         assertEquals(Types.DATE, metadata.getColumnType(++col));
+        assertEquals(Types.OTHER, metadata.getColumnType(++col));
         assertEquals(Types.TIMESTAMP, metadata.getColumnType(++col));
         if (dialect == Dialect.POSTGRESQL) {
           assertEquals(Types.BIGINT, metadata.getColumnType(++col));
@@ -91,6 +93,7 @@ public class RandomResultSetTest extends AbstractMockServerTest {
         assertEquals(Types.ARRAY, metadata.getColumnType(++col)); // nvarchar
         assertEquals(Types.ARRAY, metadata.getColumnType(++col)); // binary
         assertEquals(Types.ARRAY, metadata.getColumnType(++col)); // date
+        assertEquals(Types.ARRAY, metadata.getColumnType(++col)); // uuid
         assertEquals(Types.ARRAY, metadata.getColumnType(++col)); // timestamp
         if (dialect == Dialect.POSTGRESQL) {
           assertEquals(Types.ARRAY, metadata.getColumnType(++col)); // oid
@@ -112,9 +115,7 @@ public class RandomResultSetTest extends AbstractMockServerTest {
         while (resultSet.next()) {
           // Verify that we can get all columns as an object.
           for (col = 1; col <= resultSet.getMetaData().getColumnCount(); col++) {
-            if (dialect == Dialect.GOOGLE_STANDARD_SQL && col > 20) {
-              // Proto columns are not yet supported, so skipping.
-            } else if (dialect == Dialect.POSTGRESQL && col == 16) {
+            if (dialect == Dialect.POSTGRESQL && col == 17) {
               // getObject for ARRAY<PG_NUMERIC> tries to get the array as a List<BigDecimal>.
               // That fails if the array contains a NaN, so skipping.
             } else {
@@ -133,6 +134,7 @@ public class RandomResultSetTest extends AbstractMockServerTest {
           resultSet.getString(++col); // JSON
           resultSet.getBytes(++col);
           resultSet.getDate(++col);
+          resultSet.getObject(++col, UUID.class);
           resultSet.getTimestamp(++col);
           if (dialect == Dialect.POSTGRESQL) {
             resultSet.getLong(++col); // oid
@@ -154,6 +156,7 @@ public class RandomResultSetTest extends AbstractMockServerTest {
           resultSet.getArray(++col);
           resultSet.getArray(++col);
           resultSet.getArray(++col);
+          resultSet.getArray(++col);
           if (dialect == Dialect.POSTGRESQL) {
             resultSet.getArray(++col); // oid[]
           }