Details
-
Bug
-
Status: Open
-
Major
-
Resolution: Unresolved
-
None
-
None
-
None
Description
This is true when the PdxInstance contains a data type that is not supported by PdxToJSON (like Date or Character).
If objects like this are stored as PdxInstances:
public class Position { private String id; private Date tradeDate; private Character type; ... }
A query like this is successful:
Executing - query --query='select * from /positions' Result : true Limit : 100 Rows : 10 tradeDate | id | type ------------- | -- | ---- 1624316618413 | 3 | "a" 1624316618324 | 0 | "a" 1624316618418 | 5 | "a" 1624316618421 | 6 | "a" 1624316618407 | 1 | "a" 1624316618426 | 8 | "a" 1624316618428 | 9 | "a" 1624316618415 | 4 | "a" 1624316618423 | 7 | "a" 1624316618410 | 2 | "a"
But a query like this is not:
Executing - query --query="select key,value from /positions.entries where value.id = '0'" Result : false Message : Could not create JSON document from PdxInstance
It fails with this exception in the server:
org.apache.geode.pdx.JSONFormatterException: Could not create JSON document from PdxInstance at org.apache.geode.pdx.JSONFormatter.fromPdxInstance(JSONFormatter.java:241) at org.apache.geode.pdx.JSONFormatter.toJSON(JSONFormatter.java:226) at org.apache.geode.management.internal.cli.domain.DataCommandResult$SelectResultRow.valueToJson(DataCommandResult.java:732) at org.apache.geode.management.internal.cli.domain.DataCommandResult$SelectResultRow.resolveStructToColumns(DataCommandResult.java:717) at org.apache.geode.management.internal.cli.domain.DataCommandResult$SelectResultRow.resolveObjectToColumns(DataCommandResult.java:692) at org.apache.geode.management.internal.cli.domain.DataCommandResult$SelectResultRow.createColumnValues(DataCommandResult.java:680) at org.apache.geode.management.internal.cli.domain.DataCommandResult$SelectResultRow.<init>(DataCommandResult.java:663) at org.apache.geode.management.internal.cli.functions.DataCommandFunction.createSelectResultRow(DataCommandFunction.java:270) at org.apache.geode.management.internal.cli.functions.DataCommandFunction.select_SelectResults(DataCommandFunction.java:256) at org.apache.geode.management.internal.cli.functions.DataCommandFunction.select(DataCommandFunction.java:224) at org.apache.geode.management.internal.cli.functions.DataCommandFunction.select(DataCommandFunction.java:177) at org.apache.geode.management.internal.cli.functions.DataCommandFunction.execute(DataCommandFunction.java:126) Caused by: java.lang.IllegalStateException: PdxInstance returns unknwon pdxfield tradeDate for type Mon Jun 21 16:03:38 PDT 2021 at org.apache.geode.pdx.internal.json.PdxToJSON.writeValue(PdxToJSON.java:148) at org.apache.geode.pdx.internal.json.PdxToJSON.getJSONString(PdxToJSON.java:185) at org.apache.geode.pdx.internal.json.PdxToJSON.getJSON(PdxToJSON.java:61) at org.apache.geode.pdx.JSONFormatter.fromPdxInstance(JSONFormatter.java:239)
Its because of the difference in processing a PdxInstance (first query) and a Struct (second query) in resolveObjectToColumns:
private void resolveObjectToColumns(Map<String, String> columnData, Object value) { if (value instanceof PdxInstance) { resolvePdxToColumns(columnData, (PdxInstance) value); } else if (value instanceof Struct) { resolveStructToColumns(columnData, (StructImpl) value); } ... }
They both end up in SelectResultRow.valueToJson:
private String valueToJson(Object value) { ... if (value instanceof String) { return (String) value; } if (value instanceof PdxInstance) { return JSONFormatter.toJSON((PdxInstance) value); } ObjectMapper mapper = new ObjectMapper(); try { return mapper.writeValueAsString(value); } catch (JsonProcessingException jex) { return jex.getMessage(); } }
In the PdxInstance case, the fields are passed in individually and handled by the first condition (String) and the ObjectMapper (Date, Character):
SelectResultRow.resolveObjectToColumns value=PDX[13681235,Position]{id=3, tradeDate=Mon Jun 21 16:03:38 PDT 2021, type=a}; valueClass=class org.apache.geode.pdx.internal.PdxInstanceImpl SelectResultRow.valueToJson value=Mon Jun 21 16:03:38 PDT 2021; valueClass=class java.util.Date SelectResultRow.valueToJson value=3; valueClass=class java.lang.String SelectResultRow.valueToJson value=a; valueClass=class java.lang.Character
In the Struct case, the value passed in is a Pdxinstance which is handled by the JSONFormatter:
SelectResultRow.resolveObjectToColumns value=struct(key:0,value:PDX[13681235,Position]{id=0, tradeDate=Mon Jun 21 16:03:38 PDT 2021, type=a}); valueClass=class org.apache.geode.cache.query.internal.StructImpl SelectResultRow.valueToJson value=0; valueClass=class java.lang.String SelectResultRow.valueToJson value=PDX[13681235,Position]{id=0, tradeDate=Mon Jun 21 16:03:38 PDT 2021, type=a}; valueClass=class org.apache.geode.pdx.internal.PdxInstanceImpl
JSONFormatter delegates to PdxToJSON which doesn't understand Characters or Dates and throws the IllegalStateException.
If I change the last else clause in PdxToJSON.writeValue so that it uses ObjectMapper like SelectResultRow.valueToJson does like:
} else { ObjectMapper mapper = new ObjectMapper(); try { jg.writeString(mapper.writeValueAsString(value)); } catch (JsonProcessingException jex) { jg.writeString(jex.getMessage()); } }
The query is successful:
Executing - query --query="select key,value from /positions.entries where value.id = '0'" Result : true Limit : 100 Rows : 1 key | value --- | ----------------------------------------------------- 0 | {"tradeDate":"1624317757557","id":"0","type":"\"a\""}
I'm not sure if this is a valid fix, but it will handle a lot more cases than PdxToJSON.writeValue does now.