Skip to content

Commit 65aa28c

Browse files
JoseLionmp911de
authored andcommitted
Fix hstore decode when payload contains multi-byte characters
[closes pgjdbc#407][pgjdbc#408]
1 parent dd49447 commit 65aa28c

File tree

3 files changed

+28
-22
lines changed

3 files changed

+28
-22
lines changed

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,3 +43,6 @@ hs_err_pid*
4343
.project
4444
.settings
4545
/bin/
46+
47+
# VSCode
48+
.vscode/

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

Lines changed: 22 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@
2525
import reactor.core.publisher.Mono;
2626

2727
import javax.annotation.Nullable;
28+
29+
import java.nio.charset.StandardCharsets;
2830
import java.util.LinkedHashMap;
2931
import java.util.Map;
3032

@@ -52,10 +54,10 @@ public boolean process(byte value) {
5254

5355
}
5456

57+
private static final Class<Map> TYPE = Map.class;
58+
5559
private final ByteBufAllocator byteBufAllocator;
5660

57-
private final Class<Map> type = Map.class;
58-
5961
private final int oid;
6062

6163
/**
@@ -73,23 +75,21 @@ public boolean canDecode(int dataType, Format format, Class<?> type) {
7375
Assert.requireNonNull(format, "format must not be null");
7476
Assert.requireNonNull(type, "type must not be null");
7577

76-
Assert.requireNonNull(type, "type must not be null");
77-
78-
return dataType == this.oid && type.isAssignableFrom(this.type);
78+
return dataType == this.oid && type.isAssignableFrom(TYPE);
7979
}
8080

8181
@Override
8282
public boolean canEncode(Object value) {
8383
Assert.requireNonNull(value, "value must not be null");
8484

85-
return this.type.isInstance(value);
85+
return TYPE.isInstance(value);
8686
}
8787

8888
@Override
8989
public boolean canEncodeNull(Class<?> type) {
9090
Assert.requireNonNull(type, "type must not be null");
9191

92-
return this.type.isAssignableFrom(type);
92+
return TYPE.isAssignableFrom(type);
9393
}
9494

9595
@Override
@@ -126,10 +126,9 @@ private static Map<String, String> decodeBinaryFormat(ByteBuf buffer) {
126126

127127
private static Map<String, String> decodeTextFormat(ByteBuf buffer) {
128128
Map<String, String> map = new LinkedHashMap<>();
129-
StringBuilder mutableBuffer = new StringBuilder();
130129

131130
while (buffer.isReadable()) {
132-
String key = readString(mutableBuffer, buffer);
131+
String key = readString(buffer);
133132
if (key == null) {
134133
break;
135134
}
@@ -140,7 +139,7 @@ private static Map<String, String> decodeTextFormat(ByteBuf buffer) {
140139
value = null;
141140
buffer.skipBytes(4);// skip 'NULL'.
142141
} else {
143-
value = readString(mutableBuffer, buffer);
142+
value = readString(buffer);
144143
}
145144
map.put(key, value);
146145

@@ -163,9 +162,10 @@ private static byte peekByte(ByteBuf buffer) {
163162
}
164163

165164
@Nullable
166-
private static String readString(StringBuilder mutableBuffer, ByteBuf buffer) {
167-
mutableBuffer.setLength(0);
168-
int position = buffer.forEachByte(new IndexOfProcessor((byte) '"'));
165+
private static String readString(ByteBuf buffer) {
166+
final ByteBuf accBuffer = buffer.alloc().buffer();
167+
final int position = buffer.forEachByte(new IndexOfProcessor((byte) '"'));
168+
169169
if (position > buffer.writerIndex()) {
170170
return null;
171171
}
@@ -175,17 +175,18 @@ private static String readString(StringBuilder mutableBuffer, ByteBuf buffer) {
175175
}
176176

177177
while (buffer.isReadable()) {
178-
char c = (char) buffer.readByte();
179-
if (c == '"') {
178+
final byte current = buffer.readByte();
179+
180+
if (current == '"') {
180181
break;
181-
} else if (c == '\\') {
182-
c = (char) buffer.readByte();
183182
}
184-
mutableBuffer.append(c);
183+
184+
accBuffer.writeByte(current == '\\' ? buffer.readByte() : current);
185185
}
186186

187-
String result = mutableBuffer.toString();
188-
mutableBuffer.setLength(0);
187+
final String result = accBuffer.toString(StandardCharsets.UTF_8);
188+
189+
accBuffer.release();
189190
return result;
190191
}
191192

@@ -233,7 +234,7 @@ private EncodedParameter encodeNull(int dataType) {
233234

234235
@Override
235236
public Class<?> type() {
236-
return this.type;
237+
return TYPE;
237238
}
238239

239240
}

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

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,12 +53,14 @@ void constructorNoByteBufAllocator() {
5353
@Test
5454
void decodeAsText() {
5555
ByteBuf buffer = TEST.buffer();
56-
buffer.writeCharSequence("\"b\"=>\"\\\"2.2\", \"a\\\"\"=>\"1\", \"c\"=>NULL", Charset.defaultCharset());
56+
buffer.writeCharSequence("\"b\"=>\"\\\"2.2\", \"a\\\"\"=>\"1\", \"c\"=>NULL, \"d\"=>\"Zoë\", \"é\"=>\"120°\"", Charset.defaultCharset());
5757
Map<String, String> res = new HStoreCodec(TEST, dataType).decode(buffer, dataType, Format.FORMAT_TEXT, Map.class);
5858
Map<String, String> expect = new HashMap<>();
5959
expect.put("a\"", "1");
6060
expect.put("b", "\"2.2");
6161
expect.put("c", null);
62+
expect.put("d", "Zoë");
63+
expect.put("é", "120°");
6264

6365
Assertions.assertThat(res).isEqualTo(expect);
6466
}

0 commit comments

Comments
 (0)