#2328 - Postgres JSONB dirty checking - trim json

This commit is contained in:
rbygrave committed 2021-08-24 15:58:29 +12:00
1 parent 3bfb6dbc20
commit 60db0b0999
3 files changed
+65 -1

No files matched your search

@@ -0,0 +1,33 @@
package io.ebeaninternal.server.type;
/**
* Helper that removes whitespace from JSON. Used to normalise Postgres JSONB content.
*/
final class JsonTrim {
/**
* Return JSON with whitespace trimmed.
*/
static String trim(String json) {
if (json == null) {
return null;
}
int len = json.length();
StringBuilder builder = new StringBuilder(len);
boolean escaped = false;
boolean quoted = false;
for (int i = 0; i < len; i++) {
char c = json.charAt(i);
if (c == '\"') {
if (!escaped) quoted = !quoted;
else escaped = false;
} else if (quoted && c == '\\') {
escaped = true;
}
if (quoted || !Character.isWhitespace(c)) {
builder.append(c);
}
}
return builder.toString();
}
}
@@ -39,7 +39,11 @@ final class ScalarTypeJsonObjectMapper {
return new GenericObject(jsonManager, field, dbType, docType);
}
// using the global default MutationDetection mode (defaults to HASH)
prop.setMutationDetection(jsonManager.mutationDetection());
final MutationDetection defaultMode = jsonManager.mutationDetection();
prop.setMutationDetection(defaultMode);
if (MutationDetection.NONE == defaultMode) {
return new NoMutationDetection(jsonManager, field, dbType, docType);
}
return new GenericObject(jsonManager, field, dbType, docType);
}
@@ -68,8 +72,11 @@ final class ScalarTypeJsonObjectMapper {
*/
private static final class GenericObject extends Base<Object> {
private final boolean jsonb;
GenericObject(TypeJsonManager jsonManager, AnnotatedField field, int dbType, DocPropertyType docType) {
super(Object.class, jsonManager, field, dbType, docType);
this.jsonb = "jsonb".equals(pgType);
}
@Override
@@ -80,6 +87,9 @@ final class ScalarTypeJsonObjectMapper {
@Override
public Object read(DataReader reader) throws SQLException {
String json = reader.getString();
if (jsonb) {
json = JsonTrim.trim(json);
}
// pushJson such that we MD5 and store on EntityBeanIntercept later
reader.pushJson(json);
if (json == null || json.isEmpty()) {
@@ -0,0 +1,21 @@
package io.ebeaninternal.server.type;
import org.junit.Test;
import static org.assertj.core.api.Assertions.assertThat;
public class JsonTrimTest {
@Test
public void trim_array() {
final String trimmed = JsonTrim.trim("[{\"name\": \"one\", \"along\": 1, \"timestamp\": 1629609021559}, {\"name\": \"two\", \"along\": 2, \"timestamp\": 1629609021559}]");
assertThat(trimmed).isEqualTo("[{\"name\":\"one\",\"along\":1,\"timestamp\":1629609021559},{\"name\":\"two\",\"along\":2,\"timestamp\":1629609021559}]");
}
@Test
public void trim_object() {
final String trimmed = JsonTrim.trim("{\"name\": \"one\",\t \t \"along\": 1, \"timestamp\": 1629609021559}");
assertThat(trimmed).isEqualTo("{\"name\":\"one\",\"along\":1,\"timestamp\":1629609021559}");
}
}