Uploaded image for project: 'Geode'
  1. Geode
  2. GEODE-9392

A gfsh query returning a Struct containing a PdxInstance behaves differently than one returning just the PdxInstance in some cases

    XMLWordPrintableJSON

Details

    • Bug
    • Status: Open
    • Major
    • Resolution: Unresolved
    • None
    • None
    • gfsh
    • 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.

      Attachments

        Activity

          People

            Unassigned Unassigned
            boglesby Barrett Oglesby
            Votes:
            0 Vote for this issue
            Watchers:
            1 Start watching this issue

            Dates

              Created:
              Updated: