Skip to content

Commit c19ac40

Browse files
committed
Do not require typarray column when auto-registering extensions.
[closes #621]
1 parent 29fd8dd commit c19ac40

File tree

4 files changed

+56
-16
lines changed

4 files changed

+56
-16
lines changed

src/main/java/io/r2dbc/postgresql/codec/BuiltinDynamicCodecs.java

+10-2
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,10 @@
2323
import org.reactivestreams.Publisher;
2424
import reactor.util.annotation.Nullable;
2525

26+
import java.util.ArrayList;
2627
import java.util.Arrays;
2728
import java.util.Collections;
29+
import java.util.List;
2830
import java.util.stream.Collectors;
2931

3032
/**
@@ -62,7 +64,13 @@ public Iterable<Codec<?>> createCodec(ByteBufAllocator byteBufAllocator, int oid
6264
return Collections.singletonList(new PostgisGeometryCodec(oid));
6365
case VECTOR:
6466
VectorCodec vectorCodec = new VectorCodec(byteBufAllocator, oid, typarray);
65-
return Arrays.asList(vectorCodec, new VectorCodec.VectorArrayCodec(byteBufAllocator, vectorCodec), new VectorFloatCodec(byteBufAllocator, oid));
67+
List<Codec<?>> codecs = new ArrayList<>(3);
68+
codecs.add(vectorCodec);
69+
if (typarray != PostgresTypes.NO_SUCH_TYPE) {
70+
codecs.add(new VectorCodec.VectorArrayCodec(byteBufAllocator, vectorCodec));
71+
}
72+
codecs.add(new VectorFloatCodec(byteBufAllocator, oid));
73+
return codecs;
6674
default:
6775
throw new UnsupportedOperationException(String.format("Codec %s for OID %d not supported", name(), oid));
6876
}
@@ -97,7 +105,7 @@ public Publisher<Void> register(PostgresqlConnection connection, ByteBufAllocato
97105
.flatMap(it -> it.map((row, rowMetadata) -> {
98106

99107
int oid = PostgresqlObjectId.toInt(row.get("oid", Long.class));
100-
int typarray = PostgresqlObjectId.toInt(row.get("typarray", Long.class));
108+
int typarray = rowMetadata.contains("typarray") ? PostgresqlObjectId.toInt(row.get("typarray", Long.class)) : PostgresTypes.NO_SUCH_TYPE;
101109
String typname = row.get("typname", String.class);
102110

103111
BuiltinCodec lookup = BuiltinCodec.lookup(typname);

src/main/java/io/r2dbc/postgresql/codec/PostgresTypes.java

+16-13
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@
1818

1919
import io.r2dbc.postgresql.api.PostgresqlConnection;
2020
import io.r2dbc.postgresql.util.Assert;
21+
import io.r2dbc.spi.Row;
22+
import io.r2dbc.spi.RowMetadata;
2123
import io.r2dbc.spi.Type;
2224
import reactor.core.publisher.Flux;
2325
import reactor.core.publisher.Mono;
@@ -35,6 +37,8 @@
3537
*/
3638
public class PostgresTypes {
3739

40+
public final static int NO_SUCH_TYPE = -1;
41+
3842
// parameterized with %s for the comparator (=, IN), %s for the actual criteria value and %s for a potential LIMIT 1 statement
3943
private static final String SELECT_PG_TYPE = "SELECT pg_type.oid, typarray, typname, typcategory "
4044
+ " FROM pg_catalog.pg_type "
@@ -74,13 +78,7 @@ public Mono<PostgresType> lookupType(String typeName) {
7478
}
7579

7680
return this.connection.createStatement(String.format(SELECT_PG_TYPE, "=", "'" + typeName + "'", "LIMIT 1")).execute()
77-
.flatMap(it -> it.map((row, rowMetadata) -> {
78-
79-
Long oid = row.get("oid", Long.class);
80-
Long typarrayOid = row.get("typarray", Long.class);
81-
return new PostgresType(PostgresqlObjectId.toInt(oid), oid.longValue(), PostgresqlObjectId.toInt(typarrayOid), typarrayOid, row.get("typname", String.class), row.get("typcategory",
82-
String.class));
83-
})).singleOrEmpty();
81+
.flatMap(it -> it.map(PostgresTypes::createType)).singleOrEmpty();
8482
}
8583

8684
public Flux<PostgresType> lookupTypes(Iterable<String> typeNames) {
@@ -103,13 +101,18 @@ public Flux<PostgresType> lookupTypes(Iterable<String> typeNames) {
103101
}
104102

105103
return this.connection.createStatement(String.format(SELECT_PG_TYPE, "IN", joiner, "")).execute()
106-
.flatMap(it -> it.map((row, rowMetadata) -> {
104+
.flatMap(it -> it.map(PostgresTypes::createType));
105+
}
106+
107+
private static PostgresType createType(Row row, RowMetadata rowMetadata) {
108+
Long oid = row.get("oid", Long.class);
109+
String typname = row.get("typname", String.class);
110+
String typcategory = row.get("typcategory", String.class);
111+
Long typarrayOid = rowMetadata.contains("typarray") ? row.get("typarray", Long.class) : null;
107112

108-
Long oid = row.get("oid", Long.class);
109-
Long typarrayOid = row.get("typarray", Long.class);
110-
return new PostgresType(PostgresqlObjectId.toInt(oid), oid.longValue(), PostgresqlObjectId.toInt(typarrayOid), typarrayOid, row.get("typname", String.class), row.get("typcategory",
111-
String.class));
112-
}));
113+
long unsignedTyparray = typarrayOid != null ? typarrayOid : NO_SUCH_TYPE;
114+
int typarray = typarrayOid != null ? PostgresqlObjectId.toInt(typarrayOid) : NO_SUCH_TYPE;
115+
return new PostgresType(PostgresqlObjectId.toInt(oid), oid, typarray, unsignedTyparray, typname, typcategory);
113116
}
114117

115118
public static class PostgresType implements Type, PostgresTypeIdentifier {

src/main/java/io/r2dbc/postgresql/codec/PostgresqlObjectId.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -584,7 +584,7 @@ public static int toInt(@Nullable Long oid) {
584584

585585
public static int toInt(long oid) {
586586
if ((oid & 0xFFFFFFFF00000000L) != 0) {
587-
throw new IllegalArgumentException("Value is not an OID:" + oid);
587+
throw new IllegalArgumentException("Value is not an OID: " + oid);
588588
}
589589

590590
return (int) oid;

src/test/java/io/r2dbc/postgresql/codec/EnumCodecUnitTests.java

+29
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,35 @@ void shouldRegisterCodecAsFirst() {
113113
verify(mockCodecRegistry, never()).addLast(any(EnumCodec.class));
114114
}
115115

116+
@Test
117+
void shouldRegisterCodecWithoutTyparray() {
118+
CodecRegistrar codecRegistrar = EnumCodec
119+
.builder()
120+
.withRegistrationPriority(RegistrationPriority.FIRST)
121+
.withEnum("foo", MyEnum.class)
122+
.build();
123+
124+
ByteBufAllocator mockByteBufAllocator = mock(ByteBufAllocator.class);
125+
CodecRegistry mockCodecRegistry = mock(CodecRegistry.class);
126+
127+
MockPostgresqlStatement mockPostgresqlStatement = MockPostgresqlStatement.builder()
128+
.result(MockPostgresqlResult.builder()
129+
.row(MockRow.builder()
130+
.identified("oid", Long.class, 42L)
131+
.identified("typname", String.class, "foo")
132+
.identified("typcategory", String.class, "E")
133+
.build())
134+
.build())
135+
.build();
136+
MockPostgresqlConnection mockPostgresqlConnection = new MockPostgresqlConnection(mockPostgresqlStatement);
137+
138+
Publisher<Void> register = codecRegistrar.register(mockPostgresqlConnection, mockByteBufAllocator, mockCodecRegistry);
139+
StepVerifier.create(register).verifyComplete();
140+
141+
verify(mockCodecRegistry, only()).addFirst(any(EnumCodec.class));
142+
verify(mockCodecRegistry, never()).addLast(any(EnumCodec.class));
143+
}
144+
116145
@Test
117146
void shouldRegisterCodecAsLast() {
118147
CodecRegistrar codecRegistrar = EnumCodec

0 commit comments

Comments
 (0)