Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down
2 changes: 1 addition & 1 deletion samples/spring-data-jdbc/googlesql/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down
2 changes: 1 addition & 1 deletion samples/spring-data-jdbc/postgresql/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down
2 changes: 1 addition & 1 deletion samples/spring-data-mybatis/googlesql/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down
27 changes: 27 additions & 0 deletions src/main/java/com/google/cloud/spanner/jdbc/JdbcDataType.java
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
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 {
Expand Down Expand Up @@ -379,6 +380,32 @@ public Type getSpannerType() {
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() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -729,6 +729,12 @@ private Builder setParamWithUnknownType(ValueBinder<Builder> binder, Object valu
} 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);
Expand Down Expand Up @@ -853,6 +859,9 @@ private Builder setArrayValue(ValueBinder<Builder> binder, int type, Object valu
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(
Expand Down Expand Up @@ -907,6 +916,8 @@ private Builder setArrayValue(ValueBinder<Builder> binder, int type, Object valu
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));
Expand Down
100 changes: 72 additions & 28 deletions src/main/java/com/google/cloud/spanner/jdbc/JdbcResultSet.java
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@
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 */
Expand Down Expand Up @@ -623,6 +624,38 @@ public Timestamp getTimestamp(int columnIndex) throws SQLException {
}
}

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);
Expand Down Expand Up @@ -764,35 +797,46 @@ public Object getObject(int columnIndex) throws SQLException {
}

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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,9 @@ static Object convert(Object value, Type type, Class<?> targetType) throws SQLEx
if (value == null) {
return null;
}
if (value.getClass().equals(targetType)) {
return value;
}
try {
if (targetType.equals(Value.class)) {
return convertToSpannerValue(value, type);
Expand Down Expand Up @@ -370,7 +373,9 @@ private static Value convertToSpannerValue(Object value, Type type) throws SQLEx

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");
Expand Down
61 changes: 61 additions & 0 deletions src/main/java/com/google/cloud/spanner/jdbc/UuidType.java
Original file line number Diff line number Diff line change
@@ -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();
}
}
Loading