Project

General

Profile

Bug #10352

Fix READ-JSON dataset handle method's behaviour

Added by Paul Bodale 12 months ago. Updated 6 months ago.

Status:
Closed
Priority:
Normal
Assignee:
Paul Bodale
Target version:
-
Start date:
Due date:
% Done:

100%

billable:
No
vendor_id:
GCD
case_num:
version_reported:
version_resolved:
production:
No
env_name:
topics:

ProgressDatasetReadJsonDiagram.jpg (1.03 MB) Paul Bodale, 09/02/2025 04:54 AM


Related issues

Related to Base Language - Bug #10294: ProDataSet BufferImpl.synchronize(DataRelation) ArrayIndexOutOfBoundsException Test
Related to Base Language - Bug #10381: READ-JSON dataset handle method doesn't throw error when parsing a invalid JSON Test
Related to Base Language - Feature #10467: Make READ-JSON dataset handle method able to read json files not containing the name/serialize-name of the dataset New
Related to Base Language - Bug #10519: ProDataset WRITE-XML issue on nested PARENT-ID-RELATION Test
Related to Database - Bug #10560: Wrong buffer picking for ReadXml when seralization name of a buffer correspond to the name of the buffer from dataset Closed

History

#1 Updated by Paul Bodale 12 months ago

During testing a customer's application, it was found that this method does not behave the same as in Progress.

DEFINE TEMP-TABLE ttMain NO-UNDO SERIALIZE-NAME "Main" 
   FIELD field1 AS CHARACTER SERIALIZE-NAME "field1" 
   FIELD field2 AS CHARACTER
   FIELD parentId AS RECID SERIALIZE-HIDDEN.

DEFINE TEMP-TABLE ttSecondary NO-UNDO SERIALIZE-NAME "Secondary" 
   FIELD field1 AS CHARACTER SERIALIZE-NAME "field1" 
   FIELD field2 AS CHARACTER SERIALIZE-NAME "field2" 
   FIELD anotherField3 AS CHARACTER SERIALIZE-NAME "field3" 
   FIELD parentId AS RECID SERIALIZE-HIDDEN.

DEFINE DATASET ds SERIALIZE-HIDDEN FOR ttMain, ttSecondary
   PARENT-ID-RELATION FOR ttMain, ttSecondary PARENT-ID-FIELD parentId.

DEF VAR lcRes AS LONGCHAR VIEW-AS EDITOR SIZE 75 BY 15 LARGE.
DEF VAR content AS CHARACTER NO-UNDO.
lcRes = '~{ "Main" : [ ~{ "Secondary" : [ ~{ "field1" : "value1", "field2" : "value2" ~}, ~{ "field1" : "value3", "field2" : "value4" ~} ] ~} ] ~}'.

//CURRENT-WINDOW:WIDTH-CHARS = 128.
//CURRENT-WINDOW:HEIGHT-CHARS = 32.
//DISPLAY lcRes.

DATASET ds:READ-JSON(STRING(Progress.Reflect.DataType:LONGCHAR), lcRes, "EMPTY").

FOR FIRST ttMain:
   FOR EACH ttSecondary:
      DISPLAY ttSecondary. //Should print something
   END.
END.

I was investigating the reason for which the parentId field of the ttSecondary temp-table record was not updating with the id of the ttMain temp-table record when loading the dataset. For now, the scope for this task is to fix this synchronization issue. But in order to do this, some tweaking needs to be done for the READ-JSON method because for the program above it cannot even load the contents of the lcRes variable into the dataset. This works perfectly fine in progress.

#2 Updated by Paul Bodale 12 months ago

  • Related to Bug #10294: ProDataSet BufferImpl.synchronize(DataRelation) ArrayIndexOutOfBoundsException added

#4 Updated by Paul Bodale 12 months ago

  • Status changed from New to WIP
  • % Done changed from 0 to 30

Constantin, I have a question regarding this method:

com.goldencode.p2j.persist.serial.Importer#childFieldsByChild is a HashMap which is populated in the processHiddenFk method which is called before the actual reading of the dataset.

According to the documentation, a static dataset can define 2 types of relations:

DEFINE {[[ NEW ] SHARED ]|[ PRIVATE | PROTECTED ][ STATIC ]
  [ SERIALIZABLE | NON-SERIALIZABLE ]} DATASET dataset-name 
  [ ... ]
  [ DATA-RELATION [data-rel-name ] FOR data-rel-spec ]...
  [ PARENT-ID-RELATION [data-rel-name] FOR parent-id-rel-spec]...

The data-rel-spec component of the DATA-RELATION phrase can have the FOREIGN-KEY-HIDDEN attribute specified. My understanding is that the setParentValues method provides support for this kind of relations in a dataset. But shouldn't this be done for every foreign key and not just for the ones hidden?

In our current implementation of the static dataset, besides the relations defined using the DATA-RELATION phrase, the PARENT-ID-RELATION type relations are also stored in the relations property. I don't think that there is a problem with that. It just the fact that the correspondent of the foreign keys for temp-tables (I don't know if temp-tables have foreign keys), the fields declared using the PARENT-ID-RELATION are not satisfied.

#5 Updated by Constantin Asofiei 12 months ago

FOREIGN-KEY-HIDDEN means that fk is calculated automatically (i.e. it knows that the parent's field is used as key in the child record, and sets it automatically in the child record's fk field). If this is missing, then that FK must exist in the .xml, right (for read or write)? Otherwise the child would not know how to map the record with its parent.

To avoid confusion, please also do not use the same field names in the parent and child tables. In your example, ttmain.parentid is not even required, as ttsecondary.parentid will match with the PK of the ttmain record.

Otherwise, for PARENT-ID-RELATION, we have a field in the child table which is automatically set to the parent's recid. And it looks that this is done regardless if serialize-hidden is set at this child's field - even if it appears in the json, it is ignored, as it needs to be an actual PK.

It just the fact that the correspondent of the foreign keys for temp-tables (I don't know if temp-tables have foreign keys), the fields declared using the PARENT-ID-RELATION are not satisfied.

What do you mean by this?

#6 Updated by Paul Bodale 12 months ago

Constantin Asofiei wrote:

If this is missing, then that FK must exist in the .xml, right (for read or write)? Otherwise the child would not know how to map the record with its parent.

I have talked to Teodor about this and when parsing an .xml Progress uses its structure to understand how records should be tied together. This means that the FK doesn't need to exist at all. Teodor please correct me if I'm wrong but I think that while reading an .xml file this field is also ignored as it is for .json .

To avoid confusion, please also do not use the same field names in the parent and child tables. In your example, ttmain.parentid is not even required, as ttsecondary.parentid will match with the PK of the ttmain record.

Yes, sorry it was left by mistake.

It just the fact that the correspondent of the foreign keys for temp-tables (I don't know if temp-tables have foreign keys), the fields declared using the PARENT-ID-RELATION are not satisfied.

What do you mean by this?

There are 2 types of relations that can be declared in a dataset, PARENT-ID-RELATION and DATA-RELATION. I found the code above which looks like it updates some fields in the child record based on the parent record but it only does this if the relation type is DATA-RELATION and is declared with FOREIGN-KEY-HIDDEN. I thought that this is meant for id's like in my case and I was wondering why is this done only for the FOREIGN-KEY-HIDDEN ones.

I've analyzed the method a little better now and it looks like it's not about ids or recids but fields in general.

#7 Updated by Constantin Asofiei 12 months ago

  • Assignee set to Paul Bodale
Paul, the problems are multiple:
  • processHiddenFk - here I think all the relation fields need to be registered, regardless of relation type (parent or data-relation), or if 'foreign-key-hidden'. And, for 'parent field',
  • childFieldsByChild has as key the temp-table name, and not the serialization name of the table/buffer. In setParentValues, I think this needs to receive as parameter the buffer, and not the serialized name (i.e. resolve the buffer before setParentValues call)
  • DataRelation.getRelationFields - in case of parent-id-relation, the parent's field is 'none'. This needs to be the PK name (Session.PK)

#8 Updated by Paul Bodale 12 months ago

  • Related to Bug #10381: READ-JSON dataset handle method doesn't throw error when parsing a invalid JSON added

#9 Updated by Paul Bodale 11 months ago

Constantin Asofiei wrote:

Paul, the problems are multiple:
  • childFieldsByChild has as key the temp-table name, and not the serialization name of the table/buffer. In setParentValues, I think this needs to receive as parameter the buffer, and not the serialized name (i.e. resolve the buffer before setParentValues call)

There is a slight problem with this approach. If we have a situation like this:

DEFINE TEMP-TABLE ttMain1 NO-UNDO SERIALIZE-NAME "Main" 
   FIELD field1 AS CHARACTER SERIALIZE-NAME "field1" 
   FIELD field2 AS CHARACTER.
DEFINE TEMP-TABLE ttMain2 NO-UNDO SERIALIZE-NAME "Main" 
   FIELD field1 AS CHARACTER SERIALIZE-NAME "field1" 
   FIELD field2 AS CHARACTER.
DEFINE DATASET ds SERIALIZE-HIDDEN FOR ttMain1, ttMain2, ttSecondary
   PARENT-ID-RELATION FOR ttMain1, ttSecondary PARENT-ID-FIELD parent1Id
   PARENT-ID-RELATION FOR ttMain2, ttSecondary PARENT-ID-FIELD parent2Id.

Then the entry in the childFieldsByChild will get overriden each time and only the last one will remain because the key is the same buffer.

We could go around this by checking if the key exist first and if it is then append to the list but we need to change the structure of the childFieldsByChild's value.

I'm also having trouble understanding the existing implementation. For example, fields that are supposed to be populated from the parent are not initialized in the setParentValues method like the documentation says, but just registered along with their default value. The fields are then set to this value in the fillFromParent method.

Should I replace the implementation and make it more easier to understand?

#10 Updated by Paul Bodale 11 months ago

  • Related to Feature #10467: Make READ-JSON dataset handle method able to read json files not containing the name/serialize-name of the dataset added

#11 Updated by Paul Bodale 11 months ago

  • % Done changed from 30 to 50

Need to log this weird behavior.

If the dataset declares both DATA-RELATION and PARENT-ID-RELATION types then the PARENT-ID-RELATION realtions remain unsatisfied.

For now, I managed to make the implementation take into account the PARENT-ID-RELATION relations but I need to work on another problem. If the dataset contains 2 temp-tables that have the same serialize name, and both of them are the parent in a PARENT-ID-RELATION then both recid field of the child will get updated to the current parent which is not the desired behavior.

#12 Updated by Paul Bodale 11 months ago

  • Status changed from WIP to Internal Test
  • % Done changed from 50 to 100

I created task branch 10352a and committed rev. 16128 that introduces a fix for the PARENT-ID-RELATION relation.

There are a few things to note:
  • The solution was written over a modified version of 9457c branch and depends on some of the changes that were already present in 9457c to be able to work
  • This commit represents a modified version of that fix which is meant to be used as a clean patch for trunk that solves the issue
  • The changes that didn't make it in this commit were left out on purpose to not introduce conflicts when merging the branches with trunk and to not affect the existing functionality

#13 Updated by Paul Bodale 11 months ago

Committed rev. 16130 on branch 10352a that allows the solution to be compatible with java 8.

#14 Updated by Paul Bodale 11 months ago

Committed rev. 16132 on branch 10352a that introduces a check to avoid NPE for cases when the structure of the payload being read is not the one expected.

#15 Updated by Constantin Asofiei 11 months ago

There is another problem; note that the following is happening on an READ-XML - ctx.proxy has no record loaded:

                  try
                  {
                     ctx.parentValues.put(childFields.get(i), column.getGetter().invoke(ctx.proxy));
                  }
                  catch (IllegalAccessException | InvocationTargetException e)
                  {
                     LOG.severe("", e);
                     return false;
                  }

#16 Updated by Paul Bodale 11 months ago

Comitted rev. 16133 on branch 10352a that addresses this issue.

#17 Updated by Stefanel Pezamosca 11 months ago

Hi Paul, I took a look at 10352a by chance.
Please keep Java 8 compatibility in mind for now:
(If you use Intellij you can go to File - Project Structure - Project - Under Project language level, select 8 – Lambdas, type annotations...)

[ant:javac] /home/sp/gcd/branches/10352a/src/com/goldencode/p2j/persist/DataRelation.java:1089: error: cannot find symbol
[ant:javac]       return Arrays.stream(ret).toList();
Also, was the change from String[] to ArrayList necessary?

#18 Updated by Paul Bodale 11 months ago

Stefanel Pezamosca wrote:

Hi Paul, I took a look at 10352a by chance.
Please keep Java 8 compatibility in mind for now:
(If you use Intellij you can go to File - Project Structure - Project - Under Project language level, select 8 – Lambdas, type annotations...)

Thank you, I committed revision 16135 with the fix.

Also, was the change from String[] to ArrayList necessary?

I wouldn't say necessary but it really simplifies the code a lot and it's more convenient especially for the processRelationFlds method of the Importer class. We could go around it but it would introduce unnecessary complexity. Aside from this, the result was also transformed to a List in JsonExport and XmlExport.

#19 Updated by Lorian Sandu 11 months ago

Harness, smoketest and reports passed with 10352a.

#20 Updated by Razvan-Nicolae Chichirau 11 months ago

Smoke-test for 10352a on another customer app showed no regressions.

#21 Updated by Paul Bodale 11 months ago

There could still be work that needs to be done for this task, especially for error/exception handling. I made this diagram to better understand what is the process that progress uses for reading a json:

I don't guarantee that the diagram is complete or even fully correct but from all the tests I ran it's pretty much it. This can be used as reference for future work.

#22 Updated by Teodor Gorghe 11 months ago

  • Related to Bug #10519: ProDataset WRITE-XML issue on nested PARENT-ID-RELATION added

#23 Updated by Constantin Asofiei 10 months ago

  • Related to Bug #10560: Wrong buffer picking for ReadXml when seralization name of a buffer correspond to the name of the buffer from dataset added

#24 Updated by Constantin Asofiei 10 months ago

  • Status changed from Internal Test to WIP
  • % Done changed from 100 to 90

Paul, this code in Importer doesn't look right:

   protected boolean setParentValues(String name)
   {
      ctx.parentValues = new HashMap<>();
      if (ds.getBuffers().isEmpty()) // empty dynamic dataset
      {
         String link = savedCtx.peek().ttName;
         if (link != null)
         {
            ctx.parentValues.putIfAbsent(link + "_id", 
                     ((BufferImpl)savedCtx.peek().tt.defaultBufferHandleNative()).recordID());
         }
         return true;
      }
      List<DataRelation> childRelations = this.ds.getRelations()
                                                 .filter(relation ->
                                                         relation.getChildBuffer()
                                                                 .unwrapBuffer()
                                                                 .getSerializeName()

You are calling getSerializeName, which is valid only for JSON - and this API is used for XML, too. That should use getXmlNodeName. I think this needs to distinguish between JSON and XML processing.

Also, no files has history entry in 10352a.

#25 Updated by Paul Bodale 10 months ago

  • % Done changed from 90 to 100
  • Status changed from WIP to Review

Committed rev. 16136 on branch 10352a that addresses the review above.

#26 Updated by Teodor Gorghe 10 months ago

There is a unit test from customer application, which fails because of a RECID field.
Please see this test case, which reproduces the issue:

Description:
  • before calling WRITE-XML, in the temp-tables, there are 2 records: tt1 "Into the dataset" with RECID 2305 and tt2 "Into the dataset tt2." with RECID 2304, parentID 2305 (I am not using the H2 changes from #10519, but the issue still persists with it).
  • the longchar contents is right:
  • after calling the READ-XML, the temp-table contents are the following: tt1 "Into the dataset" with RECID 2304, tt2 "Into the dataset tt2." with RECID 2304, parentID 2305
  • as you may notice, the parent table RECID is not set.
There are two fixes for this issue and please tell to me which one is better:
  • skipping the parent RECID field on child records.
  • actually setting the RECID field on the parent table.

#27 Updated by Constantin Asofiei 10 months ago

Teodor, please explain more: usually PARENT-ID-RELATION are hidden (thus computed automatically), as the recid is not considered deterministic between multiple imports of the same .xml file. So, what exactly is the problem after calling READ-XML? What does 4GL do? You can try to 'pollute' the temp-table PKs in OpenEdge by creating some records before READ-XML, and see what happens, so the '2305' value from .xml file does not match any real PK in the parent.

#28 Updated by Constantin Asofiei 10 months ago

Paul, one more fix: Importer.processRelationFlds has the same dependency on serializeName; please fix this.

#29 Updated by Teodor Gorghe 10 months ago

Constantin Asofiei wrote:

Teodor, please explain more: usually PARENT-ID-RELATION are hidden (thus computed automatically), as the recid is not considered deterministic between multiple imports of the same .xml file. So, what exactly is the problem after calling READ-XML? What does 4GL do? You can try to 'pollute' the temp-table PKs in OpenEdge by creating some records before READ-XML, and see what happens, so the '2305' value from .xml file does not match any real PK in the parent.

OE just ignores this field at parsing and populates this field with a new RECID value, as you tell, but FWD parses this field and sets this wrong value into the parentId field.

#30 Updated by Constantin Asofiei 10 months ago

Teodor Gorghe wrote:

Constantin Asofiei wrote:

Teodor, please explain more: usually PARENT-ID-RELATION are hidden (thus computed automatically), as the recid is not considered deterministic between multiple imports of the same .xml file. So, what exactly is the problem after calling READ-XML? What does 4GL do? You can try to 'pollute' the temp-table PKs in OpenEdge by creating some records before READ-XML, and see what happens, so the '2305' value from .xml file does not match any real PK in the parent.

OE just ignores this field at parsing and populates this field with a new RECID value, as you tell, but FWD parses this field and sets this wrong value into the parentId field.

This means that the the child part of a PARENT-ID-RELATION is always assumed as 'hidden' and FWD needs to do the same - do not import, calculate automatically.

#31 Updated by Teodor Gorghe 10 months ago

Exactly.
What should I do for this issue? Create a new task/commit here?

#32 Updated by Constantin Asofiei 10 months ago

Commit to 10352a.

#33 Updated by Paul Bodale 10 months ago

Constantin Asofiei wrote:

Paul, one more fix: Importer.processRelationFlds has the same dependency on serializeName; please fix this.

Actually, processRelationFlds is needed only for xml parsing now. I committed rev. 16137 on branch 10352a in which I removed the dependency on serializeName and also moved the processRelationFlds method from Importer to XmlImport as it doesn't make sense for a JsonImport instance to have access it.

Teodor please update the branch before committing your change.

#34 Updated by Teodor Gorghe 10 months ago

Committed revision 16138 on task branch 10352a:
- Skipping relation fields when the parser reaches it. The changes affects READ-XML and READ-JSON.

Constantin, my changes are done, they are ready for review.

#35 Updated by Teodor Gorghe 10 months ago

Committed revision 16139 on task branch 10352a:
- Changed column.getXmlNodeName() with column.getFieldName()

While testing this, I have encountered another issue:
- the Importer.fillFromParent implementation is not right. The getColumn() expects to receive the XML-NODE-NAME or SERIALIZE-NAME, but the field name is used. Having these attributes different than the field name makes the READ-XML and READ-JSON to fail.

java.lang.invoke.WrongMethodTypeException: cannot convert MethodHandle(String)Column to ()Object

#36 Updated by Constantin Asofiei 10 months ago

Teodor, regarding your last note - is that fixed in 10352a?

#37 Updated by Teodor Gorghe 10 months ago

No, it's not.

#38 Updated by Teodor Gorghe 10 months ago

You can reproduce with any prodataset, with an XML-NODE-NAME on a parent id field different than the legacy name.

#39 Updated by Constantin Asofiei 10 months ago

  • Status changed from Review to WIP
  • % Done changed from 100 to 90

Teodor Gorghe wrote:

You can reproduce with any prodataset, with an XML-NODE-NAME on a parent id field different than the legacy name.

Please go ahead and fix it.

#40 Updated by Teodor Gorghe 10 months ago

I don't quite understand. Beside the issue that I have found, there is another issue. I will document it soon, but why SERIALIZE-NAME or XML-NODE-NAME is used on sorting criteria?
I understand that the actual value is statically, provided from the conversion on DATASET definition, and there are some preprocessing (removing dots), but the value for this field is actually the field legacy name and not the XML node name/SERIALIZE-NAME.

Later update:
- I found the reason for this: the name parameter is the localname from XML/JSON.

#41 Updated by Teodor Gorghe 10 months ago

Committed revision 16140 on task branch 10352a:
- Fixed the issue reported on #10352-35.

I will document the issue reported on #10352-39 and come with a fix soon.

#42 Updated by Teodor Gorghe 10 months ago

Committed revision 16141 on task branch 10352a:
- Fixed processNestedRecord when the child record has XML prefix into serialization name.

This commit fixes DATASET:READ-XML when the child temp-table has a namespace prefix into serialization name:

#43 Updated by Teodor Gorghe 10 months ago

  • Status changed from WIP to Review
  • % Done changed from 90 to 100
  • reviewer Constantin Asofiei added

#44 Updated by Constantin Asofiei 9 months ago

Teodor: please rebase 10352a.

#45 Updated by Teodor Gorghe 9 months ago

Rebased 10352a to trunk revision 16250.

#46 Updated by Constantin Asofiei 6 months ago

  • Status changed from Review to Internal Test

#47 Updated by Paul Bodale 6 months ago

Rebased branch 10352a with trunk rev. 16359

#48 Updated by Paul Bodale 6 months ago

Rebased branch 10352a with trunk rev. 16377

#49 Updated by Constantin Asofiei 6 months ago

  • Status changed from Internal Test to Merge Pending

Please merge to trunk now.

#50 Updated by Paul Bodale 6 months ago

  • Status changed from Merge Pending to Test

Branch 10352a was merged into trunk as rev. 16378 and archived.

#51 Updated by Constantin Asofiei 6 months ago

  • Status changed from Test to Closed

Also available in: Atom PDF